]> sjero.net Git - wget/blobdiff - src/connect.c
[svn] Commit IPv6 support by Thomas Lussnig.
[wget] / src / connect.c
index feb2bb52a2f023594d2ee0ae48c297195ada8c8b..43ed0083512c06364365116c89dc968ea9b2e3ab 100644 (file)
@@ -1,20 +1,20 @@
 /* Establishing and handling network connections.
    Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
 
-This file is part of Wget.
+This file is part of GNU Wget.
 
-This program is free software; you can redistribute it and/or modify
+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
 (at your option) any later version.
 
-This program is distributed in the hope that it will be useful,
+GNU Wget is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 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 this program; if not, write to the Free Software
+along with Wget; if not, write to the Free Software
 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 
 #include <config.h>
@@ -24,6 +24,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #ifdef HAVE_UNISTD_H
 # include <unistd.h>
 #endif
+#include <assert.h>
 
 #ifdef WINDOWS
 # include <winsock.h>
@@ -31,7 +32,9 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 # include <sys/socket.h>
 # include <netdb.h>
 # include <netinet/in.h>
+#ifndef __BEOS__
 # include <arpa/inet.h>
+#endif
 #endif /* WINDOWS */
 
 #include <errno.h>
@@ -56,55 +59,111 @@ extern int errno;
 static int msock = -1;
 static struct sockaddr *addr;
 
+/* A kludge, but still better than passing the host name all the way
+   to connect_to_one.  */
+static const char *connection_host_name;
 
-/* Create an internet connection to HOSTNAME on PORT.  The created
-   socket will be stored to *SOCK.  */
-uerr_t
-make_connection (int *sock, char *hostname, unsigned short port)
+void
+set_connection_host_name (const char *host)
 {
-  struct sockaddr_in sock_name;
-  /* struct hostent *hptr; */
-
-  /* Get internet address of the host.  We can do it either by calling
-     ngethostbyname, or by calling store_hostaddress, from host.c.
-     storehostaddress is better since it caches calls to
-     gethostbyname.  */
-#if 1
-  if (!store_hostaddress ((unsigned char *)&sock_name.sin_addr, hostname))
-    return HOSTERR;
-#else  /* never */
-  if (!(hptr = ngethostbyname (hostname)))
-    return HOSTERR;
-  /* Copy the address of the host to socket description.  */
-  memcpy (&sock_name.sin_addr, hptr->h_addr, hptr->h_length);
-#endif /* never */
+  if (host)
+    assert (connection_host_name == NULL);
+  else
+    assert (connection_host_name != NULL);
+
+  connection_host_name = host;
+}
+
+/* Connect to a remote host whose address has been resolved. */
+int
+connect_to_one (ip_address *addr, unsigned short port, int silent)
+{
+  wget_sockaddr sa;
+  int sock, save_errno;
 
   /* Set port and protocol */
-  sock_name.sin_family = AF_INET;
-  sock_name.sin_port = htons (port);
+  wget_sockaddr_set_address (&sa, ip_default_family, port, addr);
+
+  if (!silent)
+    {
+      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... "),
+                  connection_host_name, pretty_addr, port);
+      else
+       logprintf (LOG_VERBOSE, _("Connecting to %s:%hu... "),
+                  pretty_addr, port);
+    }
 
   /* Make an internet socket, stream type.  */
-  if ((*sock = socket (AF_INET, SOCK_STREAM, 0)) == -1)
-    return CONSOCKERR;
+  sock = socket (ip_default_family, SOCK_STREAM, 0);
+  if (sock < 0)
+    goto out;
 
-  if (opt.bind_address != NULL)
+  if (opt.bind_address)
     {
       /* Bind the client side to the requested address. */
-      if (bind (*sock, (struct sockaddr *) opt.bind_address,
-               sizeof (*opt.bind_address)))
-       return CONSOCKERR;
+      if (bind (sock, (struct sockaddr *)opt.bind_address, sockaddr_len ()))
+       {
+         close (sock);
+         sock = -1;
+         goto out;
+       }
     }
 
   /* Connect the socket to the remote host.  */
-  if (connect (*sock, (struct sockaddr *) &sock_name, sizeof (sock_name)))
+  if (connect (sock, &sa.sa, sockaddr_len ()) < 0)
     {
-      if (errno == ECONNREFUSED)
-       return CONREFUSED;
-      else
-       return CONERROR;
+      close (sock);
+      sock = -1;
+      goto out;
+    }
+
+ out:
+  if (sock >= 0)
+    {
+      /* Success. */
+      if (!silent)
+       logprintf (LOG_VERBOSE, _("connected.\n"));
+      DEBUGP (("Created socket %d.\n", sock));
+    }
+  else
+    {
+      save_errno = errno;
+      if (!silent)
+       logprintf (LOG_VERBOSE, "failed: %s.\n", strerror (errno));
+      errno = save_errno;
+    }
+
+  return sock;
+}
+
+/* Connect to a remote host whose address has been resolved. */
+int
+connect_to_many (struct address_list *al, unsigned short port, int silent)
+{
+  int i, start, end;
+
+  address_list_get_bounds (al, &start, &end);
+  for (i = start; i < end; i++)
+    {
+      ip_address addr;
+      int sock;
+      address_list_copy_one (al, i, &addr);
+
+      sock = connect_to_one (&addr, port, silent);
+      if (sock >= 0)
+       /* Success. */
+       return sock;
+
+      address_list_set_faulty (al, i);
+
+      /* The attempt to connect has failed.  Continue with the loop
+        and try next address. */
     }
-  DEBUGP (("Created fd %d.\n", *sock));
-  return NOCONERROR;
+
+  return -1;
 }
 
 int
@@ -145,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;
@@ -176,14 +232,18 @@ bindport (unsigned short *port)
   DEBUGP (("Master socket fd %d bound.\n", msock));
   if (!*port)
     {
-      size_t addrlen = sizeof (struct sockaddr_in);
-      if (getsockname (msock, addr, (int *)&addrlen) < 0)
+      /* #### 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)
        {
          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)
     {
@@ -201,7 +261,7 @@ bindport (unsigned short *port)
 
    Returns 1 if FD is accessible, 0 for timeout and -1 for error in
    select().  */
-static int
+int
 select_fd (int fd, int maxtime, int writep)
 {
   fd_set fds, exceptfds;
@@ -227,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)
@@ -252,21 +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;
-  size_t addrlen = sizeof (mysrv);
-
-  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