X-Git-Url: http://sjero.net/git/?p=wget;a=blobdiff_plain;f=src%2Fconnect.c;h=1e4a44bae3d8d98e12b6efaffc195feb7e650ac0;hp=b85998c22ceac441ea80e50498a5f3df30dec438;hb=HEAD;hpb=3d66119d345a58ab34ab996574113f619e3e16fc diff --git a/src/connect.c b/src/connect.c index b85998c2..1e4a44ba 100644 --- a/src/connect.c +++ b/src/connect.c @@ -1,11 +1,13 @@ /* Establishing and handling network connections. - Copyright (C) 1996-2005 Free Software Foundation, Inc. + Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, + 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software + Foundation, Inc. This file is part of GNU Wget. 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 +the Free Software Foundation; either version 3 of the License, or (at your option) any later version. GNU Wget is distributed in the hope that it will be useful, @@ -14,31 +16,35 @@ 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 Wget; if not, write to the Free Software Foundation, Inc., -51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +along with Wget. If not, see . -In addition, as a special exception, the Free Software Foundation -gives permission to link the code of its release of Wget with the -OpenSSL project's "OpenSSL" library (or with modified versions of it -that use the same license as the "OpenSSL" library), and distribute -the linked executables. You must obey the GNU General Public License -in all respects for all of the code used other than "OpenSSL". If you -modify this file, you may extend this exception to your version of the -file, but you are not obligated to do so. If you do not wish to do -so, delete this exception statement from your version. */ +Additional permission under GNU GPL version 3 section 7 -#include +If you modify this program, or any covered work, by linking or +combining it with the OpenSSL project's OpenSSL library (or a +modified version of that library), containing parts covered by the +terms of the OpenSSL or SSLeay licenses, the Free Software Foundation +grants you additional permission to convey the resulting work. +Corresponding Source for a non-source form of such a combination +shall include the source code for the parts of OpenSSL used as well +as that of the covered work. */ + +#include "wget.h" #include #include -#ifdef HAVE_UNISTD_H -# include -#endif +#include #include +#include +#include + #ifndef WINDOWS -# include -# include +# ifdef __VMS +# include "vms_ip.h" +# else /* def __VMS */ +# include +# endif /* def __VMS [else] */ # include # ifndef __BEOS__ # include @@ -47,16 +53,17 @@ so, delete this exception statement from your version. */ #include #include -#ifdef HAVE_SYS_SELECT_H -# include -#endif /* HAVE_SYS_SELECT_H */ - -#include "wget.h" +#include #include "utils.h" #include "host.h" #include "connect.h" #include "hash.h" +/* Apparently needed for Interix: */ +#ifdef HAVE_STDINT_H +# include +#endif + /* Define sockaddr_storage where unavailable (presumably on IPv4-only hosts). */ @@ -73,29 +80,29 @@ so, delete this exception statement from your version. */ static void sockaddr_set_data (struct sockaddr *sa, const ip_address *ip, int port) { - switch (ip->type) + switch (ip->family) { - case IPV4_ADDRESS: + case AF_INET: { - struct sockaddr_in *sin = (struct sockaddr_in *)sa; - xzero (*sin); - sin->sin_family = AF_INET; - sin->sin_port = htons (port); - sin->sin_addr = ADDRESS_IPV4_IN_ADDR (ip); - break; + struct sockaddr_in *sin = (struct sockaddr_in *)sa; + xzero (*sin); + sin->sin_family = AF_INET; + sin->sin_port = htons (port); + sin->sin_addr = ip->data.d4; + break; } #ifdef ENABLE_IPV6 - case IPV6_ADDRESS: + case AF_INET6: { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; - xzero (*sin6); - sin6->sin6_family = AF_INET6; - sin6->sin6_port = htons (port); - sin6->sin6_addr = ADDRESS_IPV6_IN6_ADDR (ip); + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; + xzero (*sin6); + sin6->sin6_family = AF_INET6; + sin6->sin6_port = htons (port); + sin6->sin6_addr = ip->data.d6; #ifdef HAVE_SOCKADDR_IN6_SCOPE_ID - sin6->sin6_scope_id = ADDRESS_IPV6_SCOPE (ip); + sin6->sin6_scope_id = ip->ipv6_scope; #endif - break; + break; } #endif /* ENABLE_IPV6 */ default: @@ -114,31 +121,31 @@ sockaddr_get_data (const struct sockaddr *sa, ip_address *ip, int *port) { case AF_INET: { - struct sockaddr_in *sin = (struct sockaddr_in *)sa; - if (ip) - { - ip->type = IPV4_ADDRESS; - ADDRESS_IPV4_IN_ADDR (ip) = sin->sin_addr; - } - if (port) - *port = ntohs (sin->sin_port); - break; + struct sockaddr_in *sin = (struct sockaddr_in *)sa; + if (ip) + { + ip->family = AF_INET; + ip->data.d4 = sin->sin_addr; + } + if (port) + *port = ntohs (sin->sin_port); + break; } #ifdef ENABLE_IPV6 case AF_INET6: { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; - if (ip) - { - ip->type = IPV6_ADDRESS; - ADDRESS_IPV6_IN6_ADDR (ip) = sin6->sin6_addr; + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; + if (ip) + { + ip->family = AF_INET6; + ip->data.d6 = sin6->sin6_addr; #ifdef HAVE_SOCKADDR_IN6_SCOPE_ID - ADDRESS_IPV6_SCOPE (ip) = sin6->sin6_scope_id; + ip->ipv6_scope = sin6->sin6_scope_id; #endif - } - if (port) - *port = ntohs (sin6->sin6_port); - break; + } + if (port) + *port = ntohs (sin6->sin6_port); + break; } #endif default: @@ -183,7 +190,7 @@ resolve_bind_address (struct sockaddr *sa) if (called) { if (should_bind) - sockaddr_set_data (sa, &ip, 0); + sockaddr_set_data (sa, &ip, 0); return should_bind; } called = true; @@ -193,8 +200,8 @@ resolve_bind_address (struct sockaddr *sa) { /* #### We should be able to print the error message here. */ logprintf (LOG_NOTQUIET, - _("%s: unable to resolve bind address `%s'; disabling bind.\n"), - exec_name, opt.bind_address); + _("%s: unable to resolve bind address %s; disabling bind.\n"), + exec_name, quote (opt.bind_address)); should_bind = false; return false; } @@ -230,7 +237,7 @@ connect_with_timeout_callback (void *arg) static int connect_with_timeout (int fd, const struct sockaddr *addr, socklen_t addrlen, - double timeout) + double timeout) { struct cwt_context ctx; ctx.fd = fd; @@ -264,11 +271,34 @@ connect_to_ip (const ip_address *ip, int port, const char *print) if (print) { const char *txt_addr = print_address (ip); - if (print && 0 != strcmp (print, txt_addr)) - logprintf (LOG_VERBOSE, _("Connecting to %s|%s|:%d... "), - escnonprint (print), txt_addr, port); + if (0 != strcmp (print, txt_addr)) + { + char *str = NULL, *name; + + if (opt.enable_iri && (name = idn_decode ((char *) print)) != NULL) + { + int len = strlen (print) + strlen (name) + 4; + str = xmalloc (len); + snprintf (str, len, "%s (%s)", name, print); + str[len-1] = '\0'; + xfree (name); + } + + logprintf (LOG_VERBOSE, _("Connecting to %s|%s|:%d... "), + str ? str : escnonprint_uri (print), txt_addr, port); + + if (str) + xfree (str); + } else - logprintf (LOG_VERBOSE, _("Connecting to %s:%d... "), txt_addr, port); + { + if (ip->family == AF_INET) + logprintf (LOG_VERBOSE, _("Connecting to %s:%d... "), txt_addr, port); +#ifdef ENABLE_IPV6 + else if (ip->family == AF_INET6) + logprintf (LOG_VERBOSE, _("Connecting to [%s]:%d... "), txt_addr, port); +#endif + } } /* Store the sockaddr info to SA. */ @@ -285,8 +315,8 @@ connect_to_ip (const ip_address *ip, int port, const char *print) /* In case of error, we will go on anyway... */ int err = setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof (on)); IF_DEBUG - if (err < 0) - DEBUGP (("Failed setting IPV6_V6ONLY: %s", strerror (errno))); + if (err < 0) + DEBUGP (("Failed setting IPV6_V6ONLY: %s", strerror (errno))); } #endif @@ -298,31 +328,31 @@ connect_to_ip (const ip_address *ip, int port, const char *print) { int bufsize = opt.limit_rate; if (bufsize < 512) - bufsize = 512; /* avoid pathologically small values */ + bufsize = 512; /* avoid pathologically small values */ #ifdef SO_RCVBUF setsockopt (sock, SOL_SOCKET, SO_RCVBUF, - (void *)&bufsize, (socklen_t)sizeof (bufsize)); + (void *)&bufsize, (socklen_t)sizeof (bufsize)); #endif /* When we add limit_rate support for writing, which is useful - for POST, we should also set SO_SNDBUF here. */ + for POST, we should also set SO_SNDBUF here. */ } if (opt.bind_address) { /* Bind the client side of the socket to the requested - address. */ + address. */ struct sockaddr_storage bind_ss; struct sockaddr *bind_sa = (struct sockaddr *)&bind_ss; if (resolve_bind_address (bind_sa)) - { + { if (bind (sock, bind_sa, sockaddr_size (bind_sa)) < 0) - goto err; - } + goto err; + } } /* Connect the socket to the remote endpoint. */ if (connect_with_timeout (sock, sa, sockaddr_size (sa), - opt.connect_timeout) < 0) + opt.connect_timeout) < 0) goto err; /* Success. */ @@ -362,7 +392,12 @@ connect_to_host (const char *host, int port) retry: if (!al) - return E_HOST; + { + logprintf (LOG_NOTQUIET, + _("%s: unable to resolve host address %s\n"), + exec_name, quote (host)); + return E_HOST; + } address_list_get_bounds (al, &start, &end); for (i = start; i < end; i++) @@ -370,15 +405,15 @@ connect_to_host (const char *host, int port) const ip_address *ip = address_list_address_at (al, i); sock = connect_to_ip (ip, port, host); if (sock >= 0) - { - /* Success. */ - address_list_set_connected (al); - address_list_release (al); - return sock; - } + { + /* Success. */ + address_list_set_connected (al); + address_list_release (al); + return sock; + } /* The attempt to connect has failed. Continue with the loop - and try next address. */ + and try next address. */ address_list_set_faulty (al, i); } @@ -388,7 +423,7 @@ connect_to_host (const char *host, int port) if (address_list_connected_p (al)) { /* We connected to AL before, but cannot do so now. That might - indicate that our DNS cache entry for HOST has expired. */ + indicate that our DNS cache entry for HOST has expired. */ address_list_release (al); al = lookup_host (host, LH_REFRESH); goto retry; @@ -415,7 +450,6 @@ int bind_local (const ip_address *bind_address, int *port) { int sock; - int family = AF_INET; struct sockaddr_storage ss; struct sockaddr *sa = (struct sockaddr *)&ss; @@ -424,12 +458,7 @@ bind_local (const ip_address *bind_address, int *port) void *setopt_ptr = (void *)&setopt_val; socklen_t setopt_size = sizeof (setopt_val); -#ifdef ENABLE_IPV6 - if (bind_address->type == IPV6_ADDRESS) - family = AF_INET6; -#endif - - sock = socket (family, SOCK_STREAM, 0); + sock = socket (bind_address->family, SOCK_STREAM, 0); if (sock < 0) return -1; @@ -451,17 +480,17 @@ bind_local (const ip_address *bind_address, int *port) { socklen_t addrlen = sockaddr_size (sa); if (getsockname (sock, sa, &addrlen) < 0) - { - /* If we can't find out the socket's local address ("name"), - something is seriously wrong with the socket, and it's - unusable for us anyway because we must know the chosen - port. */ - fd_close (sock); - return -1; - } + { + /* If we can't find out the socket's local address ("name"), + something is seriously wrong with the socket, and it's + unusable for us anyway because we must know the chosen + port. */ + fd_close (sock); + return -1; + } sockaddr_get_data (sa, NULL, port); DEBUGP (("binding to address %s using port %i.\n", - print_address (bind_address), *port)); + print_address (bind_address), *port)); } if (listen (sock, 1) < 0) { @@ -496,9 +525,9 @@ accept_connection (int local_sock) { int test = select_fd (local_sock, opt.connect_timeout, WAIT_FOR_READ); if (test == 0) - errno = ETIMEDOUT; + errno = ETIMEDOUT; if (test <= 0) - return -1; + return -1; } sock = accept (local_sock, sa, &addrlen); DEBUGP (("Accepted client at socket %d.\n", sock)); @@ -516,10 +545,11 @@ bool socket_ip_address (int sock, ip_address *ip, int endpoint) { struct sockaddr_storage storage; - struct sockaddr *sockaddr = (struct sockaddr *)&storage; + struct sockaddr *sockaddr = (struct sockaddr *) &storage; socklen_t addrlen = sizeof (storage); int ret; + memset (sockaddr, 0, addrlen); if (endpoint == ENDPOINT_LOCAL) ret = getsockname (sock, sockaddr, &addrlen); else if (endpoint == ENDPOINT_PEER) @@ -529,34 +559,64 @@ socket_ip_address (int sock, ip_address *ip, int endpoint) if (ret < 0) return false; + memset(ip, 0, sizeof(ip_address)); + ip->family = sockaddr->sa_family; switch (sockaddr->sa_family) { #ifdef ENABLE_IPV6 case AF_INET6: { - struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&storage; - ip->type = IPV6_ADDRESS; - ADDRESS_IPV6_IN6_ADDR (ip) = sa6->sin6_addr; + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&storage; + ip->data.d6 = sa6->sin6_addr; #ifdef HAVE_SOCKADDR_IN6_SCOPE_ID - ADDRESS_IPV6_SCOPE (ip) = sa6->sin6_scope_id; + ip->ipv6_scope = sa6->sin6_scope_id; #endif - DEBUGP (("conaddr is: %s\n", print_address (ip))); - return true; + DEBUGP (("conaddr is: %s\n", print_address (ip))); + return true; } #endif case AF_INET: { - 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", print_address (ip))); - return true; + struct sockaddr_in *sa = (struct sockaddr_in *)&storage; + ip->data.d4 = sa->sin_addr; + DEBUGP (("conaddr is: %s\n", print_address (ip))); + return true; } default: abort (); } } +/* Get the socket family of connection on FD and store + Return family type on success, -1 otherwise. + + If ENDPOINT is ENDPOINT_LOCAL, it returns the sock family of the local + (client) side of the socket. Else if ENDPOINT is ENDPOINT_PEER, it + returns the sock family of the remote (peer's) side of the socket. */ + +int +socket_family (int sock, int endpoint) +{ + struct sockaddr_storage storage; + struct sockaddr *sockaddr = (struct sockaddr *) &storage; + socklen_t addrlen = sizeof (storage); + int ret; + + memset (sockaddr, 0, addrlen); + + if (endpoint == ENDPOINT_LOCAL) + ret = getsockname (sock, sockaddr, &addrlen); + else if (endpoint == ENDPOINT_PEER) + ret = getpeername (sock, sockaddr, &addrlen); + else + abort (); + + if (ret < 0) + return -1; + + return sockaddr->sa_family; +} + /* Return true if the error from the connect code can be considered retryable. Wget normally retries after errors, but the exception are the "unsupported protocol" type errors (possible on IPv4/IPv6 @@ -575,7 +635,7 @@ retryable_socket_connect_error (int err) #ifdef EPFNOSUPPORT || err == EPFNOSUPPORT #endif -#ifdef ESOCKTNOSUPPORT /* no, "sockt" is not a typo! */ +#ifdef ESOCKTNOSUPPORT /* no, "sockt" is not a typo! */ || err == ESOCKTNOSUPPORT #endif #ifdef EPROTONOSUPPORT @@ -585,7 +645,7 @@ retryable_socket_connect_error (int err) || err == ENOPROTOOPT #endif /* Apparently, older versions of Linux and BSD used EINVAL - instead of EAFNOSUPPORT and such. */ + instead of EAFNOSUPPORT and such. */ || err == EINVAL ) return false; @@ -593,12 +653,12 @@ retryable_socket_connect_error (int err) if (!opt.retry_connrefused) if (err == ECONNREFUSED #ifdef ENETUNREACH - || err == ENETUNREACH /* network is unreachable */ + || err == ENETUNREACH /* network is unreachable */ #endif #ifdef EHOSTUNREACH - || err == EHOSTUNREACH /* host is unreachable */ + || err == EHOSTUNREACH /* host is unreachable */ #endif - ) + ) return false; return true; @@ -632,7 +692,14 @@ select_fd (int fd, double maxtime, int wait_for) tmout.tv_usec = 1000000 * (maxtime - (long) maxtime); do + { result = select (fd + 1, rd, wr, NULL, &tmout); +#ifdef WINDOWS + /* gnulib select() converts blocking sockets to nonblocking in windows. + wget uses blocking sockets so we must convert them back to blocking. */ + set_windows_fd_as_blocking_socket (fd); +#endif + } while (result < 0 && errno == EINTR); return result; @@ -654,6 +721,7 @@ test_socket_open (int sock) { fd_set check_set; struct timeval to; + int ret = 0; /* Check if we still have a valid (non-EOF) connection. From Andrew * Maholski's code in the Unix Socket FAQ. */ @@ -665,27 +733,25 @@ test_socket_open (int sock) 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 */ + ret = select (sock + 1, &check_set, NULL, NULL, &to); +#ifdef WINDOWS +/* gnulib select() converts blocking sockets to nonblocking in windows. +wget uses blocking sockets so we must convert them back to blocking +*/ + set_windows_fd_as_blocking_socket ( sock ); +#endif + + if ( !ret ) + /* We got a timeout, it means we're still connected. */ return true; else + /* Read now would not wait, it means we have either pending data + or EOF/error. */ return false; } /* Basic socket operations, mostly EINTR wrappers. */ -#ifdef WINDOWS -# define read(fd, buf, cnt) recv (fd, buf, cnt, 0) -# define write(fd, buf, cnt) send (fd, buf, cnt, 0) -# define close(fd) closesocket (fd) -#endif - -#ifdef __BEOS__ -# define read(fd, buf, cnt) recv (fd, buf, cnt, 0) -# define write(fd, buf, cnt) send (fd, buf, cnt, 0) -#endif - static int sock_read (int fd, char *buf, int bufsize) { @@ -770,7 +836,7 @@ fd_register_transport (int fd, struct transport_implementation *imp, void *ctx) info->ctx = ctx; if (!transport_map) transport_map = hash_table_new (0, NULL, NULL); - hash_table_put (transport_map, (void *) fd, info); + hash_table_put (transport_map, (void *)(intptr_t) fd, info); ++transport_map_modified_tick; } @@ -781,7 +847,7 @@ fd_register_transport (int fd, struct transport_implementation *imp, void *ctx) void * fd_transport_context (int fd) { - struct transport_info *info = hash_table_get (transport_map, (void *) fd); + struct transport_info *info = hash_table_get (transport_map, (void *)(intptr_t) fd); return info->ctx; } @@ -794,20 +860,21 @@ fd_transport_context (int fd) This is a macro because we want the static storage variables to be per-function. */ -#define LAZY_RETRIEVE_INFO(info) do { \ - static struct transport_info *last_info; \ - static int last_fd = -1, last_tick; \ - if (!transport_map) \ - info = NULL; \ - else if (last_fd == fd && last_tick == transport_map_modified_tick) \ - info = last_info; \ - else \ - { \ - info = hash_table_get (transport_map, (void *) fd); \ - last_fd = fd; \ - last_info = info; \ - last_tick = transport_map_modified_tick; \ - } \ +#define LAZY_RETRIEVE_INFO(info) do { \ + static struct transport_info *last_info; \ + static int last_fd = -1; \ + static unsigned int last_tick; \ + if (!transport_map) \ + info = NULL; \ + else if (last_fd == fd && last_tick == transport_map_modified_tick) \ + info = last_info; \ + else \ + { \ + info = hash_table_get (transport_map, (void *)(intptr_t) fd); \ + last_fd = fd; \ + last_info = info; \ + last_tick = transport_map_modified_tick; \ + } \ } while (0) static bool @@ -819,13 +886,13 @@ poll_internal (int fd, struct transport_info *info, int wf, double timeout) { int test; if (info && info->imp->poller) - test = info->imp->poller (fd, timeout, wf, info->ctx); + test = info->imp->poller (fd, timeout, wf, info->ctx); else - test = sock_poll (fd, timeout, wf); + test = sock_poll (fd, timeout, wf); if (test == 0) - errno = ETIMEDOUT; + errno = ETIMEDOUT; if (test <= 0) - return false; + return false; } return true; } @@ -891,13 +958,13 @@ fd_write (int fd, char *buf, int bufsize, double timeout) while (bufsize > 0) { if (!poll_internal (fd, info, WAIT_FOR_WRITE, timeout)) - return -1; + return -1; if (info && info->imp->writer) - res = info->imp->writer (fd, buf, bufsize, info->ctx); + res = info->imp->writer (fd, buf, bufsize, info->ctx); else - res = sock_write (fd, buf, bufsize); + res = sock_write (fd, buf, bufsize); if (res <= 0) - break; + break; buf += res; bufsize -= res; } @@ -911,7 +978,8 @@ fd_write (int fd, char *buf, int bufsize, double timeout) used. If the transport doesn't support error messages or doesn't supply - one, strerror(errno) is returned. */ + one, strerror(errno) is returned. The returned error message + should not be used after fd_close has been called. */ const char * fd_errstr (int fd) @@ -920,13 +988,13 @@ fd_errstr (int fd) in case of error, never in a tight loop. */ struct transport_info *info = NULL; if (transport_map) - info = hash_table_get (transport_map, (void *) fd); + info = hash_table_get (transport_map, (void *)(intptr_t) fd); if (info && info->imp->errstr) { const char *err = info->imp->errstr (fd, info->ctx); if (err) - return err; + return err; /* else, fall through and print the system error. */ } return strerror (errno); @@ -945,7 +1013,7 @@ fd_close (int fd) per socket, so that particular optimization wouldn't work. */ info = NULL; if (transport_map) - info = hash_table_get (transport_map, (void *) fd); + info = hash_table_get (transport_map, (void *)(intptr_t) fd); if (info && info->imp->closer) info->imp->closer (fd, info->ctx); @@ -954,7 +1022,7 @@ fd_close (int fd) if (info) { - hash_table_remove (transport_map, (void *) fd); + hash_table_remove (transport_map, (void *)(intptr_t) fd); xfree (info); ++transport_map_modified_tick; }