Home | History | Annotate | Download | only in cups
      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 
     38 http_addrlist_t *			/* O - Connected address or NULL on failure */
     39 httpAddrConnect(
     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 
     56 http_addrlist_t *			/* O - Connected address or NULL on failure */
     57 httpAddrConnect2(
     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 
    396 http_addrlist_t	*			/* O - New address list or @code NULL@ on error */
    397 httpAddrCopyList(
    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 
    445 void
    446 httpAddrFreeList(
    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 
    473 http_addrlist_t	*			/* O - List of addresses or NULL */
    474 httpAddrGetList(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