1/*
2 * "$Id: http-addr.c 11693 2014-03-11 01:24:45Z msweet $"
3 *
4 * HTTP address routines for CUPS.
5 *
6 * Copyright 2007-2013 by Apple Inc.
7 * Copyright 1997-2006 by Easy Software Products, all rights reserved.
8 *
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file.  If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
14 *
15 * This file is subject to the Apple OS-Developed Software exception.
16 */
17
18/*
19 * Include necessary headers...
20 */
21
22#include "cups-private.h"
23#ifdef HAVE_RESOLV_H
24#  include <resolv.h>
25#endif /* HAVE_RESOLV_H */
26#ifdef __APPLE__
27#  include <CoreFoundation/CoreFoundation.h>
28#  include <SystemConfiguration/SystemConfiguration.h>
29#endif /* __APPLE__ */
30
31
32/*
33 * 'httpAddrAny()' - Check for the "any" address.
34 *
35 * @since CUPS 1.2/OS X 10.5@
36 */
37
38int					/* O - 1 if "any", 0 otherwise */
39httpAddrAny(const http_addr_t *addr)	/* I - Address to check */
40{
41  if (!addr)
42    return (0);
43
44#ifdef AF_INET6
45  if (addr->addr.sa_family == AF_INET6 &&
46      IN6_IS_ADDR_UNSPECIFIED(&(addr->ipv6.sin6_addr)))
47    return (1);
48#endif /* AF_INET6 */
49
50  if (addr->addr.sa_family == AF_INET &&
51      ntohl(addr->ipv4.sin_addr.s_addr) == 0x00000000)
52    return (1);
53
54  return (0);
55}
56
57
58/*
59 * 'httpAddrEqual()' - Compare two addresses.
60 *
61 * @since CUPS 1.2/OS X 10.5@
62 */
63
64int						/* O - 1 if equal, 0 if not */
65httpAddrEqual(const http_addr_t *addr1,		/* I - First address */
66              const http_addr_t *addr2)		/* I - Second address */
67{
68  if (!addr1 && !addr2)
69    return (1);
70
71  if (!addr1 || !addr2)
72    return (0);
73
74  if (addr1->addr.sa_family != addr2->addr.sa_family)
75    return (0);
76
77#ifdef AF_LOCAL
78  if (addr1->addr.sa_family == AF_LOCAL)
79    return (!strcmp(addr1->un.sun_path, addr2->un.sun_path));
80#endif /* AF_LOCAL */
81
82#ifdef AF_INET6
83  if (addr1->addr.sa_family == AF_INET6)
84    return (!memcmp(&(addr1->ipv6.sin6_addr), &(addr2->ipv6.sin6_addr), 16));
85#endif /* AF_INET6 */
86
87  return (addr1->ipv4.sin_addr.s_addr == addr2->ipv4.sin_addr.s_addr);
88}
89
90
91/*
92 * 'httpAddrLength()' - Return the length of the address in bytes.
93 *
94 * @since CUPS 1.2/OS X 10.5@
95 */
96
97int					/* O - Length in bytes */
98httpAddrLength(const http_addr_t *addr)	/* I - Address */
99{
100  if (!addr)
101    return (0);
102
103#ifdef AF_INET6
104  if (addr->addr.sa_family == AF_INET6)
105    return (sizeof(addr->ipv6));
106  else
107#endif /* AF_INET6 */
108#ifdef AF_LOCAL
109  if (addr->addr.sa_family == AF_LOCAL)
110    return (offsetof(struct sockaddr_un, sun_path) +
111            strlen(addr->un.sun_path) + 1);
112  else
113#endif /* AF_LOCAL */
114  if (addr->addr.sa_family == AF_INET)
115    return (sizeof(addr->ipv4));
116  else
117    return (0);
118
119}
120
121
122/*
123 * 'httpAddrListen()' - Create a listening socket bound to the specified
124 *                      address and port.
125 *
126 * @since CUPS 1.7/OS X 10.9@
127 */
128
129int					/* O - Socket or -1 on error */
130httpAddrListen(http_addr_t *addr,	/* I - Address to bind to */
131               int         port)	/* I - Port number to bind to */
132{
133  int		fd = -1,		/* Socket */
134		val;			/* Socket value */
135
136
137 /*
138  * Range check input...
139  */
140
141  if (!addr || port <= 0)
142    return (-1);
143
144  if ((fd = socket(addr->addr.sa_family, SOCK_STREAM, 0)) < 0)
145  {
146    _cupsSetHTTPError(HTTP_STATUS_ERROR);
147    return (-1);
148  }
149
150  val = 1;
151  setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, CUPS_SOCAST &val, sizeof(val));
152
153#ifdef IPV6_V6ONLY
154  if (addr->addr.sa_family == AF_INET6)
155    setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, CUPS_SOCAST &val, sizeof(val));
156#endif /* IPV6_V6ONLY */
157
158  _httpAddrSetPort(addr, port);
159
160  if (bind(fd, (struct sockaddr *)addr, httpAddrLength(addr)))
161  {
162    _cupsSetHTTPError(HTTP_STATUS_ERROR);
163
164    close(fd);
165
166    return (-1);
167  }
168
169  if (listen(fd, 5))
170  {
171    _cupsSetHTTPError(HTTP_STATUS_ERROR);
172
173    close(fd);
174
175    return (-1);
176  }
177
178#ifdef SO_NOSIGPIPE
179 /*
180  * Disable SIGPIPE for this socket.
181  */
182
183  val = 1;
184  setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, CUPS_SOCAST &val, sizeof(val));
185#endif /* SO_NOSIGPIPE */
186
187  return (fd);
188}
189
190
191/*
192 * 'httpAddrLocalhost()' - Check for the local loopback address.
193 *
194 * @since CUPS 1.2/OS X 10.5@
195 */
196
197int					/* O - 1 if local host, 0 otherwise */
198httpAddrLocalhost(
199    const http_addr_t *addr)		/* I - Address to check */
200{
201  if (!addr)
202    return (1);
203
204#ifdef AF_INET6
205  if (addr->addr.sa_family == AF_INET6 &&
206      IN6_IS_ADDR_LOOPBACK(&(addr->ipv6.sin6_addr)))
207    return (1);
208#endif /* AF_INET6 */
209
210#ifdef AF_LOCAL
211  if (addr->addr.sa_family == AF_LOCAL)
212    return (1);
213#endif /* AF_LOCAL */
214
215  if (addr->addr.sa_family == AF_INET &&
216      (ntohl(addr->ipv4.sin_addr.s_addr) & 0xff000000) == 0x7f000000)
217    return (1);
218
219  return (0);
220}
221
222
223/*
224 * 'httpAddrLookup()' - Lookup the hostname associated with the address.
225 *
226 * @since CUPS 1.2/OS X 10.5@
227 */
228
229char *					/* O - Host name */
230httpAddrLookup(
231    const http_addr_t *addr,		/* I - Address to lookup */
232    char              *name,		/* I - Host name buffer */
233    int               namelen)		/* I - Size of name buffer */
234{
235  _cups_globals_t	*cg = _cupsGlobals();
236					/* Global data */
237
238
239  DEBUG_printf(("httpAddrLookup(addr=%p, name=%p, namelen=%d)", addr, name,
240		namelen));
241
242 /*
243  * Range check input...
244  */
245
246  if (!addr || !name || namelen <= 2)
247  {
248    if (name && namelen >= 1)
249      *name = '\0';
250
251    return (NULL);
252  }
253
254#ifdef AF_LOCAL
255  if (addr->addr.sa_family == AF_LOCAL)
256  {
257    strlcpy(name, addr->un.sun_path, namelen);
258    return (name);
259  }
260#endif /* AF_LOCAL */
261
262 /*
263  * Optimize lookups for localhost/loopback addresses...
264  */
265
266  if (httpAddrLocalhost(addr))
267  {
268    strlcpy(name, "localhost", namelen);
269    return (name);
270  }
271
272#ifdef HAVE_RES_INIT
273 /*
274  * STR #2920: Initialize resolver after failure in cups-polld
275  *
276  * If the previous lookup failed, re-initialize the resolver to prevent
277  * temporary network errors from persisting.  This *should* be handled by
278  * the resolver libraries, but apparently the glibc folks do not agree.
279  *
280  * We set a flag at the end of this function if we encounter an error that
281  * requires reinitialization of the resolver functions.  We then call
282  * res_init() if the flag is set on the next call here or in httpAddrLookup().
283  */
284
285  if (cg->need_res_init)
286  {
287    res_init();
288
289    cg->need_res_init = 0;
290  }
291#endif /* HAVE_RES_INIT */
292
293#ifdef HAVE_GETNAMEINFO
294  {
295   /*
296    * STR #2486: httpAddrLookup() fails when getnameinfo() returns EAI_AGAIN
297    *
298    * FWIW, I think this is really a bug in the implementation of
299    * getnameinfo(), but falling back on httpAddrString() is easy to
300    * do...
301    */
302
303    int error = getnameinfo(&addr->addr, httpAddrLength(addr), name, namelen,
304		            NULL, 0, 0);
305
306    if (error)
307    {
308      if (error == EAI_FAIL)
309        cg->need_res_init = 1;
310
311      return (httpAddrString(addr, name, namelen));
312    }
313  }
314#else
315  {
316    struct hostent	*host;			/* Host from name service */
317
318
319#  ifdef AF_INET6
320    if (addr->addr.sa_family == AF_INET6)
321      host = gethostbyaddr((char *)&(addr->ipv6.sin6_addr),
322                	   sizeof(struct in_addr), AF_INET6);
323    else
324#  endif /* AF_INET6 */
325    host = gethostbyaddr((char *)&(addr->ipv4.sin_addr),
326                	 sizeof(struct in_addr), AF_INET);
327
328    if (host == NULL)
329    {
330     /*
331      * No hostname, so return the raw address...
332      */
333
334      if (h_errno == NO_RECOVERY)
335        cg->need_res_init = 1;
336
337      return (httpAddrString(addr, name, namelen));
338    }
339
340    strlcpy(name, host->h_name, namelen);
341  }
342#endif /* HAVE_GETNAMEINFO */
343
344  DEBUG_printf(("1httpAddrLookup: returning \"%s\"...", name));
345
346  return (name);
347}
348
349
350/*
351 * 'httpAddrPort()' - Get the port number associated with an address.
352 *
353 * @since CUPS 1.7/OS X 10.9@
354 */
355
356int					/* O - Port number */
357httpAddrPort(http_addr_t *addr)		/* I - Address */
358{
359  if (!addr)
360    return (ippPort());
361#ifdef AF_INET6
362  else if (addr->addr.sa_family == AF_INET6)
363    return (ntohs(addr->ipv6.sin6_port));
364#endif /* AF_INET6 */
365  else if (addr->addr.sa_family == AF_INET)
366    return (ntohs(addr->ipv4.sin_port));
367  else
368    return (ippPort());
369}
370
371/* For OS X 10.8 and earlier */
372int _httpAddrPort(http_addr_t *addr) { return (httpAddrPort(addr)); }
373
374
375/*
376 * '_httpAddrSetPort()' - Set the port number associated with an address.
377 */
378
379void
380_httpAddrSetPort(http_addr_t *addr,	/* I - Address */
381                 int         port)	/* I - Port */
382{
383  if (!addr || port <= 0)
384    return;
385
386#ifdef AF_INET6
387  if (addr->addr.sa_family == AF_INET6)
388    addr->ipv6.sin6_port = htons(port);
389  else
390#endif /* AF_INET6 */
391  if (addr->addr.sa_family == AF_INET)
392    addr->ipv4.sin_port = htons(port);
393}
394
395
396/*
397 * 'httpAddrString()' - Convert an address to a numeric string.
398 *
399 * @since CUPS 1.2/OS X 10.5@
400 */
401
402char *					/* O - Numeric address string */
403httpAddrString(const http_addr_t *addr,	/* I - Address to convert */
404               char              *s,	/* I - String buffer */
405	       int               slen)	/* I - Length of string */
406{
407  DEBUG_printf(("httpAddrString(addr=%p, s=%p, slen=%d)", addr, s, slen));
408
409 /*
410  * Range check input...
411  */
412
413  if (!addr || !s || slen <= 2)
414  {
415    if (s && slen >= 1)
416      *s = '\0';
417
418    return (NULL);
419  }
420
421#ifdef AF_LOCAL
422  if (addr->addr.sa_family == AF_LOCAL)
423  {
424    if (addr->un.sun_path[0] == '/')
425      strlcpy(s, addr->un.sun_path, slen);
426    else
427      strlcpy(s, "localhost", slen);
428  }
429  else
430#endif /* AF_LOCAL */
431  if (addr->addr.sa_family == AF_INET)
432  {
433    unsigned temp;			/* Temporary address */
434
435
436    temp = ntohl(addr->ipv4.sin_addr.s_addr);
437
438    snprintf(s, slen, "%d.%d.%d.%d", (temp >> 24) & 255,
439             (temp >> 16) & 255, (temp >> 8) & 255, temp & 255);
440  }
441#ifdef AF_INET6
442  else if (addr->addr.sa_family == AF_INET6)
443  {
444    char	*sptr,			/* Pointer into string */
445		temps[64];		/* Temporary string for address */
446
447#  ifdef HAVE_GETNAMEINFO
448    if (getnameinfo(&addr->addr, httpAddrLength(addr), temps, sizeof(temps),
449                    NULL, 0, NI_NUMERICHOST))
450    {
451     /*
452      * If we get an error back, then the address type is not supported
453      * and we should zero out the buffer...
454      */
455
456      s[0] = '\0';
457
458      return (NULL);
459    }
460    else if ((sptr = strchr(temps, '%')) != NULL)
461    {
462     /*
463      * Convert "%zone" to "+zone" to match URI form...
464      */
465
466      *sptr = '+';
467    }
468
469#  else
470    int		i;			/* Looping var */
471    unsigned	temp;			/* Current value */
472    const char	*prefix;		/* Prefix for address */
473
474
475    prefix = "";
476    for (sptr = temps, i = 0; i < 4 && addr->ipv6.sin6_addr.s6_addr32[i]; i ++)
477    {
478      temp = ntohl(addr->ipv6.sin6_addr.s6_addr32[i]);
479
480      snprintf(sptr, sizeof(temps) - (sptr - temps), "%s%x", prefix,
481               (temp >> 16) & 0xffff);
482      prefix = ":";
483      sptr += strlen(sptr);
484
485      temp &= 0xffff;
486
487      if (temp || i == 3 || addr->ipv6.sin6_addr.s6_addr32[i + 1])
488      {
489        snprintf(sptr, sizeof(temps) - (sptr - temps), "%s%x", prefix, temp);
490	sptr += strlen(sptr);
491      }
492    }
493
494    if (i < 4)
495    {
496      while (i < 4 && !addr->ipv6.sin6_addr.s6_addr32[i])
497	i ++;
498
499      if (i < 4)
500      {
501        snprintf(sptr, sizeof(temps) - (sptr - temps), "%s:", prefix);
502	prefix = ":";
503	sptr += strlen(sptr);
504
505	for (; i < 4; i ++)
506	{
507          temp = ntohl(addr->ipv6.sin6_addr.s6_addr32[i]);
508
509          if ((temp & 0xffff0000) ||
510	      (i > 0 && addr->ipv6.sin6_addr.s6_addr32[i - 1]))
511	  {
512            snprintf(sptr, sizeof(temps) - (sptr - temps), "%s%x", prefix,
513	             (temp >> 16) & 0xffff);
514	    sptr += strlen(sptr);
515          }
516
517          snprintf(sptr, sizeof(temps) - (sptr - temps), "%s%x", prefix,
518	           temp & 0xffff);
519	  sptr += strlen(sptr);
520	}
521      }
522      else if (sptr == s)
523      {
524       /*
525        * Empty address...
526	*/
527
528        strlcpy(temps, "::", sizeof(temps));
529      }
530      else
531      {
532       /*
533	* Empty at end...
534	*/
535
536        strlcpy(sptr, "::", sizeof(temps) - (sptr - temps));
537      }
538    }
539#  endif /* HAVE_GETNAMEINFO */
540
541   /*
542    * Add "[v1." and "]" around IPv6 address to convert to URI form.
543    */
544
545    snprintf(s, slen, "[v1.%s]", temps);
546  }
547#endif /* AF_INET6 */
548  else
549    strlcpy(s, "UNKNOWN", slen);
550
551  DEBUG_printf(("1httpAddrString: returning \"%s\"...", s));
552
553  return (s);
554}
555
556
557/*
558 * 'httpGetHostByName()' - Lookup a hostname or IPv4 address, and return
559 *                         address records for the specified name.
560 *
561 * @deprecated@
562 */
563
564struct hostent *			/* O - Host entry */
565httpGetHostByName(const char *name)	/* I - Hostname or IP address */
566{
567  const char		*nameptr;	/* Pointer into name */
568  unsigned		ip[4];		/* IP address components */
569  _cups_globals_t	*cg = _cupsGlobals();
570  					/* Pointer to library globals */
571
572
573  DEBUG_printf(("httpGetHostByName(name=\"%s\")", name));
574
575 /*
576  * Avoid lookup delays and configuration problems when connecting
577  * to the localhost address...
578  */
579
580  if (!strcmp(name, "localhost"))
581    name = "127.0.0.1";
582
583 /*
584  * This function is needed because some operating systems have a
585  * buggy implementation of gethostbyname() that does not support
586  * IP addresses.  If the first character of the name string is a
587  * number, then sscanf() is used to extract the IP components.
588  * We then pack the components into an IPv4 address manually,
589  * since the inet_aton() function is deprecated.  We use the
590  * htonl() macro to get the right byte order for the address.
591  *
592  * We also support domain sockets when supported by the underlying
593  * OS...
594  */
595
596#ifdef AF_LOCAL
597  if (name[0] == '/')
598  {
599   /*
600    * A domain socket address, so make an AF_LOCAL entry and return it...
601    */
602
603    cg->hostent.h_name      = (char *)name;
604    cg->hostent.h_aliases   = NULL;
605    cg->hostent.h_addrtype  = AF_LOCAL;
606    cg->hostent.h_length    = strlen(name) + 1;
607    cg->hostent.h_addr_list = cg->ip_ptrs;
608    cg->ip_ptrs[0]          = (char *)name;
609    cg->ip_ptrs[1]          = NULL;
610
611    DEBUG_puts("1httpGetHostByName: returning domain socket address...");
612
613    return (&cg->hostent);
614  }
615#endif /* AF_LOCAL */
616
617  for (nameptr = name; isdigit(*nameptr & 255) || *nameptr == '.'; nameptr ++);
618
619  if (!*nameptr)
620  {
621   /*
622    * We have an IPv4 address; break it up and provide the host entry
623    * to the caller.
624    */
625
626    if (sscanf(name, "%u.%u.%u.%u", ip, ip + 1, ip + 2, ip + 3) != 4)
627      return (NULL);			/* Must have 4 numbers */
628
629    if (ip[0] > 255 || ip[1] > 255 || ip[2] > 255 || ip[3] > 255)
630      return (NULL);			/* Invalid byte ranges! */
631
632    cg->ip_addr = htonl((((((((unsigned)ip[0] << 8) | (unsigned)ip[1]) << 8) |
633                           (unsigned)ip[2]) << 8) |
634                         (unsigned)ip[3]));
635
636   /*
637    * Fill in the host entry and return it...
638    */
639
640    cg->hostent.h_name      = (char *)name;
641    cg->hostent.h_aliases   = NULL;
642    cg->hostent.h_addrtype  = AF_INET;
643    cg->hostent.h_length    = 4;
644    cg->hostent.h_addr_list = cg->ip_ptrs;
645    cg->ip_ptrs[0]          = (char *)&(cg->ip_addr);
646    cg->ip_ptrs[1]          = NULL;
647
648    DEBUG_puts("1httpGetHostByName: returning IPv4 address...");
649
650    return (&cg->hostent);
651  }
652  else
653  {
654   /*
655    * Use the gethostbyname() function to get the IPv4 address for
656    * the name...
657    */
658
659    DEBUG_puts("1httpGetHostByName: returning domain lookup address(es)...");
660
661    return (gethostbyname(name));
662  }
663}
664
665
666/*
667 * 'httpGetHostname()' - Get the FQDN for the connection or local system.
668 *
669 * When "http" points to a connected socket, return the hostname or
670 * address that was used in the call to httpConnect() or httpConnectEncrypt().
671 * Otherwise, return the FQDN for the local system using both gethostname()
672 * and gethostbyname() to get the local hostname with domain.
673 *
674 * @since CUPS 1.2/OS X 10.5@
675 */
676
677const char *				/* O - FQDN for connection or system */
678httpGetHostname(http_t *http,		/* I - HTTP connection or NULL */
679                char   *s,		/* I - String buffer for name */
680                int    slen)		/* I - Size of buffer */
681{
682  if (!s || slen <= 1)
683    return (NULL);
684
685  if (http)
686  {
687    if (http->hostname[0] == '/')
688      strlcpy(s, "localhost", slen);
689    else
690      strlcpy(s, http->hostname, slen);
691  }
692  else
693  {
694   /*
695    * Get the hostname...
696    */
697
698    if (gethostname(s, slen) < 0)
699      strlcpy(s, "localhost", slen);
700
701    if (!strchr(s, '.'))
702    {
703#ifdef HAVE_SCDYNAMICSTORECOPYCOMPUTERNAME
704     /*
705      * The hostname is not a FQDN, so use the local hostname from the
706      * SystemConfiguration framework...
707      */
708
709      SCDynamicStoreRef	sc = SCDynamicStoreCreate(kCFAllocatorDefault,
710                                                  CFSTR("libcups"), NULL, NULL);
711					/* System configuration data */
712      CFStringRef	local = sc ? SCDynamicStoreCopyLocalHostName(sc) : NULL;
713					/* Local host name */
714      char		localStr[1024];	/* Local host name C string */
715
716      if (local && CFStringGetCString(local, localStr, sizeof(localStr),
717                                      kCFStringEncodingUTF8))
718      {
719       /*
720        * Append ".local." to the hostname we get...
721	*/
722
723        snprintf(s, slen, "%s.local.", localStr);
724      }
725
726      if (local)
727        CFRelease(local);
728      if (sc)
729        CFRelease(sc);
730
731#else
732     /*
733      * The hostname is not a FQDN, so look it up...
734      */
735
736      struct hostent	*host;		/* Host entry to get FQDN */
737
738      if ((host = gethostbyname(s)) != NULL && host->h_name)
739      {
740       /*
741        * Use the resolved hostname...
742	*/
743
744	strlcpy(s, host->h_name, slen);
745      }
746#endif /* HAVE_SCDYNAMICSTORECOPYCOMPUTERNAME */
747    }
748  }
749
750 /*
751  * Return the hostname with as much domain info as we have...
752  */
753
754  return (s);
755}
756
757
758/*
759 * End of "$Id: http-addr.c 11693 2014-03-11 01:24:45Z msweet $".
760 */
761