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