]>
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-2012 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 * httpAddrConnect2() - Connect to any of the addresses in the list with a
19 * timeout and optional cancel.
20 * httpAddrFreeList() - Free an address list.
21 * httpAddrGetList() - Get a list of addresses for a hostname.
25 * Include necessary headers...
28 #include "cups-private.h"
31 #endif /* HAVE_RESOLV_H */
34 #endif /* HAVE_POLL */
38 * 'httpAddrConnect()' - Connect to any of the addresses in the list.
40 * @since CUPS 1.2/Mac OS X 10.5@
43 http_addrlist_t
* /* O - Connected address or NULL on failure */
45 http_addrlist_t
*addrlist
, /* I - List of potential addresses */
46 int *sock
) /* O - Socket */
48 DEBUG_printf(("httpAddrConnect(addrlist=%p, sock=%p)", addrlist
, sock
));
50 return (httpAddrConnect2(addrlist
, sock
, 30000, NULL
));
55 * 'httpAddrConnect2()' - Connect to any of the addresses in the list with a
56 * timeout and optional cancel.
61 http_addrlist_t
* /* O - Connected address or NULL on failure */
63 http_addrlist_t
*addrlist
, /* I - List of potential addresses */
64 int *sock
, /* O - Socket */
65 int msec
, /* I - Timeout in milliseconds */
66 int *cancel
) /* I - Pointer to "cancel" variable */
68 int val
; /* Socket option value */
70 socklen_t len
; /* Length of value */
71 int flags
, /* Socket flags */
72 remaining
; /* Remaining timeout */
73 #endif /* O_NONBLOCK */
75 struct pollfd pfd
; /* Polled file descriptor */
77 fd_set input_set
, /* select() input set */
78 output_set
; /* select() output set */
79 struct timeval timeout
; /* Timeout */
80 #endif /* HAVE_POLL */
81 int nfds
; /* Result from select()/poll() */
83 char temp
[256]; /* Temporary address string */
87 DEBUG_printf(("httpAddrConnect2(addrlist=%p, sock=%p, msec=%d, cancel=%p)",
88 addrlist
, sock
, msec
, cancel
));
93 _cupsSetError(IPP_INTERNAL_ERROR
, strerror(errno
), 0);
97 if (cancel
&& *cancel
)
104 * Loop through each address until we connect or run out of addresses...
110 * Create the socket...
113 DEBUG_printf(("2httpAddrConnect: Trying %s:%d...",
114 httpAddrString(&(addrlist
->addr
), temp
, sizeof(temp
)),
115 _httpAddrPort(&(addrlist
->addr
))));
117 if ((*sock
= (int)socket(_httpAddrFamily(&(addrlist
->addr
)), SOCK_STREAM
,
121 * Don't abort yet, as this could just be an issue with the local
122 * system not being configured with IPv4/IPv6/domain socket enabled...
125 addrlist
= addrlist
->next
;
135 setsockopt(*sock
, SOL_SOCKET
, SO_REUSEADDR
, (const char *)&val
,
138 setsockopt(*sock
, SOL_SOCKET
, SO_REUSEADDR
, &val
, sizeof(val
));
143 setsockopt(*sock
, SOL_SOCKET
, SO_REUSEPORT
, &val
, sizeof(val
));
144 #endif /* SO_REUSEPORT */
148 setsockopt(*sock
, SOL_SOCKET
, SO_NOSIGPIPE
, &val
, sizeof(val
));
149 #endif /* SO_NOSIGPIPE */
152 * Using TCP_NODELAY improves responsiveness, especially on systems
153 * with a slow loopback interface...
158 setsockopt(*sock
, IPPROTO_TCP
, TCP_NODELAY
, (const char *)&val
,
161 setsockopt(*sock
, IPPROTO_TCP
, TCP_NODELAY
, &val
, sizeof(val
));
166 * Close this socket when starting another process...
169 fcntl(*sock
, F_SETFD
, FD_CLOEXEC
);
170 #endif /* FD_CLOEXEC */
174 * Do an asynchronous connect by setting the socket non-blocking...
177 flags
= fcntl(*sock
, F_GETFL
, 0);
179 fcntl(*sock
, F_SETFL
, flags
| O_NONBLOCK
);
180 #endif /* O_NONBLOCK */
186 if (!connect(*sock
, &(addrlist
->addr
.addr
),
187 httpAddrLength(&(addrlist
->addr
))))
189 DEBUG_printf(("1httpAddrConnect: Connected to %s:%d...",
190 httpAddrString(&(addrlist
->addr
), temp
, sizeof(temp
)),
191 _httpAddrPort(&(addrlist
->addr
))));
194 fcntl(*sock
, F_SETFL
, flags
);
195 #endif /* O_NONBLOCK */
202 if (errno
== WSAEINPROGRESS
)
204 if (errno
== EINPROGRESS
)
207 for (remaining
= msec
; remaining
> 0; remaining
-= 250)
211 pfd
.events
= POLLIN
| POLLOUT
;
213 while ((nfds
= poll(&pfd
, 1, remaining
> 250 ? 250 : remaining
)) < 0 &&
214 (errno
== EINTR
|| errno
== EAGAIN
));
219 if (cancel
&& *cancel
)
222 * Close this socket and return...
237 FD_SET(*sock
, &input_set
);
238 output_set
= input_set
;
241 timeout
.tv_usec
= (remaining
> 250 ? 250 : remaining
) * 1000;
243 nfds
= select(*sock
+ 1, &input_set
, &output_set
, NULL
, &timeout
);
246 while (nfds
< 0 && (WSAGetLastError() == WSAEINTR
||
247 WSAGetLastError() == WSAEWOULDBLOCK
));
249 while (nfds
< 0 && (errno
== EINTR
|| errno
== EAGAIN
));
251 # endif /* HAVE_POLL */
256 if (getsockopt(*sock
, SOL_SOCKET
, SO_ERROR
, &val
, &len
) >= 0)
258 DEBUG_printf(("1httpAddrConnect: Connected to %s:%d...",
259 httpAddrString(&(addrlist
->addr
), temp
, sizeof(temp
)),
260 _httpAddrPort(&(addrlist
->addr
))));
262 fcntl(*sock
, F_SETFL
, flags
);
270 #endif /* O_NONBLOCK */
272 DEBUG_printf(("1httpAddrConnect: Unable to connect to %s:%d: %s",
273 httpAddrString(&(addrlist
->addr
), temp
, sizeof(temp
)),
274 _httpAddrPort(&(addrlist
->addr
)), strerror(errno
)));
277 * Close this socket and move to the next address...
287 addrlist
= addrlist
->next
;
291 _cupsSetError(IPP_SERVICE_UNAVAILABLE
, strerror(errno
), 0);
298 * 'httpAddrFreeList()' - Free an address list.
300 * @since CUPS 1.2/Mac OS X 10.5@
305 http_addrlist_t
*addrlist
) /* I - Address list to free */
307 http_addrlist_t
*next
; /* Next address in list */
311 * Free each address in the list...
316 next
= addrlist
->next
;
326 * 'httpAddrGetList()' - Get a list of addresses for a hostname.
328 * @since CUPS 1.2/Mac OS X 10.5@
331 http_addrlist_t
* /* O - List of addresses or NULL */
332 httpAddrGetList(const char *hostname
, /* I - Hostname, IP address, or NULL for passive listen address */
333 int family
, /* I - Address family or AF_UNSPEC */
334 const char *service
) /* I - Service name or port number */
336 http_addrlist_t
*first
, /* First address in list */
337 *addr
, /* Current address in list */
338 *temp
; /* New address */
339 _cups_globals_t
*cg
= _cupsGlobals();
344 _cups_debug_printf("httpAddrGetList(hostname=\"%s\", family=AF_%s, "
346 hostname
? hostname
: "(nil)",
347 family
== AF_UNSPEC
? "UNSPEC" :
349 family
== AF_LOCAL
? "LOCAL" :
350 # endif /* AF_LOCAL */
352 family
== AF_INET6
? "INET6" :
353 # endif /* AF_INET6 */
354 family
== AF_INET
? "INET" : "???", service
);
359 * STR #2920: Initialize resolver after failure in cups-polld
361 * If the previous lookup failed, re-initialize the resolver to prevent
362 * temporary network errors from persisting. This *should* be handled by
363 * the resolver libraries, but apparently the glibc folks do not agree.
365 * We set a flag at the end of this function if we encounter an error that
366 * requires reinitialization of the resolver functions. We then call
367 * res_init() if the flag is set on the next call here or in httpAddrLookup().
370 if (cg
->need_res_init
)
374 cg
->need_res_init
= 0;
376 #endif /* HAVE_RES_INIT */
379 * Lookup the address the best way we can...
385 if (hostname
&& hostname
[0] == '/')
388 * Domain socket address...
391 if ((first
= (http_addrlist_t
*)calloc(1, sizeof(http_addrlist_t
))) != NULL
)
393 first
->addr
.un
.sun_family
= AF_LOCAL
;
394 strlcpy(first
->addr
.un
.sun_path
, hostname
, sizeof(first
->addr
.un
.sun_path
));
398 #endif /* AF_LOCAL */
399 if (!hostname
|| _cups_strcasecmp(hostname
, "localhost"))
401 #ifdef HAVE_GETADDRINFO
402 struct addrinfo hints
, /* Address lookup hints */
403 *results
, /* Address lookup results */
404 *current
; /* Current result */
405 char ipv6
[64], /* IPv6 address */
406 *ipv6zone
; /* Pointer to zone separator */
407 int ipv6len
; /* Length of IPv6 address */
408 int error
; /* getaddrinfo() error */
412 * Lookup the address as needed...
415 memset(&hints
, 0, sizeof(hints
));
416 hints
.ai_family
= family
;
417 hints
.ai_flags
= hostname
? 0 : AI_PASSIVE
;
418 hints
.ai_socktype
= SOCK_STREAM
;
420 if (hostname
&& *hostname
== '[')
423 * Remove brackets from numeric IPv6 address...
426 if (!strncmp(hostname
, "[v1.", 4))
429 * Copy the newer address format which supports link-local addresses...
432 strlcpy(ipv6
, hostname
+ 4, sizeof(ipv6
));
433 if ((ipv6len
= (int)strlen(ipv6
) - 1) >= 0 && ipv6
[ipv6len
] == ']')
435 ipv6
[ipv6len
] = '\0';
439 * Convert "+zone" in address to "%zone"...
442 if ((ipv6zone
= strrchr(ipv6
, '+')) != NULL
)
449 * Copy the regular non-link-local IPv6 address...
452 strlcpy(ipv6
, hostname
+ 1, sizeof(ipv6
));
453 if ((ipv6len
= (int)strlen(ipv6
) - 1) >= 0 && ipv6
[ipv6len
] == ']')
455 ipv6
[ipv6len
] = '\0';
461 if ((error
= getaddrinfo(hostname
, service
, &hints
, &results
)) == 0)
464 * Copy the results to our own address list structure...
467 for (current
= results
; current
; current
= current
->ai_next
)
468 if (current
->ai_family
== AF_INET
|| current
->ai_family
== AF_INET6
)
471 * Copy the address over...
474 temp
= (http_addrlist_t
*)calloc(1, sizeof(http_addrlist_t
));
477 httpAddrFreeList(first
);
478 _cupsSetError(IPP_INTERNAL_ERROR
, strerror(errno
), 0);
482 if (current
->ai_family
== AF_INET6
)
483 memcpy(&(temp
->addr
.ipv6
), current
->ai_addr
,
484 sizeof(temp
->addr
.ipv6
));
486 memcpy(&(temp
->addr
.ipv4
), current
->ai_addr
,
487 sizeof(temp
->addr
.ipv4
));
490 * Append the address to the list...
503 * Free the results from getaddrinfo()...
506 freeaddrinfo(results
);
510 if (error
== EAI_FAIL
)
511 cg
->need_res_init
= 1;
513 _cupsSetError(IPP_INTERNAL_ERROR
, gai_strerror(error
), 0);
519 int i
; /* Looping vars */
520 unsigned ip
[4]; /* IPv4 address components */
521 const char *ptr
; /* Pointer into hostname */
522 struct hostent
*host
; /* Result of lookup */
523 struct servent
*port
; /* Port number for service */
524 int portnum
; /* Port number */
528 * Lookup the service...
533 else if (isdigit(*service
& 255))
534 portnum
= atoi(service
);
535 else if ((port
= getservbyname(service
, NULL
)) != NULL
)
536 portnum
= ntohs(port
->s_port
);
537 else if (!strcmp(service
, "http"))
539 else if (!strcmp(service
, "https"))
541 else if (!strcmp(service
, "ipp") || !strcmp(service
, "ipps"))
543 else if (!strcmp(service
, "lpd"))
545 else if (!strcmp(service
, "socket"))
551 * This code is needed because some operating systems have a
552 * buggy implementation of gethostbyname() that does not support
553 * IPv4 addresses. If the hostname string is an IPv4 address, then
554 * sscanf() is used to extract the IPv4 components. We then pack
555 * the components into an IPv4 address manually, since the
556 * inet_aton() function is deprecated. We use the htonl() macro
557 * to get the right byte order for the address.
560 for (ptr
= hostname
; isdigit(*ptr
& 255) || *ptr
== '.'; ptr
++);
565 * We have an IPv4 address; break it up and create an IPv4 address...
568 if (sscanf(hostname
, "%u.%u.%u.%u", ip
, ip
+ 1, ip
+ 2, ip
+ 3) == 4 &&
569 ip
[0] <= 255 && ip
[1] <= 255 && ip
[2] <= 255 && ip
[3] <= 255)
571 first
= (http_addrlist_t
*)calloc(1, sizeof(http_addrlist_t
));
575 first
->addr
.ipv4
.sin_family
= AF_INET
;
576 first
->addr
.ipv4
.sin_addr
.s_addr
= htonl(((((((ip
[0] << 8) |
578 ip
[2]) << 8) | ip
[3]));
579 first
->addr
.ipv4
.sin_port
= htons(portnum
);
582 else if ((host
= gethostbyname(hostname
)) != NULL
&&
584 (host
->h_addrtype
== AF_INET
|| host
->h_addrtype
== AF_INET6
))
586 host
->h_addrtype
== AF_INET
)
587 # endif /* AF_INET6 */
589 for (i
= 0; host
->h_addr_list
[i
]; i
++)
592 * Copy the address over...
595 temp
= (http_addrlist_t
*)calloc(1, sizeof(http_addrlist_t
));
598 httpAddrFreeList(first
);
603 if (host
->h_addrtype
== AF_INET6
)
605 temp
->addr
.ipv6
.sin6_family
= AF_INET6
;
606 memcpy(&(temp
->addr
.ipv6
.sin6_addr
), host
->h_addr_list
[i
],
607 sizeof(temp
->addr
.ipv6
));
608 temp
->addr
.ipv6
.sin6_port
= htons(portnum
);
611 # endif /* AF_INET6 */
613 temp
->addr
.ipv4
.sin_family
= AF_INET
;
614 memcpy(&(temp
->addr
.ipv4
.sin_addr
), host
->h_addr_list
[i
],
615 sizeof(temp
->addr
.ipv4
));
616 temp
->addr
.ipv4
.sin_port
= htons(portnum
);
620 * Append the address to the list...
634 if (h_errno
== NO_RECOVERY
)
635 cg
->need_res_init
= 1;
637 _cupsSetError(IPP_INTERNAL_ERROR
, hstrerror(h_errno
), 0);
640 #endif /* HAVE_GETADDRINFO */
644 * Detect some common errors and handle them sanely...
647 if (!addr
&& (!hostname
|| !_cups_strcasecmp(hostname
, "localhost")))
649 struct servent
*port
; /* Port number for service */
650 int portnum
; /* Port number */
654 * Lookup the service...
659 else if (isdigit(*service
& 255))
660 portnum
= atoi(service
);
661 else if ((port
= getservbyname(service
, NULL
)) != NULL
)
662 portnum
= ntohs(port
->s_port
);
663 else if (!strcmp(service
, "http"))
665 else if (!strcmp(service
, "https"))
667 else if (!strcmp(service
, "ipp") || !strcmp(service
, "ipps"))
669 else if (!strcmp(service
, "lpd"))
671 else if (!strcmp(service
, "socket"))
675 httpAddrFreeList(first
);
677 _cupsSetError(IPP_INTERNAL_ERROR
, _("Unknown service name."), 1);
681 if (hostname
&& !_cups_strcasecmp(hostname
, "localhost"))
684 * Unfortunately, some users ignore all of the warnings in the
685 * /etc/hosts file and delete "localhost" from it. If we get here
686 * then we were unable to resolve the name, so use the IPv6 and/or
687 * IPv4 loopback interface addresses...
691 if (family
!= AF_INET
)
694 * Add [::1] to the address list...
697 temp
= (http_addrlist_t
*)calloc(1, sizeof(http_addrlist_t
));
700 _cupsSetError(IPP_INTERNAL_ERROR
, strerror(errno
), 0);
701 httpAddrFreeList(first
);
705 temp
->addr
.ipv6
.sin6_family
= AF_INET6
;
706 temp
->addr
.ipv6
.sin6_port
= htons(portnum
);
708 temp
->addr
.ipv6
.sin6_addr
.u
.Byte
[15] = 1;
710 temp
->addr
.ipv6
.sin6_addr
.s6_addr32
[3] = htonl(1);
719 if (family
!= AF_INET6
)
720 #endif /* AF_INET6 */
723 * Add 127.0.0.1 to the address list...
726 temp
= (http_addrlist_t
*)calloc(1, sizeof(http_addrlist_t
));
729 _cupsSetError(IPP_INTERNAL_ERROR
, strerror(errno
), 0);
730 httpAddrFreeList(first
);
734 temp
->addr
.ipv4
.sin_family
= AF_INET
;
735 temp
->addr
.ipv4
.sin_port
= htons(portnum
);
736 temp
->addr
.ipv4
.sin_addr
.s_addr
= htonl(0x7f000001);
748 * Provide one or more passive listening addresses...
752 if (family
!= AF_INET
)
755 * Add [::] to the address list...
758 temp
= (http_addrlist_t
*)calloc(1, sizeof(http_addrlist_t
));
761 _cupsSetError(IPP_INTERNAL_ERROR
, strerror(errno
), 0);
762 httpAddrFreeList(first
);
766 temp
->addr
.ipv6
.sin6_family
= AF_INET6
;
767 temp
->addr
.ipv6
.sin6_port
= htons(portnum
);
775 if (family
!= AF_INET6
)
776 #endif /* AF_INET6 */
779 * Add 0.0.0.0 to the address list...
782 temp
= (http_addrlist_t
*)calloc(1, sizeof(http_addrlist_t
));
785 _cupsSetError(IPP_INTERNAL_ERROR
, strerror(errno
), 0);
786 httpAddrFreeList(first
);
790 temp
->addr
.ipv4
.sin_family
= AF_INET
;
791 temp
->addr
.ipv4
.sin_port
= htons(portnum
);
803 * Return the address list...
811 * End of "$Id: http-addrlist.c 7910 2008-09-06 00:25:17Z mike $".