/src/cups/cups/http-addrlist.c
Line | Count | Source |
1 | | /* |
2 | | * HTTP address list routines for CUPS. |
3 | | * |
4 | | * Copyright © 2022-2025 by OpenPrinting. |
5 | | * Copyright © 2007-2021 by Apple Inc. |
6 | | * Copyright © 1997-2007 by Easy Software Products, all rights reserved. |
7 | | * |
8 | | * Licensed under Apache License v2.0. See the file "LICENSE" for more |
9 | | * information. |
10 | | */ |
11 | | |
12 | | #include "cups-private.h" |
13 | | #include "debug-internal.h" |
14 | | #ifdef HAVE_RESOLV_H |
15 | | # include <resolv.h> |
16 | | #endif /* HAVE_RESOLV_H */ |
17 | | #ifndef _WIN32 |
18 | | # include <poll.h> |
19 | | # include <fcntl.h> |
20 | | #endif /* _WIN32 */ |
21 | | |
22 | | |
23 | | /* |
24 | | * 'httpAddrConnect()' - Connect to any of the addresses in the list. |
25 | | * |
26 | | * @since CUPS 1.2@ @exclude all@ |
27 | | */ |
28 | | |
29 | | http_addrlist_t * /* O - Connected address or NULL on failure */ |
30 | | httpAddrConnect( |
31 | | http_addrlist_t *addrlist, /* I - List of potential addresses */ |
32 | | int *sock) /* O - Socket */ |
33 | 0 | { |
34 | 0 | DEBUG_printf("httpAddrConnect(addrlist=%p, sock=%p)", (void *)addrlist, (void *)sock); |
35 | |
|
36 | 0 | return (httpAddrConnect2(addrlist, sock, 30000, NULL)); |
37 | 0 | } |
38 | | |
39 | | |
40 | | /* |
41 | | * 'httpAddrConnect2()' - Connect to any of the addresses in the list with a |
42 | | * timeout and optional cancel. |
43 | | * |
44 | | * @since CUPS 1.7@ |
45 | | */ |
46 | | |
47 | | http_addrlist_t * /* O - Connected address or NULL on failure */ |
48 | | httpAddrConnect2( |
49 | | http_addrlist_t *addrlist, /* I - List of potential addresses */ |
50 | | int *sock, /* O - Socket */ |
51 | | int msec, /* I - Timeout in milliseconds */ |
52 | | int *cancel) /* I - Pointer to "cancel" variable */ |
53 | 0 | { |
54 | 0 | int val; /* Socket option value */ |
55 | 0 | #ifndef _WIN32 |
56 | 0 | int i, j, /* Looping vars */ |
57 | 0 | flags, /* Socket flags */ |
58 | 0 | result; /* Result from select() or poll() */ |
59 | 0 | #endif /* !_WIN32 */ |
60 | 0 | int remaining; /* Remaining timeout */ |
61 | 0 | int nfds, /* Number of file descriptors */ |
62 | 0 | fds[100]; /* Socket file descriptors */ |
63 | 0 | http_addrlist_t *addrs[100]; /* Addresses */ |
64 | 0 | int max_fd = -1; /* Highest file descriptor */ |
65 | 0 | #ifdef O_NONBLOCK |
66 | 0 | struct pollfd pfds[100]; /* Polled file descriptors */ |
67 | 0 | #endif /* O_NONBLOCK */ |
68 | | #ifdef DEBUG |
69 | | # ifndef _WIN32 |
70 | | socklen_t len; /* Length of value */ |
71 | | http_addr_t peer; /* Peer address */ |
72 | | # endif /* !_WIN32 */ |
73 | | char temp[256]; /* Temporary address string */ |
74 | | #endif /* DEBUG */ |
75 | | |
76 | |
|
77 | 0 | DEBUG_printf("httpAddrConnect2(addrlist=%p, sock=%p, msec=%d, cancel=%p)", (void *)addrlist, (void *)sock, msec, (void *)cancel); |
78 | |
|
79 | 0 | if (!sock) |
80 | 0 | { |
81 | 0 | errno = EINVAL; |
82 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0); |
83 | 0 | return (NULL); |
84 | 0 | } |
85 | | |
86 | 0 | if (cancel && *cancel) |
87 | 0 | return (NULL); |
88 | | |
89 | 0 | httpInitialize(); |
90 | |
|
91 | 0 | if (msec <= 0) |
92 | 0 | msec = INT_MAX; |
93 | | |
94 | | /* |
95 | | * Loop through each address until we connect or run out of addresses... |
96 | | */ |
97 | |
|
98 | 0 | nfds = 0; |
99 | 0 | remaining = msec; |
100 | |
|
101 | 0 | while (remaining > 0) |
102 | 0 | { |
103 | 0 | if (cancel && *cancel) |
104 | 0 | { |
105 | 0 | while (nfds > 0) |
106 | 0 | { |
107 | 0 | nfds --; |
108 | 0 | httpAddrClose(NULL, fds[nfds]); |
109 | 0 | } |
110 | |
|
111 | 0 | return (NULL); |
112 | 0 | } |
113 | | |
114 | 0 | if (addrlist && nfds < (int)(sizeof(fds) / sizeof(fds[0]))) |
115 | 0 | { |
116 | | /* |
117 | | * Create the socket... |
118 | | */ |
119 | |
|
120 | 0 | DEBUG_printf("2httpAddrConnect2: Trying %s:%d...", httpAddrString(&(addrlist->addr), temp, sizeof(temp)), httpAddrPort(&(addrlist->addr))); |
121 | |
|
122 | 0 | if ((fds[nfds] = (int)socket(httpAddrFamily(&(addrlist->addr)), SOCK_STREAM, 0)) < 0) |
123 | 0 | { |
124 | | /* |
125 | | * Don't abort yet, as this could just be an issue with the local |
126 | | * system not being configured with IPv4/IPv6/domain socket enabled. |
127 | | * |
128 | | * Just skip this address... |
129 | | */ |
130 | |
|
131 | 0 | addrlist = addrlist->next; |
132 | 0 | continue; |
133 | 0 | } |
134 | | |
135 | | /* |
136 | | * Set options... |
137 | | */ |
138 | | |
139 | 0 | val = 1; |
140 | 0 | setsockopt(fds[nfds], SOL_SOCKET, SO_REUSEADDR, CUPS_SOCAST &val, sizeof(val)); |
141 | |
|
142 | 0 | #ifdef SO_REUSEPORT |
143 | 0 | val = 1; |
144 | 0 | setsockopt(fds[nfds], SOL_SOCKET, SO_REUSEPORT, CUPS_SOCAST &val, sizeof(val)); |
145 | 0 | #endif /* SO_REUSEPORT */ |
146 | |
|
147 | | #ifdef SO_NOSIGPIPE |
148 | | val = 1; |
149 | | setsockopt(fds[nfds], SOL_SOCKET, SO_NOSIGPIPE, CUPS_SOCAST &val, sizeof(val)); |
150 | | #endif /* SO_NOSIGPIPE */ |
151 | | |
152 | | /* |
153 | | * Using TCP_NODELAY improves responsiveness, especially on systems |
154 | | * with a slow loopback interface... |
155 | | */ |
156 | |
|
157 | 0 | val = 1; |
158 | 0 | setsockopt(fds[nfds], IPPROTO_TCP, TCP_NODELAY, CUPS_SOCAST &val, sizeof(val)); |
159 | |
|
160 | 0 | #ifdef FD_CLOEXEC |
161 | | /* |
162 | | * Close this socket when starting another process... |
163 | | */ |
164 | |
|
165 | 0 | fcntl(fds[nfds], F_SETFD, FD_CLOEXEC); |
166 | 0 | #endif /* FD_CLOEXEC */ |
167 | |
|
168 | 0 | #ifdef O_NONBLOCK |
169 | | /* |
170 | | * Do an asynchronous connect by setting the socket non-blocking... |
171 | | */ |
172 | |
|
173 | 0 | DEBUG_printf("httpAddrConnect2: Setting non-blocking connect()"); |
174 | |
|
175 | 0 | flags = fcntl(fds[nfds], F_GETFL, 0); |
176 | 0 | fcntl(fds[nfds], F_SETFL, flags | O_NONBLOCK); |
177 | 0 | #endif /* O_NONBLOCK */ |
178 | | |
179 | | /* |
180 | | * Then connect... |
181 | | */ |
182 | |
|
183 | 0 | if (!connect(fds[nfds], &(addrlist->addr.addr), (socklen_t)httpAddrLength(&(addrlist->addr)))) |
184 | 0 | { |
185 | 0 | DEBUG_printf("1httpAddrConnect2: Connected to %s:%d...", httpAddrString(&(addrlist->addr), temp, sizeof(temp)), httpAddrPort(&(addrlist->addr))); |
186 | |
|
187 | 0 | #ifdef O_NONBLOCK |
188 | 0 | fcntl(fds[nfds], F_SETFL, flags); |
189 | 0 | #endif /* O_NONBLOCK */ |
190 | |
|
191 | 0 | *sock = fds[nfds]; |
192 | |
|
193 | 0 | while (nfds > 0) |
194 | 0 | { |
195 | 0 | nfds --; |
196 | 0 | httpAddrClose(NULL, fds[nfds]); |
197 | 0 | } |
198 | |
|
199 | 0 | return (addrlist); |
200 | 0 | } |
201 | | |
202 | | #ifdef _WIN32 |
203 | | if (WSAGetLastError() != WSAEINPROGRESS && WSAGetLastError() != WSAEWOULDBLOCK) |
204 | | #else |
205 | 0 | if (errno != EINPROGRESS && errno != EWOULDBLOCK) |
206 | 0 | #endif /* _WIN32 */ |
207 | 0 | { |
208 | 0 | DEBUG_printf("1httpAddrConnect2: Unable to connect to %s:%d: %s", httpAddrString(&(addrlist->addr), temp, sizeof(temp)), httpAddrPort(&(addrlist->addr)), strerror(errno)); |
209 | 0 | httpAddrClose(NULL, fds[nfds]); |
210 | 0 | addrlist = addrlist->next; |
211 | 0 | continue; |
212 | 0 | } |
213 | | |
214 | 0 | #ifndef _WIN32 |
215 | 0 | fcntl(fds[nfds], F_SETFL, flags); |
216 | 0 | #endif /* !_WIN32 */ |
217 | |
|
218 | 0 | if (fds[nfds] > max_fd) |
219 | 0 | max_fd = fds[nfds]; |
220 | |
|
221 | 0 | addrs[nfds] = addrlist; |
222 | 0 | nfds ++; |
223 | 0 | addrlist = addrlist->next; |
224 | 0 | } |
225 | | |
226 | 0 | if (!addrlist && nfds == 0) |
227 | 0 | { |
228 | | #ifdef _WIN32 |
229 | | errno = WSAEHOSTDOWN; |
230 | | #else |
231 | 0 | errno = EHOSTDOWN; |
232 | 0 | #endif // _WIN32 |
233 | 0 | break; |
234 | 0 | } |
235 | | |
236 | | /* |
237 | | * See if we can connect to any of the addresses so far... |
238 | | */ |
239 | | |
240 | 0 | #ifdef O_NONBLOCK |
241 | 0 | DEBUG_puts("1httpAddrConnect2: Finishing async connect()"); |
242 | |
|
243 | 0 | do |
244 | 0 | { |
245 | 0 | if (cancel && *cancel) |
246 | 0 | { |
247 | | /* |
248 | | * Close this socket and return... |
249 | | */ |
250 | |
|
251 | 0 | DEBUG_puts("1httpAddrConnect2: Canceled connect()"); |
252 | |
|
253 | 0 | while (nfds > 0) |
254 | 0 | { |
255 | 0 | nfds --; |
256 | 0 | httpAddrClose(NULL, fds[nfds]); |
257 | 0 | } |
258 | |
|
259 | 0 | *sock = -1; |
260 | |
|
261 | 0 | return (NULL); |
262 | 0 | } |
263 | | |
264 | 0 | for (i = 0; i < nfds; i ++) |
265 | 0 | { |
266 | 0 | pfds[i].fd = fds[i]; |
267 | 0 | pfds[i].events = POLLIN | POLLOUT; |
268 | 0 | } |
269 | |
|
270 | 0 | result = poll(pfds, (nfds_t)nfds, addrlist ? 100 : remaining > 250 ? 250 : remaining); |
271 | |
|
272 | 0 | DEBUG_printf("1httpAddrConnect2: poll() returned %d (%d)", result, errno); |
273 | 0 | } |
274 | | # ifdef _WIN32 |
275 | | while (result < 0 && (WSAGetLastError() == WSAEINTR || WSAGetLastError() == WSAEWOULDBLOCK)); |
276 | | # else |
277 | 0 | while (result < 0 && (errno == EINTR || errno == EAGAIN)); |
278 | 0 | # endif /* _WIN32 */ |
279 | | |
280 | 0 | if (result > 0) |
281 | 0 | { |
282 | 0 | http_addrlist_t *connaddr = NULL; /* Connected address, if any */ |
283 | |
|
284 | 0 | for (i = 0; i < nfds; i ++) |
285 | 0 | { |
286 | 0 | DEBUG_printf("pfds[%d].revents=%x\n", i, pfds[i].revents); |
287 | |
|
288 | | # ifdef _WIN32 |
289 | | if (((WSAGetLastError() == WSAEINPROGRESS) && (pfds[i].revents & POLLIN) && (pfds[i].revents & POLLOUT)) || |
290 | | ((pfds[i].revents & POLLHUP) && (pfds[i].revents & (POLLIN|POLLOUT)))) |
291 | | # else |
292 | 0 | if (((errno == EINPROGRESS) && (pfds[i].revents & POLLIN) && (pfds[i].revents & POLLOUT)) || |
293 | 0 | ((pfds[i].revents & POLLHUP) && (pfds[i].revents & (POLLIN|POLLOUT)))) |
294 | 0 | # endif /* _WIN32 */ |
295 | 0 | { |
296 | | // Some systems generate POLLIN or POLLOUT together with POLLHUP when doing |
297 | | // asynchronous connections. The solution seems to be to use getsockopt to |
298 | | // check the SO_ERROR value and ignore the POLLHUP if there is no error or |
299 | | // the error is EINPROGRESS. |
300 | |
|
301 | 0 | int sres, /* Return value from getsockopt() - 0, or -1 if error */ |
302 | 0 | serr; /* Option SO_ERROR value */ |
303 | 0 | socklen_t slen = sizeof(serr); /* Option value size */ |
304 | |
|
305 | 0 | sres = getsockopt(fds[i], SOL_SOCKET, SO_ERROR, &serr, &slen); |
306 | |
|
307 | 0 | if (sres || serr) |
308 | 0 | { |
309 | 0 | pfds[i].revents |= POLLERR; |
310 | | # ifdef DEBUG |
311 | | DEBUG_printf("1httpAddrConnect2: getsockopt returned: %d with error: %s", sres, strerror(serr)); |
312 | | # endif |
313 | 0 | } |
314 | 0 | else if (pfds[i].revents && (pfds[i].revents & POLLHUP) && (pfds[i].revents & (POLLIN | POLLOUT))) |
315 | 0 | { |
316 | 0 | pfds[i].revents &= ~POLLHUP; |
317 | 0 | } |
318 | 0 | } |
319 | |
|
320 | 0 | if (pfds[i].revents && !(pfds[i].revents & (POLLERR | POLLHUP))) |
321 | 0 | { |
322 | 0 | *sock = fds[i]; |
323 | 0 | connaddr = addrs[i]; |
324 | |
|
325 | | # ifdef DEBUG |
326 | | len = sizeof(peer); |
327 | | if (!getpeername(fds[i], (struct sockaddr *)&peer, &len)) |
328 | | DEBUG_printf("1httpAddrConnect2: Connected to %s:%d...", httpAddrString(&peer, temp, sizeof(temp)), httpAddrPort(&peer)); |
329 | | # endif /* DEBUG */ |
330 | |
|
331 | 0 | break; |
332 | 0 | } |
333 | 0 | else if (pfds[i].revents & (POLLERR | POLLHUP)) |
334 | 0 | { |
335 | | /* |
336 | | * Error on socket, remove from the "pool"... |
337 | | */ |
338 | |
|
339 | 0 | httpAddrClose(NULL, fds[i]); |
340 | 0 | nfds --; |
341 | 0 | if (i < nfds) |
342 | 0 | { |
343 | 0 | memmove(fds + i, fds + i + 1, (size_t)(nfds - i) * (sizeof(fds[0]))); |
344 | 0 | memmove(addrs + i, addrs + i + 1, (size_t)(nfds - i) * (sizeof(addrs[0]))); |
345 | 0 | } |
346 | 0 | i --; |
347 | 0 | } |
348 | 0 | } |
349 | |
|
350 | 0 | if (connaddr) |
351 | 0 | { |
352 | | /* |
353 | | * Connected on one address, close all of the other sockets we have so |
354 | | * far and return... |
355 | | */ |
356 | |
|
357 | 0 | for (j = 0; j < i; j ++) |
358 | 0 | httpAddrClose(NULL, fds[j]); |
359 | |
|
360 | 0 | for (j ++; j < nfds; j ++) |
361 | 0 | httpAddrClose(NULL, fds[j]); |
362 | |
|
363 | 0 | return (connaddr); |
364 | 0 | } |
365 | 0 | } |
366 | 0 | #endif /* O_NONBLOCK */ |
367 | | |
368 | 0 | if (addrlist) |
369 | 0 | remaining -= 100; |
370 | 0 | else |
371 | 0 | remaining -= 250; |
372 | 0 | } |
373 | | |
374 | 0 | if (remaining <= 0) |
375 | 0 | errno = ETIMEDOUT; |
376 | |
|
377 | 0 | while (nfds > 0) |
378 | 0 | { |
379 | 0 | nfds --; |
380 | 0 | httpAddrClose(NULL, fds[nfds]); |
381 | 0 | } |
382 | |
|
383 | | #ifdef _WIN32 |
384 | | _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE, "Connection failed", 0); |
385 | | #else |
386 | 0 | _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE, strerror(errno), 0); |
387 | 0 | #endif /* _WIN32 */ |
388 | |
|
389 | 0 | return (NULL); |
390 | 0 | } |
391 | | |
392 | | |
393 | | /* |
394 | | * 'httpAddrCopyList()' - Copy an address list. |
395 | | * |
396 | | * @since CUPS 1.7@ |
397 | | */ |
398 | | |
399 | | http_addrlist_t * /* O - New address list or @code NULL@ on error */ |
400 | | httpAddrCopyList( |
401 | | http_addrlist_t *src) /* I - Source address list */ |
402 | 0 | { |
403 | 0 | http_addrlist_t *dst = NULL, /* First list entry */ |
404 | 0 | *prev = NULL, /* Previous list entry */ |
405 | 0 | *current = NULL;/* Current list entry */ |
406 | | |
407 | |
|
408 | 0 | while (src) |
409 | 0 | { |
410 | 0 | if ((current = malloc(sizeof(http_addrlist_t))) == NULL) |
411 | 0 | { |
412 | 0 | current = dst; |
413 | |
|
414 | 0 | while (current) |
415 | 0 | { |
416 | 0 | prev = current; |
417 | 0 | current = current->next; |
418 | |
|
419 | 0 | free(prev); |
420 | 0 | } |
421 | |
|
422 | 0 | return (NULL); |
423 | 0 | } |
424 | | |
425 | 0 | memcpy(current, src, sizeof(http_addrlist_t)); |
426 | |
|
427 | 0 | current->next = NULL; |
428 | |
|
429 | 0 | if (prev) |
430 | 0 | prev->next = current; |
431 | 0 | else |
432 | 0 | dst = current; |
433 | |
|
434 | 0 | prev = current; |
435 | 0 | src = src->next; |
436 | 0 | } |
437 | | |
438 | 0 | return (dst); |
439 | 0 | } |
440 | | |
441 | | |
442 | | /* |
443 | | * 'httpAddrFreeList()' - Free an address list. |
444 | | * |
445 | | * @since CUPS 1.2@ |
446 | | */ |
447 | | |
448 | | void |
449 | | httpAddrFreeList( |
450 | | http_addrlist_t *addrlist) /* I - Address list to free */ |
451 | 0 | { |
452 | 0 | http_addrlist_t *next; /* Next address in list */ |
453 | | |
454 | | |
455 | | /* |
456 | | * Free each address in the list... |
457 | | */ |
458 | |
|
459 | 0 | while (addrlist) |
460 | 0 | { |
461 | 0 | next = addrlist->next; |
462 | |
|
463 | 0 | free(addrlist); |
464 | |
|
465 | 0 | addrlist = next; |
466 | 0 | } |
467 | 0 | } |
468 | | |
469 | | |
470 | | /* |
471 | | * 'httpAddrGetList()' - Get a list of addresses for a hostname. |
472 | | * |
473 | | * @since CUPS 1.2@ |
474 | | */ |
475 | | |
476 | | http_addrlist_t * /* O - List of addresses or NULL */ |
477 | | httpAddrGetList(const char *hostname, /* I - Hostname, IP address, or NULL for passive listen address */ |
478 | | int family, /* I - Address family or AF_UNSPEC */ |
479 | | const char *service) /* I - Service name or port number */ |
480 | 0 | { |
481 | 0 | http_addrlist_t *first, /* First address in list */ |
482 | 0 | *addr, /* Current address in list */ |
483 | 0 | *temp; /* New address */ |
484 | 0 | char ipv6[64], /* IPv6 address */ |
485 | 0 | *ipv6zone; /* Pointer to zone separator */ |
486 | 0 | int ipv6len; /* Length of IPv6 address */ |
487 | 0 | _cups_globals_t *cg = _cupsGlobals(); |
488 | | /* Global data */ |
489 | | |
490 | |
|
491 | | #ifdef DEBUG |
492 | | _cups_debug_printf("httpAddrGetList(hostname=\"%s\", family=AF_%s, " |
493 | | "service=\"%s\")\n", |
494 | | hostname ? hostname : "(nil)", |
495 | | family == AF_UNSPEC ? "UNSPEC" : |
496 | | # ifdef AF_LOCAL |
497 | | family == AF_LOCAL ? "LOCAL" : |
498 | | # endif /* AF_LOCAL */ |
499 | | # ifdef AF_INET6 |
500 | | family == AF_INET6 ? "INET6" : |
501 | | # endif /* AF_INET6 */ |
502 | | family == AF_INET ? "INET" : "???", service); |
503 | | #endif /* DEBUG */ |
504 | |
|
505 | 0 | httpInitialize(); |
506 | |
|
507 | 0 | #ifdef HAVE_RES_INIT |
508 | | /* |
509 | | * STR #2920: Initialize resolver after failure in cups-polld |
510 | | * |
511 | | * If the previous lookup failed, re-initialize the resolver to prevent |
512 | | * temporary network errors from persisting. This *should* be handled by |
513 | | * the resolver libraries, but apparently the glibc folks do not agree. |
514 | | * |
515 | | * We set a flag at the end of this function if we encounter an error that |
516 | | * requires reinitialization of the resolver functions. We then call |
517 | | * res_init() if the flag is set on the next call here or in httpAddrLookup(). |
518 | | */ |
519 | |
|
520 | 0 | if (cg->need_res_init) |
521 | 0 | { |
522 | 0 | res_init(); |
523 | |
|
524 | 0 | cg->need_res_init = 0; |
525 | 0 | } |
526 | 0 | #endif /* HAVE_RES_INIT */ |
527 | | |
528 | | /* |
529 | | * Lookup the address the best way we can... |
530 | | */ |
531 | |
|
532 | 0 | first = addr = NULL; |
533 | |
|
534 | 0 | #ifdef AF_LOCAL |
535 | 0 | if (hostname && hostname[0] == '/') |
536 | 0 | { |
537 | | /* |
538 | | * Domain socket address... |
539 | | */ |
540 | |
|
541 | 0 | if ((first = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t))) != NULL) |
542 | 0 | { |
543 | 0 | addr = first; |
544 | 0 | first->addr.un.sun_family = AF_LOCAL; |
545 | 0 | cupsCopyString(first->addr.un.sun_path, hostname, sizeof(first->addr.un.sun_path)); |
546 | 0 | } |
547 | 0 | } |
548 | 0 | else |
549 | 0 | #endif /* AF_LOCAL */ |
550 | 0 | if (!hostname || _cups_strcasecmp(hostname, "localhost")) |
551 | 0 | { |
552 | 0 | struct addrinfo hints, /* Address lookup hints */ |
553 | 0 | *results, /* Address lookup results */ |
554 | 0 | *current; /* Current result */ |
555 | 0 | int error; /* getaddrinfo() error */ |
556 | | |
557 | | |
558 | | /* |
559 | | * Lookup the address as needed... |
560 | | */ |
561 | |
|
562 | 0 | memset(&hints, 0, sizeof(hints)); |
563 | 0 | hints.ai_family = family; |
564 | 0 | hints.ai_flags = hostname ? 0 : AI_PASSIVE; |
565 | 0 | hints.ai_socktype = SOCK_STREAM; |
566 | |
|
567 | 0 | if (hostname && *hostname == '[') |
568 | 0 | { |
569 | | /* |
570 | | * Remove brackets from numeric IPv6 address... |
571 | | */ |
572 | |
|
573 | 0 | if (!strncmp(hostname, "[v1.", 4)) |
574 | 0 | { |
575 | | /* |
576 | | * Copy the newer address format which supports link-local addresses... |
577 | | */ |
578 | |
|
579 | 0 | cupsCopyString(ipv6, hostname + 4, sizeof(ipv6)); |
580 | 0 | if ((ipv6len = (int)strlen(ipv6) - 1) >= 0 && ipv6[ipv6len] == ']') |
581 | 0 | { |
582 | 0 | ipv6[ipv6len] = '\0'; |
583 | 0 | hostname = ipv6; |
584 | | |
585 | | /* |
586 | | * Convert "+zone" in address to "%zone"... |
587 | | */ |
588 | |
|
589 | 0 | if ((ipv6zone = strrchr(ipv6, '+')) != NULL) |
590 | 0 | *ipv6zone = '%'; |
591 | 0 | } |
592 | 0 | } |
593 | 0 | else |
594 | 0 | { |
595 | | /* |
596 | | * Copy the regular non-link-local IPv6 address... |
597 | | */ |
598 | |
|
599 | 0 | cupsCopyString(ipv6, hostname + 1, sizeof(ipv6)); |
600 | 0 | if ((ipv6len = (int)strlen(ipv6) - 1) >= 0 && ipv6[ipv6len] == ']') |
601 | 0 | { |
602 | 0 | ipv6[ipv6len] = '\0'; |
603 | 0 | hostname = ipv6; |
604 | 0 | } |
605 | 0 | } |
606 | 0 | } |
607 | |
|
608 | 0 | if ((error = getaddrinfo(hostname, service, &hints, &results)) == 0) |
609 | 0 | { |
610 | | /* |
611 | | * Copy the results to our own address list structure... |
612 | | */ |
613 | |
|
614 | 0 | for (current = results; current; current = current->ai_next) |
615 | 0 | if (current->ai_family == AF_INET || current->ai_family == AF_INET6) |
616 | 0 | { |
617 | | /* |
618 | | * Copy the address over... |
619 | | */ |
620 | |
|
621 | 0 | temp = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t)); |
622 | 0 | if (!temp) |
623 | 0 | { |
624 | 0 | httpAddrFreeList(first); |
625 | 0 | freeaddrinfo(results); |
626 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0); |
627 | 0 | return (NULL); |
628 | 0 | } |
629 | | |
630 | 0 | if (current->ai_family == AF_INET6) |
631 | 0 | memcpy(&(temp->addr.ipv6), current->ai_addr, |
632 | 0 | sizeof(temp->addr.ipv6)); |
633 | 0 | else |
634 | 0 | memcpy(&(temp->addr.ipv4), current->ai_addr, |
635 | 0 | sizeof(temp->addr.ipv4)); |
636 | | |
637 | | /* |
638 | | * Append the address to the list... |
639 | | */ |
640 | |
|
641 | 0 | if (!first) |
642 | 0 | first = temp; |
643 | |
|
644 | 0 | if (addr) |
645 | 0 | addr->next = temp; |
646 | |
|
647 | 0 | addr = temp; |
648 | 0 | } |
649 | | |
650 | | /* |
651 | | * Free the results from getaddrinfo()... |
652 | | */ |
653 | | |
654 | 0 | freeaddrinfo(results); |
655 | 0 | } |
656 | 0 | else |
657 | 0 | { |
658 | 0 | if (error == EAI_FAIL) |
659 | 0 | cg->need_res_init = 1; |
660 | |
|
661 | | # ifdef _WIN32 /* Really, Microsoft?!? */ |
662 | | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, gai_strerrorA(error), 0); |
663 | | # else |
664 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, gai_strerror(error), 0); |
665 | 0 | # endif /* _WIN32 */ |
666 | 0 | } |
667 | 0 | } |
668 | | |
669 | | /* |
670 | | * Detect some common errors and handle them sanely... |
671 | | */ |
672 | | |
673 | 0 | if (!addr && (!hostname || !_cups_strcasecmp(hostname, "localhost"))) |
674 | 0 | { |
675 | 0 | struct servent *port; /* Port number for service */ |
676 | 0 | int portnum; /* Port number */ |
677 | | |
678 | | |
679 | | /* |
680 | | * Lookup the service... |
681 | | */ |
682 | |
|
683 | 0 | if (!service) |
684 | 0 | portnum = 0; |
685 | 0 | else if (isdigit(*service & 255)) |
686 | 0 | portnum = atoi(service); |
687 | 0 | else if ((port = getservbyname(service, NULL)) != NULL) |
688 | 0 | portnum = ntohs(port->s_port); |
689 | 0 | else if (!strcmp(service, "http")) |
690 | 0 | portnum = 80; |
691 | 0 | else if (!strcmp(service, "https")) |
692 | 0 | portnum = 443; |
693 | 0 | else if (!strcmp(service, "ipp") || !strcmp(service, "ipps")) |
694 | 0 | portnum = 631; |
695 | 0 | else if (!strcmp(service, "lpd")) |
696 | 0 | portnum = 515; |
697 | 0 | else if (!strcmp(service, "socket")) |
698 | 0 | portnum = 9100; |
699 | 0 | else |
700 | 0 | { |
701 | 0 | httpAddrFreeList(first); |
702 | |
|
703 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unknown service name."), 1); |
704 | 0 | return (NULL); |
705 | 0 | } |
706 | | |
707 | 0 | if (hostname && !_cups_strcasecmp(hostname, "localhost")) |
708 | 0 | { |
709 | | /* |
710 | | * Unfortunately, some users ignore all of the warnings in the |
711 | | * /etc/hosts file and delete "localhost" from it. If we get here |
712 | | * then we were unable to resolve the name, so use the IPv6 and/or |
713 | | * IPv4 loopback interface addresses... |
714 | | */ |
715 | |
|
716 | 0 | #ifdef AF_INET6 |
717 | 0 | if (family != AF_INET) |
718 | 0 | { |
719 | | /* |
720 | | * Add [::1] to the address list... |
721 | | */ |
722 | |
|
723 | 0 | temp = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t)); |
724 | 0 | if (!temp) |
725 | 0 | { |
726 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0); |
727 | 0 | httpAddrFreeList(first); |
728 | 0 | return (NULL); |
729 | 0 | } |
730 | | |
731 | 0 | temp->addr.ipv6.sin6_family = AF_INET6; |
732 | 0 | temp->addr.ipv6.sin6_port = htons(portnum); |
733 | | # ifdef _WIN32 |
734 | | temp->addr.ipv6.sin6_addr.u.Byte[15] = 1; |
735 | | # else |
736 | 0 | temp->addr.ipv6.sin6_addr.s6_addr32[3] = htonl(1); |
737 | 0 | # endif /* _WIN32 */ |
738 | |
|
739 | 0 | if (!first) |
740 | 0 | first = temp; |
741 | |
|
742 | 0 | addr = temp; |
743 | 0 | } |
744 | | |
745 | 0 | if (family != AF_INET6) |
746 | 0 | #endif /* AF_INET6 */ |
747 | 0 | { |
748 | | /* |
749 | | * Add 127.0.0.1 to the address list... |
750 | | */ |
751 | |
|
752 | 0 | temp = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t)); |
753 | 0 | if (!temp) |
754 | 0 | { |
755 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0); |
756 | 0 | httpAddrFreeList(first); |
757 | 0 | return (NULL); |
758 | 0 | } |
759 | | |
760 | 0 | temp->addr.ipv4.sin_family = AF_INET; |
761 | 0 | temp->addr.ipv4.sin_port = htons(portnum); |
762 | 0 | temp->addr.ipv4.sin_addr.s_addr = htonl(0x7f000001); |
763 | |
|
764 | 0 | if (!first) |
765 | 0 | first = temp; |
766 | |
|
767 | 0 | if (addr) |
768 | 0 | addr->next = temp; |
769 | 0 | } |
770 | 0 | } |
771 | 0 | else if (!hostname) |
772 | 0 | { |
773 | | /* |
774 | | * Provide one or more passive listening addresses... |
775 | | */ |
776 | |
|
777 | 0 | #ifdef AF_INET6 |
778 | 0 | if (family != AF_INET) |
779 | 0 | { |
780 | | /* |
781 | | * Add [::] to the address list... |
782 | | */ |
783 | |
|
784 | 0 | temp = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t)); |
785 | 0 | if (!temp) |
786 | 0 | { |
787 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0); |
788 | 0 | httpAddrFreeList(first); |
789 | 0 | return (NULL); |
790 | 0 | } |
791 | | |
792 | 0 | temp->addr.ipv6.sin6_family = AF_INET6; |
793 | 0 | temp->addr.ipv6.sin6_port = htons(portnum); |
794 | |
|
795 | 0 | if (!first) |
796 | 0 | first = temp; |
797 | |
|
798 | 0 | addr = temp; |
799 | 0 | } |
800 | | |
801 | 0 | if (family != AF_INET6) |
802 | 0 | #endif /* AF_INET6 */ |
803 | 0 | { |
804 | | /* |
805 | | * Add 0.0.0.0 to the address list... |
806 | | */ |
807 | |
|
808 | 0 | temp = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t)); |
809 | 0 | if (!temp) |
810 | 0 | { |
811 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0); |
812 | 0 | httpAddrFreeList(first); |
813 | 0 | return (NULL); |
814 | 0 | } |
815 | | |
816 | 0 | temp->addr.ipv4.sin_family = AF_INET; |
817 | 0 | temp->addr.ipv4.sin_port = htons(portnum); |
818 | |
|
819 | 0 | if (!first) |
820 | 0 | first = temp; |
821 | |
|
822 | 0 | if (addr) |
823 | 0 | addr->next = temp; |
824 | 0 | } |
825 | 0 | } |
826 | 0 | } |
827 | | |
828 | | /* |
829 | | * Return the address list... |
830 | | */ |
831 | | |
832 | 0 | return (first); |
833 | 0 | } |