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