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.
+ (at your option) any later version.
GNU Wget is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
#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
extern int errno;
#endif
-/* Variables shared by bindport and acceptport: */
-static int msock = -1;
-static struct sockaddr *addr;
-
-static ip_address bind_address;
-static int bind_address_resolved;
+\f
+/* Fill SA as per the data in IP and PORT. SA shoult point to struct
+ sockaddr_storage if ENABLE_IPV6 is defined, to struct sockaddr_in
+ otherwise. */
static void
-resolve_bind_address (void)
+sockaddr_set_data (struct sockaddr *sa, const ip_address *ip, int port)
+{
+ switch (ip->type)
+ {
+ case IPV4_ADDRESS:
+ {
+ struct sockaddr_in *sin = (struct sockaddr_in *)sa;
+ sin->sin_family = AF_INET;
+ sin->sin_port = htons (port);
+ sin->sin_addr = ADDRESS_IPV4_IN_ADDR (ip);
+ break;
+ }
+#ifdef ENABLE_IPV6
+ case IPV6_ADDRESS:
+ {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = htons (port);
+ sin6->sin6_addr = ADDRESS_IPV6_IN6_ADDR (ip);
+#ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
+ sin6->sin6_scope_id = ADDRESS_IPV6_SCOPE (ip);
+#endif
+ break;
+ }
+#endif /* ENABLE_IPV6 */
+ default:
+ abort ();
+ }
+}
+
+/* Get the data of SA, specifically the IP address and the port. If
+ you're not interested in one or the other information, pass NULL as
+ the pointer. */
+
+void
+sockaddr_get_data (const struct sockaddr *sa, ip_address *ip, int *port)
+{
+ switch (sa->sa_family)
+ {
+ 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;
+ }
+#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;
+#ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
+ ADDRESS_IPV6_SCOPE (ip) = sin6->sin6_scope_id;
+#endif
+ }
+ if (port)
+ *port = ntohs (sin6->sin6_port);
+ break;
+ }
+#endif
+ default:
+ abort ();
+ }
+}
+
+/* Return the size of the sockaddr structure depending on its
+ family. */
+
+static socklen_t
+sockaddr_size (const struct sockaddr *sa)
+{
+ switch (sa->sa_family)
+ {
+ case AF_INET:
+ return sizeof (struct sockaddr_in);
+#ifdef ENABLE_IPV6
+ case AF_INET6:
+ return sizeof (struct sockaddr_in6);
+#endif
+ default:
+ abort ();
+ return 0; /* so the compiler shuts up. */
+ }
+}
+\f
+static int
+resolve_bind_address (const char *host, struct sockaddr *sa, int flags)
{
struct address_list *al;
- if (bind_address_resolved || opt.bind_address == NULL)
- /* Nothing to do. */
- return;
+ /* #### Shouldn't we do this only once? opt.bind_address won't
+ change during a Wget run! */
- al = lookup_host (opt.bind_address, 1);
- if (!al)
+ al = lookup_host (host, 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);
- return;
+ return 0;
}
- address_list_copy_one (al, 0, &bind_address);
+ /* Pick the first address in the list and use it as bind address.
+ Perhaps we should try multiple addresses, but I don't think
+ that's necessary in practice. */
+ sockaddr_set_data (sa, address_list_address_at (al, 0), 0);
address_list_release (al);
- bind_address_resolved = 1;
+ return 1;
}
\f
struct cwt_context {
int fd;
const struct sockaddr *addr;
- int addrlen;
+ socklen_t addrlen;
int result;
};
ETIMEDOUT. */
static int
-connect_with_timeout (int fd, const struct sockaddr *addr, int addrlen,
+connect_with_timeout (int fd, const struct sockaddr *addr, socklen_t addrlen,
double timeout)
{
struct cwt_context ctx;
return ctx.result;
}
\f
-/* A kludge, but still better than passing the host name all the way
- to connect_to_one. */
-static const char *connection_host_name;
+/* Connect to a remote endpoint whose IP address is known. */
-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)
+connect_to_ip (const ip_address *ip, int port, const char *print)
{
- 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);
-
- if (!silent)
+ /* If PRINT is non-NULL, print the "Connecting to..." line, with
+ PRINT being the host name we're connecting to. */
+ if (print)
{
- 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);
+ const char *txt_addr = pretty_print_address (ip);
+ if (print && 0 != strcmp (print, txt_addr))
+ logprintf (LOG_VERBOSE,
+ _("Connecting to %s{%s}:%d... "), print, txt_addr, port);
else
- logprintf (LOG_VERBOSE, _("Connecting to %s:%hu... "),
- pretty_addr, port);
+ logprintf (LOG_VERBOSE, _("Connecting to %s:%d... "), txt_addr, port);
}
- /* Make an internet socket, stream type. */
- sock = socket (ip_default_family, SOCK_STREAM, 0);
+ /* Store the sockaddr info to SA. */
+ sockaddr_set_data (sa, ip, port);
+
+ /* Create the socket of the family appropriate for the address. */
+ sock = socket (sa->sa_family, SOCK_STREAM, 0);
if (sock < 0)
goto out;
-#ifdef SO_RCVBUF
/* For very small rate limits, set the buffer size (and hence,
- hopefully, the size of the kernel window) to the size of the
- limit. */
+ hopefully, the kernel's TCP window size) to the per-second limit.
+ That way we should never have to 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;
+ bufsize = 512; /* avoid pathologically small values */
+#ifdef SO_RCVBUF
setsockopt (sock, SOL_SOCKET, SO_RCVBUF,
- (char *)&bufsize, 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. */
+ }
- resolve_bind_address ();
- if (bind_address_resolved)
+ 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 ()))
+ /* Bind the client side of the socket to the requested
+ address. */
+ struct sockaddr_storage bind_ss;
+ struct sockaddr *bind_sa = (struct sockaddr *)&bind_ss;
+ if (resolve_bind_address (opt.bind_address, bind_sa, 0))
{
- close (sock);
- sock = -1;
- goto out;
+ if (bind (sock, bind_sa, sockaddr_size (bind_sa)) < 0)
+ {
+ CLOSE (sock);
+ sock = -1;
+ goto out;
+ }
}
}
- /* Connect the socket to the remote host. */
- if (connect_with_timeout (sock, &sa.sa, sockaddr_len (),
+ /* Connect the socket to the remote endpoint. */
+ if (connect_with_timeout (sock, sa, sockaddr_size (sa),
opt.connect_timeout) < 0)
{
- close (sock);
+ CLOSE (sock);
sock = -1;
goto out;
}
if (sock >= 0)
{
/* Success. */
- if (!silent)
+ if (print)
logprintf (LOG_VERBOSE, _("connected.\n"));
DEBUGP (("Created socket %d.\n", sock));
}
else
{
save_errno = errno;
- if (!silent)
+ if (print)
logprintf (LOG_VERBOSE, "failed: %s.\n", strerror (errno));
errno = save_errno;
}
return sock;
}
-/* Connect to a remote host whose address has been resolved. */
+/* Connect to a remote endpoint specified by host name. */
+
int
-connect_to_many (struct address_list *al, unsigned short port, int silent)
+connect_to_host (const char *host, int port)
{
int i, start, end;
+ struct address_list *al;
+ int sock = -1;
+
+ again:
+ al = lookup_host (host, 0);
+ if (!al)
+ return E_HOST;
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);
+ const ip_address *ip = address_list_address_at (al, i);
+ sock = connect_to_ip (ip, port, host);
if (sock >= 0)
/* Success. */
- return sock;
+ break;
address_list_set_faulty (al, i);
/* The attempt to connect has failed. Continue with the loop
and try next address. */
}
+ address_list_release (al);
- return -1;
+ if (sock < 0 && address_list_cached_p (al))
+ {
+ /* We were unable to connect to any address in a list we've
+ obtained from cache. There is a possibility that the host is
+ under dynamic DNS and has changed its address. Resolve it
+ again. */
+ forget_host_lookup (host);
+ goto again;
+ }
+
+ return sock;
}
int
#endif
}
-/* Bind the local port PORT. This does all the necessary work, which
- is creating a socket, setting SO_REUSEADDR option on it, then
- calling bind() and listen(). If *PORT is 0, a random port is
- 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. */
+/* Create a socket and bind it to PORT locally. Calling accept() on
+ such a socket waits for and accepts incoming TCP connections. The
+ resulting socket is stored to LOCAL_SOCK. */
+
uerr_t
-bindport (unsigned short *port, int family)
+bindport (const ip_address *bind_address, int *port, int *local_sock)
{
- int optval = 1;
- wget_sockaddr srv;
- memset (&srv, 0, sizeof (wget_sockaddr));
+ int msock;
+ int family = AF_INET;
+ int optval;
+ struct sockaddr_storage ss;
+ struct sockaddr *sa = (struct sockaddr *)&ss;
+ xzero (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;
+ (void *)&optval, (socklen_t)sizeof (optval)) < 0)
+ {
+ CLOSE (msock);
+ return CONSOCKERR;
+ }
+#endif
+
+#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,
+ (void *)&optval, (socklen_t)sizeof (optval));
+ }
+# endif
#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)
+ sockaddr_set_data (sa, bind_address, *port);
+ if (bind (msock, sa, sockaddr_size (sa)) < 0)
{
CLOSE (msock);
- msock = -1;
return BINDERR;
}
- DEBUGP (("Master socket fd %d bound.\n", msock));
+ DEBUGP (("Local 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_size (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));
+ sockaddr_get_data (sa, NULL, port);
+ DEBUGP (("binding to address %s using port %i.\n",
+ pretty_print_address (bind_address), *port));
}
if (listen (msock, 1) < 0)
{
CLOSE (msock);
- msock = -1;
return LISTENERR;
}
+ *local_sock = msock;
return BINDOK;
}
}
#endif /* HAVE_SELECT */
-/* 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.CONNECT_TIMEOUT seconds, the
+/* Accept a connection on LOCAL_SOCK, and store the new socket to
+ *SOCK. It blocks the caller until a connection is established. If
+ no connection is established for opt.connect_timeout seconds, the
function exits with an error status. */
+
uerr_t
-acceptport (int *sock)
+acceptport (int local_sock, 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.connect_timeout, 0) <= 0)
+ if (select_fd (local_sock, opt.connect_timeout, 0) <= 0)
return ACCEPTERR;
#endif
- if ((*sock = accept (msock, addr, &addrlen)) < 0)
+ if ((*sock = accept (local_sock, sa, &addrlen)) < 0)
return ACCEPTERR;
DEBUGP (("Created socket fd %d.\n", *sock));
return ACCEPTOK;
}
-/* Close SOCK, as well as the most recently remembered MSOCK, created
- via bindport(). If SOCK is -1, close MSOCK only. */
-void
-closeport (int sock)
-{
- /*shutdown (sock, 2);*/
- if (sock != -1)
- CLOSE (sock);
- if (msock != -1)
- CLOSE (msock);
- msock = -1;
-}
-
/* Return the local IP address associated with the connection on FD. */
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;
}