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