4 * HTTP address list routines for CUPS.
6 * Copyright 2007-2014 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/".
15 * This file is subject to the Apple OS-Developed Software exception.
19 * Include necessary headers...
22 #include "cups-private.h"
25 #endif /* HAVE_RESOLV_H */
28 #endif /* HAVE_POLL */
35 * 'httpAddrConnect()' - Connect to any of the addresses in the list.
37 * @since CUPS 1.2/OS X 10.5@
40 http_addrlist_t
* /* O - Connected address or NULL on failure */
42 http_addrlist_t
*addrlist
, /* I - List of potential addresses */
43 int *sock
) /* O - Socket */
45 DEBUG_printf(("httpAddrConnect(addrlist=%p, sock=%p)", addrlist
, sock
));
47 return (httpAddrConnect2(addrlist
, sock
, 30000, NULL
));
52 * 'httpAddrConnect2()' - Connect to any of the addresses in the list with a
53 * timeout and optional cancel.
55 * @since CUPS 1.7/OS X 10.9@
58 http_addrlist_t
* /* O - Connected address or NULL on failure */
60 http_addrlist_t
*addrlist
, /* I - List of potential addresses */
61 int *sock
, /* O - Socket */
62 int msec
, /* I - Timeout in milliseconds */
63 int *cancel
) /* I - Pointer to "cancel" variable */
65 int val
; /* Socket option value */
67 socklen_t len
; /* Length of value */
68 http_addr_t peer
; /* Peer address */
69 int flags
, /* Socket flags */
70 remaining
; /* Remaining timeout */
71 int i
, /* Looping var */
72 nfds
, /* Number of file descriptors */
73 fds
[100], /* Socket file descriptors */
74 result
; /* Result from select() or poll() */
75 http_addrlist_t
*addrs
[100]; /* Addresses */
77 struct pollfd pfds
[100]; /* Polled file descriptors */
79 int max_fd
= -1; /* Highest file descriptor */
80 fd_set input_set
, /* select() input set */
81 output_set
; /* select() output set */
82 struct timeval timeout
; /* Timeout */
83 # endif /* HAVE_POLL */
84 #endif /* O_NONBLOCK */
86 char temp
[256]; /* Temporary address string */
90 DEBUG_printf(("httpAddrConnect2(addrlist=%p, sock=%p, msec=%d, cancel=%p)",
91 addrlist
, sock
, msec
, cancel
));
96 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(errno
), 0);
100 if (cancel
&& *cancel
)
107 * Loop through each address until we connect or run out of addresses...
110 for (nfds
= 0; addrlist
&& nfds
< (int)(sizeof(fds
) / sizeof(fds
[0])); addrlist
= addrlist
->next
)
112 if (cancel
&& *cancel
)
117 httpAddrClose(NULL
, fds
[nfds
]);
124 * Create the socket...
127 DEBUG_printf(("2httpAddrConnect2: Trying %s:%d...", httpAddrString(&(addrlist
->addr
), temp
, sizeof(temp
)), httpAddrPort(&(addrlist
->addr
))));
129 if ((fds
[nfds
] = (int)socket(httpAddrFamily(&(addrlist
->addr
)), SOCK_STREAM
, 0)) < 0)
132 * Don't abort yet, as this could just be an issue with the local
133 * system not being configured with IPv4/IPv6/domain socket enabled...
144 setsockopt(fds
[nfds
], SOL_SOCKET
, SO_REUSEADDR
, CUPS_SOCAST
&val
, sizeof(val
));
148 setsockopt(fds
[nfds
], SOL_SOCKET
, SO_REUSEPORT
, CUPS_SOCAST
&val
, sizeof(val
));
149 #endif /* SO_REUSEPORT */
153 setsockopt(fds
[nfds
], SOL_SOCKET
, SO_NOSIGPIPE
, CUPS_SOCAST
&val
, sizeof(val
));
154 #endif /* SO_NOSIGPIPE */
157 * Using TCP_NODELAY improves responsiveness, especially on systems
158 * with a slow loopback interface...
162 setsockopt(fds
[nfds
], IPPROTO_TCP
, TCP_NODELAY
, CUPS_SOCAST
&val
, sizeof(val
));
166 * Close this socket when starting another process...
169 fcntl(fds
[nfds
], F_SETFD
, FD_CLOEXEC
);
170 #endif /* FD_CLOEXEC */
174 * Do an asynchronous connect by setting the socket non-blocking...
177 DEBUG_printf(("httpAddrConnect2: Setting non-blocking connect()"));
179 flags
= fcntl(fds
[nfds
], F_GETFL
, 0);
180 fcntl(fds
[nfds
], F_SETFL
, flags
| O_NONBLOCK
);
181 #endif /* O_NONBLOCK */
187 if (!connect(fds
[nfds
], &(addrlist
->addr
.addr
), (socklen_t
)httpAddrLength(&(addrlist
->addr
))))
189 DEBUG_printf(("1httpAddrConnect2: Connected to %s:%d...", httpAddrString(&(addrlist
->addr
), temp
, sizeof(temp
)), httpAddrPort(&(addrlist
->addr
))));
192 fcntl(fds
[nfds
], F_SETFL
, flags
);
193 #endif /* O_NONBLOCK */
200 httpAddrClose(NULL
, fds
[nfds
]);
207 if (WSAGetLastError() != WSAEINPROGRESS
&& WSAGetLastError() != WSAEWOULDBLOCK
)
209 if (errno
!= EINPROGRESS
&& errno
!= EWOULDBLOCK
)
212 DEBUG_printf(("1httpAddrConnect2: Unable to connect to %s:%d: %s", httpAddrString(&(addrlist
->addr
), temp
, sizeof(temp
)), httpAddrPort(&(addrlist
->addr
)), strerror(errno
)));
213 httpAddrClose(NULL
, fds
[nfds
]);
217 fcntl(fds
[nfds
], F_SETFL
, flags
);
220 if (fds
[nfds
] > max_fd
)
222 #endif /* !HAVE_POLL */
224 addrs
[nfds
] = addrlist
;
229 DEBUG_puts("1httpAddrConnect2: Finishing async connect()");
231 for (remaining
= msec
; remaining
> 0; remaining
-= 250)
235 if (cancel
&& *cancel
)
238 * Close this socket and return...
241 DEBUG_puts("1httpAddrConnect2: Canceled connect()");
246 httpAddrClose(NULL
, fds
[nfds
]);
255 for (i
= 0; i
< nfds
; i
++)
258 pfds
[i
].events
= POLLIN
| POLLOUT
;
261 result
= poll(pfds
, (nfds_t
)nfds
, remaining
> 250 ? 250 : remaining
);
263 DEBUG_printf(("1httpAddrConnect2: poll() returned %d (%d)", result
, errno
));
267 for (i
= 0; i
< nfds
; i
++)
268 FD_SET(fds
[i
], &input_set
);
269 output_set
= input_set
;
272 timeout
.tv_usec
= (remaining
> 250 ? 250 : remaining
) * 1000;
274 result
= select(max_fd
+ 1, &input_set
, &output_set
, NULL
, &timeout
);
276 DEBUG_printf(("1httpAddrConnect2: select() returned %d (%d)", result
, errno
));
277 # endif /* HAVE_POLL */
280 while (result
< 0 && (WSAGetLastError() == WSAEINTR
|| WSAGetLastError() == WSAEWOULDBLOCK
));
282 while (result
< 0 && (errno
== EINTR
|| errno
== EAGAIN
));
287 for (i
= 0; i
< nfds
; i
++)
290 DEBUG_printf(("pfds[%d].revents=%x\n", i
, pfds
[i
].revents
));
293 if (FD_ISSET(fds
[i
], &input
))
294 # endif /* HAVE_POLL */
298 if (!getpeername(fds
[i
], (struct sockaddr
*)&peer
, &len
))
300 DEBUG_printf(("1httpAddrConnect2: Connected to %s:%d...", httpAddrString(&peer
, temp
, sizeof(temp
)), httpAddrPort(&peer
)));
306 httpAddrClose(NULL
, fds
[i
]);
312 #endif /* O_NONBLOCK */
317 httpAddrClose(NULL
, fds
[nfds
]);
321 _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE
, "Connection failed", 0);
323 _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE
, strerror(errno
), 0);
331 * 'httpAddrCopyList()' - Copy an address list.
333 * @since CUPS 1.7/OS X 10.9@
336 http_addrlist_t
* /* O - New address list or @code NULL@ on error */
338 http_addrlist_t
*src
) /* I - Source address list */
340 http_addrlist_t
*dst
= NULL
, /* First list entry */
341 *prev
= NULL
, /* Previous list entry */
342 *current
= NULL
;/* Current list entry */
347 if ((current
= malloc(sizeof(http_addrlist_t
))) == NULL
)
354 current
= current
->next
;
362 memcpy(current
, src
, sizeof(http_addrlist_t
));
364 current
->next
= NULL
;
367 prev
->next
= current
;
380 * 'httpAddrFreeList()' - Free an address list.
382 * @since CUPS 1.2/OS X 10.5@
387 http_addrlist_t
*addrlist
) /* I - Address list to free */
389 http_addrlist_t
*next
; /* Next address in list */
393 * Free each address in the list...
398 next
= addrlist
->next
;
408 * 'httpAddrGetList()' - Get a list of addresses for a hostname.
410 * @since CUPS 1.2/OS X 10.5@
413 http_addrlist_t
* /* O - List of addresses or NULL */
414 httpAddrGetList(const char *hostname
, /* I - Hostname, IP address, or NULL for passive listen address */
415 int family
, /* I - Address family or AF_UNSPEC */
416 const char *service
) /* I - Service name or port number */
418 http_addrlist_t
*first
, /* First address in list */
419 *addr
, /* Current address in list */
420 *temp
; /* New address */
421 _cups_globals_t
*cg
= _cupsGlobals();
426 _cups_debug_printf("httpAddrGetList(hostname=\"%s\", family=AF_%s, "
428 hostname
? hostname
: "(nil)",
429 family
== AF_UNSPEC
? "UNSPEC" :
431 family
== AF_LOCAL
? "LOCAL" :
432 # endif /* AF_LOCAL */
434 family
== AF_INET6
? "INET6" :
435 # endif /* AF_INET6 */
436 family
== AF_INET
? "INET" : "???", service
);
441 * STR #2920: Initialize resolver after failure in cups-polld
443 * If the previous lookup failed, re-initialize the resolver to prevent
444 * temporary network errors from persisting. This *should* be handled by
445 * the resolver libraries, but apparently the glibc folks do not agree.
447 * We set a flag at the end of this function if we encounter an error that
448 * requires reinitialization of the resolver functions. We then call
449 * res_init() if the flag is set on the next call here or in httpAddrLookup().
452 if (cg
->need_res_init
)
456 cg
->need_res_init
= 0;
458 #endif /* HAVE_RES_INIT */
461 * Lookup the address the best way we can...
467 if (hostname
&& hostname
[0] == '/')
470 * Domain socket address...
473 if ((first
= (http_addrlist_t
*)calloc(1, sizeof(http_addrlist_t
))) != NULL
)
476 first
->addr
.un
.sun_family
= AF_LOCAL
;
477 strlcpy(first
->addr
.un
.sun_path
, hostname
, sizeof(first
->addr
.un
.sun_path
));
481 #endif /* AF_LOCAL */
482 if (!hostname
|| _cups_strcasecmp(hostname
, "localhost"))
484 #ifdef HAVE_GETADDRINFO
485 struct addrinfo hints
, /* Address lookup hints */
486 *results
, /* Address lookup results */
487 *current
; /* Current result */
488 char ipv6
[64], /* IPv6 address */
489 *ipv6zone
; /* Pointer to zone separator */
490 int ipv6len
; /* Length of IPv6 address */
491 int error
; /* getaddrinfo() error */
495 * Lookup the address as needed...
498 memset(&hints
, 0, sizeof(hints
));
499 hints
.ai_family
= family
;
500 hints
.ai_flags
= hostname
? 0 : AI_PASSIVE
;
501 hints
.ai_socktype
= SOCK_STREAM
;
503 if (hostname
&& *hostname
== '[')
506 * Remove brackets from numeric IPv6 address...
509 if (!strncmp(hostname
, "[v1.", 4))
512 * Copy the newer address format which supports link-local addresses...
515 strlcpy(ipv6
, hostname
+ 4, sizeof(ipv6
));
516 if ((ipv6len
= (int)strlen(ipv6
) - 1) >= 0 && ipv6
[ipv6len
] == ']')
518 ipv6
[ipv6len
] = '\0';
522 * Convert "+zone" in address to "%zone"...
525 if ((ipv6zone
= strrchr(ipv6
, '+')) != NULL
)
532 * Copy the regular non-link-local IPv6 address...
535 strlcpy(ipv6
, hostname
+ 1, sizeof(ipv6
));
536 if ((ipv6len
= (int)strlen(ipv6
) - 1) >= 0 && ipv6
[ipv6len
] == ']')
538 ipv6
[ipv6len
] = '\0';
544 if ((error
= getaddrinfo(hostname
, service
, &hints
, &results
)) == 0)
547 * Copy the results to our own address list structure...
550 for (current
= results
; current
; current
= current
->ai_next
)
551 if (current
->ai_family
== AF_INET
|| current
->ai_family
== AF_INET6
)
554 * Copy the address over...
557 temp
= (http_addrlist_t
*)calloc(1, sizeof(http_addrlist_t
));
560 httpAddrFreeList(first
);
561 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(errno
), 0);
565 if (current
->ai_family
== AF_INET6
)
566 memcpy(&(temp
->addr
.ipv6
), current
->ai_addr
,
567 sizeof(temp
->addr
.ipv6
));
569 memcpy(&(temp
->addr
.ipv4
), current
->ai_addr
,
570 sizeof(temp
->addr
.ipv4
));
573 * Append the address to the list...
586 * Free the results from getaddrinfo()...
589 freeaddrinfo(results
);
593 if (error
== EAI_FAIL
)
594 cg
->need_res_init
= 1;
596 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, gai_strerror(error
), 0);
602 int i
; /* Looping vars */
603 unsigned ip
[4]; /* IPv4 address components */
604 const char *ptr
; /* Pointer into hostname */
605 struct hostent
*host
; /* Result of lookup */
606 struct servent
*port
; /* Port number for service */
607 int portnum
; /* Port number */
611 * Lookup the service...
616 else if (isdigit(*service
& 255))
617 portnum
= atoi(service
);
618 else if ((port
= getservbyname(service
, NULL
)) != NULL
)
619 portnum
= ntohs(port
->s_port
);
620 else if (!strcmp(service
, "http"))
622 else if (!strcmp(service
, "https"))
624 else if (!strcmp(service
, "ipp") || !strcmp(service
, "ipps"))
626 else if (!strcmp(service
, "lpd"))
628 else if (!strcmp(service
, "socket"))
634 * This code is needed because some operating systems have a
635 * buggy implementation of gethostbyname() that does not support
636 * IPv4 addresses. If the hostname string is an IPv4 address, then
637 * sscanf() is used to extract the IPv4 components. We then pack
638 * the components into an IPv4 address manually, since the
639 * inet_aton() function is deprecated. We use the htonl() macro
640 * to get the right byte order for the address.
643 for (ptr
= hostname
; isdigit(*ptr
& 255) || *ptr
== '.'; ptr
++);
648 * We have an IPv4 address; break it up and create an IPv4 address...
651 if (sscanf(hostname
, "%u.%u.%u.%u", ip
, ip
+ 1, ip
+ 2, ip
+ 3) == 4 &&
652 ip
[0] <= 255 && ip
[1] <= 255 && ip
[2] <= 255 && ip
[3] <= 255)
654 first
= (http_addrlist_t
*)calloc(1, sizeof(http_addrlist_t
));
658 first
->addr
.ipv4
.sin_family
= AF_INET
;
659 first
->addr
.ipv4
.sin_addr
.s_addr
= htonl((((((((unsigned)ip
[0] << 8) |
660 (unsigned)ip
[1]) << 8) |
661 (unsigned)ip
[2]) << 8) |
663 first
->addr
.ipv4
.sin_port
= htons(portnum
);
666 else if ((host
= gethostbyname(hostname
)) != NULL
&&
668 (host
->h_addrtype
== AF_INET
|| host
->h_addrtype
== AF_INET6
))
670 host
->h_addrtype
== AF_INET
)
671 # endif /* AF_INET6 */
673 for (i
= 0; host
->h_addr_list
[i
]; i
++)
676 * Copy the address over...
679 temp
= (http_addrlist_t
*)calloc(1, sizeof(http_addrlist_t
));
682 httpAddrFreeList(first
);
687 if (host
->h_addrtype
== AF_INET6
)
689 temp
->addr
.ipv6
.sin6_family
= AF_INET6
;
690 memcpy(&(temp
->addr
.ipv6
.sin6_addr
), host
->h_addr_list
[i
],
691 sizeof(temp
->addr
.ipv6
));
692 temp
->addr
.ipv6
.sin6_port
= htons(portnum
);
695 # endif /* AF_INET6 */
697 temp
->addr
.ipv4
.sin_family
= AF_INET
;
698 memcpy(&(temp
->addr
.ipv4
.sin_addr
), host
->h_addr_list
[i
],
699 sizeof(temp
->addr
.ipv4
));
700 temp
->addr
.ipv4
.sin_port
= htons(portnum
);
704 * Append the address to the list...
718 if (h_errno
== NO_RECOVERY
)
719 cg
->need_res_init
= 1;
721 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, hstrerror(h_errno
), 0);
724 #endif /* HAVE_GETADDRINFO */
728 * Detect some common errors and handle them sanely...
731 if (!addr
&& (!hostname
|| !_cups_strcasecmp(hostname
, "localhost")))
733 struct servent
*port
; /* Port number for service */
734 int portnum
; /* Port number */
738 * Lookup the service...
743 else if (isdigit(*service
& 255))
744 portnum
= atoi(service
);
745 else if ((port
= getservbyname(service
, NULL
)) != NULL
)
746 portnum
= ntohs(port
->s_port
);
747 else if (!strcmp(service
, "http"))
749 else if (!strcmp(service
, "https"))
751 else if (!strcmp(service
, "ipp") || !strcmp(service
, "ipps"))
753 else if (!strcmp(service
, "lpd"))
755 else if (!strcmp(service
, "socket"))
759 httpAddrFreeList(first
);
761 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, _("Unknown service name."), 1);
765 if (hostname
&& !_cups_strcasecmp(hostname
, "localhost"))
768 * Unfortunately, some users ignore all of the warnings in the
769 * /etc/hosts file and delete "localhost" from it. If we get here
770 * then we were unable to resolve the name, so use the IPv6 and/or
771 * IPv4 loopback interface addresses...
775 if (family
!= AF_INET
)
778 * Add [::1] to the address list...
781 temp
= (http_addrlist_t
*)calloc(1, sizeof(http_addrlist_t
));
784 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(errno
), 0);
785 httpAddrFreeList(first
);
789 temp
->addr
.ipv6
.sin6_family
= AF_INET6
;
790 temp
->addr
.ipv6
.sin6_port
= htons(portnum
);
792 temp
->addr
.ipv6
.sin6_addr
.u
.Byte
[15] = 1;
794 temp
->addr
.ipv6
.sin6_addr
.s6_addr32
[3] = htonl(1);
803 if (family
!= AF_INET6
)
804 #endif /* AF_INET6 */
807 * Add 127.0.0.1 to the address list...
810 temp
= (http_addrlist_t
*)calloc(1, sizeof(http_addrlist_t
));
813 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(errno
), 0);
814 httpAddrFreeList(first
);
818 temp
->addr
.ipv4
.sin_family
= AF_INET
;
819 temp
->addr
.ipv4
.sin_port
= htons(portnum
);
820 temp
->addr
.ipv4
.sin_addr
.s_addr
= htonl(0x7f000001);
832 * Provide one or more passive listening addresses...
836 if (family
!= AF_INET
)
839 * Add [::] to the address list...
842 temp
= (http_addrlist_t
*)calloc(1, sizeof(http_addrlist_t
));
845 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(errno
), 0);
846 httpAddrFreeList(first
);
850 temp
->addr
.ipv6
.sin6_family
= AF_INET6
;
851 temp
->addr
.ipv6
.sin6_port
= htons(portnum
);
859 if (family
!= AF_INET6
)
860 #endif /* AF_INET6 */
863 * Add 0.0.0.0 to the address list...
866 temp
= (http_addrlist_t
*)calloc(1, sizeof(http_addrlist_t
));
869 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(errno
), 0);
870 httpAddrFreeList(first
);
874 temp
->addr
.ipv4
.sin_family
= AF_INET
;
875 temp
->addr
.ipv4
.sin_port
= htons(portnum
);
887 * Return the address list...