]> sjero.net Git - wget/blobdiff - src/connect.c
[svn] Updated IPv6 code.
[wget] / src / connect.c
index 483d4c54cf73aadd6720daa4aa220a254aa84337..7e52b691c103c1cee3736b28d40bf36151196038 100644 (file)
@@ -37,16 +37,14 @@ so, delete this exception statement from your version.  */
 #endif
 #include <assert.h>
 
-#ifdef WINDOWS
-# include <winsock.h>
-#else
+#ifndef WINDOWS
 # include <sys/socket.h>
 # include <netdb.h>
 # include <netinet/in.h>
-#ifndef __BEOS__
-# include <arpa/inet.h>
-#endif
-#endif /* WINDOWS */
+# ifndef __BEOS__
+#  include <arpa/inet.h>
+# endif
+#endif /* not WINDOWS */
 
 #include <errno.h>
 #ifdef HAVE_STRING_H
@@ -69,38 +67,52 @@ extern int errno;
 
 /* Variables shared by bindport and acceptport: */
 static int msock = -1;
-static struct sockaddr *addr;
+/*static struct sockaddr *addr;*/
 
-static ip_address bind_address;
-static int bind_address_resolved;
-
-static void
-resolve_bind_address (void)
+static int
+resolve_bind_address (int flags, ip_address *addr)
 {
-  struct address_list *al;
+  struct address_list *al = NULL;
+  int resolved = 0;
 
-  if (bind_address_resolved || opt.bind_address == NULL)
-    /* Nothing to do. */
-    return;
+  if (opt.bind_address != NULL)
+    {
+      al = lookup_host (opt.bind_address, flags | LH_SILENT | LH_PASSIVE);
+      if (al == NULL)
+        {
+          logprintf (LOG_NOTQUIET,
+                    _("Unable to convert `%s' to a bind address.  Reverting to ANY.\n"),
+                    opt.bind_address);
+       }
+      else 
+        resolved = 1;
+    }
 
-  al = lookup_host (opt.bind_address, 1);
-  if (!al)
+  if (al == NULL)
     {
-      logprintf (LOG_NOTQUIET,
-                _("Unable to convert `%s' to a bind address.  Reverting to ANY.\n"),
-                opt.bind_address);
-      return;
+      /* #### Is there really a need for this?  Shouldn't we simply
+        return 0 and have the caller use sockaddr_set_address to
+        specify INADDR_ANY/in6addr_any?  */
+      const char *unspecified_address = "0.0.0.0";
+#ifdef ENABLE_IPV6
+      if (flags & BIND_ON_IPV6_ONLY)
+       unspecified_address = "::";
+#endif
+      al = lookup_host (unspecified_address, LH_SILENT | LH_PASSIVE);
     }
 
-  address_list_copy_one (al, 0, &bind_address);
+  assert (al != NULL);
+
+  address_list_copy_one (al, 0, addr);
   address_list_release (al);
-  bind_address_resolved = 1;
+
+  return resolved;
 }
 \f
 struct cwt_context {
   int fd;
   const struct sockaddr *addr;
-  int addrlen;
+  socklen_t addrlen;
   int result;
 };
 
@@ -116,7 +128,7 @@ connect_with_timeout_callback (void *arg)
    ETIMEDOUT.  */
 
 static int
-connect_with_timeout (int fd, const struct sockaddr *addr, int addrlen,
+connect_with_timeout (int fd, const struct sockaddr *addr, socklen_t addrlen,
                      double timeout)
 {
   struct cwt_context ctx;
@@ -153,15 +165,16 @@ set_connection_host_name (const char *host)
 int
 connect_to_one (ip_address *addr, unsigned short port, int silent)
 {
-  wget_sockaddr sa;
+  struct sockaddr_storage ss;
+  struct sockaddr *sa = (struct sockaddr *)&ss;
   int sock, save_errno;
 
   /* Set port and protocol */
-  wget_sockaddr_set_address (&sa, ip_default_family, port, addr);
+  sockaddr_set_address (sa, port, addr);
 
   if (!silent)
     {
-      char *pretty_addr = pretty_print_address (addr);
+      const char *pretty_addr = pretty_print_address (addr);
       if (connection_host_name
          && 0 != strcmp (connection_host_name, pretty_addr))
        logprintf (LOG_VERBOSE, _("Connecting to %s[%s]:%hu... "),
@@ -171,8 +184,8 @@ connect_to_one (ip_address *addr, unsigned short port, int silent)
                   pretty_addr, port);
     }
 
-  /* Make an internet socket, stream type.  */
-  sock = socket (ip_default_family, SOCK_STREAM, 0);
+  /* Create the socket of the family appropriate for the address.  */
+  sock = socket (sa->sa_family, SOCK_STREAM, 0);
   if (sock < 0)
     goto out;
 
@@ -193,25 +206,29 @@ connect_to_one (ip_address *addr, unsigned short port, int silent)
         `--post-file', also set SO_SNDBUF here.  */
     }
 
-  resolve_bind_address ();
-  if (bind_address_resolved)
+  if (opt.bind_address)
     {
       /* Bind the client side to the requested address. */
-      wget_sockaddr bsa;
-      wget_sockaddr_set_address (&bsa, ip_default_family, 0, &bind_address);
-      if (bind (sock, &bsa.sa, sockaddr_len ()))
-       {
-         close (sock);
-         sock = -1;
-         goto out;
+      ip_address bind_address;
+      if (resolve_bind_address (0, &bind_address))
+        {
+          struct sockaddr_storage bss;
+          struct sockaddr *bsa = (struct sockaddr *)&bss;
+          sockaddr_set_address (bsa, 0, &bind_address);
+          if (bind (sock, bsa, sockaddr_len (bsa)))
+           {
+             CLOSE (sock);
+             sock = -1;
+             goto out;
+           }
        }
     }
 
   /* Connect the socket to the remote host.  */
-  if (connect_with_timeout (sock, &sa.sa, sockaddr_len (),
+  if (connect_with_timeout (sock, sa, sockaddr_len (sa),
                            opt.connect_timeout) < 0)
     {
-      close (sock);
+      CLOSE (sock);
       sock = -1;
       goto out;
     }
@@ -299,28 +316,47 @@ test_socket_open (int sock)
    chosen by the system, and its value is stored to *PORT.  The
    internal variable MPORT is set to the value of the ensuing master
    socket.  Call acceptport() to block for and accept a connection.  */
+
 uerr_t
-bindport (unsigned short *port, int family)
+bindport (const ip_address *bind_address, unsigned short *port)
 {
-  int optval = 1;
-  wget_sockaddr srv;
-  memset (&srv, 0, sizeof (wget_sockaddr));
+  int family = AF_INET;
+  int optval;
+  struct sockaddr_storage ss;
+  struct sockaddr *sa = (struct sockaddr *)&ss;
+  memset (&ss, 0, sizeof (ss));
 
   msock = -1;
 
+#ifdef ENABLE_IPV6
+  if (bind_address->type == IPV6_ADDRESS) 
+    family = AF_INET6;
+#endif
+  
   if ((msock = socket (family, SOCK_STREAM, 0)) < 0)
     return CONSOCKERR;
 
 #ifdef SO_REUSEADDR
+  optval = 1;
   if (setsockopt (msock, SOL_SOCKET, SO_REUSEADDR,
                  (char *)&optval, sizeof (optval)) < 0)
     return CONSOCKERR;
 #endif
 
-  resolve_bind_address ();
-  wget_sockaddr_set_address (&srv, ip_default_family, htons (*port),
-                            bind_address_resolved ? &bind_address : NULL);
-  if (bind (msock, &srv.sa, sockaddr_len ()) < 0)
+#ifdef ENABLE_IPV6
+# ifdef HAVE_IPV6_V6ONLY
+  if (family == AF_INET6)
+    {
+      optval = 1;
+      /* if setsockopt fails, go on anyway */
+      setsockopt (msock, IPPROTO_IPV6, IPV6_V6ONLY,
+                  (char *)&optval, sizeof (optval));
+    }
+# endif
+#endif
+  
+  sockaddr_set_address (sa, htons (*port), bind_address);
+  if (bind (msock, sa, sockaddr_len (sa)) < 0)
     {
       CLOSE (msock);
       msock = -1;
@@ -329,18 +365,16 @@ bindport (unsigned short *port, int family)
   DEBUGP (("Master socket fd %d bound.\n", msock));
   if (!*port)
     {
-      /* #### addrlen should be a 32-bit type, which int is not
-         guaranteed to be.  Oh, and don't try to make it a size_t,
-         because that can be 64-bit.  */
-      int sa_len = sockaddr_len ();
-      if (getsockname (msock, &srv.sa, &sa_len) < 0)
+      socklen_t sa_len = sockaddr_len (sa);
+      if (getsockname (msock, sa, &sa_len) < 0)
        {
          CLOSE (msock);
          msock = -1;
          return CONPORTERR;
        }
-      *port = wget_sockaddr_get_port (&srv);
-      DEBUGP (("using port %i.\n", *port));
+      *port = sockaddr_get_port (sa);
+      DEBUGP (("binding to address %s using port %i.\n", 
+              pretty_print_address (bind_address), *port));
     }
   if (listen (msock, 1) < 0)
     {
@@ -394,13 +428,15 @@ select_fd (int fd, double maxtime, int writep)
 uerr_t
 acceptport (int *sock)
 {
-  int addrlen = sockaddr_len ();
+  struct sockaddr_storage ss;
+  struct sockaddr *sa = (struct sockaddr *)&ss;
+  socklen_t addrlen = sizeof (ss);
 
 #ifdef HAVE_SELECT
   if (select_fd (msock, opt.connect_timeout, 0) <= 0)
     return ACCEPTERR;
 #endif
-  if ((*sock = accept (msock, addr, &addrlen)) < 0)
+  if ((*sock = accept (msock, sa, &addrlen)) < 0)
     return ACCEPTERR;
   DEBUGP (("Created socket fd %d.\n", *sock));
   return ACCEPTOK;
@@ -424,27 +460,40 @@ closeport (int sock)
 int
 conaddr (int fd, ip_address *ip)
 {
-  wget_sockaddr mysrv;
-
-  /* see bindport() for discussion of using `int' here. */
-  int addrlen = sizeof (mysrv);        
+  struct sockaddr_storage storage;
+  struct sockaddr *sockaddr = (struct sockaddr *)&storage;
+  socklen_t addrlen = sizeof (storage);
 
-  if (getsockname (fd, &mysrv.sa, (int *)&addrlen) < 0)
+  if (getsockname (fd, sockaddr, &addrlen) < 0)
     return 0;
 
-  switch (mysrv.sa.sa_family)
+  switch (sockaddr->sa_family)
     {
 #ifdef ENABLE_IPV6
     case AF_INET6:
-      memcpy (ip, &mysrv.sin6.sin6_addr, 16);
-      return 1;
+      {
+       struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&storage;
+       ip->type = IPV6_ADDRESS;
+       ADDRESS_IPV6_IN6_ADDR (ip) = sa6->sin6_addr;
+#ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
+       ADDRESS_IPV6_SCOPE (ip) = sa6->sin6_scope_id;
+#endif
+       DEBUGP (("conaddr is: %s\n", pretty_print_address (ip)));
+       return 1;
+      }
 #endif
     case AF_INET:
-      map_ipv4_to_ip ((ip4_address *)&mysrv.sin.sin_addr, ip);
-      return 1;
+      {
+       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)));
+       return 1;
+      }
     default:
       abort ();
     }
+
   return 0;
 }