]> sjero.net Git - wget/blobdiff - src/connect.c
[svn] Updated IPv6 code.
[wget] / src / connect.c
index 26dc404d830978ef81527d00e953450b137ec882..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,8 +128,8 @@ connect_with_timeout_callback (void *arg)
    ETIMEDOUT.  */
 
 static int
-connect_with_timeout (int fd, const struct sockaddr *addr, int addrlen,
-                     int timeout)
+connect_with_timeout (int fd, const struct sockaddr *addr, socklen_t addrlen,
+                     double timeout)
 {
   struct cwt_context ctx;
   ctx.fd = fd;
@@ -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,29 +184,51 @@ 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;
 
-  resolve_bind_address ();
-  if (bind_address_resolved)
+  /* For very small rate limits, set the buffer size (and hence,
+     hopefully, the size of the kernel window) to the size of the
+     limit.  That way we don't sleep for more than 1s between network
+     reads.  */
+  if (opt.limit_rate && opt.limit_rate < 8192)
+    {
+      int bufsize = opt.limit_rate;
+      if (bufsize < 512)
+       bufsize = 512;
+#ifdef SO_RCVBUF
+      setsockopt (sock, SOL_SOCKET, SO_RCVBUF,
+                 (char *)&bufsize, sizeof (bufsize));
+#endif
+      /* When we add opt.limit_rate support for writing, as with
+        `--post-file', also set SO_SNDBUF here.  */
+    }
+
+  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 (), opt.timeout) < 0)
+  if (connect_with_timeout (sock, sa, sockaddr_len (sa),
+                           opt.connect_timeout) < 0)
     {
-      close (sock);
+      CLOSE (sock);
       sock = -1;
       goto out;
     }
@@ -281,25 +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;
@@ -308,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)
     {
@@ -338,7 +393,7 @@ bindport (unsigned short *port, int family)
    Returns 1 if FD is available, 0 for timeout and -1 for error.  */
 
 int
-select_fd (int fd, int maxtime, int writep)
+select_fd (int fd, double maxtime, int writep)
 {
   fd_set fds;
   fd_set *rd = NULL, *wrt = NULL;
@@ -349,8 +404,8 @@ select_fd (int fd, int maxtime, int writep)
   FD_SET (fd, &fds);
   *(writep ? &wrt : &rd) = &fds;
 
-  tmout.tv_sec = maxtime;
-  tmout.tv_usec = 0;
+  tmout.tv_sec = (long)maxtime;
+  tmout.tv_usec = 1000000L * (maxtime - (long)maxtime);
 
   do
     result = select (fd + 1, rd, wrt, NULL, &tmout);
@@ -368,18 +423,20 @@ select_fd (int fd, int maxtime, int writep)
 /* Call accept() on MSOCK and store the result to *SOCK.  This assumes
    that bindport() has been used to initialize MSOCK to a correct
    value.  It blocks the caller until a connection is established.  If
-   no connection is established for OPT.TIMEOUT seconds, the function
-   exits with an error status.  */
+   no connection is established for OPT.CONNECT_TIMEOUT seconds, the
+   function exits with an error status.  */
 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.timeout, 0) <= 0)
+  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;
@@ -403,34 +460,47 @@ closeport (int sock)
 int
 conaddr (int fd, ip_address *ip)
 {
-  wget_sockaddr mysrv;
+  struct sockaddr_storage storage;
+  struct sockaddr *sockaddr = (struct sockaddr *)&storage;
+  socklen_t addrlen = sizeof (storage);
 
-  /* see bindport() for discussion of using `int' here. */
-  int addrlen = sizeof (mysrv);        
-
-  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;
 }
 
 /* Read at most LEN bytes from FD, storing them to BUF.  This is
    virtually the same as read(), but takes care of EINTR braindamage
    and uses select() to timeout the stale connections (a connection is
-   stale if more than OPT.TIMEOUT time is spent in select() or
+   stale if more than OPT.READ_TIMEOUT time is spent in select() or
    read()).  */
 
 int
@@ -439,8 +509,8 @@ iread (int fd, char *buf, int len)
   int res;
 
 #ifdef HAVE_SELECT
-  if (opt.timeout)
-    if (select_fd (fd, opt.timeout, 0) <= 0)
+  if (opt.read_timeout)
+    if (select_fd (fd, opt.read_timeout, 0) <= 0)
       return -1;
 #endif
   do
@@ -467,8 +537,8 @@ iwrite (int fd, char *buf, int len)
   while (len > 0)
     {
 #ifdef HAVE_SELECT
-      if (opt.timeout)
-       if (select_fd (fd, opt.timeout, 1) <= 0)
+      if (opt.read_timeout)
+       if (select_fd (fd, opt.read_timeout, 1) <= 0)
          return -1;
 #endif
       do