1/*
2 * HTTP address routines for CUPS.
3 *
4 * Copyright 2007-2014 by Apple Inc.
5 * Copyright 1997-2006 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#include <sys/stat.h>
22#ifdef HAVE_RESOLV_H
23#  include <resolv.h>
24#endif /* HAVE_RESOLV_H */
25#ifdef __APPLE__
26#  include <CoreFoundation/CoreFoundation.h>
27#  include <SystemConfiguration/SystemConfiguration.h>
28#endif /* __APPLE__ */
29
30
31/*
32 * 'httpAddrAny()' - Check for the "any" address.
33 *
34 * @since CUPS 1.2/macOS 10.5@
35 */
36
37int					/* O - 1 if "any", 0 otherwise */
38httpAddrAny(const http_addr_t *addr)	/* I - Address to check */
39{
40  if (!addr)
41    return (0);
42
43#ifdef AF_INET6
44  if (addr->addr.sa_family == AF_INET6 &&
45      IN6_IS_ADDR_UNSPECIFIED(&(addr->ipv6.sin6_addr)))
46    return (1);
47#endif /* AF_INET6 */
48
49  if (addr->addr.sa_family == AF_INET &&
50      ntohl(addr->ipv4.sin_addr.s_addr) == 0x00000000)
51    return (1);
52
53  return (0);
54}
55
56
57/*
58 * 'httpAddrClose()' - Close a socket created by @link httpAddrConnect@ or
59 *                     @link httpAddrListen@.
60 *
61 * Pass @code NULL@ for sockets created with @link httpAddrConnect@ and the
62 * listen address for sockets created with @link httpAddrListen@. This will
63 * ensure that domain sockets are removed when closed.
64 *
65 * @since CUPS 2.0/OS 10.10@
66 */
67
68int						/* O - 0 on success, -1 on failure */
69httpAddrClose(http_addr_t *addr,		/* I - Listen address or @code NULL@ */
70              int         fd)			/* I - Socket file descriptor */
71{
72#ifdef WIN32
73  if (closesocket(fd))
74#else
75  if (close(fd))
76#endif /* WIN32 */
77    return (-1);
78
79#ifdef AF_LOCAL
80  if (addr && addr->addr.sa_family == AF_LOCAL)
81    return (unlink(addr->un.sun_path));
82#endif /* AF_LOCAL */
83
84  return (0);
85}
86
87
88/*
89 * 'httpAddrEqual()' - Compare two addresses.
90 *
91 * @since CUPS 1.2/macOS 10.5@
92 */
93
94int						/* O - 1 if equal, 0 if not */
95httpAddrEqual(const http_addr_t *addr1,		/* I - First address */
96              const http_addr_t *addr2)		/* I - Second address */
97{
98  if (!addr1 && !addr2)
99    return (1);
100
101  if (!addr1 || !addr2)
102    return (0);
103
104  if (addr1->addr.sa_family != addr2->addr.sa_family)
105    return (0);
106
107#ifdef AF_LOCAL
108  if (addr1->addr.sa_family == AF_LOCAL)
109    return (!strcmp(addr1->un.sun_path, addr2->un.sun_path));
110#endif /* AF_LOCAL */
111
112#ifdef AF_INET6
113  if (addr1->addr.sa_family == AF_INET6)
114    return (!memcmp(&(addr1->ipv6.sin6_addr), &(addr2->ipv6.sin6_addr), 16));
115#endif /* AF_INET6 */
116
117  return (addr1->ipv4.sin_addr.s_addr == addr2->ipv4.sin_addr.s_addr);
118}
119
120
121/*
122 * 'httpAddrLength()' - Return the length of the address in bytes.
123 *
124 * @since CUPS 1.2/macOS 10.5@
125 */
126
127int					/* O - Length in bytes */
128httpAddrLength(const http_addr_t *addr)	/* I - Address */
129{
130  if (!addr)
131    return (0);
132
133#ifdef AF_INET6
134  if (addr->addr.sa_family == AF_INET6)
135    return (sizeof(addr->ipv6));
136  else
137#endif /* AF_INET6 */
138#ifdef AF_LOCAL
139  if (addr->addr.sa_family == AF_LOCAL)
140    return ((int)(offsetof(struct sockaddr_un, sun_path) + strlen(addr->un.sun_path) + 1));
141  else
142#endif /* AF_LOCAL */
143  if (addr->addr.sa_family == AF_INET)
144    return (sizeof(addr->ipv4));
145  else
146    return (0);
147
148}
149
150
151/*
152 * 'httpAddrListen()' - Create a listening socket bound to the specified
153 *                      address and port.
154 *
155 * @since CUPS 1.7/macOS 10.9@
156 */
157
158int					/* O - Socket or -1 on error */
159httpAddrListen(http_addr_t *addr,	/* I - Address to bind to */
160               int         port)	/* I - Port number to bind to */
161{
162  int		fd = -1,		/* Socket */
163		val,			/* Socket value */
164                status;			/* Bind status */
165
166
167 /*
168  * Range check input...
169  */
170
171  if (!addr || port < 0)
172    return (-1);
173
174 /*
175  * Create the socket and set options...
176  */
177
178  if ((fd = socket(addr->addr.sa_family, SOCK_STREAM, 0)) < 0)
179  {
180    _cupsSetHTTPError(HTTP_STATUS_ERROR);
181    return (-1);
182  }
183
184  val = 1;
185  setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, CUPS_SOCAST &val, sizeof(val));
186
187#ifdef IPV6_V6ONLY
188  if (addr->addr.sa_family == AF_INET6)
189    setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, CUPS_SOCAST &val, sizeof(val));
190#endif /* IPV6_V6ONLY */
191
192 /*
193  * Bind the socket...
194  */
195
196#ifdef AF_LOCAL
197  if (addr->addr.sa_family == AF_LOCAL)
198  {
199    mode_t	mask;			/* Umask setting */
200
201   /*
202    * Remove any existing domain socket file...
203    */
204
205    unlink(addr->un.sun_path);
206
207   /*
208    * Save the current umask and set it to 0 so that all users can access
209    * the domain socket...
210    */
211
212    mask = umask(0);
213
214   /*
215    * Bind the domain socket...
216    */
217
218    status = bind(fd, (struct sockaddr *)addr, (socklen_t)httpAddrLength(addr));
219
220   /*
221    * Restore the umask and fix permissions...
222    */
223
224    umask(mask);
225    chmod(addr->un.sun_path, 0140777);
226  }
227  else
228#endif /* AF_LOCAL */
229  {
230    _httpAddrSetPort(addr, port);
231
232    status = bind(fd, (struct sockaddr *)addr, (socklen_t)httpAddrLength(addr));
233  }
234
235  if (status)
236  {
237    _cupsSetHTTPError(HTTP_STATUS_ERROR);
238
239    close(fd);
240
241    return (-1);
242  }
243
244 /*
245  * Listen...
246  */
247
248  if (listen(fd, 5))
249  {
250    _cupsSetHTTPError(HTTP_STATUS_ERROR);
251
252    close(fd);
253
254    return (-1);
255  }
256
257 /*
258  * Close on exec...
259  */
260
261#ifndef WIN32
262  fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
263#endif /* !WIN32 */
264
265#ifdef SO_NOSIGPIPE
266 /*
267  * Disable SIGPIPE for this socket.
268  */
269
270  val = 1;
271  setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, CUPS_SOCAST &val, sizeof(val));
272#endif /* SO_NOSIGPIPE */
273
274  return (fd);
275}
276
277
278/*
279 * 'httpAddrLocalhost()' - Check for the local loopback address.
280 *
281 * @since CUPS 1.2/macOS 10.5@
282 */
283
284int					/* O - 1 if local host, 0 otherwise */
285httpAddrLocalhost(
286    const http_addr_t *addr)		/* I - Address to check */
287{
288  if (!addr)
289    return (1);
290
291#ifdef AF_INET6
292  if (addr->addr.sa_family == AF_INET6 &&
293      IN6_IS_ADDR_LOOPBACK(&(addr->ipv6.sin6_addr)))
294    return (1);
295#endif /* AF_INET6 */
296
297#ifdef AF_LOCAL
298  if (addr->addr.sa_family == AF_LOCAL)
299    return (1);
300#endif /* AF_LOCAL */
301
302  if (addr->addr.sa_family == AF_INET &&
303      (ntohl(addr->ipv4.sin_addr.s_addr) & 0xff000000) == 0x7f000000)
304    return (1);
305
306  return (0);
307}
308
309
310/*
311 * 'httpAddrLookup()' - Lookup the hostname associated with the address.
312 *
313 * @since CUPS 1.2/macOS 10.5@
314 */
315
316char *					/* O - Host name */
317httpAddrLookup(
318    const http_addr_t *addr,		/* I - Address to lookup */
319    char              *name,		/* I - Host name buffer */
320    int               namelen)		/* I - Size of name buffer */
321{
322  _cups_globals_t	*cg = _cupsGlobals();
323					/* Global data */
324
325
326  DEBUG_printf(("httpAddrLookup(addr=%p, name=%p, namelen=%d)", (void *)addr, (void *)name, namelen));
327
328 /*
329  * Range check input...
330  */
331
332  if (!addr || !name || namelen <= 2)
333  {
334    if (name && namelen >= 1)
335      *name = '\0';
336
337    return (NULL);
338  }
339
340#ifdef AF_LOCAL
341  if (addr->addr.sa_family == AF_LOCAL)
342  {
343    strlcpy(name, addr->un.sun_path, (size_t)namelen);
344    return (name);
345  }
346#endif /* AF_LOCAL */
347
348 /*
349  * Optimize lookups for localhost/loopback addresses...
350  */
351
352  if (httpAddrLocalhost(addr))
353  {
354    strlcpy(name, "localhost", (size_t)namelen);
355    return (name);
356  }
357
358#ifdef HAVE_RES_INIT
359 /*
360  * STR #2920: Initialize resolver after failure in cups-polld
361  *
362  * If the previous lookup failed, re-initialize the resolver to prevent
363  * temporary network errors from persisting.  This *should* be handled by
364  * the resolver libraries, but apparently the glibc folks do not agree.
365  *
366  * We set a flag at the end of this function if we encounter an error that
367  * requires reinitialization of the resolver functions.  We then call
368  * res_init() if the flag is set on the next call here or in httpAddrLookup().
369  */
370
371  if (cg->need_res_init)
372  {
373    res_init();
374
375    cg->need_res_init = 0;
376  }
377#endif /* HAVE_RES_INIT */
378
379#ifdef HAVE_GETNAMEINFO
380  {
381   /*
382    * STR #2486: httpAddrLookup() fails when getnameinfo() returns EAI_AGAIN
383    *
384    * FWIW, I think this is really a bug in the implementation of
385    * getnameinfo(), but falling back on httpAddrString() is easy to
386    * do...
387    */
388
389    int error = getnameinfo(&addr->addr, (socklen_t)httpAddrLength(addr), name, (socklen_t)namelen, NULL, 0, 0);
390
391    if (error)
392    {
393      if (error == EAI_FAIL)
394        cg->need_res_init = 1;
395
396      return (httpAddrString(addr, name, namelen));
397    }
398  }
399#else
400  {
401    struct hostent	*host;			/* Host from name service */
402
403
404#  ifdef AF_INET6
405    if (addr->addr.sa_family == AF_INET6)
406      host = gethostbyaddr((char *)&(addr->ipv6.sin6_addr),
407                	   sizeof(struct in_addr), AF_INET6);
408    else
409#  endif /* AF_INET6 */
410    host = gethostbyaddr((char *)&(addr->ipv4.sin_addr),
411                	 sizeof(struct in_addr), AF_INET);
412
413    if (host == NULL)
414    {
415     /*
416      * No hostname, so return the raw address...
417      */
418
419      if (h_errno == NO_RECOVERY)
420        cg->need_res_init = 1;
421
422      return (httpAddrString(addr, name, namelen));
423    }
424
425    strlcpy(name, host->h_name, (size_t)namelen);
426  }
427#endif /* HAVE_GETNAMEINFO */
428
429  DEBUG_printf(("1httpAddrLookup: returning \"%s\"...", name));
430
431  return (name);
432}
433
434
435/*
436 * 'httpAddrFamily()' - Get the address family of an address.
437 */
438
439int					/* O - Address family */
440httpAddrFamily(http_addr_t *addr)	/* I - Address */
441{
442  if (addr)
443    return (addr->addr.sa_family);
444  else
445    return (0);
446}
447
448
449/*
450 * 'httpAddrPort()' - Get the port number associated with an address.
451 *
452 * @since CUPS 1.7/macOS 10.9@
453 */
454
455int					/* O - Port number */
456httpAddrPort(http_addr_t *addr)		/* I - Address */
457{
458  if (!addr)
459    return (-1);
460#ifdef AF_INET6
461  else if (addr->addr.sa_family == AF_INET6)
462    return (ntohs(addr->ipv6.sin6_port));
463#endif /* AF_INET6 */
464  else if (addr->addr.sa_family == AF_INET)
465    return (ntohs(addr->ipv4.sin_port));
466  else
467    return (0);
468}
469
470
471/*
472 * '_httpAddrSetPort()' - Set the port number associated with an address.
473 */
474
475void
476_httpAddrSetPort(http_addr_t *addr,	/* I - Address */
477                 int         port)	/* I - Port */
478{
479  if (!addr || port <= 0)
480    return;
481
482#ifdef AF_INET6
483  if (addr->addr.sa_family == AF_INET6)
484    addr->ipv6.sin6_port = htons(port);
485  else
486#endif /* AF_INET6 */
487  if (addr->addr.sa_family == AF_INET)
488    addr->ipv4.sin_port = htons(port);
489}
490
491
492/*
493 * 'httpAddrString()' - Convert an address to a numeric string.
494 *
495 * @since CUPS 1.2/macOS 10.5@
496 */
497
498char *					/* O - Numeric address string */
499httpAddrString(const http_addr_t *addr,	/* I - Address to convert */
500               char              *s,	/* I - String buffer */
501	       int               slen)	/* I - Length of string */
502{
503  DEBUG_printf(("httpAddrString(addr=%p, s=%p, slen=%d)", (void *)addr, (void *)s, slen));
504
505 /*
506  * Range check input...
507  */
508
509  if (!addr || !s || slen <= 2)
510  {
511    if (s && slen >= 1)
512      *s = '\0';
513
514    return (NULL);
515  }
516
517#ifdef AF_LOCAL
518  if (addr->addr.sa_family == AF_LOCAL)
519  {
520    if (addr->un.sun_path[0] == '/')
521      strlcpy(s, addr->un.sun_path, (size_t)slen);
522    else
523      strlcpy(s, "localhost", (size_t)slen);
524  }
525  else
526#endif /* AF_LOCAL */
527  if (addr->addr.sa_family == AF_INET)
528  {
529    unsigned temp;			/* Temporary address */
530
531    temp = ntohl(addr->ipv4.sin_addr.s_addr);
532
533    snprintf(s, (size_t)slen, "%d.%d.%d.%d", (temp >> 24) & 255,
534             (temp >> 16) & 255, (temp >> 8) & 255, temp & 255);
535  }
536#ifdef AF_INET6
537  else if (addr->addr.sa_family == AF_INET6)
538  {
539    char	*sptr,			/* Pointer into string */
540		temps[64];		/* Temporary string for address */
541
542#  ifdef HAVE_GETNAMEINFO
543    if (getnameinfo(&addr->addr, (socklen_t)httpAddrLength(addr), temps, sizeof(temps), NULL, 0, NI_NUMERICHOST))
544    {
545     /*
546      * If we get an error back, then the address type is not supported
547      * and we should zero out the buffer...
548      */
549
550      s[0] = '\0';
551
552      return (NULL);
553    }
554    else if ((sptr = strchr(temps, '%')) != NULL)
555    {
556     /*
557      * Convert "%zone" to "+zone" to match URI form...
558      */
559
560      *sptr = '+';
561    }
562
563#  else
564    int		i;			/* Looping var */
565    unsigned	temp;			/* Current value */
566    const char	*prefix;		/* Prefix for address */
567
568
569    prefix = "";
570    for (sptr = temps, i = 0; i < 4 && addr->ipv6.sin6_addr.s6_addr32[i]; i ++)
571    {
572      temp = ntohl(addr->ipv6.sin6_addr.s6_addr32[i]);
573
574      snprintf(sptr, sizeof(temps) - (size_t)(sptr - temps), "%s%x", prefix, (temp >> 16) & 0xffff);
575      prefix = ":";
576      sptr += strlen(sptr);
577
578      temp &= 0xffff;
579
580      if (temp || i == 3 || addr->ipv6.sin6_addr.s6_addr32[i + 1])
581      {
582        snprintf(sptr, sizeof(temps) - (size_t)(sptr - temps), "%s%x", prefix, temp);
583	sptr += strlen(sptr);
584      }
585    }
586
587    if (i < 4)
588    {
589      while (i < 4 && !addr->ipv6.sin6_addr.s6_addr32[i])
590	i ++;
591
592      if (i < 4)
593      {
594        snprintf(sptr, sizeof(temps) - (size_t)(sptr - temps), "%s:", prefix);
595	prefix = ":";
596	sptr += strlen(sptr);
597
598	for (; i < 4; i ++)
599	{
600          temp = ntohl(addr->ipv6.sin6_addr.s6_addr32[i]);
601
602          if ((temp & 0xffff0000) ||
603	      (i > 0 && addr->ipv6.sin6_addr.s6_addr32[i - 1]))
604	  {
605            snprintf(sptr, sizeof(temps) - (size_t)(sptr - temps), "%s%x", prefix, (temp >> 16) & 0xffff);
606	    sptr += strlen(sptr);
607          }
608
609          snprintf(sptr, sizeof(temps) - (size_t)(sptr - temps), "%s%x", prefix, temp & 0xffff);
610	  sptr += strlen(sptr);
611	}
612      }
613      else if (sptr == s)
614      {
615       /*
616        * Empty address...
617	*/
618
619        strlcpy(temps, "::", sizeof(temps));
620      }
621      else
622      {
623       /*
624	* Empty at end...
625	*/
626
627        strlcpy(sptr, "::", sizeof(temps) - (size_t)(sptr - temps));
628      }
629    }
630#  endif /* HAVE_GETNAMEINFO */
631
632   /*
633    * Add "[v1." and "]" around IPv6 address to convert to URI form.
634    */
635
636    snprintf(s, (size_t)slen, "[v1.%s]", temps);
637  }
638#endif /* AF_INET6 */
639  else
640    strlcpy(s, "UNKNOWN", (size_t)slen);
641
642  DEBUG_printf(("1httpAddrString: returning \"%s\"...", s));
643
644  return (s);
645}
646
647
648/*
649 * 'httpGetAddress()' - Get the address of the connected peer of a connection.
650 *
651 * Returns @code NULL@ if the socket is currently unconnected.
652 *
653 * @since CUPS 2.0/OS 10.10@
654 */
655
656http_addr_t *				/* O - Connected address or @code NULL@ */
657httpGetAddress(http_t *http)		/* I - HTTP connection */
658{
659  if (http)
660    return (http->hostaddr);
661  else
662    return (NULL);
663}
664
665
666/*
667 * 'httpGetHostByName()' - Lookup a hostname or IPv4 address, and return
668 *                         address records for the specified name.
669 *
670 * @deprecated@
671 */
672
673struct hostent *			/* O - Host entry */
674httpGetHostByName(const char *name)	/* I - Hostname or IP address */
675{
676  const char		*nameptr;	/* Pointer into name */
677  unsigned		ip[4];		/* IP address components */
678  _cups_globals_t	*cg = _cupsGlobals();
679  					/* Pointer to library globals */
680
681
682  DEBUG_printf(("httpGetHostByName(name=\"%s\")", name));
683
684 /*
685  * Avoid lookup delays and configuration problems when connecting
686  * to the localhost address...
687  */
688
689  if (!strcmp(name, "localhost"))
690    name = "127.0.0.1";
691
692 /*
693  * This function is needed because some operating systems have a
694  * buggy implementation of gethostbyname() that does not support
695  * IP addresses.  If the first character of the name string is a
696  * number, then sscanf() is used to extract the IP components.
697  * We then pack the components into an IPv4 address manually,
698  * since the inet_aton() function is deprecated.  We use the
699  * htonl() macro to get the right byte order for the address.
700  *
701  * We also support domain sockets when supported by the underlying
702  * OS...
703  */
704
705#ifdef AF_LOCAL
706  if (name[0] == '/')
707  {
708   /*
709    * A domain socket address, so make an AF_LOCAL entry and return it...
710    */
711
712    cg->hostent.h_name      = (char *)name;
713    cg->hostent.h_aliases   = NULL;
714    cg->hostent.h_addrtype  = AF_LOCAL;
715    cg->hostent.h_length    = (int)strlen(name) + 1;
716    cg->hostent.h_addr_list = cg->ip_ptrs;
717    cg->ip_ptrs[0]          = (char *)name;
718    cg->ip_ptrs[1]          = NULL;
719
720    DEBUG_puts("1httpGetHostByName: returning domain socket address...");
721
722    return (&cg->hostent);
723  }
724#endif /* AF_LOCAL */
725
726  for (nameptr = name; isdigit(*nameptr & 255) || *nameptr == '.'; nameptr ++);
727
728  if (!*nameptr)
729  {
730   /*
731    * We have an IPv4 address; break it up and provide the host entry
732    * to the caller.
733    */
734
735    if (sscanf(name, "%u.%u.%u.%u", ip, ip + 1, ip + 2, ip + 3) != 4)
736      return (NULL);			/* Must have 4 numbers */
737
738    if (ip[0] > 255 || ip[1] > 255 || ip[2] > 255 || ip[3] > 255)
739      return (NULL);			/* Invalid byte ranges! */
740
741    cg->ip_addr = htonl((((((((unsigned)ip[0] << 8) | (unsigned)ip[1]) << 8) |
742                           (unsigned)ip[2]) << 8) |
743                         (unsigned)ip[3]));
744
745   /*
746    * Fill in the host entry and return it...
747    */
748
749    cg->hostent.h_name      = (char *)name;
750    cg->hostent.h_aliases   = NULL;
751    cg->hostent.h_addrtype  = AF_INET;
752    cg->hostent.h_length    = 4;
753    cg->hostent.h_addr_list = cg->ip_ptrs;
754    cg->ip_ptrs[0]          = (char *)&(cg->ip_addr);
755    cg->ip_ptrs[1]          = NULL;
756
757    DEBUG_puts("1httpGetHostByName: returning IPv4 address...");
758
759    return (&cg->hostent);
760  }
761  else
762  {
763   /*
764    * Use the gethostbyname() function to get the IPv4 address for
765    * the name...
766    */
767
768    DEBUG_puts("1httpGetHostByName: returning domain lookup address(es)...");
769
770    return (gethostbyname(name));
771  }
772}
773
774
775/*
776 * 'httpGetHostname()' - Get the FQDN for the connection or local system.
777 *
778 * When "http" points to a connected socket, return the hostname or
779 * address that was used in the call to httpConnect() or httpConnectEncrypt(),
780 * or the address of the client for the connection from httpAcceptConnection().
781 * Otherwise, return the FQDN for the local system using both gethostname()
782 * and gethostbyname() to get the local hostname with domain.
783 *
784 * @since CUPS 1.2/macOS 10.5@
785 */
786
787const char *				/* O - FQDN for connection or system */
788httpGetHostname(http_t *http,		/* I - HTTP connection or NULL */
789                char   *s,		/* I - String buffer for name */
790                int    slen)		/* I - Size of buffer */
791{
792  if (http)
793  {
794    if (!s || slen <= 1)
795    {
796      if (http->hostname[0] == '/')
797	return ("localhost");
798      else
799	return (http->hostname);
800    }
801    else if (http->hostname[0] == '/')
802      strlcpy(s, "localhost", (size_t)slen);
803    else
804      strlcpy(s, http->hostname, (size_t)slen);
805  }
806  else
807  {
808   /*
809    * Get the hostname...
810    */
811
812    if (!s || slen <= 1)
813      return (NULL);
814
815    if (gethostname(s, (size_t)slen) < 0)
816      strlcpy(s, "localhost", (size_t)slen);
817
818    if (!strchr(s, '.'))
819    {
820#ifdef HAVE_SCDYNAMICSTORECOPYCOMPUTERNAME
821     /*
822      * The hostname is not a FQDN, so use the local hostname from the
823      * SystemConfiguration framework...
824      */
825
826      SCDynamicStoreRef	sc = SCDynamicStoreCreate(kCFAllocatorDefault,
827                                                  CFSTR("libcups"), NULL, NULL);
828					/* System configuration data */
829      CFStringRef	local = sc ? SCDynamicStoreCopyLocalHostName(sc) : NULL;
830					/* Local host name */
831      char		localStr[1024];	/* Local host name C string */
832
833      if (local && CFStringGetCString(local, localStr, sizeof(localStr),
834                                      kCFStringEncodingUTF8))
835      {
836       /*
837        * Append ".local." to the hostname we get...
838	*/
839
840        snprintf(s, (size_t)slen, "%s.local.", localStr);
841      }
842
843      if (local)
844        CFRelease(local);
845      if (sc)
846        CFRelease(sc);
847
848#else
849     /*
850      * The hostname is not a FQDN, so look it up...
851      */
852
853      struct hostent	*host;		/* Host entry to get FQDN */
854
855      if ((host = gethostbyname(s)) != NULL && host->h_name)
856      {
857       /*
858        * Use the resolved hostname...
859	*/
860
861	strlcpy(s, host->h_name, (size_t)slen);
862      }
863#endif /* HAVE_SCDYNAMICSTORECOPYCOMPUTERNAME */
864    }
865
866   /*
867    * Make sure .local hostnames end with a period...
868    */
869
870    if (strlen(s) > 6 && !strcmp(s + strlen(s) - 6, ".local"))
871      strlcat(s, ".", (size_t)slen);
872  }
873
874 /*
875  * Convert the hostname to lowercase as needed...
876  */
877
878  if (s[0] != '/')
879  {
880    char	*ptr;			/* Pointer into string */
881
882    for (ptr = s; *ptr; ptr ++)
883      *ptr = (char)_cups_tolower((int)*ptr);
884  }
885
886 /*
887  * Return the hostname with as much domain info as we have...
888  */
889
890  return (s);
891}
892
893
894/*
895 * 'httpResolveHostname()' - Resolve the hostname of the HTTP connection
896 *                           address.
897 *
898 * @since CUPS 2.0/OS 10.10@
899 */
900
901const char *				/* O - Resolved hostname or @code NULL@ */
902httpResolveHostname(http_t *http,	/* I - HTTP connection */
903                    char   *buffer,	/* I - Hostname buffer */
904                    size_t bufsize)	/* I - Size of buffer */
905{
906  if (!http)
907    return (NULL);
908
909  if (isdigit(http->hostname[0] & 255) || http->hostname[0] == '[')
910  {
911    char	temp[1024];		/* Temporary string */
912
913    if (httpAddrLookup(http->hostaddr, temp, sizeof(temp)))
914      strlcpy(http->hostname, temp, sizeof(http->hostname));
915    else
916      return (NULL);
917  }
918
919  if (buffer)
920  {
921    if (http->hostname[0] == '/')
922      strlcpy(buffer, "localhost", bufsize);
923    else
924      strlcpy(buffer, http->hostname, bufsize);
925
926    return (buffer);
927  }
928  else if (http->hostname[0] == '/')
929    return ("localhost");
930  else
931    return (http->hostname);
932}
933