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