2 * HTTP address list routines for CUPS.
4 * Copyright 2007-2018 by Apple Inc.
5 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more
12 * Include necessary headers...
15 #include "cups-private.h"
16 #include "debug-internal.h"
19 #endif /* HAVE_RESOLV_H */
22 #endif /* HAVE_POLL */
29 * 'httpAddrConnect()' - Connect to any of the addresses in the list.
31 * @since CUPS 1.2/macOS 10.5@ @exclude all@
34 http_addrlist_t
* /* O - Connected address or NULL on failure */
36 http_addrlist_t
*addrlist
, /* I - List of potential addresses */
37 int *sock
) /* O - Socket */
39 DEBUG_printf(("httpAddrConnect(addrlist=%p, sock=%p)", (void *)addrlist
, (void *)sock
));
41 return (httpAddrConnect2(addrlist
, sock
, 30000, NULL
));
46 * 'httpAddrConnect2()' - Connect to any of the addresses in the list with a
47 * timeout and optional cancel.
49 * @since CUPS 1.7/macOS 10.9@
52 http_addrlist_t
* /* O - Connected address or NULL on failure */
54 http_addrlist_t
*addrlist
, /* I - List of potential addresses */
55 int *sock
, /* O - Socket */
56 int msec
, /* I - Timeout in milliseconds */
57 int *cancel
) /* I - Pointer to "cancel" variable */
59 int val
; /* Socket option value */
61 int i
, j
, /* Looping vars */
62 flags
, /* Socket flags */
63 result
; /* Result from select() or poll() */
65 int remaining
; /* Remaining timeout */
66 int nfds
, /* Number of file descriptors */
67 fds
[100]; /* Socket file descriptors */
68 http_addrlist_t
*addrs
[100]; /* Addresses */
70 int max_fd
= -1; /* Highest file descriptor */
71 #endif /* !HAVE_POLL */
74 struct pollfd pfds
[100]; /* Polled file descriptors */
76 fd_set input_set
, /* select() input set */
77 output_set
, /* select() output set */
78 error_set
; /* select() error set */
79 struct timeval timeout
; /* Timeout */
80 # endif /* HAVE_POLL */
81 #endif /* O_NONBLOCK */
84 socklen_t len
; /* Length of value */
85 http_addr_t peer
; /* Peer address */
87 char temp
[256]; /* Temporary address string */
91 DEBUG_printf(("httpAddrConnect2(addrlist=%p, sock=%p, msec=%d, cancel=%p)", (void *)addrlist
, (void *)sock
, msec
, (void *)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...
113 while (remaining
> 0)
115 if (cancel
&& *cancel
)
120 httpAddrClose(NULL
, fds
[nfds
]);
126 if (addrlist
&& nfds
< (int)(sizeof(fds
) / sizeof(fds
[0])))
129 * Create the socket...
132 DEBUG_printf(("2httpAddrConnect2: Trying %s:%d...", httpAddrString(&(addrlist
->addr
), temp
, sizeof(temp
)), httpAddrPort(&(addrlist
->addr
))));
134 if ((fds
[nfds
] = (int)socket(httpAddrFamily(&(addrlist
->addr
)), SOCK_STREAM
, 0)) < 0)
137 * Don't abort yet, as this could just be an issue with the local
138 * system not being configured with IPv4/IPv6/domain socket enabled.
140 * Just skip this address...
143 addrlist
= addrlist
->next
;
152 setsockopt(fds
[nfds
], SOL_SOCKET
, SO_REUSEADDR
, CUPS_SOCAST
&val
, sizeof(val
));
156 setsockopt(fds
[nfds
], SOL_SOCKET
, SO_REUSEPORT
, CUPS_SOCAST
&val
, sizeof(val
));
157 #endif /* SO_REUSEPORT */
161 setsockopt(fds
[nfds
], SOL_SOCKET
, SO_NOSIGPIPE
, CUPS_SOCAST
&val
, sizeof(val
));
162 #endif /* SO_NOSIGPIPE */
165 * Using TCP_NODELAY improves responsiveness, especially on systems
166 * with a slow loopback interface...
170 setsockopt(fds
[nfds
], IPPROTO_TCP
, TCP_NODELAY
, CUPS_SOCAST
&val
, sizeof(val
));
174 * Close this socket when starting another process...
177 fcntl(fds
[nfds
], F_SETFD
, FD_CLOEXEC
);
178 #endif /* FD_CLOEXEC */
182 * Do an asynchronous connect by setting the socket non-blocking...
185 DEBUG_printf(("httpAddrConnect2: Setting non-blocking connect()"));
187 flags
= fcntl(fds
[nfds
], F_GETFL
, 0);
188 fcntl(fds
[nfds
], F_SETFL
, flags
| O_NONBLOCK
);
189 #endif /* O_NONBLOCK */
195 if (!connect(fds
[nfds
], &(addrlist
->addr
.addr
), (socklen_t
)httpAddrLength(&(addrlist
->addr
))))
197 DEBUG_printf(("1httpAddrConnect2: Connected to %s:%d...", httpAddrString(&(addrlist
->addr
), temp
, sizeof(temp
)), httpAddrPort(&(addrlist
->addr
))));
200 fcntl(fds
[nfds
], F_SETFL
, flags
);
201 #endif /* O_NONBLOCK */
208 httpAddrClose(NULL
, fds
[nfds
]);
215 if (WSAGetLastError() != WSAEINPROGRESS
&& WSAGetLastError() != WSAEWOULDBLOCK
)
217 if (errno
!= EINPROGRESS
&& errno
!= EWOULDBLOCK
)
220 DEBUG_printf(("1httpAddrConnect2: Unable to connect to %s:%d: %s", httpAddrString(&(addrlist
->addr
), temp
, sizeof(temp
)), httpAddrPort(&(addrlist
->addr
)), strerror(errno
)));
221 httpAddrClose(NULL
, fds
[nfds
]);
222 addrlist
= addrlist
->next
;
227 fcntl(fds
[nfds
], F_SETFL
, flags
);
231 if (fds
[nfds
] > max_fd
)
233 #endif /* !HAVE_POLL */
235 addrs
[nfds
] = addrlist
;
237 addrlist
= addrlist
->next
;
240 if (!addrlist
&& nfds
== 0)
243 errno
= WSAEHOSTDOWN
;
251 * See if we can connect to any of the addresses so far...
255 DEBUG_puts("1httpAddrConnect2: Finishing async connect()");
259 if (cancel
&& *cancel
)
262 * Close this socket and return...
265 DEBUG_puts("1httpAddrConnect2: Canceled connect()");
270 httpAddrClose(NULL
, fds
[nfds
]);
279 for (i
= 0; i
< nfds
; i
++)
282 pfds
[i
].events
= POLLIN
| POLLOUT
;
285 result
= poll(pfds
, (nfds_t
)nfds
, addrlist
? 100 : remaining
> 250 ? 250 : remaining
);
287 DEBUG_printf(("1httpAddrConnect2: poll() returned %d (%d)", result
, errno
));
291 for (i
= 0; i
< nfds
; i
++)
292 FD_SET(fds
[i
], &input_set
);
293 output_set
= input_set
;
294 error_set
= input_set
;
297 timeout
.tv_usec
= (addrlist
? 100 : remaining
> 250 ? 250 : remaining
) * 1000;
299 result
= select(max_fd
+ 1, &input_set
, &output_set
, &error_set
, &timeout
);
301 DEBUG_printf(("1httpAddrConnect2: select() returned %d (%d)", result
, errno
));
302 # endif /* HAVE_POLL */
305 while (result
< 0 && (WSAGetLastError() == WSAEINTR
|| WSAGetLastError() == WSAEWOULDBLOCK
));
307 while (result
< 0 && (errno
== EINTR
|| errno
== EAGAIN
));
312 http_addrlist_t
*connaddr
= NULL
; /* Connected address, if any */
314 for (i
= 0; i
< nfds
; i
++)
317 DEBUG_printf(("pfds[%d].revents=%x\n", i
, pfds
[i
].revents
));
318 if (pfds
[i
].revents
&& !(pfds
[i
].revents
& (POLLERR
| POLLHUP
)))
320 if (FD_ISSET(fds
[i
], &input_set
) && !FD_ISSET(fds
[i
], &error_set
))
321 # endif /* HAVE_POLL */
328 if (!getpeername(fds
[i
], (struct sockaddr
*)&peer
, &len
))
329 DEBUG_printf(("1httpAddrConnect2: Connected to %s:%d...", httpAddrString(&peer
, temp
, sizeof(temp
)), httpAddrPort(&peer
)));
335 else if (pfds
[i
].revents
& (POLLERR
| POLLHUP
))
337 else if (FD_ISSET(fds
[i
], &error_set
))
338 # endif /* HAVE_POLL */
341 * Error on socket, remove from the "pool"...
344 httpAddrClose(NULL
, fds
[i
]);
348 memmove(fds
+ i
, fds
+ i
+ 1, (size_t)(nfds
- i
) * (sizeof(fds
[0])));
349 memmove(addrs
+ i
, addrs
+ i
+ 1, (size_t)(nfds
- i
) * (sizeof(addrs
[0])));
358 * Connected on one address, close all of the other sockets we have so
362 for (j
= 0; j
< i
; j
++)
363 httpAddrClose(NULL
, fds
[j
]);
365 for (j
++; j
< nfds
; j
++)
366 httpAddrClose(NULL
, fds
[j
]);
371 #endif /* O_NONBLOCK */
385 httpAddrClose(NULL
, fds
[nfds
]);
389 _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE
, "Connection failed", 0);
391 _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE
, strerror(errno
), 0);
399 * 'httpAddrCopyList()' - Copy an address list.
401 * @since CUPS 1.7/macOS 10.9@
404 http_addrlist_t
* /* O - New address list or @code NULL@ on error */
406 http_addrlist_t
*src
) /* I - Source address list */
408 http_addrlist_t
*dst
= NULL
, /* First list entry */
409 *prev
= NULL
, /* Previous list entry */
410 *current
= NULL
;/* Current list entry */
415 if ((current
= malloc(sizeof(http_addrlist_t
))) == NULL
)
422 current
= current
->next
;
430 memcpy(current
, src
, sizeof(http_addrlist_t
));
432 current
->next
= NULL
;
435 prev
->next
= current
;
448 * 'httpAddrFreeList()' - Free an address list.
450 * @since CUPS 1.2/macOS 10.5@
455 http_addrlist_t
*addrlist
) /* I - Address list to free */
457 http_addrlist_t
*next
; /* Next address in list */
461 * Free each address in the list...
466 next
= addrlist
->next
;
476 * 'httpAddrGetList()' - Get a list of addresses for a hostname.
478 * @since CUPS 1.2/macOS 10.5@
481 http_addrlist_t
* /* O - List of addresses or NULL */
482 httpAddrGetList(const char *hostname
, /* I - Hostname, IP address, or NULL for passive listen address */
483 int family
, /* I - Address family or AF_UNSPEC */
484 const char *service
) /* I - Service name or port number */
486 http_addrlist_t
*first
, /* First address in list */
487 *addr
, /* Current address in list */
488 *temp
; /* New address */
489 _cups_globals_t
*cg
= _cupsGlobals();
494 _cups_debug_printf("httpAddrGetList(hostname=\"%s\", family=AF_%s, "
496 hostname
? hostname
: "(nil)",
497 family
== AF_UNSPEC
? "UNSPEC" :
499 family
== AF_LOCAL
? "LOCAL" :
500 # endif /* AF_LOCAL */
502 family
== AF_INET6
? "INET6" :
503 # endif /* AF_INET6 */
504 family
== AF_INET
? "INET" : "???", service
);
509 * STR #2920: Initialize resolver after failure in cups-polld
511 * If the previous lookup failed, re-initialize the resolver to prevent
512 * temporary network errors from persisting. This *should* be handled by
513 * the resolver libraries, but apparently the glibc folks do not agree.
515 * We set a flag at the end of this function if we encounter an error that
516 * requires reinitialization of the resolver functions. We then call
517 * res_init() if the flag is set on the next call here or in httpAddrLookup().
520 if (cg
->need_res_init
)
524 cg
->need_res_init
= 0;
526 #endif /* HAVE_RES_INIT */
529 * Lookup the address the best way we can...
535 if (hostname
&& hostname
[0] == '/')
538 * Domain socket address...
541 if ((first
= (http_addrlist_t
*)calloc(1, sizeof(http_addrlist_t
))) != NULL
)
544 first
->addr
.un
.sun_family
= AF_LOCAL
;
545 strlcpy(first
->addr
.un
.sun_path
, hostname
, sizeof(first
->addr
.un
.sun_path
));
549 #endif /* AF_LOCAL */
550 if (!hostname
|| _cups_strcasecmp(hostname
, "localhost"))
552 #ifdef HAVE_GETADDRINFO
553 struct addrinfo hints
, /* Address lookup hints */
554 *results
, /* Address lookup results */
555 *current
; /* Current result */
556 char ipv6
[64], /* IPv6 address */
557 *ipv6zone
; /* Pointer to zone separator */
558 int ipv6len
; /* Length of IPv6 address */
559 int error
; /* getaddrinfo() error */
563 * Lookup the address as needed...
566 memset(&hints
, 0, sizeof(hints
));
567 hints
.ai_family
= family
;
568 hints
.ai_flags
= hostname
? 0 : AI_PASSIVE
;
569 hints
.ai_socktype
= SOCK_STREAM
;
571 if (hostname
&& *hostname
== '[')
574 * Remove brackets from numeric IPv6 address...
577 if (!strncmp(hostname
, "[v1.", 4))
580 * Copy the newer address format which supports link-local addresses...
583 strlcpy(ipv6
, hostname
+ 4, sizeof(ipv6
));
584 if ((ipv6len
= (int)strlen(ipv6
) - 1) >= 0 && ipv6
[ipv6len
] == ']')
586 ipv6
[ipv6len
] = '\0';
590 * Convert "+zone" in address to "%zone"...
593 if ((ipv6zone
= strrchr(ipv6
, '+')) != NULL
)
600 * Copy the regular non-link-local IPv6 address...
603 strlcpy(ipv6
, hostname
+ 1, sizeof(ipv6
));
604 if ((ipv6len
= (int)strlen(ipv6
) - 1) >= 0 && ipv6
[ipv6len
] == ']')
606 ipv6
[ipv6len
] = '\0';
612 if ((error
= getaddrinfo(hostname
, service
, &hints
, &results
)) == 0)
615 * Copy the results to our own address list structure...
618 for (current
= results
; current
; current
= current
->ai_next
)
619 if (current
->ai_family
== AF_INET
|| current
->ai_family
== AF_INET6
)
622 * Copy the address over...
625 temp
= (http_addrlist_t
*)calloc(1, sizeof(http_addrlist_t
));
628 httpAddrFreeList(first
);
629 freeaddrinfo(results
);
630 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(errno
), 0);
634 if (current
->ai_family
== AF_INET6
)
635 memcpy(&(temp
->addr
.ipv6
), current
->ai_addr
,
636 sizeof(temp
->addr
.ipv6
));
638 memcpy(&(temp
->addr
.ipv4
), current
->ai_addr
,
639 sizeof(temp
->addr
.ipv4
));
642 * Append the address to the list...
655 * Free the results from getaddrinfo()...
658 freeaddrinfo(results
);
662 if (error
== EAI_FAIL
)
663 cg
->need_res_init
= 1;
665 # ifdef _WIN32 /* Really, Microsoft?!? */
666 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, gai_strerrorA(error
), 0);
668 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, gai_strerror(error
), 0);
675 int i
; /* Looping vars */
676 unsigned ip
[4]; /* IPv4 address components */
677 const char *ptr
; /* Pointer into hostname */
678 struct hostent
*host
; /* Result of lookup */
679 struct servent
*port
; /* Port number for service */
680 int portnum
; /* Port number */
684 * Lookup the service...
689 else if (isdigit(*service
& 255))
690 portnum
= atoi(service
);
691 else if ((port
= getservbyname(service
, NULL
)) != NULL
)
692 portnum
= ntohs(port
->s_port
);
693 else if (!strcmp(service
, "http"))
695 else if (!strcmp(service
, "https"))
697 else if (!strcmp(service
, "ipp") || !strcmp(service
, "ipps"))
699 else if (!strcmp(service
, "lpd"))
701 else if (!strcmp(service
, "socket"))
707 * This code is needed because some operating systems have a
708 * buggy implementation of gethostbyname() that does not support
709 * IPv4 addresses. If the hostname string is an IPv4 address, then
710 * sscanf() is used to extract the IPv4 components. We then pack
711 * the components into an IPv4 address manually, since the
712 * inet_aton() function is deprecated. We use the htonl() macro
713 * to get the right byte order for the address.
716 for (ptr
= hostname
; isdigit(*ptr
& 255) || *ptr
== '.'; ptr
++);
721 * We have an IPv4 address; break it up and create an IPv4 address...
724 if (sscanf(hostname
, "%u.%u.%u.%u", ip
, ip
+ 1, ip
+ 2, ip
+ 3) == 4 &&
725 ip
[0] <= 255 && ip
[1] <= 255 && ip
[2] <= 255 && ip
[3] <= 255)
727 first
= (http_addrlist_t
*)calloc(1, sizeof(http_addrlist_t
));
731 first
->addr
.ipv4
.sin_family
= AF_INET
;
732 first
->addr
.ipv4
.sin_addr
.s_addr
= htonl((((((((unsigned)ip
[0] << 8) |
733 (unsigned)ip
[1]) << 8) |
734 (unsigned)ip
[2]) << 8) |
736 first
->addr
.ipv4
.sin_port
= htons(portnum
);
739 else if ((host
= gethostbyname(hostname
)) != NULL
&&
741 (host
->h_addrtype
== AF_INET
|| host
->h_addrtype
== AF_INET6
))
743 host
->h_addrtype
== AF_INET
)
744 # endif /* AF_INET6 */
746 for (i
= 0; host
->h_addr_list
[i
]; i
++)
749 * Copy the address over...
752 temp
= (http_addrlist_t
*)calloc(1, sizeof(http_addrlist_t
));
755 httpAddrFreeList(first
);
760 if (host
->h_addrtype
== AF_INET6
)
762 temp
->addr
.ipv6
.sin6_family
= AF_INET6
;
763 memcpy(&(temp
->addr
.ipv6
.sin6_addr
), host
->h_addr_list
[i
],
764 sizeof(temp
->addr
.ipv6
));
765 temp
->addr
.ipv6
.sin6_port
= htons(portnum
);
768 # endif /* AF_INET6 */
770 temp
->addr
.ipv4
.sin_family
= AF_INET
;
771 memcpy(&(temp
->addr
.ipv4
.sin_addr
), host
->h_addr_list
[i
],
772 sizeof(temp
->addr
.ipv4
));
773 temp
->addr
.ipv4
.sin_port
= htons(portnum
);
777 * Append the address to the list...
791 if (h_errno
== NO_RECOVERY
)
792 cg
->need_res_init
= 1;
794 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, hstrerror(h_errno
), 0);
797 #endif /* HAVE_GETADDRINFO */
801 * Detect some common errors and handle them sanely...
804 if (!addr
&& (!hostname
|| !_cups_strcasecmp(hostname
, "localhost")))
806 struct servent
*port
; /* Port number for service */
807 int portnum
; /* Port number */
811 * Lookup the service...
816 else if (isdigit(*service
& 255))
817 portnum
= atoi(service
);
818 else if ((port
= getservbyname(service
, NULL
)) != NULL
)
819 portnum
= ntohs(port
->s_port
);
820 else if (!strcmp(service
, "http"))
822 else if (!strcmp(service
, "https"))
824 else if (!strcmp(service
, "ipp") || !strcmp(service
, "ipps"))
826 else if (!strcmp(service
, "lpd"))
828 else if (!strcmp(service
, "socket"))
832 httpAddrFreeList(first
);
834 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, _("Unknown service name."), 1);
838 if (hostname
&& !_cups_strcasecmp(hostname
, "localhost"))
841 * Unfortunately, some users ignore all of the warnings in the
842 * /etc/hosts file and delete "localhost" from it. If we get here
843 * then we were unable to resolve the name, so use the IPv6 and/or
844 * IPv4 loopback interface addresses...
848 if (family
!= AF_INET
)
851 * Add [::1] to the address list...
854 temp
= (http_addrlist_t
*)calloc(1, sizeof(http_addrlist_t
));
857 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(errno
), 0);
858 httpAddrFreeList(first
);
862 temp
->addr
.ipv6
.sin6_family
= AF_INET6
;
863 temp
->addr
.ipv6
.sin6_port
= htons(portnum
);
865 temp
->addr
.ipv6
.sin6_addr
.u
.Byte
[15] = 1;
867 temp
->addr
.ipv6
.sin6_addr
.s6_addr32
[3] = htonl(1);
876 if (family
!= AF_INET6
)
877 #endif /* AF_INET6 */
880 * Add 127.0.0.1 to the address list...
883 temp
= (http_addrlist_t
*)calloc(1, sizeof(http_addrlist_t
));
886 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(errno
), 0);
887 httpAddrFreeList(first
);
891 temp
->addr
.ipv4
.sin_family
= AF_INET
;
892 temp
->addr
.ipv4
.sin_port
= htons(portnum
);
893 temp
->addr
.ipv4
.sin_addr
.s_addr
= htonl(0x7f000001);
905 * Provide one or more passive listening addresses...
909 if (family
!= AF_INET
)
912 * Add [::] to the address list...
915 temp
= (http_addrlist_t
*)calloc(1, sizeof(http_addrlist_t
));
918 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(errno
), 0);
919 httpAddrFreeList(first
);
923 temp
->addr
.ipv6
.sin6_family
= AF_INET6
;
924 temp
->addr
.ipv6
.sin6_port
= htons(portnum
);
932 if (family
!= AF_INET6
)
933 #endif /* AF_INET6 */
936 * Add 0.0.0.0 to the address list...
939 temp
= (http_addrlist_t
*)calloc(1, sizeof(http_addrlist_t
));
942 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(errno
), 0);
943 httpAddrFreeList(first
);
947 temp
->addr
.ipv4
.sin_family
= AF_INET
;
948 temp
->addr
.ipv4
.sin_port
= htons(portnum
);
960 * Return the address list...