]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/http-addrlist.c
Import CUPS 1.4svn-r7226.
[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
180 printf("httpAddrGetList(hostname=\"%s\", family=AF_%s, service=\"%s\")\n",
181 hostname ? hostname : "(nil)",
182 family == AF_UNSPEC ? "UNSPEC" :
183# ifdef AF_LOCAL
184 family == AF_LOCAL ? "LOCAL" :
185# endif /* AF_LOCAL */
186# ifdef AF_INET6
187 family == AF_INET6 ? "INET6" :
188# endif /* AF_INET6 */
189 family == AF_INET ? "INET" : "???", service);
190#endif /* DEBUG */
191
192 /*
193 * Lookup the address the best way we can...
194 */
195
196 first = addr = NULL;
197
198#ifdef AF_LOCAL
199 if (hostname && hostname[0] == '/')
200 {
201 /*
202 * Domain socket address...
203 */
204
91c84a35
MS
205 if ((first = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t))) != NULL)
206 {
207 first->addr.un.sun_family = AF_LOCAL;
208 strlcpy(first->addr.un.sun_path, hostname, sizeof(first->addr.un.sun_path));
209 }
ef416fc2 210 }
211 else
212#endif /* AF_LOCAL */
213 {
214#ifdef HAVE_GETADDRINFO
215 struct addrinfo hints, /* Address lookup hints */
216 *results, /* Address lookup results */
217 *current; /* Current result */
218 char ipv6[1024], /* IPv6 address */
219 *ipv6zone; /* Pointer to zone separator */
220 int ipv6len; /* Length of IPv6 address */
221
222 /*
223 * Lookup the address as needed...
224 */
225
226 memset(&hints, 0, sizeof(hints));
227 hints.ai_family = family;
228 hints.ai_flags = hostname ? 0 : AI_PASSIVE;
229 hints.ai_socktype = SOCK_STREAM;
230
231 if (hostname && *hostname == '[')
232 {
233 /*
234 * Remove brackets from numeric IPv6 address...
235 */
236
237 if (!strncmp(hostname, "[v1.", 4))
238 {
239 /*
240 * Copy the newer address format which supports link-local addresses...
241 */
242
243 strlcpy(ipv6, hostname + 4, sizeof(ipv6));
b86bc4cf 244 if ((ipv6len = (int)strlen(ipv6) - 1) >= 0 && ipv6[ipv6len] == ']')
ef416fc2 245 {
246 ipv6[ipv6len] = '\0';
247 hostname = ipv6;
248
249 /*
250 * Convert "+zone" in address to "%zone"...
251 */
252
253 if ((ipv6zone = strrchr(ipv6, '+')) != NULL)
254 *ipv6zone = '%';
255 }
256 }
257 else
258 {
259 /*
260 * Copy the regular non-link-local IPv6 address...
261 */
262
263 strlcpy(ipv6, hostname + 1, sizeof(ipv6));
b86bc4cf 264 if ((ipv6len = (int)strlen(ipv6) - 1) >= 0 && ipv6[ipv6len] == ']')
ef416fc2 265 {
266 ipv6[ipv6len] = '\0';
267 hostname = ipv6;
268 }
269 }
270 }
271
272 if (!getaddrinfo(hostname, service, &hints, &results))
273 {
274 /*
275 * Copy the results to our own address list structure...
276 */
277
278 for (current = results; current; current = current->ai_next)
279 if (current->ai_family == AF_INET || current->ai_family == AF_INET6)
280 {
281 /*
282 * Copy the address over...
283 */
284
285 temp = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t));
286 if (!temp)
287 {
288 httpAddrFreeList(first);
289 return (NULL);
290 }
291
292 if (current->ai_family == AF_INET6)
293 memcpy(&(temp->addr.ipv6), current->ai_addr,
294 sizeof(temp->addr.ipv6));
295 else
296 memcpy(&(temp->addr.ipv4), current->ai_addr,
297 sizeof(temp->addr.ipv4));
298
299 /*
300 * Append the address to the list...
301 */
302
303 if (!first)
304 first = temp;
305
306 if (addr)
307 addr->next = temp;
308
309 addr = temp;
310 }
311
312 /*
313 * Free the results from getaddrinfo()...
314 */
315
316 freeaddrinfo(results);
317 }
318#else
319 if (hostname)
320 {
321 int i; /* Looping vars */
322 unsigned ip[4]; /* IPv4 address components */
323 const char *ptr; /* Pointer into hostname */
324 struct hostent *host; /* Result of lookup */
325 struct servent *port; /* Port number for service */
326 int portnum; /* Port number */
327
328
329 /*
330 * Lookup the service...
331 */
332
333 if (!service)
334 portnum = 0;
335 else if (isdigit(*service & 255))
336 portnum = atoi(service);
337 else if ((port = getservbyname(service, NULL)) != NULL)
338 portnum = ntohs(port->s_port);
339 else if (!strcmp(service, "http"))
340 portnum = 80;
341 else if (!strcmp(service, "https"))
342 portnum = 443;
343 else if (!strcmp(service, "ipp"))
344 portnum = 631;
345 else if (!strcmp(service, "lpd"))
346 portnum = 515;
347 else if (!strcmp(service, "socket"))
348 portnum = 9100;
349 else
350 return (NULL);
351
352 /*
353 * This code is needed because some operating systems have a
354 * buggy implementation of gethostbyname() that does not support
355 * IPv4 addresses. If the hostname string is an IPv4 address, then
356 * sscanf() is used to extract the IPv4 components. We then pack
357 * the components into an IPv4 address manually, since the
358 * inet_aton() function is deprecated. We use the htonl() macro
359 * to get the right byte order for the address.
360 */
361
362 for (ptr = hostname; isdigit(*ptr & 255) || *ptr == '.'; ptr ++);
363
364 if (!*ptr)
365 {
366 /*
367 * We have an IPv4 address; break it up and create an IPv4 address...
368 */
369
370 if (sscanf(hostname, "%u.%u.%u.%u", ip, ip + 1, ip + 2, ip + 3) == 4 &&
371 ip[0] <= 255 && ip[1] <= 255 && ip[2] <= 255 && ip[3] <= 255)
372 {
373 first = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t));
374 if (!first)
375 return (NULL);
376
377 first->addr.ipv4.sin_family = AF_INET;
378 first->addr.ipv4.sin_addr.s_addr = htonl(((((((ip[0] << 8) |
379 ip[1]) << 8) |
380 ip[2]) << 8) | ip[3]));
381 first->addr.ipv4.sin_port = htons(portnum);
382 }
383 }
384 else if ((host = gethostbyname(hostname)) != NULL &&
385# ifdef AF_INET6
386 (host->h_addrtype == AF_INET || host->h_addrtype == AF_INET6))
387# else
388 host->h_addrtype == AF_INET)
389# endif /* AF_INET6 */
390 {
391 for (i = 0; host->h_addr_list[i]; i ++)
392 {
393 /*
394 * Copy the address over...
395 */
396
397 temp = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t));
398 if (!temp)
399 {
400 httpAddrFreeList(first);
401 return (NULL);
402 }
403
404# ifdef AF_INET6
405 if (host->h_addrtype == AF_INET6)
406 {
d6ae789d 407 temp->addr.ipv6.sin6_family = AF_INET6;
ed486911 408 memcpy(&(temp->addr.ipv6.sin6_addr), host->h_addr_list[i],
ef416fc2 409 sizeof(temp->addr.ipv6));
410 temp->addr.ipv6.sin6_port = htons(portnum);
411 }
412 else
413# endif /* AF_INET6 */
414 {
d6ae789d 415 temp->addr.ipv4.sin_family = AF_INET;
ed486911 416 memcpy(&(temp->addr.ipv4.sin_addr), host->h_addr_list[i],
ef416fc2 417 sizeof(temp->addr.ipv4));
418 temp->addr.ipv4.sin_port = htons(portnum);
419 }
420
421 /*
422 * Append the address to the list...
423 */
424
425 if (!first)
426 first = temp;
427
428 if (addr)
429 addr->next = temp;
430
431 addr = temp;
432 }
433 }
434 }
435#endif /* HAVE_GETADDRINFO */
436 }
437
438 /*
439 * Detect some common errors and handle them sanely...
440 */
441
442 if (!addr && (!hostname || !strcmp(hostname, "localhost")))
443 {
444 struct servent *port; /* Port number for service */
445 int portnum; /* Port number */
446
447
448 /*
449 * Lookup the service...
450 */
451
452 if (!service)
453 portnum = 0;
454 else if (isdigit(*service & 255))
455 portnum = atoi(service);
456 else if ((port = getservbyname(service, NULL)) != NULL)
457 portnum = ntohs(port->s_port);
458 else if (!strcmp(service, "http"))
459 portnum = 80;
460 else if (!strcmp(service, "https"))
461 portnum = 443;
462 else if (!strcmp(service, "ipp"))
463 portnum = 631;
464 else if (!strcmp(service, "lpd"))
465 portnum = 515;
466 else if (!strcmp(service, "socket"))
467 portnum = 9100;
468 else
469 return (NULL);
470
471 if (hostname && !strcmp(hostname, "localhost"))
472 {
473 /*
474 * Unfortunately, some users ignore all of the warnings in the
475 * /etc/hosts file and delete "localhost" from it. If we get here
476 * then we were unable to resolve the name, so use the IPv6 and/or
477 * IPv4 loopback interface addresses...
478 */
479
480#ifdef AF_INET6
481 if (family != AF_INET)
482 {
483 /*
484 * Add [::1] to the address list...
485 */
486
487 temp = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t));
488 if (!temp)
489 {
490 httpAddrFreeList(first);
491 return (NULL);
492 }
493
494 temp->addr.ipv6.sin6_family = AF_INET6;
495 temp->addr.ipv6.sin6_port = htons(portnum);
496# ifdef WIN32
497 temp->addr.ipv6.sin6_addr.u.Byte[15] = 1;
498# else
499 temp->addr.ipv6.sin6_addr.s6_addr32[3] = htonl(1);
500# endif /* WIN32 */
501
ed486911 502 if (!first)
503 first = temp;
504
ef416fc2 505 addr = temp;
506 }
507
508 if (family != AF_INET6)
509#endif /* AF_INET6 */
510 {
511 /*
512 * Add 127.0.0.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.ipv4.sin_family = AF_INET;
523 temp->addr.ipv4.sin_port = htons(portnum);
524 temp->addr.ipv4.sin_addr.s_addr = htonl(0x7f000001);
525
ed486911 526 if (!first)
527 first = temp;
528
ef416fc2 529 if (addr)
530 addr->next = temp;
531 else
532 addr = temp;
533 }
534 }
535 else if (!hostname)
536 {
537 /*
538 * Provide one or more passive listening addresses...
539 */
540
541#ifdef AF_INET6
542 if (family != AF_INET)
543 {
544 /*
545 * Add [::] to the address list...
546 */
547
548 temp = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t));
549 if (!temp)
550 {
551 httpAddrFreeList(first);
552 return (NULL);
553 }
554
555 temp->addr.ipv6.sin6_family = AF_INET6;
556 temp->addr.ipv6.sin6_port = htons(portnum);
557
ed486911 558 if (!first)
559 first = temp;
560
ef416fc2 561 addr = temp;
562 }
563
564 if (family != AF_INET6)
565#endif /* AF_INET6 */
566 {
567 /*
568 * Add 0.0.0.0 to the address list...
569 */
570
571 temp = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t));
572 if (!temp)
573 {
574 httpAddrFreeList(first);
575 return (NULL);
576 }
577
578 temp->addr.ipv4.sin_family = AF_INET;
579 temp->addr.ipv4.sin_port = htons(portnum);
580
ed486911 581 if (!first)
582 first = temp;
583
ef416fc2 584 if (addr)
585 addr->next = temp;
586 else
587 addr = temp;
588 }
589 }
590 }
591
592 /*
593 * Return the address list...
594 */
595
596 return (first);
597}
598
599
600/*
bc44d920 601 * End of "$Id: http-addrlist.c 6649 2007-07-11 21:46:42Z mike $".
ef416fc2 602 */