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