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