X-Git-Url: http://sjero.net/git/?a=blobdiff_plain;f=src%2Fconnect.c;h=29a8b6e0f09c12c3270fac8de09b96fe4aa21eb3;hb=f3d3a50a5697957befaf94cb44797234a68068c3;hp=175db605121680096a549a5d1f9cf4e3c5c458b3;hpb=31d6616c483359af431f4c33c3c5b237cd8d4426;p=wget diff --git a/src/connect.c b/src/connect.c index 175db605..29a8b6e0 100644 --- a/src/connect.c +++ b/src/connect.c @@ -1,20 +1,20 @@ /* Establishing and handling network connections. - Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc. + Copyright (C) 1995, 1996, 1997, 2001, 2002 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 @@ -24,6 +24,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef HAVE_UNISTD_H # include #endif +#include #ifdef WINDOWS # include @@ -31,7 +32,9 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ # include # include # include +#ifndef __BEOS__ # include +#endif #endif /* WINDOWS */ #include @@ -45,8 +48,9 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #endif /* HAVE_SYS_SELECT_H */ #include "wget.h" -#include "connect.h" +#include "utils.h" #include "host.h" +#include "connect.h" #ifndef errno extern int errno; @@ -56,47 +60,208 @@ extern int errno; static int msock = -1; static struct sockaddr *addr; +static ip_address bind_address; +static int bind_address_resolved; -/* 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) +static void +resolve_bind_address (void) { - 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 */ + struct address_list *al; + + if (bind_address_resolved || opt.bind_address == NULL) + /* Nothing to do. */ + return; + + al = lookup_host (opt.bind_address, 1); + if (!al) + { + logprintf (LOG_NOTQUIET, + _("Unable to convert `%s' to a bind address. Reverting to ANY.\n"), + opt.bind_address); + return; + } + + address_list_copy_one (al, 0, &bind_address); + address_list_release (al); + bind_address_resolved = 1; +} + +struct cwt_context { + int fd; + const struct sockaddr *addr; + int addrlen; + int result; +}; + +static void +connect_with_timeout_callback (void *arg) +{ + struct cwt_context *ctx = (struct cwt_context *)arg; + ctx->result = connect (ctx->fd, ctx->addr, ctx->addrlen); +} + +/* Like connect, but specifies a timeout. If connecting takes longer + than TIMEOUT seconds, -1 is returned and errno is set to + ETIMEDOUT. */ + +static int +connect_with_timeout (int fd, const struct sockaddr *addr, int addrlen, + int timeout) +{ + struct cwt_context ctx; + ctx.fd = fd; + ctx.addr = addr; + ctx.addrlen = addrlen; + + if (run_with_timeout (timeout, connect_with_timeout_callback, &ctx)) + { + errno = ETIMEDOUT; + return -1; + } + if (ctx.result == -1 && errno == EINTR) + errno = ETIMEDOUT; + return ctx.result; +} + +/* A kludge, but still better than passing the host name all the way + to connect_to_one. */ +static const char *connection_host_name; + +void +set_connection_host_name (const char *host) +{ + 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; + + resolve_bind_address (); + if (bind_address_resolved) + { + /* 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; + } + } /* Connect the socket to the remote host. */ - if (connect (*sock, (struct sockaddr *) &sock_name, sizeof (sock_name))) + if (connect_with_timeout (sock, &sa.sa, sockaddr_len (), opt.timeout) < 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; } - DEBUGP (("Created fd %d.\n", *sock)); - return NOCONERROR; + + 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. */ + } + + return -1; +} + +int +test_socket_open (int sock) +{ +#ifdef HAVE_SELECT + fd_set check_set; + struct timeval to; + + /* Check if we still have a valid (non-EOF) connection. From Andrew + * Maholski's code in the Unix Socket FAQ. */ + + FD_ZERO (&check_set); + FD_SET (sock, &check_set); + + /* Wait one microsecond */ + 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 1; + } + else + return 0; +#else + /* Without select, it's hard to know for sure. */ + return 1; +#endif } /* Bind the local port PORT. This does all the necessary work, which @@ -106,22 +271,24 @@ make_connection (int *sock, char *hostname, unsigned short port) 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; - srv.sin_family = AF_INET; - srv.sin_addr.s_addr = htonl (INADDR_ANY); - srv.sin_port = htons (*port); - if (bind (msock, addr, sizeof (struct sockaddr_in)) < 0) + + 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) { CLOSE (msock); msock = -1; @@ -130,14 +297,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) { @@ -155,7 +326,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; @@ -181,7 +352,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) @@ -206,21 +377,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