• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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