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