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