]> sjero.net Git - wget/commitdiff
[svn] Commit IPv6 support by Thomas Lussnig.
authorhniksic <devnull@localhost>
Thu, 24 Jan 2002 05:45:54 +0000 (21:45 -0800)
committerhniksic <devnull@localhost>
Thu, 24 Jan 2002 05:45:54 +0000 (21:45 -0800)
Published in <sxsk7u8715e.fsf@florida.arsdigita.de>.

src/ChangeLog
src/connect.c
src/connect.h
src/ftp-basic.c
src/ftp.c
src/ftp.h
src/gen_sslfunc.c
src/host.c
src/host.h
src/init.c
src/options.h

index 1f40548ac62b3fbf3e2ced75c3b8d022e14bbff5..75445b27b5699cc7897d5230cc3af5539d7ad7c4 100644 (file)
@@ -1,3 +1,7 @@
+2002-01-24  Hrvoje Niksic  <hniksic@arsdigita.com>
+
+       * source: Integrated IPv6 support by Thomas Lussnig.
+
 2002-01-15  Ian Abbott  <abbotti@mev.co.uk>
 
        * init.c (cmd_file): Change `\' to `/' for Windows (yes, really!)
index 28d2f6aa3395a5b37c904a0717286ed562490661..43ed0083512c06364365116c89dc968ea9b2e3ab 100644 (file)
@@ -76,15 +76,13 @@ set_connection_host_name (const char *host)
 
 /* Connect to a remote host whose address has been resolved. */
 int
-connect_to_one (const unsigned char *addr, unsigned short port, int silent)
+connect_to_one (ip_address *addr, unsigned short port, int silent)
 {
-  struct sockaddr_in sock_name;
+  wget_sockaddr sa;
   int sock, save_errno;
 
   /* Set port and protocol */
-  sock_name.sin_family = AF_INET;
-  sock_name.sin_port = htons (port);
-  memcpy ((unsigned char *)&sock_name.sin_addr, addr, 4);
+  wget_sockaddr_set_address (&sa, ip_default_family, port, addr);
 
   if (!silent)
     {
@@ -99,15 +97,14 @@ connect_to_one (const unsigned char *addr, unsigned short port, int silent)
     }
 
   /* Make an internet socket, stream type.  */
-  sock = socket (AF_INET, SOCK_STREAM, 0);
+  sock = socket (ip_default_family, SOCK_STREAM, 0);
   if (sock < 0)
     goto out;
 
   if (opt.bind_address)
     {
       /* Bind the client side to the requested address. */
-      if (bind (sock, (struct sockaddr *)opt.bind_address,
-               sizeof (*opt.bind_address)))
+      if (bind (sock, (struct sockaddr *)opt.bind_address, sockaddr_len ()))
        {
          close (sock);
          sock = -1;
@@ -116,7 +113,7 @@ connect_to_one (const unsigned char *addr, unsigned short port, int silent)
     }
 
   /* Connect the socket to the remote host.  */
-  if (connect (sock, (struct sockaddr *)&sock_name, sizeof (sock_name)) < 0)
+  if (connect (sock, &sa.sa, sockaddr_len ()) < 0)
     {
       close (sock);
       sock = -1;
@@ -151,11 +148,11 @@ connect_to_many (struct address_list *al, unsigned short port, int silent)
   address_list_get_bounds (al, &start, &end);
   for (i = start; i < end; i++)
     {
-      unsigned char addr[4];
+      ip_address addr;
       int sock;
-      address_list_copy_one (al, i, addr);
+      address_list_copy_one (al, i, &addr);
 
-      sock = connect_to_one (addr, port, silent);
+      sock = connect_to_one (&addr, port, silent);
       if (sock >= 0)
        /* Success. */
        return sock;
@@ -207,29 +204,26 @@ test_socket_open (int sock)
    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)
+bindport (unsigned short *port, int family)
 {
   int optval = 1;
-  static struct sockaddr_in srv;
+  wget_sockaddr srv;
+  memset (&srv, 0, sizeof (wget_sockaddr));
 
   msock = -1;
-  addr = (struct sockaddr *) &srv;
-  if ((msock = socket (AF_INET, SOCK_STREAM, 0)) < 0)
+
+  if ((msock = socket (family, SOCK_STREAM, 0)) < 0)
     return CONSOCKERR;
   if (setsockopt (msock, SOL_SOCKET, SO_REUSEADDR,
                  (char *)&optval, sizeof (optval)) < 0)
     return CONSOCKERR;
 
   if (opt.bind_address == NULL)
-    {
-      srv.sin_family = AF_INET;
-      srv.sin_addr.s_addr = htonl (INADDR_ANY);
-    }
+    wget_sockaddr_set_address (&srv, ip_default_family, htons (*port), NULL);
   else
     srv = *opt.bind_address;
-
-  srv.sin_port = htons (*port);
-  if (bind (msock, addr, sizeof (struct sockaddr_in)) < 0)
+  wget_sockaddr_set_port (&srv, *port);
+  if (bind (msock, &srv.sa, sockaddr_len ()) < 0)
     {
       CLOSE (msock);
       msock = -1;
@@ -241,14 +235,15 @@ bindport (unsigned short *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 addrlen = sizeof (struct sockaddr_in);
-      if (getsockname (msock, addr, &addrlen) < 0)
+      int sa_len = sockaddr_len ();
+      if (getsockname (msock, &srv.sa, &sa_len) < 0)
        {
          CLOSE (msock);
          msock = -1;
          return CONPORTERR;
        }
-      *port = ntohs (srv.sin_port);
+      *port = wget_sockaddr_get_port (&srv);
+      DEBUGP (("using port %i.\n", *port));
     }
   if (listen (msock, 1) < 0)
     {
@@ -292,7 +287,7 @@ select_fd (int fd, int maxtime, int writep)
 uerr_t
 acceptport (int *sock)
 {
-  int addrlen = sizeof (struct sockaddr_in);
+  int addrlen = sockaddr_len ();
 
 #ifdef HAVE_SELECT
   if (select_fd (msock, opt.timeout, 0) <= 0)
@@ -317,22 +312,33 @@ closeport (int sock)
   msock = -1;
 }
 
-/* Return the local IP address associated with the connection on FD.
-   It is returned in a static buffer.  */
-unsigned char *
-conaddr (int fd)
+/* Return the local IP address associated with the connection on FD.  */
+
+int
+conaddr (int fd, ip_address *ip)
 {
-  static unsigned char res[4];
-  struct sockaddr_in mysrv;
-  struct sockaddr *myaddr;
-  int addrlen = sizeof (mysrv);        /* see bindport() for discussion of
-                                   using `int' here. */
-
-  myaddr = (struct sockaddr *) (&mysrv);
-  if (getsockname (fd, myaddr, (int *)&addrlen) < 0)
-    return NULL;
-  memcpy (res, &mysrv.sin_addr, 4);
-  return res;
+  wget_sockaddr mysrv;
+
+  /* see bindport() for discussion of using `int' here. */
+  int addrlen = sizeof (mysrv);        
+
+  if (getsockname (fd, &mysrv.sa, (int *)&addrlen) < 0)
+    return 0;
+
+  switch (mysrv.sa.sa_family)
+    {
+#ifdef INET6
+    case AF_INET6:
+      memcpy (ip, &mysrv.sin6.sin6_addr, 16);
+      return 1;
+#endif
+    case AF_INET:
+      map_ipv4_to_ip ((ip4_address *)&mysrv.sin.sin_addr, ip);
+      return 1;
+    default:
+      abort ();
+    }
+  return 0;
 }
 
 /* Read at most LEN bytes from FD, storing them to BUF.  This is
index 1924cba3fc63ddc4174033c7bbbd45ab0d74eb21..4b637ea0e5a18738bab71ec205838402afede2d7 100644 (file)
@@ -24,16 +24,16 @@ struct address_list;
 
 /* Function declarations */
 
-int connect_to_one PARAMS ((const unsigned char *, unsigned short, int));
+int connect_to_one PARAMS ((ip_address *, unsigned short, int));
 int connect_to_many PARAMS ((struct address_list *, unsigned short, int));
 void set_connection_host_name PARAMS ((const char *));
 
 int test_socket_open PARAMS ((int));
 int select_fd PARAMS ((int, int, int));
-uerr_t bindport PARAMS ((unsigned short *));
+uerr_t bindport PARAMS ((unsigned short *, int));
 uerr_t acceptport PARAMS ((int *));
 void closeport PARAMS ((int));
-unsigned char *conaddr PARAMS ((int));
+int conaddr PARAMS ((int, ip_address *));
 
 int iread PARAMS ((int, char *, int));
 int iwrite PARAMS ((int, char *, int));
index 69e94f022380292cbb4a1181c75c6abd55ac9b58..b3b14a7aa2c9f5d574d85f3c8d6473b58f513bdd 100644 (file)
@@ -33,6 +33,10 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #endif
 #include <sys/types.h>
 
+/* For inet_ntop. */
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
 #ifdef WINDOWS
 # include <winsock.h>
 #endif
@@ -235,6 +239,62 @@ ftp_login (struct rbuf *rbuf, const char *acc, const char *pass)
   return FTPOK;
 }
 
+#ifdef INET6
+uerr_t
+ftp_eprt (struct rbuf *rbuf)
+{
+  uerr_t err;
+
+  char *request, *respline;
+  ip_address in_addr;
+  unsigned short port;
+
+  char ipv6 [8 * (4 * 3 + 3) + 8];
+  char *bytes;
+
+  /* Setting port to 0 lets the system choose a free port.  */
+  port = 0;
+  err = bindport (&port, ip_default_family);
+  if (err != BINDOK)   /* Bind the port.  */
+    return err;
+
+  /* Get the address of this side of the connection.  */
+  if (!conaddr (RBUF_FD (rbuf), &in_addr))
+    /* Huh?  This is not BINDERR! */
+    return BINDERR;
+  inet_ntop (AF_INET6, &in_addr, ipv6, sizeof (ipv6));
+
+  /* Construct the argument of EPRT (of the form |2|IPv6.ascii|PORT.ascii|). */
+  bytes = alloca (3 + strlen (ipv6) + 1 + numdigit (port) + 1 + 1);
+  sprintf (bytes, "|2|%s|%u|", ipv6, port);
+  /* Send PORT request.  */
+  request = ftp_request ("EPRT", bytes);
+  if (0 > iwrite (RBUF_FD (rbuf), request, strlen (request)))
+    {
+      closeport (port);
+      xfree (request);
+      return WRITEFAILED;
+    }
+  xfree (request);
+  /* Get appropriate response.  */
+  err = ftp_response (rbuf, &respline);
+  if (err != FTPOK)
+    {
+      closeport (port);
+      xfree (respline);
+      return err;
+    }
+  if (*respline != '2')
+    {
+      closeport (port);
+      xfree (respline);
+      return FTPPORTERR;
+    }
+  xfree (respline);
+  return FTPOK;
+}
+#endif
+
 /* Bind a port and send the appropriate PORT command to the FTP
    server.  Use acceptport after RETR, to get the socket of data
    connection.  */
@@ -242,25 +302,48 @@ uerr_t
 ftp_port (struct rbuf *rbuf)
 {
   uerr_t err;
-  char *request, *respline, *bytes;
-  unsigned char *in_addr;
+  char *request, *respline;
+  char bytes[6 * 4 +1];
+
+  ip_address in_addr;
+  ip4_address in_addr_4;
+  unsigned char *in_addr4_ptr = (unsigned char *)&in_addr_4;
+
   int nwritten;
   unsigned short port;
-
+#ifdef INET6
+  /*
+    Only try the Extented Version if we actually use IPv6
+  */
+  if (ip_default_family == AF_INET6)
+    {
+      err = ftp_eprt (rbuf);
+      if (err == FTPOK)
+       return err;
+    }
+#endif
   /* Setting port to 0 lets the system choose a free port.  */
   port = 0;
-  /* Bind the port.  */
-  err = bindport (&port);
+
+  err = bindport (&port, AF_INET);
   if (err != BINDOK)
     return err;
-  /* Get the address of this side of the connection.  */
-  if (!(in_addr = conaddr (RBUF_FD (rbuf))))
+
+  /* Get the address of this side of the connection and convert it
+     (back) to IPv4.  */
+  if (!conaddr (RBUF_FD (rbuf), &in_addr))
+    /* Huh?  This is not BINDERR! */
+    return BINDERR;
+  if (!map_ip_to_ipv4 (&in_addr, &in_addr_4))
     return BINDERR;
-  /* Construct the argument of PORT (of the form a,b,c,d,e,f).  */
-  bytes = (char *)alloca (6 * 4 + 1);
-  sprintf (bytes, "%d,%d,%d,%d,%d,%d", in_addr[0], in_addr[1],
-          in_addr[2], in_addr[3], (unsigned) (port & 0xff00) >> 8,
-          port & 0xff);
+
+  /* Construct the argument of PORT (of the form a,b,c,d,e,f).  Port
+     is unsigned short so (unsigned) (port & 0xff000) >> 8 is the same
+     like port >> 8
+   */
+  sprintf (bytes, "%d,%d,%d,%d,%d,%d",
+          in_addr4_ptr[0], in_addr4_ptr[1], in_addr4_ptr[2], in_addr4_ptr[3],
+          port >> 8, port & 0xff);
   /* Send PORT request.  */
   request = ftp_request ("PORT", bytes);
   nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
@@ -286,16 +369,88 @@ ftp_port (struct rbuf *rbuf)
   return FTPOK;
 }
 
+#ifdef INET6
+uerr_t
+ftp_epsv (struct rbuf *rbuf, ip_address *addr, unsigned short *port, 
+         char *typ)
+{
+  int err;
+  char *s, *respline;
+  char *request = ftp_request ("EPSV", typ);
+  if (0 > iwrite (RBUF_FD (rbuf), request, strlen (request)))
+    {
+      xfree (request);
+      return WRITEFAILED;
+    }
+  /* Get the server response.  */
+  err = ftp_response (rbuf, &respline);
+  if (err != FTPOK)
+    {
+      xfree (respline);
+      return err;
+    }
+  if (*respline != '2')
+    {
+      xfree (respline);
+      return FTPNOPASV;
+    }
+  /* Parse the request.  */
+  s = respline;
+  /* respline::=229 Entering Extended Passive Mode (|||6446|) */
+  for (s += 4; *s && !ISDIGIT (*s); s++);
+  if (!*s)
+    return FTPINVPASV;
+  *port=0; 
+  for (; ISDIGIT (*s); s++) 
+    *port = (*s - '0') + 10 * (*port);
+  xfree (respline);
+  /* Now we have the port but we need the IPv6 :-( */
+  {
+    wget_sockaddr remote;
+    int len = sizeof (remote);
+    struct sockaddr_in *ipv4_sock = ( struct sockaddr_in *)&remote;
+    getpeername (RBUF_FD (rbuf), (struct sockaddr*)&remote, &len);
+    switch(remote.sa.sa_family)
+      {
+        case AF_INET6:
+          memcpy (addr, &remote.sin6.sin6_addr, 16);
+         break;
+       case AF_INET:  
+          map_ipv4_to_ip ((ip4_address *)&ipv4_sock->sin_addr, addr);
+         break;
+       default:
+         abort();
+         return FTPINVPASV;
+         /* realy bad */
+      }
+  }
+  return FTPOK;
+}
+#endif
+
+
 /* Similar to ftp_port, but uses `PASV' to initiate the passive FTP
    transfer.  Reads the response from server and parses it.  Reads the
    host and port addresses and returns them.  */
 uerr_t
-ftp_pasv (struct rbuf *rbuf, unsigned char *addr)
+ftp_pasv (struct rbuf *rbuf, ip_address *addr, unsigned short *port)
 {
   char *request, *respline, *s;
   int nwritten, i;
   uerr_t err;
+  unsigned char addr4[4];
 
+#ifdef INET6
+  if (ip_default_family == AF_INET6) 
+    {
+      err = ftp_epsv (rbuf, addr, port, "2");  /* try IPv6 with EPSV */
+      if (FTPOK == err) 
+        return FTPOK;
+      err = ftp_epsv (rbuf, addr, port, "1");  /* try IPv4 with EPSV */
+      if (FTPOK == err) 
+        return FTPOK;
+    }
+#endif  
   /* Form the request.  */
   request = ftp_request ("PASV", NULL);
   /* And send it.  */
@@ -319,24 +474,45 @@ ftp_pasv (struct rbuf *rbuf, unsigned char *addr)
       return FTPNOPASV;
     }
   /* Parse the request.  */
+  /* respline::=227 Entering Passive Mode (h1,h2,h3,h4,p1,p2).         */
   s = respline;
   for (s += 4; *s && !ISDIGIT (*s); s++);
   if (!*s)
     return FTPINVPASV;
-  for (i = 0; i < 6; i++)
+  for (i = 0; i < 4; i++)
     {
-      addr[i] = 0;
+      addr4[i] = 0;
       for (; ISDIGIT (*s); s++)
-        addr[i] = (*s - '0') + 10 * addr[i];
+        addr4[i] = (*s - '0') + 10 * addr4[i];
       if (*s == ',')
         s++;
-      else if (i < 5)
+      else
         {
-          /* When on the last number, anything can be a terminator.  */
           xfree (respline);
           return FTPINVPASV;
         }
     }
+
+  /* Eventually make an IPv4 in IPv6 adress if needed */
+  map_ipv4_to_ip ((ip4_address *)addr4, addr);
+
+  *port=0;
+  for (; ISDIGIT (*s); s++)
+    *port = (*s - '0') + 10 * (*port);
+  if (*s == ',') 
+    s++;
+  else
+    {
+      xfree (respline);
+      return FTPINVPASV;
+    }
+
+  {
+    unsigned short port2 = 0; 
+    for (; ISDIGIT (*s); s++) 
+      port2 = (*s - '0') + 10 * port2;
+    *port = (*port) * 256 + port2;
+  }
   xfree (respline);
   return FTPOK;
 }
index 1b78fd333f9bfe7159599de469e1e2c6bf31b90e..e02c018b8478daa89ccaa8af299f54b0123ef66e 100644 (file)
--- a/src/ftp.c
+++ b/src/ftp.c
@@ -119,9 +119,8 @@ getftp (struct url *u, long *len, long restval, ccon *con)
   FILE *fp;
   char *user, *passwd, *respline;
   char *tms, *tmrate;
-  unsigned char pasv_addr[6];
   int cmd = con->cmd;
-  int passive_mode_open = 0;
+  int pasv_mode_open = 0;
   long expected_bytes = 0L;
 
   assert (con != NULL);
@@ -489,9 +488,11 @@ Error in server response, closing control connection.\n"));
     {
       if (opt.ftp_pasv > 0)
        {
+         ip_address     passive_addr;
+         unsigned short passive_port;
          if (!opt.server_response)
            logputs (LOG_VERBOSE, "==> PASV ... ");
-         err = ftp_pasv (&con->rbuf, pasv_addr);
+         err = ftp_pasv (&con->rbuf, &passive_addr, &passive_port);
          /* FTPRERR, WRITEFAILED, FTPNOPASV, FTPINVPASV */
          switch (err)
            {
@@ -525,32 +526,28 @@ Error in server response, closing control connection.\n"));
            default:
              abort ();
              break;
-           }
+           }   /* switch(err) */
          if (err==FTPOK)
            {
-             unsigned short tport;
-
-             tport = (pasv_addr[4] << 8) + pasv_addr[5];
-             dtsock = connect_to_one (pasv_addr, tport, 1);
-
+             dtsock = connect_to_one (&passive_addr, passive_port, 1);
              if (dtsock < 0)
                {
                  int save_errno = errno;
                  CLOSE (csock);
                  rbuf_uninitialize (&con->rbuf);
                  logprintf (LOG_VERBOSE, _("couldn't connect to %s:%hu: %s\n"),
-                            pretty_print_address (pasv_addr), tport,
+                            pretty_print_address (&passive_addr), passive_port,
                             strerror (save_errno));
                  return save_errno == ECONNREFUSED ? CONREFUSED : CONERROR;
                }
 
-             passive_mode_open = 1;  /* Flag to avoid accept port */
+             pasv_mode_open = 1;  /* Flag to avoid accept port */
              if (!opt.server_response)
                logputs (LOG_VERBOSE, _("done.    "));
            } /* err==FTP_OK */
        }
 
-      if (!passive_mode_open)   /* Try to use a port command if PASV failed */
+      if (!pasv_mode_open)   /* Try to use a port command if PASV failed */
        {
          if (!opt.server_response)
            logputs (LOG_VERBOSE, "==> PORT ... ");
@@ -782,7 +779,7 @@ Error in server response, closing control connection.\n"));
   if (!(cmd & (DO_LIST | DO_RETR)))
     return RETRFINISHED;
 
-  if (!passive_mode_open)  /* we are not using pasive mode so we need
+  if (!pasv_mode_open)  /* we are not using pasive mode so we need
                              to accept */
     {
       /* Open the data transmission socket by calling acceptport().  */
index f988ca7fa4b0cee3da35a9c8bca4636373dca6e3..0da39a36ec6a5c03ea3c1565bbf0677eb0aa50da 100644 (file)
--- a/src/ftp.h
+++ b/src/ftp.h
@@ -36,7 +36,11 @@ enum stype
 uerr_t ftp_response PARAMS ((struct rbuf *, char **));
 uerr_t ftp_login PARAMS ((struct rbuf *, const char *, const char *));
 uerr_t ftp_port PARAMS ((struct rbuf *));
-uerr_t ftp_pasv PARAMS ((struct rbuf *, unsigned char *));
+uerr_t ftp_pasv PARAMS ((struct rbuf *, ip_address *, unsigned short *));
+#ifdef INET6
+uerr_t ftp_epsv PARAMS ((struct rbuf *, ip_address *, unsigned short *,
+                        char *));
+#endif
 uerr_t ftp_type PARAMS ((struct rbuf *, int));
 uerr_t ftp_cwd PARAMS ((struct rbuf *, const char *));
 uerr_t ftp_retr PARAMS ((struct rbuf *, const char *));
index 8af78895c62a878d4b4676b86e721bcda7c23043..da84492d5fdb968bac917375af55dd1d52f6181c 100644 (file)
@@ -125,7 +125,8 @@ init_ssl (SSL_CTX **ctx)
   meth = SSLv23_client_method ();
   *ctx = SSL_CTX_new (meth);
   SSL_CTX_set_verify (*ctx, verify, verify_callback);
-  if (*ctx == NULL) return SSLERRCTXCREATE;
+  if (*ctx == NULL) 
+    return SSLERRCTXCREATE;
   if (opt.sslcertfile)
     {
       if (SSL_CTX_use_certificate_file (*ctx, opt.sslcertfile,
index ddc04445e0fe2d17909769a33b280a552802889b..e9df830d68585b351e078495826a5a458a3279cd 100644 (file)
@@ -18,6 +18,7 @@ along with Wget; if not, write to the Free Software
 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 
 #include <config.h>
+#include <netdb.h>
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -65,8 +66,11 @@ extern int h_errno;
 # endif
 #endif
 
-/* An IPv4 address is simply a 4-byte quantity. */
-typedef unsigned char ipv4_address[4];
+#ifdef INET6
+int     ip_default_family = AF_INET6;
+#else
+int     ip_default_family = AF_INET;
+#endif
 
 /* Mapping between known hosts and to lists of their addresses. */
 
@@ -77,12 +81,10 @@ static struct hash_table *host_name_addresses_map;
 
 struct address_list {
   int count;                   /* number of adrresses */
-  ipv4_address *addresses;     /* pointer to the string of addresses */
+  ip_address *addresses;       /* pointer to the string of addresses */
 
-  int faulty;                  /* number of addresses known not to
-                                  work. */
-  int refcount;                        /* so we know whether to free it or
-                                  not. */
+  int faulty;                  /* number of addresses known not to work. */
+  int refcount;                        /* so we know whether to free it or not. */
 };
 
 /* Get the bounds of the address list.  */
@@ -97,11 +99,10 @@ address_list_get_bounds (struct address_list *al, int *start, int *end)
 /* Copy address number INDEX to IP_STORE.  */
 
 void
-address_list_copy_one (struct address_list *al, int index,
-                      unsigned char *ip_store)
+address_list_copy_one (struct address_list *al, int index, ip_address *ip_store)
 {
-  assert (index >= al->faulty && index < al->count);
-  memcpy (ip_store, al->addresses + index, sizeof (ipv4_address));
+  assert (index >= al->faulty && index < al->count && ip_store!=NULL );
+  memcpy (ip_store, al->addresses + index, sizeof (ip_address));
 }
 
 /* Check whether two address lists have all their IPs in common.  */
@@ -114,7 +115,7 @@ address_list_match_all (struct address_list *al1, struct address_list *al2)
   if (al1->count != al2->count)
     return 0;
   return 0 == memcmp (al1->addresses, al2->addresses,
-                     al1->count * sizeof (ipv4_address));
+                     al1->count * sizeof (ip_address));
 }
 
 /* Mark the INDEXth element of AL as faulty, so that the next time
@@ -137,9 +138,57 @@ address_list_set_faulty (struct address_list *al, int index)
     al->faulty = 0;
 }
 
+#ifdef INET6
+/**
+  * address_list_from_addrinfo
+  *
+  * This function transform an addrinfo links list in and address_list.
+  *
+  * Input:
+  * addrinfo*          Linkt list of addrinfo
+  *
+  * Output:
+  * address_list*      New allocated address_list
+  */
+static struct address_list *
+address_list_from_addrinfo (struct addrinfo *ai)
+{
+  struct address_list *al;
+  struct addrinfo *ai_head = ai;
+  int cnt = 0;
+  int i;
+
+  for (ai = ai_head; ai; ai = ai->ai_next)
+    if (ai->ai_family == AF_INET || ai->ai_family == AF_INET6)
+      ++cnt;
+  if (cnt == 0)
+    return NULL;
+
+  al = xmalloc (sizeof (struct address_list));
+  al->addresses = xmalloc (cnt * sizeof (ip_address));
+  al->count     = cnt;
+  al->faulty    = 0;
+  al->refcount  = 1;
+
+  for (i = 0, ai = ai_head; ai; ai = ai->ai_next)
+    if (ai->ai_family == AF_INET6) 
+      {
+       struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)ai->ai_addr;
+       memcpy (al->addresses + i, &sin6->sin6_addr, 16);
+       ++i;
+      } 
+    else if (ai->ai_family == AF_INET)
+      {
+       struct sockaddr_in *sin = (struct sockaddr_in *)ai->ai_addr;
+        map_ipv4_to_ip ((ip4_address *)&sin->sin_addr, al->addresses + i);
+       ++i;
+      }
+  assert (i == cnt);
+  return al;
+}
+#else
 /* Create an address_list out of a NULL-terminated list of addresses,
    as returned by gethostbyname.  */
-
 static struct address_list *
 address_list_new (char **h_addr_list)
 {
@@ -152,26 +201,27 @@ address_list_new (char **h_addr_list)
   assert (count > 0);
   al->count     = count;
   al->faulty    = 0;
-  al->addresses = xmalloc (count * sizeof (ipv4_address));
+  al->addresses = xmalloc (count * sizeof (ip_address));
   al->refcount  = 1;
 
   for (i = 0; i < count; i++)
-    memcpy (al->addresses + i, h_addr_list[i], sizeof (ipv4_address));
+    memcpy (al->addresses + i, h_addr_list[i], sizeof (ip_address));
 
   return al;
 }
+#endif
 
 /* Like address_list_new, but initialized with only one address. */
 
 static struct address_list *
-address_list_new_one (const char *addr)
+address_list_new_one (ip_address *addr)
 {
   struct address_list *al = xmalloc (sizeof (struct address_list));
   al->count     = 1;
   al->faulty    = 0;
-  al->addresses = xmalloc (sizeof (ipv4_address));
+  al->addresses = xmalloc (sizeof (ip_address));
   al->refcount  = 1;
-  memcpy (al->addresses, addr, sizeof (ipv4_address));
+  memcpy (al->addresses, addr, sizeof (ip_address));
 
   return al;
 }
@@ -195,12 +245,229 @@ address_list_release (struct address_list *al)
     }
 }
 \f
-/* The same as inet_ntoa, but without the need for a cast, or for
-   #including the netinet stuff.  */
+/**
+  * wget_sockaddr_set_address
+  *
+  * This function takes an wget_sockaddr and fill in the protocol type,
+  * the port number and the address, there NULL in address means wildcard.
+  * Unsuported adress family will abort the whole programm.
+  *
+  * Input:
+  * wget_sockaddr*     The space to be filled
+  * int                        The wished protocol
+  * unsigned short     The port
+  * const ip_address   The Binary IP adress
+  *
+  * Return:
+  * -                  Only modify 1. param
+  */
+void
+wget_sockaddr_set_address (wget_sockaddr *sa, 
+                          int ip_family, unsigned short port, ip_address *addr)
+{
+  if (ip_family == AF_INET) 
+    {
+      sa->sin.sin_family = ip_family;
+      sa->sin.sin_port = htons (port);
+      if (addr == NULL) 
+       memset ((unsigned char*)&sa->sin.sin_addr, 0,    sizeof(ip_address));
+      else      
+       memcpy ((unsigned char*)&sa->sin.sin_addr, addr, sizeof(ip_address));
+      return;
+    }
+#ifdef INET6
+  if (ip_family == AF_INET6) 
+    {
+      sa->sin6.sin6_family = ip_family;
+      sa->sin6.sin6_port = htons (port);
+      if (addr == NULL) 
+       memset (&sa->sin6.sin6_addr, 0   , sizeof(ip_address));
+      else          
+       memcpy (&sa->sin6.sin6_addr, addr, sizeof(ip_address));
+      return;
+    }
+#endif  
+  abort();
+}
+
+/**
+  * wget_sockaddr_set_port
+  *
+  * This funtion only fill the port of the socket information.
+  * If the protocol is not supported nothing is done.
+  * Unsuported adress family will abort the whole programm.
+  * 
+  * Require:
+  * that the IP-Protocol already is set.
+  *
+  * Input:
+  * wget_sockaddr*     The space there port should be entered
+  * unsigned int       The port that should be entered in host order
+  *
+  * Return:
+  * -                  Only modify 1. param
+  */
+void 
+wget_sockaddr_set_port (wget_sockaddr *sa, unsigned short port)
+{
+  if (sa->sa.sa_family == AF_INET)
+    {
+      sa->sin.sin_port = htons (port);
+      return;
+    }  
+#ifdef INET6
+  if (sa->sa.sa_family == AF_INET6)
+    {
+      sa->sin6.sin6_port = htons (port);
+      return;
+    }
+#endif
+  abort();
+}
+
+/**
+  * wget_sockaddr_get_addr
+  *
+  * This function return the adress from an sockaddr as byte string.
+  * Unsuported adress family will abort the whole programm.
+  * 
+  * Require:
+  * that the IP-Protocol already is set.
+  *
+  * Input:
+  * wget_sockaddr*     Socket Information
+  *
+  * Output:
+  * unsigned char *    IP address as byte string.
+  */
+void *
+wget_sockaddr_get_addr (wget_sockaddr *sa)
+{ 
+  if (sa->sa.sa_family == AF_INET)
+    return &sa->sin.sin_addr;
+#ifdef INET6
+  if (sa->sa.sa_family == AF_INET6)
+    return &sa->sin6.sin6_addr;
+#endif
+  abort();
+  /* unreached */
+  return NULL;
+}
+
+/**
+  * wget_sockaddr_get_port
+  *
+  * This function only return the port from the input structure
+  * Unsuported adress family will abort the whole programm.
+  * 
+  * Require:
+  * that the IP-Protocol already is set.
+  *
+  * Input:
+  * wget_sockaddr*     Information where to get the port
+  *
+  * Output:
+  * unsigned short     Port Number in host order.
+  */
+unsigned short 
+wget_sockaddr_get_port (const wget_sockaddr *sa)
+{
+  if (sa->sa.sa_family == AF_INET)
+      return htons (sa->sin.sin_port);
+#ifdef INET6
+  if (sa->sa.sa_family == AF_INET6)
+      return htons (sa->sin6.sin6_port);
+#endif
+  abort();
+  /* do not complain about return nothing */
+  return -1;
+}
+
+/**
+  * sockaddr_len
+  *
+  * This function return the length of the sockaddr corresponding to 
+  * the acutall prefered protocol for (bind, connect etc...)
+  * Unsuported adress family will abort the whole programm.
+  * 
+  * Require:
+  * that the IP-Protocol already is set.
+  *
+  * Input:
+  * -          Public IP-Family Information
+  *
+  * Output:
+  * int                structure length for socket options
+  */
+int 
+sockaddr_len () 
+{
+  if (ip_default_family == AF_INET) 
+    return sizeof (struct sockaddr_in);
+#ifdef INET6
+  if (ip_default_family == AF_INET6) 
+    return sizeof (struct sockaddr_in6);
+#endif
+  abort();
+  /* do not complain about return nothing */
+  return 0;
+}
+
+/**
+  * Map an IPv4 adress to the internal adress format.
+  */
+void 
+map_ipv4_to_ip (ip4_address *ipv4, ip_address *ip) 
+{
+#ifdef INET6
+  static unsigned char ipv64[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff};
+  memcpy ((char *)ip + 12, ipv4 , 4);
+  memcpy ((char *)ip + 0, ipv64, 12);
+#else
+  if ((char *)ip != (char *)ipv4)
+    memcpy (ip, ipv4, 4);
+#endif
+}
+
+/* Detect whether an IP adress represents an IPv4 address and, if so,
+   copy it to IPV4.  0 is returned on failure.
+   This operation always succeeds when Wget is compiled without IPv6.
+   If IPV4 is NULL, don't copy, just detect.  */
+
+int 
+map_ip_to_ipv4 (ip_address *ip, ip4_address *ipv4) 
+{
+#ifdef INET6
+  static unsigned char ipv64[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff};
+  if (0 != memcmp (ip, ipv64, 12))
+    return 0;
+  if (ipv4)
+    memcpy (ipv4, (char *)ip + 12, 4);
+#else
+  if (ipv4)
+    memcpy (ipv4, (char *)ip, 4);
+#endif
+  return 1;
+}
+\f
+/* Pretty-print ADDR.  When compiled without IPv6, this is the same as
+   inet_ntoa.  With IPv6, it either prints an IPv6 address or an IPv4
+   address.  */
 
 char *
-pretty_print_address (const void *addr)
+pretty_print_address (ip_address *addr)
 {
+#ifdef INET6
+  ip4_address addr4;
+  static char buf[128];
+
+  if (map_ip_to_ipv4 (addr, &addr4))
+    return inet_ntoa (*(struct in_addr *)&addr4);
+
+  if (!inet_ntop (AF_INET6, addr, buf, sizeof (buf)))
+    return "<unknown>";
+  return buf;
+#endif
   return inet_ntoa (*(struct in_addr *)addr);
 }
 
@@ -233,59 +500,85 @@ struct address_list *
 lookup_host (const char *host, int silent)
 {
   struct address_list *al = NULL;
-  unsigned long addr;
-  struct hostent *hptr;
+  unsigned long addr_ipv4;     /* #### use a 32-bit type here. */
+  ip_address addr;
+
+  /* First, try to check whether the address is already a numeric
+     address.  */
+
+#ifdef INET6
+  if (inet_pton (AF_INET6, host, &addr) > 0)
+    return address_list_new_one (&addr);
+#endif
 
-  /* If the address is of the form d.d.d.d, no further lookup is
-     needed.  */
-  addr = (unsigned long)inet_addr (host);
-  if ((int)addr != -1)
+  addr_ipv4 = (unsigned long)inet_addr (host);
+  if ((int)addr_ipv4 != -1)
     {
       /* ADDR is defined to be in network byte order, which is what
         this returns, so we can just copy it to STORE_IP.  However,
         on big endian 64-bit architectures the value will be stored
         in the *last*, not first four bytes.  OFFSET makes sure that
         we copy the correct four bytes.  */
-      int offset;
+      int offset = 0;
 #ifdef WORDS_BIGENDIAN
-      offset = sizeof (unsigned long) - sizeof (ipv4_address);
-#else
-      offset = 0;
+      offset = sizeof (unsigned long) - sizeof (ip_address);
 #endif
-      return address_list_new_one ((char *)&addr + offset);
+      map_ipv4_to_ip ((ip4_address *)((char *)&addr_ipv4 + offset), &addr);
+      return address_list_new_one (&addr);
     }
 
-  /* By now we know that the host name we got is not of the form
-     d.d.d.d.  Try to find it in our cache of host names.  */
   if (host_name_addresses_map)
-    al = hash_table_get (host_name_addresses_map, host);
-
-  if (al)
     {
-      DEBUGP (("Found %s in host_name_addresses_map (%p)\n", host, al));
-      ++al->refcount;
-      return al;
+      al = hash_table_get (host_name_addresses_map, host);
+
+      if (al)
+       {
+         DEBUGP (("Found %s in host_name_addresses_map (%p)\n", host, al));
+         ++al->refcount;
+         return al;
+       }
     }
 
   if (!silent)
     logprintf (LOG_VERBOSE, _("Resolving %s... "), host);
 
-  /* Look up the host using gethostbyname().  */
-  hptr = gethostbyname (host);
-  if (!hptr)
-    {
-      if (!silent)
-       logprintf (LOG_VERBOSE, _("failed: %s.\n"), herrmsg (h_errno));
-      return NULL;
-    }
+#ifdef INET6
+  {
+    struct addrinfo hints, *ai;
+    int err;
+
+    memset (&hints, 0, sizeof (hints));
+    hints.ai_family   = PF_UNSPEC;
+    hints.ai_socktype = SOCK_STREAM;
+    err = getaddrinfo (host, NULL, &hints, &ai);
+
+    if (err != 0 || ai == NULL)
+      {
+        if (!silent)
+         logprintf (LOG_VERBOSE, _("failed: %s.\n"), gai_strerror (err));
+        return NULL;
+      }
+    al = address_list_from_addrinfo (ai);
+    freeaddrinfo (ai);
+  }
+#else
+  {
+    struct hostent *hptr = gethostbyname (host);
+    if (!hptr)
+      {
+       if (!silent)
+         logprintf (LOG_VERBOSE, _("failed: %s.\n"), herrmsg (h_errno));
+       return NULL;
+      }
+    /* Do all systems have h_addr_list, or is it a newer thing?  If
+       the latter, use address_list_new_one.  */
+    al = address_list_new (hptr->h_addr_list);
+  }
+#endif
 
   if (!silent)
     logprintf (LOG_VERBOSE, _("done.\n"));
 
-  /* Do all systems have h_addr_list, or is it a newer thing?  If the
-     latter, use address_list_new_one.  */
-  al = address_list_new (hptr->h_addr_list);
-
   /* Cache the lookup information. */
   cache_host_lookup (host, al);
 
index f99c636c2ae788a3bf374eddcf7c068cfcd7060a..4d89ecf5a37f0a2350cd7c8633c8e41f524b45d3 100644 (file)
@@ -20,28 +20,71 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #ifndef HOST_H
 #define HOST_H
 
+#include <netdb.h>
+
+#undef INET6
+
 struct url;
 struct address_list;
 
+/* wget_sockaddr is used instead of sockaddr where an IPV6 address
+   must fit.  */
+
+typedef union {
+       struct sockaddr     sa;   /* Generic but too small */
+       struct sockaddr_in  sin;  /* IPv4 socket address */
+#ifdef INET6
+       struct sockaddr_in6 sin6; /* IPv6 socket address */
+#endif
+} wget_sockaddr;
+
+typedef struct {
+  unsigned char bytes[4];
+} ip4_address;
+
+/* If compiled with IPv6 support, we internally represent all IP
+   addresses as IPv6 addresses.  IPv4 addresses are dynamically mapped
+   to IPv6, i.e. stored in the format ::ffff:<Ipv4>.  */
+
+#ifdef INET6
+# define MAX_IP_ADDRESS_SIZE 16
+#else
+# define MAX_IP_ADDRESS_SIZE 4
+#endif
+
+typedef struct {
+  unsigned char bytes[MAX_IP_ADDRESS_SIZE];
+} ip_address;
+
 /* Function declarations */
 struct address_list *lookup_host PARAMS ((const char *, int));
 char *herrmsg PARAMS ((int));
 
 void address_list_get_bounds PARAMS ((struct address_list *, int *, int *));
 void address_list_copy_one PARAMS ((struct address_list *, int,
-                                   unsigned char *));
+                                   ip_address *));
 int address_list_match_all PARAMS ((struct address_list *,
                                    struct address_list *));
 void address_list_set_faulty PARAMS ((struct address_list *, int));
 void address_list_release PARAMS ((struct address_list *));
 
-/* This was originally going to be a macro, but then every caller
-   would have to #include the netinet stuff.  */
-char *pretty_print_address PARAMS ((const void *));
+char *pretty_print_address PARAMS ((ip_address *));
 
 int accept_domain PARAMS ((struct url *));
 int sufmatch PARAMS ((const char **, const char *));
 
 void host_cleanup PARAMS ((void));
 
+void wget_sockaddr_set_address PARAMS((wget_sockaddr *, int, 
+                                      unsigned short, ip_address *));
+void wget_sockaddr_set_port PARAMS((wget_sockaddr *, unsigned short));
+void *wget_sockaddr_get_addr PARAMS((wget_sockaddr *));
+unsigned short wget_sockaddr_get_port PARAMS((const wget_sockaddr *));
+int sockaddr_len PARAMS(());
+void map_ipv4_to_ip PARAMS((ip4_address *, ip_address *));
+int  map_ip_to_ipv4 PARAMS((ip_address *, ip4_address *));
+extern int     ip_default_family;      /* defined in host.c */
+
+
 #endif /* HOST_H */
index 033307752e628f3338e2acfe7e72666f0360766b..20bbc6a29ccb06f8de6e00210ecdc508eec7daab 100644 (file)
@@ -527,10 +527,10 @@ static int
 cmd_address (const char *com, const char *val, void *closure)
 {
   struct address_list *al;
-  struct sockaddr_in sin;
-  struct sockaddr_in **target = (struct sockaddr_in **)closure;
+  wget_sockaddr sa;
+  wget_sockaddr **target = (wget_sockaddr **)closure;
 
-  memset (&sin, '\0', sizeof (sin));
+  memset (&sa, '\0', sizeof (sa));
 
   al = lookup_host (val, 1);
   if (!al)
@@ -539,16 +539,15 @@ cmd_address (const char *com, const char *val, void *closure)
               exec_name, com, val);
       return 0;
     }
-  address_list_copy_one (al, 0, (unsigned char *)&sin.sin_addr);
+  sa.sa.sa_family = ip_default_family;
+  wget_sockaddr_set_port (&sa, 0);
+  address_list_copy_one (al, 0, wget_sockaddr_get_addr (&sa));
   address_list_release (al);
 
-  sin.sin_family = AF_INET;
-  sin.sin_port = 0;
-
   FREE_MAYBE (*target);
 
-  *target = xmalloc (sizeof (sin));
-  memcpy (*target, &sin, sizeof (sin));
+  *target = xmalloc (sizeof (sa));
+  memcpy (*target, &sa, sizeof (sa));
 
   return 1;
 }
index e8cd8fb2122340cdd8136737eae3adf68b2077ae..a07c641e96b57b72b9b4fe35e968b80ac82ee593 100644 (file)
@@ -19,6 +19,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 
 /* Needed for FDP.  */
 #include <stdio.h>
+#include "host.h"
 
 struct options
 {
@@ -152,9 +153,7 @@ struct options
 
   int page_requisites;         /* Whether we need to download all files
                                   necessary to display a page properly. */
-
-  struct sockaddr_in *bind_address; /* What local IP address to bind to. */
-
+  wget_sockaddr *bind_address;  /* What local IP address to bind to. */
 #ifdef HAVE_SSL
   char *sslcertfile;           /* external client cert to use. */
   char *sslcertkey;            /* the keyfile for this certificate