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