]>
git.ipfire.org Git - thirdparty/cups.git/blob - cups/http-addrlist.c
2 * "$Id: http-addrlist.c 7910 2008-09-06 00:25:17Z mike $"
4 * HTTP address list routines for CUPS.
6 * Copyright 2007-2011 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
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/".
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.
23 * Include necessary headers...
26 #include "cups-private.h"
29 #endif /* HAVE_RESOLV_H */
33 * 'httpAddrConnect()' - Connect to any of the addresses in the list.
35 * @since CUPS 1.2/Mac OS X 10.5@
38 http_addrlist_t
* /* O - Connected address or NULL on failure */
40 http_addrlist_t
*addrlist
, /* I - List of potential addresses */
41 int *sock
) /* O - Socket */
43 int val
; /* Socket option value */
45 struct timeval timeout
; /* Socket timeout value */
46 #endif /* __APPLE__ */
48 char temp
[256]; /* Temporary address string */
52 DEBUG_printf(("httpAddrConnect(addrlist=%p, sock=%p)", addrlist
, sock
));
61 * Loop through each address until we connect or run out of addresses...
67 * Create the socket...
70 DEBUG_printf(("2httpAddrConnect: Trying %s:%d...",
71 httpAddrString(&(addrlist
->addr
), temp
, sizeof(temp
)),
72 _httpAddrPort(&(addrlist
->addr
))));
74 if ((*sock
= (int)socket(_httpAddrFamily(&(addrlist
->addr
)), SOCK_STREAM
,
78 * Don't abort yet, as this could just be an issue with the local
79 * system not being configured with IPv4/IPv6/domain socket enabled...
82 addrlist
= addrlist
->next
;
92 setsockopt(*sock
, SOL_SOCKET
, SO_REUSEADDR
, (const char *)&val
,
95 setsockopt(*sock
, SOL_SOCKET
, SO_REUSEADDR
, &val
, sizeof(val
));
100 setsockopt(*sock
, SOL_SOCKET
, SO_REUSEPORT
, &val
, sizeof(val
));
101 #endif /* SO_REUSEPORT */
105 setsockopt(*sock
, SOL_SOCKET
, SO_NOSIGPIPE
, &val
, sizeof(val
));
106 #endif /* SO_NOSIGPIPE */
110 * Use a 30-second read timeout when connecting to limit the amount of time
116 setsockopt(*sock
, SOL_SOCKET
, SO_RCVTIMEO
, &timeout
, sizeof(timeout
));
117 #endif /* __APPLE__ */
120 * Using TCP_NODELAY improves responsiveness, especially on systems
121 * with a slow loopback interface...
126 setsockopt(*sock
, IPPROTO_TCP
, TCP_NODELAY
, (const char *)&val
,
129 setsockopt(*sock
, IPPROTO_TCP
, TCP_NODELAY
, &val
, sizeof(val
));
134 * Close this socket when starting another process...
137 fcntl(*sock
, F_SETFD
, FD_CLOEXEC
);
138 #endif /* FD_CLOEXEC */
144 if (!connect(*sock
, &(addrlist
->addr
.addr
),
145 httpAddrLength(&(addrlist
->addr
))))
147 DEBUG_printf(("1httpAddrConnect: Connected to %s:%d...",
148 httpAddrString(&(addrlist
->addr
), temp
, sizeof(temp
)),
149 _httpAddrPort(&(addrlist
->addr
))));
153 DEBUG_printf(("1httpAddrConnect: Unable to connect to %s:%d: %s",
154 httpAddrString(&(addrlist
->addr
), temp
, sizeof(temp
)),
155 _httpAddrPort(&(addrlist
->addr
)), strerror(errno
)));
158 * Close this socket and move to the next address...
168 addrlist
= addrlist
->next
;
172 _cupsSetError(IPP_SERVICE_UNAVAILABLE
, _("Unable to connect to server"), 1);
179 * 'httpAddrFreeList()' - Free an address list.
181 * @since CUPS 1.2/Mac OS X 10.5@
186 http_addrlist_t
*addrlist
) /* I - Address list to free */
188 http_addrlist_t
*next
; /* Next address in list */
192 * Free each address in the list...
197 next
= addrlist
->next
;
207 * 'httpAddrGetList()' - Get a list of addresses for a hostname.
209 * @since CUPS 1.2/Mac OS X 10.5@
212 http_addrlist_t
* /* O - List of addresses or NULL */
213 httpAddrGetList(const char *hostname
, /* I - Hostname, IP address, or NULL for passive listen address */
214 int family
, /* I - Address family or AF_UNSPEC */
215 const char *service
) /* I - Service name or port number */
217 http_addrlist_t
*first
, /* First address in list */
218 *addr
, /* Current address in list */
219 *temp
; /* New address */
220 _cups_globals_t
*cg
= _cupsGlobals();
225 _cups_debug_printf("httpAddrGetList(hostname=\"%s\", family=AF_%s, "
227 hostname
? hostname
: "(nil)",
228 family
== AF_UNSPEC
? "UNSPEC" :
230 family
== AF_LOCAL
? "LOCAL" :
231 # endif /* AF_LOCAL */
233 family
== AF_INET6
? "INET6" :
234 # endif /* AF_INET6 */
235 family
== AF_INET
? "INET" : "???", service
);
240 * STR #2920: Initialize resolver after failure in cups-polld
242 * If the previous lookup failed, re-initialize the resolver to prevent
243 * temporary network errors from persisting. This *should* be handled by
244 * the resolver libraries, but apparently the glibc folks do not agree.
246 * We set a flag at the end of this function if we encounter an error that
247 * requires reinitialization of the resolver functions. We then call
248 * res_init() if the flag is set on the next call here or in httpAddrLookup().
251 if (cg
->need_res_init
)
255 cg
->need_res_init
= 0;
257 #endif /* HAVE_RES_INIT */
261 * Lookup the address the best way we can...
267 if (hostname
&& hostname
[0] == '/')
270 * Domain socket address...
273 if ((first
= (http_addrlist_t
*)calloc(1, sizeof(http_addrlist_t
))) != NULL
)
275 first
->addr
.un
.sun_family
= AF_LOCAL
;
276 strlcpy(first
->addr
.un
.sun_path
, hostname
, sizeof(first
->addr
.un
.sun_path
));
280 #endif /* AF_LOCAL */
281 if (!hostname
|| _cups_strcasecmp(hostname
, "localhost"))
283 #ifdef HAVE_GETADDRINFO
284 struct addrinfo hints
, /* Address lookup hints */
285 *results
, /* Address lookup results */
286 *current
; /* Current result */
287 char ipv6
[64], /* IPv6 address */
288 *ipv6zone
; /* Pointer to zone separator */
289 int ipv6len
; /* Length of IPv6 address */
290 int error
; /* getaddrinfo() error */
294 * Lookup the address as needed...
297 memset(&hints
, 0, sizeof(hints
));
298 hints
.ai_family
= family
;
299 hints
.ai_flags
= hostname
? 0 : AI_PASSIVE
;
300 hints
.ai_socktype
= SOCK_STREAM
;
302 if (hostname
&& *hostname
== '[')
305 * Remove brackets from numeric IPv6 address...
308 if (!strncmp(hostname
, "[v1.", 4))
311 * Copy the newer address format which supports link-local addresses...
314 strlcpy(ipv6
, hostname
+ 4, sizeof(ipv6
));
315 if ((ipv6len
= (int)strlen(ipv6
) - 1) >= 0 && ipv6
[ipv6len
] == ']')
317 ipv6
[ipv6len
] = '\0';
321 * Convert "+zone" in address to "%zone"...
324 if ((ipv6zone
= strrchr(ipv6
, '+')) != NULL
)
331 * Copy the regular non-link-local IPv6 address...
334 strlcpy(ipv6
, hostname
+ 1, sizeof(ipv6
));
335 if ((ipv6len
= (int)strlen(ipv6
) - 1) >= 0 && ipv6
[ipv6len
] == ']')
337 ipv6
[ipv6len
] = '\0';
343 if ((error
= getaddrinfo(hostname
, service
, &hints
, &results
)) == 0)
346 * Copy the results to our own address list structure...
349 for (current
= results
; current
; current
= current
->ai_next
)
350 if (current
->ai_family
== AF_INET
|| current
->ai_family
== AF_INET6
)
353 * Copy the address over...
356 temp
= (http_addrlist_t
*)calloc(1, sizeof(http_addrlist_t
));
359 httpAddrFreeList(first
);
363 if (current
->ai_family
== AF_INET6
)
364 memcpy(&(temp
->addr
.ipv6
), current
->ai_addr
,
365 sizeof(temp
->addr
.ipv6
));
367 memcpy(&(temp
->addr
.ipv4
), current
->ai_addr
,
368 sizeof(temp
->addr
.ipv4
));
371 * Append the address to the list...
384 * Free the results from getaddrinfo()...
387 freeaddrinfo(results
);
389 else if (error
== EAI_FAIL
)
390 cg
->need_res_init
= 1;
395 int i
; /* Looping vars */
396 unsigned ip
[4]; /* IPv4 address components */
397 const char *ptr
; /* Pointer into hostname */
398 struct hostent
*host
; /* Result of lookup */
399 struct servent
*port
; /* Port number for service */
400 int portnum
; /* Port number */
404 * Lookup the service...
409 else if (isdigit(*service
& 255))
410 portnum
= atoi(service
);
411 else if ((port
= getservbyname(service
, NULL
)) != NULL
)
412 portnum
= ntohs(port
->s_port
);
413 else if (!strcmp(service
, "http"))
415 else if (!strcmp(service
, "https"))
417 else if (!strcmp(service
, "ipp") || !strcmp(service
, "ipps"))
419 else if (!strcmp(service
, "lpd"))
421 else if (!strcmp(service
, "socket"))
427 * This code is needed because some operating systems have a
428 * buggy implementation of gethostbyname() that does not support
429 * IPv4 addresses. If the hostname string is an IPv4 address, then
430 * sscanf() is used to extract the IPv4 components. We then pack
431 * the components into an IPv4 address manually, since the
432 * inet_aton() function is deprecated. We use the htonl() macro
433 * to get the right byte order for the address.
436 for (ptr
= hostname
; isdigit(*ptr
& 255) || *ptr
== '.'; ptr
++);
441 * We have an IPv4 address; break it up and create an IPv4 address...
444 if (sscanf(hostname
, "%u.%u.%u.%u", ip
, ip
+ 1, ip
+ 2, ip
+ 3) == 4 &&
445 ip
[0] <= 255 && ip
[1] <= 255 && ip
[2] <= 255 && ip
[3] <= 255)
447 first
= (http_addrlist_t
*)calloc(1, sizeof(http_addrlist_t
));
451 first
->addr
.ipv4
.sin_family
= AF_INET
;
452 first
->addr
.ipv4
.sin_addr
.s_addr
= htonl(((((((ip
[0] << 8) |
454 ip
[2]) << 8) | ip
[3]));
455 first
->addr
.ipv4
.sin_port
= htons(portnum
);
458 else if ((host
= gethostbyname(hostname
)) != NULL
&&
460 (host
->h_addrtype
== AF_INET
|| host
->h_addrtype
== AF_INET6
))
462 host
->h_addrtype
== AF_INET
)
463 # endif /* AF_INET6 */
465 for (i
= 0; host
->h_addr_list
[i
]; i
++)
468 * Copy the address over...
471 temp
= (http_addrlist_t
*)calloc(1, sizeof(http_addrlist_t
));
474 httpAddrFreeList(first
);
479 if (host
->h_addrtype
== AF_INET6
)
481 temp
->addr
.ipv6
.sin6_family
= AF_INET6
;
482 memcpy(&(temp
->addr
.ipv6
.sin6_addr
), host
->h_addr_list
[i
],
483 sizeof(temp
->addr
.ipv6
));
484 temp
->addr
.ipv6
.sin6_port
= htons(portnum
);
487 # endif /* AF_INET6 */
489 temp
->addr
.ipv4
.sin_family
= AF_INET
;
490 memcpy(&(temp
->addr
.ipv4
.sin_addr
), host
->h_addr_list
[i
],
491 sizeof(temp
->addr
.ipv4
));
492 temp
->addr
.ipv4
.sin_port
= htons(portnum
);
496 * Append the address to the list...
508 else if (h_errno
== NO_RECOVERY
)
509 cg
->need_res_init
= 1;
511 #endif /* HAVE_GETADDRINFO */
515 * Detect some common errors and handle them sanely...
518 if (!addr
&& (!hostname
|| !_cups_strcasecmp(hostname
, "localhost")))
520 struct servent
*port
; /* Port number for service */
521 int portnum
; /* Port number */
525 * Lookup the service...
530 else if (isdigit(*service
& 255))
531 portnum
= atoi(service
);
532 else if ((port
= getservbyname(service
, NULL
)) != NULL
)
533 portnum
= ntohs(port
->s_port
);
534 else if (!strcmp(service
, "http"))
536 else if (!strcmp(service
, "https"))
538 else if (!strcmp(service
, "ipp") || !strcmp(service
, "ipps"))
540 else if (!strcmp(service
, "lpd"))
542 else if (!strcmp(service
, "socket"))
546 httpAddrFreeList(first
);
550 if (hostname
&& !_cups_strcasecmp(hostname
, "localhost"))
553 * Unfortunately, some users ignore all of the warnings in the
554 * /etc/hosts file and delete "localhost" from it. If we get here
555 * then we were unable to resolve the name, so use the IPv6 and/or
556 * IPv4 loopback interface addresses...
560 if (family
!= AF_INET
)
563 * Add [::1] to the address list...
566 temp
= (http_addrlist_t
*)calloc(1, sizeof(http_addrlist_t
));
569 httpAddrFreeList(first
);
573 temp
->addr
.ipv6
.sin6_family
= AF_INET6
;
574 temp
->addr
.ipv6
.sin6_port
= htons(portnum
);
576 temp
->addr
.ipv6
.sin6_addr
.u
.Byte
[15] = 1;
578 temp
->addr
.ipv6
.sin6_addr
.s6_addr32
[3] = htonl(1);
587 if (family
!= AF_INET6
)
588 #endif /* AF_INET6 */
591 * Add 127.0.0.1 to the address list...
594 temp
= (http_addrlist_t
*)calloc(1, sizeof(http_addrlist_t
));
597 httpAddrFreeList(first
);
601 temp
->addr
.ipv4
.sin_family
= AF_INET
;
602 temp
->addr
.ipv4
.sin_port
= htons(portnum
);
603 temp
->addr
.ipv4
.sin_addr
.s_addr
= htonl(0x7f000001);
615 * Provide one or more passive listening addresses...
619 if (family
!= AF_INET
)
622 * Add [::] to the address list...
625 temp
= (http_addrlist_t
*)calloc(1, sizeof(http_addrlist_t
));
628 httpAddrFreeList(first
);
632 temp
->addr
.ipv6
.sin6_family
= AF_INET6
;
633 temp
->addr
.ipv6
.sin6_port
= htons(portnum
);
641 if (family
!= AF_INET6
)
642 #endif /* AF_INET6 */
645 * Add 0.0.0.0 to the address list...
648 temp
= (http_addrlist_t
*)calloc(1, sizeof(http_addrlist_t
));
651 httpAddrFreeList(first
);
655 temp
->addr
.ipv4
.sin_family
= AF_INET
;
656 temp
->addr
.ipv4
.sin_port
= htons(portnum
);
668 * Return the address list...
676 * End of "$Id: http-addrlist.c 7910 2008-09-06 00:25:17Z mike $".