X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=cups%2Fhttp-addrlist.c;h=c68b629ddbb8f6a7666b99a576718919f7475860;hb=503b54c9302c8de6207e079a80a89a787eb612ea;hp=d5685312efa459f5242710948158b1ca9726f932;hpb=1ff0402e472496cf5165dbd796df1bdc131a360b;p=thirdparty%2Fcups.git diff --git a/cups/http-addrlist.c b/cups/http-addrlist.c index d5685312e..c68b629dd 100644 --- a/cups/http-addrlist.c +++ b/cups/http-addrlist.c @@ -1,39 +1,38 @@ /* - * "$Id: http-addrlist.c 7460 2008-04-16 02:19:54Z mike $" + * HTTP address list routines for CUPS. * - * HTTP address list routines for the Common UNIX Printing System (CUPS). + * Copyright 2007-2014 by Apple Inc. + * Copyright 1997-2007 by Easy Software Products, all rights reserved. * - * Copyright 2007-2008 by Apple Inc. - * Copyright 1997-2007 by Easy Software Products, all rights reserved. + * These coded instructions, statements, and computer programs are the + * property of Apple Inc. and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "LICENSE.txt" + * which should have been included with this file. If this file is + * file is missing or damaged, see the license at "http://www.cups.org/". * - * These coded instructions, statements, and computer programs are the - * property of Apple Inc. and are protected by Federal copyright - * law. Distribution and use rights are outlined in the file "LICENSE.txt" - * which should have been included with this file. If this file is - * file is missing or damaged, see the license at "http://www.cups.org/". - * - * Contents: - * - * httpAddrConnect() - Connect to any of the addresses in the list. - * httpAddrFreeList() - Free an address list. - * httpAddrGetList() - Get a list of addresses for a hostname. + * This file is subject to the Apple OS-Developed Software exception. */ /* * Include necessary headers... */ -#include "http-private.h" -#include "globals.h" -#include "debug.h" -#include -#include +#include "cups-private.h" +#ifdef HAVE_RESOLV_H +# include +#endif /* HAVE_RESOLV_H */ +#ifdef HAVE_POLL +# include +#endif /* HAVE_POLL */ +#ifndef WIN32 +# include +#endif /* WIN32 */ /* * 'httpAddrConnect()' - Connect to any of the addresses in the list. * - * @since CUPS 1.2@ + * @since CUPS 1.2/OS X 10.5@ */ http_addrlist_t * /* O - Connected address or NULL on failure */ @@ -41,43 +40,96 @@ httpAddrConnect( http_addrlist_t *addrlist, /* I - List of potential addresses */ int *sock) /* O - Socket */ { - int val; /* Socket option value */ + DEBUG_printf(("httpAddrConnect(addrlist=%p, sock=%p)", (void *)addrlist, (void *)sock)); + + return (httpAddrConnect2(addrlist, sock, 30000, NULL)); +} + + +/* + * 'httpAddrConnect2()' - Connect to any of the addresses in the list with a + * timeout and optional cancel. + * + * @since CUPS 1.7/OS X 10.9@ + */ + +http_addrlist_t * /* O - Connected address or NULL on failure */ +httpAddrConnect2( + http_addrlist_t *addrlist, /* I - List of potential addresses */ + int *sock, /* O - Socket */ + int msec, /* I - Timeout in milliseconds */ + int *cancel) /* I - Pointer to "cancel" variable */ +{ + int val; /* Socket option value */ +#ifdef O_NONBLOCK + socklen_t len; /* Length of value */ + http_addr_t peer; /* Peer address */ + int flags, /* Socket flags */ + remaining; /* Remaining timeout */ + int i, /* Looping var */ + nfds, /* Number of file descriptors */ + fds[100], /* Socket file descriptors */ + result; /* Result from select() or poll() */ + http_addrlist_t *addrs[100]; /* Addresses */ +# ifdef HAVE_POLL + struct pollfd pfds[100]; /* Polled file descriptors */ +# else + int max_fd = -1; /* Highest file descriptor */ + fd_set input_set, /* select() input set */ + output_set; /* select() output set */ + struct timeval timeout; /* Timeout */ +# endif /* HAVE_POLL */ +#endif /* O_NONBLOCK */ #ifdef DEBUG - char temp[256]; /* Temporary address string */ + char temp[256]; /* Temporary address string */ #endif /* DEBUG */ - DEBUG_printf(("httpAddrConnect(addrlist=%p, sock=%p)\n", addrlist, sock)); + DEBUG_printf(("httpAddrConnect2(addrlist=%p, sock=%p, msec=%d, cancel=%p)", (void *)addrlist, (void *)sock, msec, (void *)cancel)); if (!sock) { errno = EINVAL; + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0); return (NULL); } + if (cancel && *cancel) + return (NULL); + + if (msec <= 0) + msec = INT_MAX; + /* * Loop through each address until we connect or run out of addresses... */ - while (addrlist) + for (nfds = 0; addrlist && nfds < (int)(sizeof(fds) / sizeof(fds[0])); addrlist = addrlist->next) { + if (cancel && *cancel) + { + while (nfds > 0) + { + nfds --; + httpAddrClose(NULL, fds[nfds]); + } + + return (NULL); + } + /* * Create the socket... */ - DEBUG_printf(("httpAddrConnect: Trying %s:%d...\n", - httpAddrString(&(addrlist->addr), temp, sizeof(temp)), - _httpAddrPort(&(addrlist->addr)))); + DEBUG_printf(("2httpAddrConnect2: Trying %s:%d...", httpAddrString(&(addrlist->addr), temp, sizeof(temp)), httpAddrPort(&(addrlist->addr)))); - if ((*sock = (int)socket(addrlist->addr.addr.sa_family, SOCK_STREAM, - 0)) < 0) + if ((fds[nfds] = (int)socket(httpAddrFamily(&(addrlist->addr)), SOCK_STREAM, 0)) < 0) { /* * Don't abort yet, as this could just be an issue with the local * system not being configured with IPv4/IPv6/domain socket enabled... */ - addrlist = addrlist->next; continue; } @@ -86,21 +138,16 @@ httpAddrConnect( */ val = 1; -#ifdef WIN32 - setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, - sizeof(val)); -#else - setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); -#endif /* WIN32 */ + setsockopt(fds[nfds], SOL_SOCKET, SO_REUSEADDR, CUPS_SOCAST &val, sizeof(val)); #ifdef SO_REUSEPORT val = 1; - setsockopt(*sock, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val)); + setsockopt(fds[nfds], SOL_SOCKET, SO_REUSEPORT, CUPS_SOCAST &val, sizeof(val)); #endif /* SO_REUSEPORT */ #ifdef SO_NOSIGPIPE val = 1; - setsockopt(*sock, SOL_SOCKET, SO_NOSIGPIPE, &val, sizeof(val)); + setsockopt(fds[nfds], SOL_SOCKET, SO_NOSIGPIPE, CUPS_SOCAST &val, sizeof(val)); #endif /* SO_NOSIGPIPE */ /* @@ -109,60 +156,227 @@ httpAddrConnect( */ val = 1; -#ifdef WIN32 - setsockopt(*sock, IPPROTO_TCP, TCP_NODELAY, (const char *)&val, - sizeof(val)); -#else - setsockopt(*sock, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)); -#endif /* WIN32 */ + setsockopt(fds[nfds], IPPROTO_TCP, TCP_NODELAY, CUPS_SOCAST &val, sizeof(val)); #ifdef FD_CLOEXEC /* * Close this socket when starting another process... */ - fcntl(*sock, F_SETFD, FD_CLOEXEC); + fcntl(fds[nfds], F_SETFD, FD_CLOEXEC); #endif /* FD_CLOEXEC */ +#ifdef O_NONBLOCK + /* + * Do an asynchronous connect by setting the socket non-blocking... + */ + + DEBUG_printf(("httpAddrConnect2: Setting non-blocking connect()")); + + flags = fcntl(fds[nfds], F_GETFL, 0); + fcntl(fds[nfds], F_SETFL, flags | O_NONBLOCK); +#endif /* O_NONBLOCK */ + /* * Then connect... */ - if (!connect(*sock, &(addrlist->addr.addr), - httpAddrLength(&(addrlist->addr)))) + if (!connect(fds[nfds], &(addrlist->addr.addr), (socklen_t)httpAddrLength(&(addrlist->addr)))) { - DEBUG_printf(("httpAddrConnect: Connected to %s:%d...\n", - httpAddrString(&(addrlist->addr), temp, sizeof(temp)), - _httpAddrPort(&(addrlist->addr)))); - break; + DEBUG_printf(("1httpAddrConnect2: Connected to %s:%d...", httpAddrString(&(addrlist->addr), temp, sizeof(temp)), httpAddrPort(&(addrlist->addr)))); + +#ifdef O_NONBLOCK + fcntl(fds[nfds], F_SETFL, flags); +#endif /* O_NONBLOCK */ + + *sock = fds[nfds]; + + while (nfds > 0) + { + nfds --; + httpAddrClose(NULL, fds[nfds]); + } + + return (addrlist); + } + +#ifdef WIN32 + if (WSAGetLastError() != WSAEINPROGRESS && WSAGetLastError() != WSAEWOULDBLOCK) +#else + if (errno != EINPROGRESS && errno != EWOULDBLOCK) +#endif /* WIN32 */ + { + DEBUG_printf(("1httpAddrConnect2: Unable to connect to %s:%d: %s", httpAddrString(&(addrlist->addr), temp, sizeof(temp)), httpAddrPort(&(addrlist->addr)), strerror(errno))); + httpAddrClose(NULL, fds[nfds]); + continue; } - DEBUG_printf(("httpAddrConnect: Unable to connect to %s:%d: %s\n", - httpAddrString(&(addrlist->addr), temp, sizeof(temp)), - _httpAddrPort(&(addrlist->addr)), strerror(errno))); + fcntl(fds[nfds], F_SETFL, flags); - /* - * Close this socket and move to the next address... - */ +#ifndef HAVE_POLL + if (fds[nfds] > max_fd) + max_fd = fds[nfds]; +#endif /* !HAVE_POLL */ + + addrs[nfds] = addrlist; + nfds ++; + } + +#ifdef O_NONBLOCK + DEBUG_puts("1httpAddrConnect2: Finishing async connect()"); + + for (remaining = msec; remaining > 0; remaining -= 250) + { + do + { + if (cancel && *cancel) + { + /* + * Close this socket and return... + */ + + DEBUG_puts("1httpAddrConnect2: Canceled connect()"); + + while (nfds > 0) + { + nfds --; + httpAddrClose(NULL, fds[nfds]); + } + + *sock = -1; + + return (NULL); + } + +# ifdef HAVE_POLL + for (i = 0; i < nfds; i ++) + { + pfds[i].fd = fds[i]; + pfds[i].events = POLLIN | POLLOUT; + } + + result = poll(pfds, (nfds_t)nfds, remaining > 250 ? 250 : remaining); + + DEBUG_printf(("1httpAddrConnect2: poll() returned %d (%d)", result, errno)); + +# else + FD_ZERO(&input_set); + for (i = 0; i < nfds; i ++) + FD_SET(fds[i], &input_set); + output_set = input_set; + + timeout.tv_sec = 0; + timeout.tv_usec = (remaining > 250 ? 250 : remaining) * 1000; + + result = select(max_fd + 1, &input_set, &output_set, NULL, &timeout); + + DEBUG_printf(("1httpAddrConnect2: select() returned %d (%d)", result, errno)); +# endif /* HAVE_POLL */ + } +# ifdef WIN32 + while (result < 0 && (WSAGetLastError() == WSAEINTR || WSAGetLastError() == WSAEWOULDBLOCK)); +# else + while (result < 0 && (errno == EINTR || errno == EAGAIN)); +# endif /* WIN32 */ + + if (result > 0) + { + for (i = 0; i < nfds; i ++) + { +# ifdef HAVE_POLL + DEBUG_printf(("pfds[%d].revents=%x\n", i, pfds[i].revents)); + if (pfds[i].revents) +# else + if (FD_ISSET(fds[i], &input)) +# endif /* HAVE_POLL */ + { + *sock = fds[i]; + len = sizeof(peer); + if (!getpeername(fds[i], (struct sockaddr *)&peer, &len)) + { + DEBUG_printf(("1httpAddrConnect2: Connected to %s:%d...", httpAddrString(&peer, temp, sizeof(temp)), httpAddrPort(&peer))); + + addrlist = addrs[i]; + } + } + else + httpAddrClose(NULL, fds[i]); + } + + return (addrlist); + } + } +#endif /* O_NONBLOCK */ + + while (nfds > 0) + { + nfds --; + httpAddrClose(NULL, fds[nfds]); + } #ifdef WIN32 - closesocket(*sock); + _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE, "Connection failed", 0); #else - close(*sock); + _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE, strerror(errno), 0); #endif /* WIN32 */ - *sock = -1; - addrlist = addrlist->next; + return (NULL); +} + + +/* + * 'httpAddrCopyList()' - Copy an address list. + * + * @since CUPS 1.7/OS X 10.9@ + */ + +http_addrlist_t * /* O - New address list or @code NULL@ on error */ +httpAddrCopyList( + http_addrlist_t *src) /* I - Source address list */ +{ + http_addrlist_t *dst = NULL, /* First list entry */ + *prev = NULL, /* Previous list entry */ + *current = NULL;/* Current list entry */ + + + while (src) + { + if ((current = malloc(sizeof(http_addrlist_t))) == NULL) + { + current = dst; + + while (current) + { + prev = current; + current = current->next; + + free(prev); + } + + return (NULL); + } + + memcpy(current, src, sizeof(http_addrlist_t)); + + current->next = NULL; + + if (prev) + prev->next = current; + else + dst = current; + + prev = current; + src = src->next; } - return (addrlist); + return (dst); } /* * 'httpAddrFreeList()' - Free an address list. * - * @since CUPS 1.2@ + * @since CUPS 1.2/OS X 10.5@ */ void @@ -190,7 +404,7 @@ httpAddrFreeList( /* * 'httpAddrGetList()' - Get a list of addresses for a hostname. * - * @since CUPS 1.2@ + * @since CUPS 1.2/OS X 10.5@ */ http_addrlist_t * /* O - List of addresses or NULL */ @@ -201,6 +415,8 @@ httpAddrGetList(const char *hostname, /* I - Hostname, IP address, or NULL for p http_addrlist_t *first, /* First address in list */ *addr, /* Current address in list */ *temp; /* New address */ + _cups_globals_t *cg = _cupsGlobals(); + /* Global data */ #ifdef DEBUG @@ -217,6 +433,27 @@ httpAddrGetList(const char *hostname, /* I - Hostname, IP address, or NULL for p family == AF_INET ? "INET" : "???", service); #endif /* DEBUG */ +#ifdef HAVE_RES_INIT + /* + * STR #2920: Initialize resolver after failure in cups-polld + * + * If the previous lookup failed, re-initialize the resolver to prevent + * temporary network errors from persisting. This *should* be handled by + * the resolver libraries, but apparently the glibc folks do not agree. + * + * We set a flag at the end of this function if we encounter an error that + * requires reinitialization of the resolver functions. We then call + * res_init() if the flag is set on the next call here or in httpAddrLookup(). + */ + + if (cg->need_res_init) + { + res_init(); + + cg->need_res_init = 0; + } +#endif /* HAVE_RES_INIT */ + /* * Lookup the address the best way we can... */ @@ -232,20 +469,24 @@ httpAddrGetList(const char *hostname, /* I - Hostname, IP address, or NULL for p if ((first = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t))) != NULL) { + addr = first; first->addr.un.sun_family = AF_LOCAL; strlcpy(first->addr.un.sun_path, hostname, sizeof(first->addr.un.sun_path)); } } else #endif /* AF_LOCAL */ + if (!hostname || _cups_strcasecmp(hostname, "localhost")) { #ifdef HAVE_GETADDRINFO struct addrinfo hints, /* Address lookup hints */ *results, /* Address lookup results */ *current; /* Current result */ - char ipv6[1024], /* IPv6 address */ + char ipv6[64], /* IPv6 address */ *ipv6zone; /* Pointer to zone separator */ int ipv6len; /* Length of IPv6 address */ + int error; /* getaddrinfo() error */ + /* * Lookup the address as needed... @@ -297,7 +538,7 @@ httpAddrGetList(const char *hostname, /* I - Hostname, IP address, or NULL for p } } - if (!getaddrinfo(hostname, service, &hints, &results)) + if ((error = getaddrinfo(hostname, service, &hints, &results)) == 0) { /* * Copy the results to our own address list structure... @@ -314,6 +555,7 @@ httpAddrGetList(const char *hostname, /* I - Hostname, IP address, or NULL for p if (!temp) { httpAddrFreeList(first); + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0); return (NULL); } @@ -343,6 +585,14 @@ httpAddrGetList(const char *hostname, /* I - Hostname, IP address, or NULL for p freeaddrinfo(results); } + else + { + if (error == EAI_FAIL) + cg->need_res_init = 1; + + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, gai_strerror(error), 0); + } + #else if (hostname) { @@ -368,7 +618,7 @@ httpAddrGetList(const char *hostname, /* I - Hostname, IP address, or NULL for p portnum = 80; else if (!strcmp(service, "https")) portnum = 443; - else if (!strcmp(service, "ipp")) + else if (!strcmp(service, "ipp") || !strcmp(service, "ipps")) portnum = 631; else if (!strcmp(service, "lpd")) portnum = 515; @@ -403,9 +653,10 @@ httpAddrGetList(const char *hostname, /* I - Hostname, IP address, or NULL for p return (NULL); first->addr.ipv4.sin_family = AF_INET; - first->addr.ipv4.sin_addr.s_addr = htonl(((((((ip[0] << 8) | - ip[1]) << 8) | - ip[2]) << 8) | ip[3])); + first->addr.ipv4.sin_addr.s_addr = htonl((((((((unsigned)ip[0] << 8) | + (unsigned)ip[1]) << 8) | + (unsigned)ip[2]) << 8) | + (unsigned)ip[3])); first->addr.ipv4.sin_port = htons(portnum); } } @@ -459,6 +710,13 @@ httpAddrGetList(const char *hostname, /* I - Hostname, IP address, or NULL for p addr = temp; } } + else + { + if (h_errno == NO_RECOVERY) + cg->need_res_init = 1; + + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, hstrerror(h_errno), 0); + } } #endif /* HAVE_GETADDRINFO */ } @@ -467,7 +725,7 @@ httpAddrGetList(const char *hostname, /* I - Hostname, IP address, or NULL for p * Detect some common errors and handle them sanely... */ - if (!addr && (!hostname || !strcmp(hostname, "localhost"))) + if (!addr && (!hostname || !_cups_strcasecmp(hostname, "localhost"))) { struct servent *port; /* Port number for service */ int portnum; /* Port number */ @@ -487,16 +745,21 @@ httpAddrGetList(const char *hostname, /* I - Hostname, IP address, or NULL for p portnum = 80; else if (!strcmp(service, "https")) portnum = 443; - else if (!strcmp(service, "ipp")) + else if (!strcmp(service, "ipp") || !strcmp(service, "ipps")) portnum = 631; else if (!strcmp(service, "lpd")) portnum = 515; else if (!strcmp(service, "socket")) portnum = 9100; else + { + httpAddrFreeList(first); + + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unknown service name."), 1); return (NULL); + } - if (hostname && !strcmp(hostname, "localhost")) + if (hostname && !_cups_strcasecmp(hostname, "localhost")) { /* * Unfortunately, some users ignore all of the warnings in the @@ -515,6 +778,7 @@ httpAddrGetList(const char *hostname, /* I - Hostname, IP address, or NULL for p temp = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t)); if (!temp) { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0); httpAddrFreeList(first); return (NULL); } @@ -543,6 +807,7 @@ httpAddrGetList(const char *hostname, /* I - Hostname, IP address, or NULL for p temp = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t)); if (!temp) { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0); httpAddrFreeList(first); return (NULL); } @@ -574,6 +839,7 @@ httpAddrGetList(const char *hostname, /* I - Hostname, IP address, or NULL for p temp = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t)); if (!temp) { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0); httpAddrFreeList(first); return (NULL); } @@ -597,6 +863,7 @@ httpAddrGetList(const char *hostname, /* I - Hostname, IP address, or NULL for p temp = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t)); if (!temp) { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0); httpAddrFreeList(first); return (NULL); } @@ -619,8 +886,3 @@ httpAddrGetList(const char *hostname, /* I - Hostname, IP address, or NULL for p return (first); } - - -/* - * End of "$Id: http-addrlist.c 7460 2008-04-16 02:19:54Z mike $". - */