]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/http-addrlist.c
Merge changes from CUPS 1.4svn-r8606.
[thirdparty/cups.git] / cups / http-addrlist.c
1 /*
2 * "$Id: http-addrlist.c 7910 2008-09-06 00:25:17Z mike $"
3 *
4 * HTTP address list routines for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 2007-2009 by Apple Inc.
7 * Copyright 1997-2007 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 * httpAddrConnect() - Connect to any of the addresses in the list.
18 * httpAddrFreeList() - Free an address list.
19 * httpAddrGetList() - Get a list of addresses for a hostname.
20 */
21
22 /*
23 * Include necessary headers...
24 */
25
26 #include "http-private.h"
27 #include "globals.h"
28 #include "debug.h"
29 #include <stdlib.h>
30 #include <errno.h>
31 #ifdef HAVE_RESOLV_H
32 # include <resolv.h>
33 #endif /* HAVE_RESOLV_H */
34
35
36 /*
37 * 'httpAddrConnect()' - Connect to any of the addresses in the list.
38 *
39 * @since CUPS 1.2/Mac OS X 10.5@
40 */
41
42 http_addrlist_t * /* O - Connected address or NULL on failure */
43 httpAddrConnect(
44 http_addrlist_t *addrlist, /* I - List of potential addresses */
45 int *sock) /* O - Socket */
46 {
47 int val; /* Socket option value */
48 #ifdef DEBUG
49 char temp[256]; /* Temporary address string */
50 #endif /* DEBUG */
51
52
53 DEBUG_printf(("httpAddrConnect(addrlist=%p, sock=%p)", addrlist, sock));
54
55 if (!sock)
56 {
57 errno = EINVAL;
58 return (NULL);
59 }
60
61 /*
62 * Loop through each address until we connect or run out of addresses...
63 */
64
65 while (addrlist)
66 {
67 /*
68 * Create the socket...
69 */
70
71 DEBUG_printf(("2httpAddrConnect: Trying %s:%d...",
72 httpAddrString(&(addrlist->addr), temp, sizeof(temp)),
73 _httpAddrPort(&(addrlist->addr))));
74
75 if ((*sock = (int)socket(addrlist->addr.addr.sa_family, SOCK_STREAM,
76 0)) < 0)
77 {
78 /*
79 * Don't abort yet, as this could just be an issue with the local
80 * system not being configured with IPv4/IPv6/domain socket enabled...
81 */
82
83 addrlist = addrlist->next;
84 continue;
85 }
86
87 /*
88 * Set options...
89 */
90
91 val = 1;
92 #ifdef WIN32
93 setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&val,
94 sizeof(val));
95 #else
96 setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
97 #endif /* WIN32 */
98
99 #ifdef SO_REUSEPORT
100 val = 1;
101 setsockopt(*sock, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val));
102 #endif /* SO_REUSEPORT */
103
104 #ifdef SO_NOSIGPIPE
105 val = 1;
106 setsockopt(*sock, SOL_SOCKET, SO_NOSIGPIPE, &val, sizeof(val));
107 #endif /* SO_NOSIGPIPE */
108
109 /*
110 * Using TCP_NODELAY improves responsiveness, especially on systems
111 * with a slow loopback interface...
112 */
113
114 val = 1;
115 #ifdef WIN32
116 setsockopt(*sock, IPPROTO_TCP, TCP_NODELAY, (const char *)&val,
117 sizeof(val));
118 #else
119 setsockopt(*sock, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
120 #endif /* WIN32 */
121
122 #ifdef FD_CLOEXEC
123 /*
124 * Close this socket when starting another process...
125 */
126
127 fcntl(*sock, F_SETFD, FD_CLOEXEC);
128 #endif /* FD_CLOEXEC */
129
130 /*
131 * Then connect...
132 */
133
134 if (!connect(*sock, &(addrlist->addr.addr),
135 httpAddrLength(&(addrlist->addr))))
136 {
137 DEBUG_printf(("1httpAddrConnect: Connected to %s:%d...",
138 httpAddrString(&(addrlist->addr), temp, sizeof(temp)),
139 _httpAddrPort(&(addrlist->addr))));
140 break;
141 }
142
143 DEBUG_printf(("1httpAddrConnect: Unable to connect to %s:%d: %s",
144 httpAddrString(&(addrlist->addr), temp, sizeof(temp)),
145 _httpAddrPort(&(addrlist->addr)), strerror(errno)));
146
147 /*
148 * Close this socket and move to the next address...
149 */
150
151 #ifdef WIN32
152 closesocket(*sock);
153 #else
154 close(*sock);
155 #endif /* WIN32 */
156
157 *sock = -1;
158 addrlist = addrlist->next;
159 }
160
161 return (addrlist);
162 }
163
164
165 /*
166 * 'httpAddrFreeList()' - Free an address list.
167 *
168 * @since CUPS 1.2/Mac OS X 10.5@
169 */
170
171 void
172 httpAddrFreeList(
173 http_addrlist_t *addrlist) /* I - Address list to free */
174 {
175 http_addrlist_t *next; /* Next address in list */
176
177
178 /*
179 * Free each address in the list...
180 */
181
182 while (addrlist)
183 {
184 next = addrlist->next;
185
186 free(addrlist);
187
188 addrlist = next;
189 }
190 }
191
192
193 /*
194 * 'httpAddrGetList()' - Get a list of addresses for a hostname.
195 *
196 * @since CUPS 1.2/Mac OS X 10.5@
197 */
198
199 http_addrlist_t * /* O - List of addresses or NULL */
200 httpAddrGetList(const char *hostname, /* I - Hostname, IP address, or NULL for passive listen address */
201 int family, /* I - Address family or AF_UNSPEC */
202 const char *service) /* I - Service name or port number */
203 {
204 http_addrlist_t *first, /* First address in list */
205 *addr, /* Current address in list */
206 *temp; /* New address */
207 _cups_globals_t *cg = _cupsGlobals();
208 /* Global data */
209
210
211 #ifdef DEBUG
212 _cups_debug_printf("httpAddrGetList(hostname=\"%s\", family=AF_%s, "
213 "service=\"%s\")\n",
214 hostname ? hostname : "(nil)",
215 family == AF_UNSPEC ? "UNSPEC" :
216 # ifdef AF_LOCAL
217 family == AF_LOCAL ? "LOCAL" :
218 # endif /* AF_LOCAL */
219 # ifdef AF_INET6
220 family == AF_INET6 ? "INET6" :
221 # endif /* AF_INET6 */
222 family == AF_INET ? "INET" : "???", service);
223 #endif /* DEBUG */
224
225 #ifdef HAVE_RES_INIT
226 /*
227 * STR #2920: Initialize resolver after failure in cups-polld
228 *
229 * If the previous lookup failed, re-initialize the resolver to prevent
230 * temporary network errors from persisting. This *should* be handled by
231 * the resolver libraries, but apparently the glibc folks do not agree.
232 *
233 * We set a flag at the end of this function if we encounter an error that
234 * requires reinitialization of the resolver functions. We then call
235 * res_init() if the flag is set on the next call here or in httpAddrLookup().
236 */
237
238 if (cg->need_res_init)
239 {
240 res_init();
241
242 cg->need_res_init = 0;
243 }
244 #endif /* HAVE_RES_INIT */
245
246
247 /*
248 * Lookup the address the best way we can...
249 */
250
251 first = addr = NULL;
252
253 #ifdef AF_LOCAL
254 if (hostname && hostname[0] == '/')
255 {
256 /*
257 * Domain socket address...
258 */
259
260 if ((first = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t))) != NULL)
261 {
262 first->addr.un.sun_family = AF_LOCAL;
263 strlcpy(first->addr.un.sun_path, hostname, sizeof(first->addr.un.sun_path));
264 }
265 }
266 else
267 #endif /* AF_LOCAL */
268 if (!hostname || strcasecmp(hostname, "localhost"))
269 {
270 #ifdef HAVE_GETADDRINFO
271 struct addrinfo hints, /* Address lookup hints */
272 *results, /* Address lookup results */
273 *current; /* Current result */
274 char ipv6[1024], /* IPv6 address */
275 *ipv6zone; /* Pointer to zone separator */
276 int ipv6len; /* Length of IPv6 address */
277 int error; /* getaddrinfo() error */
278
279
280 /*
281 * Lookup the address as needed...
282 */
283
284 memset(&hints, 0, sizeof(hints));
285 hints.ai_family = family;
286 hints.ai_flags = hostname ? 0 : AI_PASSIVE;
287 hints.ai_socktype = SOCK_STREAM;
288
289 if (hostname && *hostname == '[')
290 {
291 /*
292 * Remove brackets from numeric IPv6 address...
293 */
294
295 if (!strncmp(hostname, "[v1.", 4))
296 {
297 /*
298 * Copy the newer address format which supports link-local addresses...
299 */
300
301 strlcpy(ipv6, hostname + 4, sizeof(ipv6));
302 if ((ipv6len = (int)strlen(ipv6) - 1) >= 0 && ipv6[ipv6len] == ']')
303 {
304 ipv6[ipv6len] = '\0';
305 hostname = ipv6;
306
307 /*
308 * Convert "+zone" in address to "%zone"...
309 */
310
311 if ((ipv6zone = strrchr(ipv6, '+')) != NULL)
312 *ipv6zone = '%';
313 }
314 }
315 else
316 {
317 /*
318 * Copy the regular non-link-local IPv6 address...
319 */
320
321 strlcpy(ipv6, hostname + 1, sizeof(ipv6));
322 if ((ipv6len = (int)strlen(ipv6) - 1) >= 0 && ipv6[ipv6len] == ']')
323 {
324 ipv6[ipv6len] = '\0';
325 hostname = ipv6;
326 }
327 }
328 }
329
330 if ((error = getaddrinfo(hostname, service, &hints, &results)) == 0)
331 {
332 /*
333 * Copy the results to our own address list structure...
334 */
335
336 for (current = results; current; current = current->ai_next)
337 if (current->ai_family == AF_INET || current->ai_family == AF_INET6)
338 {
339 /*
340 * Copy the address over...
341 */
342
343 temp = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t));
344 if (!temp)
345 {
346 httpAddrFreeList(first);
347 return (NULL);
348 }
349
350 if (current->ai_family == AF_INET6)
351 memcpy(&(temp->addr.ipv6), current->ai_addr,
352 sizeof(temp->addr.ipv6));
353 else
354 memcpy(&(temp->addr.ipv4), current->ai_addr,
355 sizeof(temp->addr.ipv4));
356
357 /*
358 * Append the address to the list...
359 */
360
361 if (!first)
362 first = temp;
363
364 if (addr)
365 addr->next = temp;
366
367 addr = temp;
368 }
369
370 /*
371 * Free the results from getaddrinfo()...
372 */
373
374 freeaddrinfo(results);
375 }
376 else if (error == EAI_FAIL)
377 cg->need_res_init = 1;
378
379 #else
380 if (hostname)
381 {
382 int i; /* Looping vars */
383 unsigned ip[4]; /* IPv4 address components */
384 const char *ptr; /* Pointer into hostname */
385 struct hostent *host; /* Result of lookup */
386 struct servent *port; /* Port number for service */
387 int portnum; /* Port number */
388
389
390 /*
391 * Lookup the service...
392 */
393
394 if (!service)
395 portnum = 0;
396 else if (isdigit(*service & 255))
397 portnum = atoi(service);
398 else if ((port = getservbyname(service, NULL)) != NULL)
399 portnum = ntohs(port->s_port);
400 else if (!strcmp(service, "http"))
401 portnum = 80;
402 else if (!strcmp(service, "https"))
403 portnum = 443;
404 else if (!strcmp(service, "ipp"))
405 portnum = 631;
406 else if (!strcmp(service, "lpd"))
407 portnum = 515;
408 else if (!strcmp(service, "socket"))
409 portnum = 9100;
410 else
411 return (NULL);
412
413 /*
414 * This code is needed because some operating systems have a
415 * buggy implementation of gethostbyname() that does not support
416 * IPv4 addresses. If the hostname string is an IPv4 address, then
417 * sscanf() is used to extract the IPv4 components. We then pack
418 * the components into an IPv4 address manually, since the
419 * inet_aton() function is deprecated. We use the htonl() macro
420 * to get the right byte order for the address.
421 */
422
423 for (ptr = hostname; isdigit(*ptr & 255) || *ptr == '.'; ptr ++);
424
425 if (!*ptr)
426 {
427 /*
428 * We have an IPv4 address; break it up and create an IPv4 address...
429 */
430
431 if (sscanf(hostname, "%u.%u.%u.%u", ip, ip + 1, ip + 2, ip + 3) == 4 &&
432 ip[0] <= 255 && ip[1] <= 255 && ip[2] <= 255 && ip[3] <= 255)
433 {
434 first = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t));
435 if (!first)
436 return (NULL);
437
438 first->addr.ipv4.sin_family = AF_INET;
439 first->addr.ipv4.sin_addr.s_addr = htonl(((((((ip[0] << 8) |
440 ip[1]) << 8) |
441 ip[2]) << 8) | ip[3]));
442 first->addr.ipv4.sin_port = htons(portnum);
443 }
444 }
445 else if ((host = gethostbyname(hostname)) != NULL &&
446 # ifdef AF_INET6
447 (host->h_addrtype == AF_INET || host->h_addrtype == AF_INET6))
448 # else
449 host->h_addrtype == AF_INET)
450 # endif /* AF_INET6 */
451 {
452 for (i = 0; host->h_addr_list[i]; i ++)
453 {
454 /*
455 * Copy the address over...
456 */
457
458 temp = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t));
459 if (!temp)
460 {
461 httpAddrFreeList(first);
462 return (NULL);
463 }
464
465 # ifdef AF_INET6
466 if (host->h_addrtype == AF_INET6)
467 {
468 temp->addr.ipv6.sin6_family = AF_INET6;
469 memcpy(&(temp->addr.ipv6.sin6_addr), host->h_addr_list[i],
470 sizeof(temp->addr.ipv6));
471 temp->addr.ipv6.sin6_port = htons(portnum);
472 }
473 else
474 # endif /* AF_INET6 */
475 {
476 temp->addr.ipv4.sin_family = AF_INET;
477 memcpy(&(temp->addr.ipv4.sin_addr), host->h_addr_list[i],
478 sizeof(temp->addr.ipv4));
479 temp->addr.ipv4.sin_port = htons(portnum);
480 }
481
482 /*
483 * Append the address to the list...
484 */
485
486 if (!first)
487 first = temp;
488
489 if (addr)
490 addr->next = temp;
491
492 addr = temp;
493 }
494 }
495 else if (h_errno == NO_RECOVERY)
496 cg->need_res_init = 1;
497 }
498 #endif /* HAVE_GETADDRINFO */
499 }
500
501 /*
502 * Detect some common errors and handle them sanely...
503 */
504
505 if (!addr && (!hostname || !strcasecmp(hostname, "localhost")))
506 {
507 struct servent *port; /* Port number for service */
508 int portnum; /* Port number */
509
510
511 /*
512 * Lookup the service...
513 */
514
515 if (!service)
516 portnum = 0;
517 else if (isdigit(*service & 255))
518 portnum = atoi(service);
519 else if ((port = getservbyname(service, NULL)) != NULL)
520 portnum = ntohs(port->s_port);
521 else if (!strcmp(service, "http"))
522 portnum = 80;
523 else if (!strcmp(service, "https"))
524 portnum = 443;
525 else if (!strcmp(service, "ipp"))
526 portnum = 631;
527 else if (!strcmp(service, "lpd"))
528 portnum = 515;
529 else if (!strcmp(service, "socket"))
530 portnum = 9100;
531 else
532 return (NULL);
533
534 if (hostname && !strcasecmp(hostname, "localhost"))
535 {
536 /*
537 * Unfortunately, some users ignore all of the warnings in the
538 * /etc/hosts file and delete "localhost" from it. If we get here
539 * then we were unable to resolve the name, so use the IPv6 and/or
540 * IPv4 loopback interface addresses...
541 */
542
543 #ifdef AF_INET6
544 if (family != AF_INET)
545 {
546 /*
547 * Add [::1] to the address list...
548 */
549
550 temp = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t));
551 if (!temp)
552 {
553 httpAddrFreeList(first);
554 return (NULL);
555 }
556
557 temp->addr.ipv6.sin6_family = AF_INET6;
558 temp->addr.ipv6.sin6_port = htons(portnum);
559 # ifdef WIN32
560 temp->addr.ipv6.sin6_addr.u.Byte[15] = 1;
561 # else
562 temp->addr.ipv6.sin6_addr.s6_addr32[3] = htonl(1);
563 # endif /* WIN32 */
564
565 if (!first)
566 first = temp;
567
568 addr = temp;
569 }
570
571 if (family != AF_INET6)
572 #endif /* AF_INET6 */
573 {
574 /*
575 * Add 127.0.0.1 to the address list...
576 */
577
578 temp = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t));
579 if (!temp)
580 {
581 httpAddrFreeList(first);
582 return (NULL);
583 }
584
585 temp->addr.ipv4.sin_family = AF_INET;
586 temp->addr.ipv4.sin_port = htons(portnum);
587 temp->addr.ipv4.sin_addr.s_addr = htonl(0x7f000001);
588
589 if (!first)
590 first = temp;
591
592 if (addr)
593 addr->next = temp;
594 }
595 }
596 else if (!hostname)
597 {
598 /*
599 * Provide one or more passive listening addresses...
600 */
601
602 #ifdef AF_INET6
603 if (family != AF_INET)
604 {
605 /*
606 * Add [::] to the address list...
607 */
608
609 temp = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t));
610 if (!temp)
611 {
612 httpAddrFreeList(first);
613 return (NULL);
614 }
615
616 temp->addr.ipv6.sin6_family = AF_INET6;
617 temp->addr.ipv6.sin6_port = htons(portnum);
618
619 if (!first)
620 first = temp;
621
622 addr = temp;
623 }
624
625 if (family != AF_INET6)
626 #endif /* AF_INET6 */
627 {
628 /*
629 * Add 0.0.0.0 to the address list...
630 */
631
632 temp = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t));
633 if (!temp)
634 {
635 httpAddrFreeList(first);
636 return (NULL);
637 }
638
639 temp->addr.ipv4.sin_family = AF_INET;
640 temp->addr.ipv4.sin_port = htons(portnum);
641
642 if (!first)
643 first = temp;
644
645 if (addr)
646 addr->next = temp;
647 }
648 }
649 }
650
651 /*
652 * Return the address list...
653 */
654
655 return (first);
656 }
657
658
659 /*
660 * End of "$Id: http-addrlist.c 7910 2008-09-06 00:25:17Z mike $".
661 */