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