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