]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/http-addrlist.c
b0fbb84c00dabfad8147759aa41f80511002ae00
[thirdparty/cups.git] / cups / http-addrlist.c
1 /*
2 * "$Id: http-addrlist.c 181 2006-06-22 20:01:18Z jlovell $"
3 *
4 * HTTP address list routines for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 1997-2006 by Easy Software Products, all rights reserved.
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
47 http_addrlist_t * /* O - Connected address or NULL on failure */
48 httpAddrConnect(
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
93 #ifdef SO_NOSIGPIPE
94 val = 1;
95 setsockopt(*sock, SOL_SOCKET, SO_NOSIGPIPE, &val, sizeof(val));
96 #endif /* SO_NOSIGPIPE */
97
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
131 #ifdef WIN32
132 closesocket(*sock);
133 #else
134 close(*sock);
135 #endif /* WIN32 */
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
150 void
151 httpAddrFreeList(
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
178 http_addrlist_t * /* O - List of addresses or NULL */
179 httpAddrGetList(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 {
414 temp->addr.ipv6.sin6_family = AF_INET6;
415 memcpy(&(temp->addr.ipv6.sin6_addr), 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 {
422 temp->addr.ipv4.sin_family = AF_INET;
423 memcpy(&(temp->addr.ipv4.sin_addr), 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 if (!first)
510 first = temp;
511
512 addr = temp;
513 }
514
515 if (family != AF_INET6)
516 #endif /* AF_INET6 */
517 {
518 /*
519 * Add 127.0.0.1 to the address list...
520 */
521
522 temp = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t));
523 if (!temp)
524 {
525 httpAddrFreeList(first);
526 return (NULL);
527 }
528
529 temp->addr.ipv4.sin_family = AF_INET;
530 temp->addr.ipv4.sin_port = htons(portnum);
531 temp->addr.ipv4.sin_addr.s_addr = htonl(0x7f000001);
532
533 if (!first)
534 first = temp;
535
536 if (addr)
537 addr->next = temp;
538 else
539 addr = temp;
540 }
541 }
542 else if (!hostname)
543 {
544 /*
545 * Provide one or more passive listening addresses...
546 */
547
548 #ifdef AF_INET6
549 if (family != AF_INET)
550 {
551 /*
552 * Add [::] to the address list...
553 */
554
555 temp = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t));
556 if (!temp)
557 {
558 httpAddrFreeList(first);
559 return (NULL);
560 }
561
562 temp->addr.ipv6.sin6_family = AF_INET6;
563 temp->addr.ipv6.sin6_port = htons(portnum);
564
565 if (!first)
566 first = temp;
567
568 addr = temp;
569 }
570
571 if (family != AF_INET6)
572 #endif /* AF_INET6 */
573 {
574 /*
575 * Add 0.0.0.0 to the address list...
576 */
577
578 temp = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t));
579 if (!temp)
580 {
581 httpAddrFreeList(first);
582 return (NULL);
583 }
584
585 temp->addr.ipv4.sin_family = AF_INET;
586 temp->addr.ipv4.sin_port = htons(portnum);
587
588 if (!first)
589 first = temp;
590
591 if (addr)
592 addr->next = temp;
593 else
594 addr = temp;
595 }
596 }
597 }
598
599 /*
600 * Return the address list...
601 */
602
603 return (first);
604 }
605
606
607 /*
608 * End of "$Id: http-addrlist.c 181 2006-06-22 20:01:18Z jlovell $".
609 */