]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - cups/http-addrlist.c
Migrate Windows conditional code to _WIN32 define.
[thirdparty/cups.git] / cups / http-addrlist.c
index 97923ae6f63098efcc947388a745d724f908e4d2..23031f50aecbd61c515e6e7d4ca9a02a6b93363d 100644 (file)
@@ -1,25 +1,11 @@
 /*
- * "$Id: http-addrlist.c 7910 2008-09-06 00:25:17Z mike $"
+ * HTTP address list routines for CUPS.
  *
- *   HTTP address list routines for CUPS.
+ * Copyright 2007-2018 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
- *   Copyright 2007-2013 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/".
- *
- * Contents:
- *
- *   httpAddrConnect() - Connect to any of the addresses in the list.
- *   httpAddrConnect2() - Connect to any of the addresses in the list with a
- *                       timeout and optional cancel.
- *   httpAddrCopyList() - Copy an address list.
- *   httpAddrFreeList() - Free an address list.
- *   httpAddrGetList() - Get a list of addresses for a hostname.
+ * Licensed under Apache License v2.0.  See the file "LICENSE" for more
+ * information.
  */
 
 /*
 #ifdef HAVE_POLL
 #  include <poll.h>
 #endif /* HAVE_POLL */
-#ifndef WIN32
+#ifndef _WIN32
 #  include <fcntl.h>
-#endif /* WIN32 */
+#endif /* _WIN32 */
 
 
 /*
  * 'httpAddrConnect()' - Connect to any of the addresses in the list.
  *
- * @since CUPS 1.2/OS X 10.5@
+ * @since CUPS 1.2/macOS 10.5@ @exclude all@
  */
 
 http_addrlist_t *                      /* O - Connected address or NULL on failure */
@@ -49,7 +35,7 @@ httpAddrConnect(
     http_addrlist_t *addrlist,         /* I - List of potential addresses */
     int             *sock)             /* O - Socket */
 {
-  DEBUG_printf(("httpAddrConnect(addrlist=%p, sock=%p)", addrlist, sock));
+  DEBUG_printf(("httpAddrConnect(addrlist=%p, sock=%p)", (void *)addrlist, (void *)sock));
 
   return (httpAddrConnect2(addrlist, sock, 30000, NULL));
 }
@@ -59,7 +45,7 @@ httpAddrConnect(
  * 'httpAddrConnect2()' - Connect to any of the addresses in the list with a
  *                        timeout and optional cancel.
  *
- * @since CUPS 1.7@
+ * @since CUPS 1.7/macOS 10.9@
  */
 
 http_addrlist_t *                      /* O - Connected address or NULL on failure */
@@ -70,27 +56,36 @@ httpAddrConnect2(
     int             *cancel)           /* I - Pointer to "cancel" variable */
 {
   int                  val;            /* Socket option value */
+#ifndef _WIN32
+  int                  flags;          /* Socket flags */
+#endif /* !_WIN32 */
+  int                  remaining;      /* Remaining timeout */
+  int                  i, j,           /* Looping vars */
+                       nfds,           /* Number of file descriptors */
+                       fds[100],       /* Socket file descriptors */
+                       result;         /* Result from select() or poll() */
+  http_addrlist_t      *addrs[100];    /* Addresses */
+#ifndef HAVE_POLL
+  int                  max_fd = -1;    /* Highest file descriptor */
+#endif /* !HAVE_POLL */
 #ifdef O_NONBLOCK
-  socklen_t            len;            /* Length of value */
-  http_addr_t          peer;           /* Peer address */
-  int                  flags,          /* Socket flags */
-                       remaining;      /* Remaining timeout */
 #  ifdef HAVE_POLL
-  struct pollfd                pfd;            /* Polled file descriptor */
+  struct pollfd                pfds[100];      /* Polled file descriptors */
 #  else
   fd_set               input_set,      /* select() input set */
-                       output_set;     /* select() output set */
+                       output_set,     /* select() output set */
+                       error_set;      /* select() error set */
   struct timeval       timeout;        /* Timeout */
 #  endif /* HAVE_POLL */
-  int                  nfds;           /* Result from select()/poll() */
 #endif /* O_NONBLOCK */
 #ifdef DEBUG
+  socklen_t            len;            /* Length of value */
+  http_addr_t          peer;           /* Peer address */
   char                 temp[256];      /* Temporary address string */
 #endif /* DEBUG */
 
 
-  DEBUG_printf(("httpAddrConnect2(addrlist=%p, sock=%p, msec=%d, cancel=%p)",
-                addrlist, sock, msec, cancel));
+  DEBUG_printf(("httpAddrConnect2(addrlist=%p, sock=%p, msec=%d, cancel=%p)", (void *)addrlist, (void *)sock, msec, (void *)cancel));
 
   if (!sock)
   {
@@ -102,227 +97,295 @@ httpAddrConnect2(
   if (cancel && *cancel)
     return (NULL);
 
-  if (msec <= 0 || getenv("CUPS_DISABLE_ASYNC_CONNECT"))
+  if (msec <= 0)
     msec = INT_MAX;
 
  /*
   * Loop through each address until we connect or run out of addresses...
   */
 
-  while (addrlist)
+  nfds      = 0;
+  remaining = msec;
+
+  while (remaining > 0)
   {
     if (cancel && *cancel)
-      return (NULL);
-
-   /*
-    * Create the socket...
-    */
+    {
+      while (nfds > 0)
+      {
+        nfds --;
+       httpAddrClose(NULL, fds[nfds]);
+      }
 
-    DEBUG_printf(("2httpAddrConnect2: Trying %s:%d...",
-                 httpAddrString(&(addrlist->addr), temp, sizeof(temp)),
-                 httpAddrPort(&(addrlist->addr))));
+      return (NULL);
+    }
 
-    if ((*sock = (int)socket(_httpAddrFamily(&(addrlist->addr)), SOCK_STREAM,
-                             0)) < 0)
+    if (addrlist && nfds < (int)(sizeof(fds) / sizeof(fds[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...
+      * Create the socket...
       */
 
-      addrlist = addrlist->next;
-      continue;
-    }
+      DEBUG_printf(("2httpAddrConnect2: Trying %s:%d...", httpAddrString(&(addrlist->addr), temp, sizeof(temp)), httpAddrPort(&(addrlist->addr))));
 
-   /*
-    * Set options...
-    */
+      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.
+       *
+       * Just skip this address...
+       */
 
-    val = 1;
-    setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, CUPS_SOCAST &val, sizeof(val));
+        addrlist = addrlist->next;
+       continue;
+      }
+
+     /*
+      * Set options...
+      */
+
+      val = 1;
+      setsockopt(fds[nfds], SOL_SOCKET, SO_REUSEADDR, CUPS_SOCAST &val, sizeof(val));
 
 #ifdef SO_REUSEPORT
-    val = 1;
-    setsockopt(*sock, SOL_SOCKET, SO_REUSEPORT, CUPS_SOCAST &val, sizeof(val));
+      val = 1;
+      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, CUPS_SOCAST &val, sizeof(val));
+      val = 1;
+      setsockopt(fds[nfds], SOL_SOCKET, SO_NOSIGPIPE, CUPS_SOCAST &val, sizeof(val));
 #endif /* SO_NOSIGPIPE */
 
-   /*
-    * Using TCP_NODELAY improves responsiveness, especially on systems
-    * with a slow loopback interface...
-    */
+     /*
+      * Using TCP_NODELAY improves responsiveness, especially on systems
+      * with a slow loopback interface...
+      */
 
-    val = 1;
-    setsockopt(*sock, IPPROTO_TCP, TCP_NODELAY, CUPS_SOCAST &val, sizeof(val));
+      val = 1;
+      setsockopt(fds[nfds], IPPROTO_TCP, TCP_NODELAY, CUPS_SOCAST &val, sizeof(val));
 
 #ifdef FD_CLOEXEC
-   /*
-    * Close this socket when starting another process...
-    */
+     /*
+      * 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()"));
+     /*
+      * Do an asynchronous connect by setting the socket non-blocking...
+      */
 
-    flags = fcntl(*sock, F_GETFL, 0);
-    if (msec != INT_MAX)
-    {
-      DEBUG_puts("httpAddrConnect2: Setting non-blocking connect()");
+      DEBUG_printf(("httpAddrConnect2: Setting non-blocking connect()"));
 
-      fcntl(*sock, F_SETFL, flags | O_NONBLOCK);
-    }
+      flags = fcntl(fds[nfds], F_GETFL, 0);
+      fcntl(fds[nfds], F_SETFL, flags | O_NONBLOCK);
 #endif /* O_NONBLOCK */
 
-   /*
-    * Then connect...
-    */
+     /*
+      * Then connect...
+      */
 
-    if (!connect(*sock, &(addrlist->addr.addr),
-                 httpAddrLength(&(addrlist->addr))))
-    {
-      DEBUG_printf(("1httpAddrConnect2: Connected to %s:%d...",
-                   httpAddrString(&(addrlist->addr), temp, sizeof(temp)),
-                   httpAddrPort(&(addrlist->addr))));
+      if (!connect(fds[nfds], &(addrlist->addr.addr), (socklen_t)httpAddrLength(&(addrlist->addr))))
+      {
+       DEBUG_printf(("1httpAddrConnect2: Connected to %s:%d...", httpAddrString(&(addrlist->addr), temp, sizeof(temp)), httpAddrPort(&(addrlist->addr))));
 
 #ifdef O_NONBLOCK
-      fcntl(*sock, F_SETFL, flags);
+       fcntl(fds[nfds], F_SETFL, flags);
 #endif /* O_NONBLOCK */
 
-      return (addrlist);
+       *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]);
+       addrlist = addrlist->next;
+       continue;
+      }
+
+#ifndef _WIN32
+      fcntl(fds[nfds], F_SETFL, flags);
+#endif /* !_WIN32 */
+
+#ifndef HAVE_POLL
+      if (fds[nfds] > max_fd)
+       max_fd = fds[nfds];
+#endif /* !HAVE_POLL */
+
+      addrs[nfds] = addrlist;
+      nfds ++;
+      addrlist = addrlist->next;
     }
 
-#ifdef O_NONBLOCK
-#  ifdef WIN32
-    if (WSAGetLastError() == WSAEINPROGRESS ||
-        WSAGetLastError() == WSAEWOULDBLOCK)
-#  else
-    if (errno == EINPROGRESS || errno == EWOULDBLOCK)
-#  endif /* WIN32 */
-    {
-      DEBUG_puts("1httpAddrConnect2: Finishing async connect()");
+    if (!addrlist && nfds == 0)
+      break;
+
+   /*
+    * See if we can connect to any of the addresses so far...
+    */
 
-      fcntl(*sock, F_SETFL, flags);
+#ifdef O_NONBLOCK
+    DEBUG_puts("1httpAddrConnect2: Finishing async connect()");
 
-      for (remaining = msec; remaining > 0; remaining -= 250)
+    do
+    {
+      if (cancel && *cancel)
       {
-       do
-        {
-          if (cancel && *cancel)
-          {
-          /*
-           * Close this socket and return...
-           */
+       /*
+       * Close this socket and return...
+       */
 
-            DEBUG_puts("1httpAddrConnect2: Canceled connect()");
+       DEBUG_puts("1httpAddrConnect2: Canceled connect()");
 
-#    ifdef WIN32
-           closesocket(*sock);
-#    else
-           close(*sock);
-#    endif /* WIN32 */
+       while (nfds > 0)
+       {
+         nfds --;
+         httpAddrClose(NULL, fds[nfds]);
+       }
 
-           *sock = -1;
+       *sock = -1;
 
-           return (NULL);
-          }
+       return (NULL);
+      }
 
 #  ifdef HAVE_POLL
-         pfd.fd     = *sock;
-         pfd.events = POLLIN | POLLOUT;
+      for (i = 0; i < nfds; i ++)
+      {
+       pfds[i].fd     = fds[i];
+       pfds[i].events = POLLIN | POLLOUT;
+      }
 
-          nfds = poll(&pfd, 1, remaining > 250 ? 250 : remaining);
+      result = poll(pfds, (nfds_t)nfds, addrlist ? 100 : remaining > 250 ? 250 : remaining);
 
-         DEBUG_printf(("1httpAddrConnect2: poll() returned %d (%d)", nfds,
-                       errno));
+      DEBUG_printf(("1httpAddrConnect2: poll() returned %d (%d)", result, errno));
 
 #  else
-         FD_ZERO(&input_set);
-         FD_SET(*sock, &input_set);
-         output_set = input_set;
+      FD_ZERO(&input_set);
+      for (i = 0; i < nfds; i ++)
+       FD_SET(fds[i], &input_set);
+      output_set = input_set;
+      error_set  = input_set;
 
-         timeout.tv_sec  = 0;
-         timeout.tv_usec = (remaining > 250 ? 250 : remaining) * 1000;
+      timeout.tv_sec  = 0;
+      timeout.tv_usec = (addrlist ? 100 : remaining > 250 ? 250 : remaining) * 1000;
 
-         nfds = select(*sock + 1, &input_set, &output_set, NULL, &timeout);
+      result = select(max_fd + 1, &input_set, &output_set, &error_set, &timeout);
 
-         DEBUG_printf(("1httpAddrConnect2: select() returned %d (%d)", nfds,
-                       errno));
+      DEBUG_printf(("1httpAddrConnect2: select() returned %d (%d)", result, errno));
 #  endif /* HAVE_POLL */
-       }
-#  ifdef WIN32
-       while (nfds < 0 && (WSAGetLastError() == WSAEINTR ||
-                           WSAGetLastError() == WSAEWOULDBLOCK));
+    }
+#  ifdef _WIN32
+    while (result < 0 && (WSAGetLastError() == WSAEINTR || WSAGetLastError() == WSAEWOULDBLOCK));
 #  else
-       while (nfds < 0 && (errno == EINTR || errno == EAGAIN));
-#  endif /* WIN32 */
+    while (result < 0 && (errno == EINTR || errno == EAGAIN));
+#  endif /* _WIN32 */
 
-        if (nfds > 0)
-        {
-          len = sizeof(peer);
-          if (!getpeername(*sock, (struct sockaddr *)&peer, &len))
-          {
-           DEBUG_printf(("1httpAddrConnect2: Connected to %s:%d...",
-                         httpAddrString(&peer, temp, sizeof(temp)),
-                         httpAddrPort(&peer)));
+    if (result > 0)
+    {
+      http_addrlist_t *connaddr = NULL;        /* Connected address, if any */
 
-           return (addrlist);
-         }
+      for (i = 0; i < nfds; i ++)
+      {
+#  ifdef HAVE_POLL
+       DEBUG_printf(("pfds[%d].revents=%x\n", i, pfds[i].revents));
+       if (pfds[i].revents && !(pfds[i].revents & (POLLERR | POLLHUP)))
+#  else
+       if (FD_ISSET(fds[i], &input_set) && !FD_ISSET(fds[i], &error_set))
+#  endif /* HAVE_POLL */
+       {
+         *sock    = fds[i];
+         connaddr = addrs[i];
+
+#  ifdef DEBUG
+         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)));
+#  endif /* DEBUG */
 
           break;
+       }
+#  ifdef HAVE_POLL
+       else if (pfds[i].revents & (POLLERR | POLLHUP))
+#  else
+       else if (FD_ISSET(fds[i], &error_set))
+#  endif /* HAVE_POLL */
+        {
+         /*
+          * Error on socket, remove from the "pool"...
+          */
+
+         httpAddrClose(NULL, fds[i]);
+          nfds --;
+          if (i < nfds)
+          {
+            memmove(fds + i, fds + i + 1, (size_t)(nfds - i) * (sizeof(fds[0])));
+            memmove(addrs + i, addrs + i + 1, (size_t)(nfds - i) * (sizeof(addrs[0])));
+          }
+          i --;
         }
       }
-    }
-#endif /* O_NONBLOCK */
 
-    DEBUG_printf(("1httpAddrConnect2: Unable to connect to %s:%d: %s",
-                 httpAddrString(&(addrlist->addr), temp, sizeof(temp)),
-                 httpAddrPort(&(addrlist->addr)), strerror(errno)));
+      if (connaddr)
+      {
+       /*
+        * Connected on one address, close all of the other sockets we have so
+        * far and return...
+        */
 
-#ifndef WIN32
-    if (errno == EINPROGRESS)
-      errno = ETIMEDOUT;
-#endif /* !WIN32 */
+        for (j = 0; j < i; j ++)
+          httpAddrClose(NULL, fds[j]);
 
-   /*
-    * Close this socket and move to the next address...
-    */
+        for (j ++; j < nfds; j ++)
+          httpAddrClose(NULL, fds[j]);
 
-#ifdef WIN32
-    closesocket(*sock);
-#else
-    close(*sock);
-#endif /* WIN32 */
+        return (connaddr);
+      }
+    }
+#endif /* O_NONBLOCK */
 
-    *sock    = -1;
-    addrlist = addrlist->next;
+    if (addrlist)
+      remaining -= 100;
+    else
+      remaining -= 250;
   }
 
-  if (!addrlist)
-#ifdef WIN32
-    _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE, "Connection failed", 0);
+  while (nfds > 0)
+  {
+    nfds --;
+    httpAddrClose(NULL, fds[nfds]);
+  }
+
+#ifdef _WIN32
+  _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE, "Connection failed", 0);
 #else
-    _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE, strerror(errno), 0);
-#endif /* WIN32 */
+  _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE, strerror(errno), 0);
+#endif /* _WIN32 */
 
-  return (addrlist);
+  return (NULL);
 }
 
 
-
 /*
  * 'httpAddrCopyList()' - Copy an address list.
  *
- * @since CUPS 1.7@
+ * @since CUPS 1.7/macOS 10.9@
  */
 
 http_addrlist_t        *                       /* O - New address list or @code NULL@ on error */
@@ -371,7 +434,7 @@ httpAddrCopyList(
 /*
  * 'httpAddrFreeList()' - Free an address list.
  *
- * @since CUPS 1.2/OS X 10.5@
+ * @since CUPS 1.2/macOS 10.5@
  */
 
 void
@@ -399,7 +462,7 @@ httpAddrFreeList(
 /*
  * 'httpAddrGetList()' - Get a list of addresses for a hostname.
  *
- * @since CUPS 1.2/OS X 10.5@
+ * @since CUPS 1.2/macOS 10.5@
  */
 
 http_addrlist_t        *                       /* O - List of addresses or NULL */
@@ -464,6 +527,7 @@ 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));
     }
@@ -549,6 +613,7 @@ httpAddrGetList(const char *hostname,       /* I - Hostname, IP address, or NULL for p
          if (!temp)
          {
            httpAddrFreeList(first);
+           freeaddrinfo(results);
            _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
            return (NULL);
          }
@@ -647,9 +712,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);
        }
       }
@@ -778,11 +844,11 @@ httpAddrGetList(const char *hostname,     /* I - Hostname, IP address, or NULL for p
 
         temp->addr.ipv6.sin6_family            = AF_INET6;
        temp->addr.ipv6.sin6_port              = htons(portnum);
-#  ifdef WIN32
+#  ifdef _WIN32
        temp->addr.ipv6.sin6_addr.u.Byte[15]   = 1;
 #  else
        temp->addr.ipv6.sin6_addr.s6_addr32[3] = htonl(1);
-#  endif /* WIN32 */
+#  endif /* _WIN32 */
 
         if (!first)
           first = temp;
@@ -879,8 +945,3 @@ httpAddrGetList(const char *hostname,       /* I - Hostname, IP address, or NULL for p
 
   return (first);
 }
-
-
-/*
- * End of "$Id: http-addrlist.c 7910 2008-09-06 00:25:17Z mike $".
- */