]> sjero.net Git - wget/blobdiff - src/connect.c
[svn] Merge of fix for bugs 20341 and 20410.
[wget] / src / connect.c
index 5ce6f1dfe5de8fefa7c445c0fbd5b7bff945a5ea..ab1fd08e7a03e7430136622db3ab54e3b274ac05 100644 (file)
@@ -1,11 +1,11 @@
 /* Establishing and handling network connections.
-   Copyright (C) 1995, 1996, 1997, 2001, 2002 Free Software Foundation, Inc.
+   Copyright (C) 1996-2006 Free Software Foundation, Inc.
 
 This file is part of GNU Wget.
 
 GNU Wget is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
+the Free Software Foundation; either version 3 of the License, or
  (at your option) any later version.
 
 GNU Wget is distributed in the hope that it will be useful,
@@ -14,8 +14,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
-along with Wget; if not, write to the Free Software
-Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+along with Wget.  If not, see <http://www.gnu.org/licenses/>.
 
 In addition, as a special exception, the Free Software Foundation
 gives permission to link the code of its release of Wget with the
@@ -73,27 +72,27 @@ so, delete this exception statement from your version.  */
 static void
 sockaddr_set_data (struct sockaddr *sa, const ip_address *ip, int port)
 {
-  switch (ip->type)
+  switch (ip->family)
     {
-    case IPV4_ADDRESS:
+    case AF_INET:
       {
        struct sockaddr_in *sin = (struct sockaddr_in *)sa;
        xzero (*sin);
        sin->sin_family = AF_INET;
        sin->sin_port = htons (port);
-       sin->sin_addr = ADDRESS_IPV4_IN_ADDR (ip);
+       sin->sin_addr = ip->data.d4;
        break;
       }
 #ifdef ENABLE_IPV6
-    case IPV6_ADDRESS:
+    case AF_INET6:
       {
        struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
        xzero (*sin6);
        sin6->sin6_family = AF_INET6;
        sin6->sin6_port = htons (port);
-       sin6->sin6_addr = ADDRESS_IPV6_IN6_ADDR (ip);
+       sin6->sin6_addr = ip->data.d6;
 #ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
-       sin6->sin6_scope_id = ADDRESS_IPV6_SCOPE (ip);
+       sin6->sin6_scope_id = ip->ipv6_scope;
 #endif
        break;
       }
@@ -117,8 +116,8 @@ sockaddr_get_data (const struct sockaddr *sa, ip_address *ip, int *port)
        struct sockaddr_in *sin = (struct sockaddr_in *)sa;
        if (ip)
          {
-           ip->type = IPV4_ADDRESS;
-           ADDRESS_IPV4_IN_ADDR (ip) = sin->sin_addr;
+           ip->family = AF_INET;
+           ip->data.d4 = sin->sin_addr;
          }
        if (port)
          *port = ntohs (sin->sin_port);
@@ -130,10 +129,10 @@ sockaddr_get_data (const struct sockaddr *sa, ip_address *ip, int *port)
        struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
        if (ip)
          {
-           ip->type = IPV6_ADDRESS;
-           ADDRESS_IPV6_IN6_ADDR (ip) = sin6->sin6_addr;
+           ip->family = AF_INET6;
+           ip->data.d6 = sin6->sin6_addr;
 #ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
-           ADDRESS_IPV6_SCOPE (ip) = sin6->sin6_scope_id;
+           ip->ipv6_scope = sin6->sin6_scope_id;
 #endif
          }
        if (port)
@@ -263,7 +262,7 @@ connect_to_ip (const ip_address *ip, int port, const char *print)
      PRINT being the host name we're connecting to.  */
   if (print)
     {
-      const char *txt_addr = pretty_print_address (ip);
+      const char *txt_addr = print_address (ip);
       if (print && 0 != strcmp (print, txt_addr))
        logprintf (LOG_VERBOSE, _("Connecting to %s|%s|:%d... "),
                   escnonprint (print), txt_addr, port);
@@ -415,7 +414,6 @@ int
 bind_local (const ip_address *bind_address, int *port)
 {
   int sock;
-  int family = AF_INET;
   struct sockaddr_storage ss;
   struct sockaddr *sa = (struct sockaddr *)&ss;
 
@@ -424,12 +422,7 @@ bind_local (const ip_address *bind_address, int *port)
   void *setopt_ptr = (void *)&setopt_val;
   socklen_t setopt_size = sizeof (setopt_val);
 
-#ifdef ENABLE_IPV6
-  if (bind_address->type == IPV6_ADDRESS) 
-    family = AF_INET6;
-#endif
-
-  sock = socket (family, SOCK_STREAM, 0);
+  sock = socket (bind_address->family, SOCK_STREAM, 0);
   if (sock < 0)
     return -1;
 
@@ -460,8 +453,8 @@ bind_local (const ip_address *bind_address, int *port)
          return -1;
        }
       sockaddr_get_data (sa, NULL, port);
-      DEBUGP (("binding to address %s using port %i.\n", 
-              pretty_print_address (bind_address), *port));
+      DEBUGP (("binding to address %s using port %i.\n",
+              print_address (bind_address), *port));
     }
   if (listen (sock, 1) < 0)
     {
@@ -529,27 +522,26 @@ socket_ip_address (int sock, ip_address *ip, int endpoint)
   if (ret < 0)
     return false;
 
+  ip->family = sockaddr->sa_family;
   switch (sockaddr->sa_family)
     {
 #ifdef ENABLE_IPV6
     case AF_INET6:
       {
        struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&storage;
-       ip->type = IPV6_ADDRESS;
-       ADDRESS_IPV6_IN6_ADDR (ip) = sa6->sin6_addr;
+       ip->data.d6 = sa6->sin6_addr;
 #ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
-       ADDRESS_IPV6_SCOPE (ip) = sa6->sin6_scope_id;
+       ip->ipv6_scope = sa6->sin6_scope_id;
 #endif
-       DEBUGP (("conaddr is: %s\n", pretty_print_address (ip)));
+       DEBUGP (("conaddr is: %s\n", print_address (ip)));
        return true;
       }
 #endif
     case AF_INET:
       {
        struct sockaddr_in *sa = (struct sockaddr_in *)&storage;
-       ip->type = IPV4_ADDRESS;
-       ADDRESS_IPV4_IN_ADDR (ip) = sa->sin_addr;
-       DEBUGP (("conaddr is: %s\n", pretty_print_address (ip)));
+       ip->data.d4 = sa->sin_addr;
+       DEBUGP (("conaddr is: %s\n", print_address (ip)));
        return true;
       }
     default:
@@ -638,6 +630,17 @@ select_fd (int fd, double maxtime, int wait_for)
   return result;
 }
 
+/* Return true iff the connection to the remote site established
+   through SOCK is still open.
+
+   Specifically, this function returns true if SOCK is not ready for
+   reading.  This is because, when the connection closes, the socket
+   is ready for reading because EOF is about to be delivered.  A side
+   effect of this method is that sockets that have pending data are
+   considered non-open.  This is actually a good thing for callers of
+   this function, where such pending data can only be unwanted
+   leftover from a previous request.  */
+
 bool
 test_socket_open (int sock)
 {
@@ -654,13 +657,12 @@ test_socket_open (int sock)
   to.tv_sec = 0;
   to.tv_usec = 1;
 
-  /* If we get a timeout, then that means still connected */
   if (select (sock + 1, &check_set, NULL, NULL, &to) == 0)
-    {
-      /* Connection is valid (not EOF), so continue */
-      return true;
-    }
+    /* We got a timeout, it means we're still connected. */
+    return true;
   else
+    /* Read now would not wait, it means we have either pending data
+       or EOF/error. */
     return false;
 }
 \f
@@ -732,14 +734,10 @@ sock_close (int fd)
    or SSL_read or whatever is necessary.  */
 
 static struct hash_table *transport_map;
-static int transport_map_modified_tick;
+static unsigned int transport_map_modified_tick;
 
 struct transport_info {
-  fd_reader_t reader;
-  fd_writer_t writer;
-  fd_poller_t poller;
-  fd_peeker_t peeker;
-  fd_closer_t closer;
+  struct transport_implementation *imp;
   void *ctx;
 };
 
@@ -751,9 +749,7 @@ struct transport_info {
    call getpeername, etc.  */
 
 void
-fd_register_transport (int fd, fd_reader_t reader, fd_writer_t writer,
-                      fd_poller_t poller, fd_peeker_t peeker,
-                      fd_closer_t closer, void *ctx)
+fd_register_transport (int fd, struct transport_implementation *imp, void *ctx)
 {
   struct transport_info *info;
 
@@ -763,15 +759,11 @@ fd_register_transport (int fd, fd_reader_t reader, fd_writer_t writer,
   assert (fd >= 0);
 
   info = xnew (struct transport_info);
-  info->reader = reader;
-  info->writer = writer;
-  info->poller = poller;
-  info->peeker = peeker;
-  info->closer = closer;
+  info->imp = imp;
   info->ctx = ctx;
   if (!transport_map)
     transport_map = hash_table_new (0, NULL, NULL);
-  hash_table_put (transport_map, (void *) fd, info);
+  hash_table_put (transport_map, (void *)(intptr_t) fd, info);
   ++transport_map_modified_tick;
 }
 
@@ -782,7 +774,7 @@ fd_register_transport (int fd, fd_reader_t reader, fd_writer_t writer,
 void *
 fd_transport_context (int fd)
 {
-  struct transport_info *info = hash_table_get (transport_map, (void *) fd);
+  struct transport_info *info = hash_table_get (transport_map, (void *)(intptr_t) fd);
   return info->ctx;
 }
 
@@ -797,14 +789,15 @@ fd_transport_context (int fd)
 
 #define LAZY_RETRIEVE_INFO(info) do {                                  \
   static struct transport_info *last_info;                             \
-  static int last_fd = -1, last_tick;                                  \
+  static int last_fd = -1;                                             \
+  static unsigned int last_tick;                                       \
   if (!transport_map)                                                  \
     info = NULL;                                                       \
   else if (last_fd == fd && last_tick == transport_map_modified_tick)  \
     info = last_info;                                                  \
   else                                                                 \
     {                                                                  \
-      info = hash_table_get (transport_map, (void *) fd);              \
+      info = hash_table_get (transport_map, (void *)(intptr_t) fd);    \
       last_fd = fd;                                                    \
       last_info = info;                                                        \
       last_tick = transport_map_modified_tick;                         \
@@ -819,8 +812,8 @@ poll_internal (int fd, struct transport_info *info, int wf, double timeout)
   if (timeout)
     {
       int test;
-      if (info && info->poller)
-       test = info->poller (fd, timeout, wf, info->ctx);
+      if (info && info->imp->poller)
+       test = info->imp->poller (fd, timeout, wf, info->ctx);
       else
        test = sock_poll (fd, timeout, wf);
       if (test == 0)
@@ -843,8 +836,8 @@ fd_read (int fd, char *buf, int bufsize, double timeout)
   LAZY_RETRIEVE_INFO (info);
   if (!poll_internal (fd, info, WAIT_FOR_READ, timeout))
     return -1;
-  if (info && info->reader)
-    return info->reader (fd, buf, bufsize, info->ctx);
+  if (info && info->imp->reader)
+    return info->imp->reader (fd, buf, bufsize, info->ctx);
   else
     return sock_read (fd, buf, bufsize);
 }
@@ -868,8 +861,8 @@ fd_peek (int fd, char *buf, int bufsize, double timeout)
   LAZY_RETRIEVE_INFO (info);
   if (!poll_internal (fd, info, WAIT_FOR_READ, timeout))
     return -1;
-  if (info && info->peeker)
-    return info->peeker (fd, buf, bufsize, info->ctx);
+  if (info && info->imp->peeker)
+    return info->imp->peeker (fd, buf, bufsize, info->ctx);
   else
     return sock_peek (fd, buf, bufsize);
 }
@@ -893,8 +886,8 @@ fd_write (int fd, char *buf, int bufsize, double timeout)
     {
       if (!poll_internal (fd, info, WAIT_FOR_WRITE, timeout))
        return -1;
-      if (info && info->writer)
-       res = info->writer (fd, buf, bufsize, info->ctx);
+      if (info && info->imp->writer)
+       res = info->imp->writer (fd, buf, bufsize, info->ctx);
       else
        res = sock_write (fd, buf, bufsize);
       if (res <= 0)
@@ -905,6 +898,35 @@ fd_write (int fd, char *buf, int bufsize, double timeout)
   return res;
 }
 
+/* Report the most recent error(s) on FD.  This should only be called
+   after fd_* functions, such as fd_read and fd_write, and only if
+   they return a negative result.  For errors coming from other calls
+   such as setsockopt or fopen, strerror should continue to be
+   used.
+
+   If the transport doesn't support error messages or doesn't supply
+   one, strerror(errno) is returned.  The returned error message
+   should not be used after fd_close has been called.  */
+
+const char *
+fd_errstr (int fd)
+{
+  /* Don't bother with LAZY_RETRIEVE_INFO, as this will only be called
+     in case of error, never in a tight loop.  */
+  struct transport_info *info = NULL;
+  if (transport_map)
+    info = hash_table_get (transport_map, (void *)(intptr_t) fd);
+
+  if (info && info->imp->errstr)
+    {
+      const char *err = info->imp->errstr (fd, info->ctx);
+      if (err)
+       return err;
+      /* else, fall through and print the system error. */
+    }
+  return strerror (errno);
+}
+
 /* Close the file descriptor FD.  */
 
 void
@@ -918,16 +940,16 @@ fd_close (int fd)
      per socket, so that particular optimization wouldn't work.  */
   info = NULL;
   if (transport_map)
-    info = hash_table_get (transport_map, (void *) fd);
+    info = hash_table_get (transport_map, (void *)(intptr_t) fd);
 
-  if (info && info->closer)
-    info->closer (fd, info->ctx);
+  if (info && info->imp->closer)
+    info->imp->closer (fd, info->ctx);
   else
     sock_close (fd);
 
   if (info)
     {
-      hash_table_remove (transport_map, (void *) fd);
+      hash_table_remove (transport_map, (void *)(intptr_t) fd);
       xfree (info);
       ++transport_map_modified_tick;
     }