Merge changes from CUPS 1.4svn-r7874.
[thirdparty/cups.git] / cups / http-addr.c
1 /*
2  * "$Id: http-addr.c 6814 2007-08-20 20:09:25Z mike $"
3  *
4  *   HTTP address routines for the Common UNIX Printing System (CUPS).
5  *
6  *   Copyright 2007-2008 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  *   httpAddrString()    - Convert an IP address to a dotted string.
23  *   httpGetHostByName() - Lookup a hostname or IP address, and return
24  *                         address records for the specified name.
25  *   httpGetHostname()   - Get the FQDN for the local system.
26  */
27
28 /*
29  * Include necessary headers...
30  */
31
32 #include "globals.h"
33 #include "debug.h"
34 #include <stdlib.h>
35 #include <stddef.h>
36
37
38 /*
39  * 'httpAddrAny()' - Check for the "any" address.
40  *
41  * @since CUPS 1.2@
42  */
43
44 int                                     /* O - 1 if "any", 0 otherwise */
45 httpAddrAny(const http_addr_t *addr)    /* I - Address to check */
46 {
47   if (!addr)
48     return (0);
49
50 #ifdef AF_INET6
51   if (addr->addr.sa_family == AF_INET6 &&
52       IN6_IS_ADDR_UNSPECIFIED(&(addr->ipv6.sin6_addr)))
53     return (1);
54 #endif /* AF_INET6 */
55
56   if (addr->addr.sa_family == AF_INET &&
57       ntohl(addr->ipv4.sin_addr.s_addr) == 0x00000000)
58     return (1);
59
60   return (0);
61 }
62
63
64 /*
65  * 'httpAddrEqual()' - Compare two addresses.
66  *
67  * @since CUPS 1.2@
68  */
69
70 int                                             /* O - 1 if equal, 0 if not */
71 httpAddrEqual(const http_addr_t *addr1,         /* I - First address */
72               const http_addr_t *addr2)         /* I - Second address */
73 {
74   if (!addr1 && !addr2)
75     return (1);
76
77   if (!addr1 || !addr2)
78     return (0);
79
80   if (addr1->addr.sa_family != addr2->addr.sa_family)
81     return (0);
82
83 #ifdef AF_LOCAL
84   if (addr1->addr.sa_family == AF_LOCAL)
85     return (!strcmp(addr1->un.sun_path, addr2->un.sun_path));
86 #endif /* AF_LOCAL */
87
88 #ifdef AF_INET6
89   if (addr1->addr.sa_family == AF_INET6)
90     return (!memcmp(&(addr1->ipv6.sin6_addr), &(addr2->ipv6.sin6_addr), 16));
91 #endif /* AF_INET6 */
92
93   return (addr1->ipv4.sin_addr.s_addr == addr2->ipv4.sin_addr.s_addr);
94 }
95
96
97 /*
98  * 'httpAddrLength()' - Return the length of the address in bytes.
99  *
100  * @since CUPS 1.2@
101  */
102
103 int                                     /* O - Length in bytes */
104 httpAddrLength(const http_addr_t *addr) /* I - Address */
105 {
106   if (!addr)
107     return (0);
108
109 #ifdef AF_INET6
110   if (addr->addr.sa_family == AF_INET6)
111     return (sizeof(addr->ipv6));
112   else
113 #endif /* AF_INET6 */
114 #ifdef AF_LOCAL
115   if (addr->addr.sa_family == AF_LOCAL)
116     return (offsetof(struct sockaddr_un, sun_path) +
117             strlen(addr->un.sun_path) + 1);
118   else
119 #endif /* AF_LOCAL */
120   if (addr->addr.sa_family == AF_INET)
121     return (sizeof(addr->ipv4));
122   else
123     return (0);
124
125 }
126
127
128 /*
129  * 'httpAddrLocalhost()' - Check for the local loopback address.
130  *
131  * @since CUPS 1.2@
132  */
133
134 int                                     /* O - 1 if local host, 0 otherwise */
135 httpAddrLocalhost(
136     const http_addr_t *addr)            /* I - Address to check */
137 {
138   if (!addr)
139     return (1);
140
141 #ifdef AF_INET6
142   if (addr->addr.sa_family == AF_INET6 &&
143       IN6_IS_ADDR_LOOPBACK(&(addr->ipv6.sin6_addr)))
144     return (1);
145 #endif /* AF_INET6 */
146
147 #ifdef AF_LOCAL
148   if (addr->addr.sa_family == AF_LOCAL)
149     return (1);
150 #endif /* AF_LOCAL */
151
152   if (addr->addr.sa_family == AF_INET &&
153       ntohl(addr->ipv4.sin_addr.s_addr) == 0x7f000001)
154     return (1);
155
156   return (0);
157 }
158
159
160 #ifdef __sgi
161 #  define ADDR_CAST (struct sockaddr *)
162 #else
163 #  define ADDR_CAST (char *)
164 #endif /* __sgi */
165
166
167 /*
168  * 'httpAddrLookup()' - Lookup the hostname associated with the address.
169  *
170  * @since CUPS 1.2@
171  */
172
173 char *                                  /* O - Host name */
174 httpAddrLookup(
175     const http_addr_t *addr,            /* I - Address to lookup */
176     char              *name,            /* I - Host name buffer */
177     int               namelen)          /* I - Size of name buffer */
178 {
179   DEBUG_printf(("httpAddrLookup(addr=%p, name=%p, namelen=%d)\n",
180                 addr, name, namelen));
181
182  /*
183   * Range check input...
184   */
185
186   if (!addr || !name || namelen <= 2)
187   {
188     if (name && namelen >= 1)
189       *name = '\0';
190
191     return (NULL);
192   }
193
194 #ifdef AF_LOCAL
195   if (addr->addr.sa_family == AF_LOCAL)
196     strlcpy(name, addr->un.sun_path, namelen);
197   else
198 #endif /* AF_LOCAL */
199 #ifdef HAVE_GETNAMEINFO
200   {
201    /*
202     * STR #2486: httpAddrLookup() fails when getnameinfo() returns EAI_AGAIN
203     *
204     * FWIW, I think this is really a bug in the implementation of
205     * getnameinfo(), but falling back on httpAddrString() is easy to
206     * do...
207     */
208
209     if (getnameinfo(&addr->addr, httpAddrLength(addr), name, namelen,
210                     NULL, 0, 0))
211       return (httpAddrString(addr, name, namelen));
212   }
213 #else
214   {
215     struct hostent      *host;                  /* Host from name service */
216
217
218 #  ifdef AF_INET6
219     if (addr->addr.sa_family == AF_INET6)
220       host = gethostbyaddr(ADDR_CAST &(addr->ipv6.sin6_addr),
221                            sizeof(struct in_addr), AF_INET6);
222     else
223 #  endif /* AF_INET6 */
224     host = gethostbyaddr(ADDR_CAST &(addr->ipv4.sin_addr),
225                          sizeof(struct in_addr), AF_INET);
226
227     if (host == NULL)
228     {
229      /*
230       * No hostname, so return the raw address...
231       */
232
233       httpAddrString(addr, name, namelen);
234       return (NULL);
235     }
236
237     strlcpy(name, host->h_name, namelen);
238   }
239 #endif /* HAVE_GETNAMEINFO */
240
241   return (name);
242 }
243
244
245 /*
246  * '_httpAddrPort()' - Get the port number associated with an address.
247  */
248
249 int                                     /* O - Port number */
250 _httpAddrPort(http_addr_t *addr)        /* I - Address */
251 {
252   if (!addr)
253     return (ippPort());
254 #ifdef AF_INET6
255   else if (addr->addr.sa_family == AF_INET6)
256     return (ntohs(addr->ipv6.sin6_port));
257 #endif /* AF_INET6 */
258   else if (addr->addr.sa_family == AF_INET)
259     return (ntohs(addr->ipv4.sin_port));
260   else
261     return (ippPort());
262 }
263
264
265 /*
266  * 'httpAddrString()' - Convert an address to a numeric string.
267  *
268  * @since CUPS 1.2@
269  */
270
271 char *                                  /* O - Numeric address string */
272 httpAddrString(const http_addr_t *addr, /* I - Address to convert */
273                char              *s,    /* I - String buffer */
274                int               slen)  /* I - Length of string */
275 {
276   DEBUG_printf(("httpAddrString(addr=%p, s=%p, slen=%d)\n",
277                 addr, s, slen));
278
279  /*
280   * Range check input...
281   */
282
283   if (!addr || !s || slen <= 2)
284   {
285     if (s && slen >= 1)
286       *s = '\0';
287
288     return (NULL);
289   }
290
291 #ifdef AF_LOCAL
292   if (addr->addr.sa_family == AF_LOCAL)
293     strlcpy(s, addr->un.sun_path, slen);
294   else
295 #endif /* AF_LOCAL */
296   if (addr->addr.sa_family == AF_INET)
297   {
298     unsigned temp;                      /* Temporary address */
299
300
301     temp = ntohl(addr->ipv4.sin_addr.s_addr);
302
303     snprintf(s, slen, "%d.%d.%d.%d", (temp >> 24) & 255,
304              (temp >> 16) & 255, (temp >> 8) & 255, temp & 255);
305   }
306 #ifdef AF_INET6
307   else if (addr->addr.sa_family == AF_INET6)
308   {
309 #  ifdef HAVE_GETNAMEINFO
310     if (getnameinfo(&addr->addr, httpAddrLength(addr), s, slen,
311                     NULL, 0, NI_NUMERICHOST))
312     {
313      /*
314       * If we get an error back, then the address type is not supported
315       * and we should zero out the buffer...
316       */
317
318       s[0] = '\0';
319
320       return (NULL);
321     }
322 #  else
323     char        *sptr;                  /* Pointer into string */
324     int         i;                      /* Looping var */
325     unsigned    temp;                   /* Current value */
326     const char  *prefix;                /* Prefix for address */
327
328
329     prefix = "";
330     for (sptr = s, i = 0; i < 4 && addr->ipv6.sin6_addr.s6_addr32[i]; i ++)
331     {
332       temp = ntohl(addr->ipv6.sin6_addr.s6_addr32[i]);
333
334       snprintf(sptr, slen, "%s%x", prefix, (temp >> 16) & 0xffff);
335       prefix = ":";
336       slen -= strlen(sptr);
337       sptr += strlen(sptr);
338
339       temp &= 0xffff;
340
341       if (temp || i == 3 || addr->ipv6.sin6_addr.s6_addr32[i + 1])
342       {
343         snprintf(sptr, slen, "%s%x", prefix, temp);
344         slen -= strlen(sptr);
345         sptr += strlen(sptr);
346       }
347     }
348
349     if (i < 4)
350     {
351       while (i < 4 && !addr->ipv6.sin6_addr.s6_addr32[i])
352         i ++;
353
354       if (i < 4)
355       {
356         snprintf(sptr, slen, "%s:", prefix);
357         prefix = ":";
358         slen -= strlen(sptr);
359         sptr += strlen(sptr);
360
361         for (; i < 4; i ++)
362         {
363           temp = ntohl(addr->ipv6.sin6_addr.s6_addr32[i]);
364
365           if ((temp & 0xffff0000) || addr->ipv6.sin6_addr.s6_addr32[i - 1])
366           {
367             snprintf(sptr, slen, "%s%x", prefix, (temp >> 16) & 0xffff);
368             slen -= strlen(sptr);
369             sptr += strlen(sptr);
370           }
371
372           snprintf(sptr, slen, "%s%x", prefix, temp & 0xffff);
373           slen -= strlen(sptr);
374           sptr += strlen(sptr);
375         }
376       }
377       else if (sptr == s)
378       {
379        /*
380         * Empty address...
381         */
382
383         strlcpy(s, "::", slen);
384         sptr = s + 2;
385         slen -= 2;
386       }
387       else
388       {
389        /*
390         * Empty at end...
391         */
392
393         strlcpy(sptr, "::", slen);
394         sptr += 2;
395         slen -= 2;
396       }
397     }
398 #  endif /* HAVE_GETNAMEINFO */
399   }
400 #endif /* AF_INET6 */
401   else
402     strlcpy(s, "UNKNOWN", slen);
403
404   DEBUG_printf(("httpAddrString: returning \"%s\"...\n", s));
405
406   return (s);
407 }
408
409
410 /*
411  * 'httpGetHostByName()' - Lookup a hostname or IPv4 address, and return
412  *                         address records for the specified name.
413  *
414  * @deprecated@
415  */
416
417 struct hostent *                        /* O - Host entry */
418 httpGetHostByName(const char *name)     /* I - Hostname or IP address */
419 {
420   const char            *nameptr;       /* Pointer into name */
421   unsigned              ip[4];          /* IP address components */
422   _cups_globals_t       *cg = _cupsGlobals();
423                                         /* Pointer to library globals */
424
425
426   DEBUG_printf(("httpGetHostByName(name=\"%s\")\n", name));
427
428  /*
429   * Avoid lookup delays and configuration problems when connecting
430   * to the localhost address...
431   */
432
433   if (!strcmp(name, "localhost"))
434     name = "127.0.0.1";
435
436  /*
437   * This function is needed because some operating systems have a
438   * buggy implementation of gethostbyname() that does not support
439   * IP addresses.  If the first character of the name string is a
440   * number, then sscanf() is used to extract the IP components.
441   * We then pack the components into an IPv4 address manually,
442   * since the inet_aton() function is deprecated.  We use the
443   * htonl() macro to get the right byte order for the address.
444   *
445   * We also support domain sockets when supported by the underlying
446   * OS...
447   */
448
449 #ifdef AF_LOCAL
450   if (name[0] == '/')
451   {
452    /*
453     * A domain socket address, so make an AF_LOCAL entry and return it...
454     */
455
456     cg->hostent.h_name      = (char *)name;
457     cg->hostent.h_aliases   = NULL;
458     cg->hostent.h_addrtype  = AF_LOCAL;
459     cg->hostent.h_length    = strlen(name) + 1;
460     cg->hostent.h_addr_list = cg->ip_ptrs;
461     cg->ip_ptrs[0]          = (char *)name;
462     cg->ip_ptrs[1]          = NULL;
463
464     DEBUG_puts("httpGetHostByName: returning domain socket address...");
465
466     return (&cg->hostent);
467   }
468 #endif /* AF_LOCAL */
469
470   for (nameptr = name; isdigit(*nameptr & 255) || *nameptr == '.'; nameptr ++);
471
472   if (!*nameptr)
473   {
474    /*
475     * We have an IPv4 address; break it up and provide the host entry
476     * to the caller.
477     */
478
479     if (sscanf(name, "%u.%u.%u.%u", ip, ip + 1, ip + 2, ip + 3) != 4)
480       return (NULL);                    /* Must have 4 numbers */
481
482     if (ip[0] > 255 || ip[1] > 255 || ip[2] > 255 || ip[3] > 255)
483       return (NULL);                    /* Invalid byte ranges! */
484
485     cg->ip_addr = htonl(((((((ip[0] << 8) | ip[1]) << 8) | ip[2]) << 8) |
486                          ip[3]));
487
488    /*
489     * Fill in the host entry and return it...
490     */
491
492     cg->hostent.h_name      = (char *)name;
493     cg->hostent.h_aliases   = NULL;
494     cg->hostent.h_addrtype  = AF_INET;
495     cg->hostent.h_length    = 4;
496     cg->hostent.h_addr_list = cg->ip_ptrs;
497     cg->ip_ptrs[0]          = (char *)&(cg->ip_addr);
498     cg->ip_ptrs[1]          = NULL;
499
500     DEBUG_puts("httpGetHostByName: returning IPv4 address...");
501
502     return (&cg->hostent);
503   }
504   else
505   {
506    /*
507     * Use the gethostbyname() function to get the IPv4 address for
508     * the name...
509     */
510
511     DEBUG_puts("httpGetHostByName: returning domain lookup address(es)...");
512
513     return (gethostbyname(name));
514   }
515 }
516
517
518 /*
519  * 'httpGetHostname()' - Get the FQDN for the connection or local system.
520  *
521  * When "http" points to a connected socket, return the hostname or
522  * address that was used in the call to httpConnect() or httpConnectEncrypt().
523  * Otherwise, return the FQDN for the local system using both gethostname()
524  * and gethostbyname() to get the local hostname with domain.
525  *
526  * @since CUPS 1.2@
527  */
528
529 const char *                            /* O - FQDN for connection or system */
530 httpGetHostname(http_t *http,           /* I - HTTP connection or NULL */
531                 char   *s,              /* I - String buffer for name */
532                 int    slen)            /* I - Size of buffer */
533 {
534   struct hostent        *host;          /* Host entry to get FQDN */
535
536
537   if (!s || slen <= 1)
538     return (NULL);
539
540   if (http)
541   {
542     if (http->hostname[0] == '/')
543       strlcpy(s, "localhost", slen);
544     else
545       strlcpy(s, http->hostname, slen);
546   }
547   else
548   {
549    /*
550     * Get the hostname...
551     */
552
553     if (gethostname(s, slen) < 0)
554       strlcpy(s, "localhost", slen);
555
556     if (!strchr(s, '.'))
557     {
558      /*
559       * The hostname is not a FQDN, so look it up...
560       */
561
562       if ((host = gethostbyname(s)) != NULL && host->h_name)
563         strlcpy(s, host->h_name, slen);
564     }
565   }
566
567  /*
568   * Return the hostname with as much domain info as we have...
569   */
570
571   return (s);
572 }
573
574
575 /*
576  * End of "$Id: http-addr.c 6814 2007-08-20 20:09:25Z mike $".
577  */