[svn] Imported Mauro's IPv6 changes.

This commit is contained in:
hniksic 2003-10-29 10:23:56 -08:00
parent dd6e9914e6
commit 90cb3309da
8 changed files with 1027 additions and 449 deletions

View File

@ -67,32 +67,44 @@ extern int errno;
/* Variables shared by bindport and acceptport: */
static int msock = -1;
static struct sockaddr *addr;
/*static struct sockaddr *addr;*/
static ip_address bind_address;
static int bind_address_resolved;
static void
resolve_bind_address (void)
static int
resolve_bind_address (int flags, ip_address *addr)
{
struct address_list *al;
struct address_list *al = NULL;
int bind_address_resolved = 0;
if (bind_address_resolved || opt.bind_address == NULL)
/* Nothing to do. */
return;
al = lookup_host (opt.bind_address, 1);
if (!al)
if (opt.bind_address != NULL)
{
logprintf (LOG_NOTQUIET,
_("Unable to convert `%s' to a bind address. Reverting to ANY.\n"),
opt.bind_address);
return;
al = lookup_host (opt.bind_address, 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);
}
else
bind_address_resolved = 1;
}
address_list_copy_one (al, 0, &bind_address);
if (al == NULL)
{
const char *unspecified_address = "0.0.0.0";
#ifdef ENABLE_IPV6
if (flags & BIND_ON_IPV6_ONLY)
unspecified_address = "::";
#endif
al = lookup_host (unspecified_address, LH_SILENT | LH_PASSIVE);
}
assert (al != NULL);
address_list_copy_one (al, 0, addr);
address_list_release (al);
bind_address_resolved = 1;
return bind_address_resolved;
}
struct cwt_context {
@ -151,15 +163,16 @@ set_connection_host_name (const char *host)
int
connect_to_one (ip_address *addr, unsigned short port, int silent)
{
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);
sockaddr_set_address (sa, port, addr);
if (!silent)
{
char *pretty_addr = pretty_print_address (addr);
const 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... "),
@ -170,7 +183,7 @@ connect_to_one (ip_address *addr, unsigned short port, int silent)
}
/* Make an internet socket, stream type. */
sock = socket (ip_default_family, SOCK_STREAM, 0);
sock = socket (sa->sa_family, SOCK_STREAM, 0);
if (sock < 0)
goto out;
@ -191,22 +204,26 @@ connect_to_one (ip_address *addr, unsigned short port, int silent)
`--post-file', 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 ()))
{
CLOSE (sock);
sock = -1;
goto out;
ip_address bind_address;
if (resolve_bind_address (0, &bind_address))
{
struct sockaddr_storage bss;
struct sockaddr *bsa = (struct sockaddr *)&bss;
sockaddr_set_address (bsa, 0, &bind_address);
if (bind (sock, bsa, sockaddr_len (bsa)))
{
CLOSE (sock);
sock = -1;
goto out;
}
}
}
/* Connect the socket to the remote host. */
if (connect_with_timeout (sock, &sa.sa, sockaddr_len (),
if (connect_with_timeout (sock, sa, sockaddr_len (sa),
opt.connect_timeout) < 0)
{
CLOSE (sock);
@ -297,28 +314,47 @@ test_socket_open (int sock)
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. */
uerr_t
bindport (unsigned short *port, int family)
bindport (const ip_address *bind_address, unsigned short *port)
{
int optval = 1;
wget_sockaddr srv;
memset (&srv, 0, sizeof (wget_sockaddr));
int family = AF_INET;
int optval;
struct sockaddr_storage ss;
struct sockaddr *sa = (struct sockaddr *)&ss;
memset (&ss, 0, sizeof (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;
#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)
#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,
(char *)&optval, sizeof (optval));
}
# endif
#endif
sockaddr_set_address (sa, htons (*port), bind_address);
if (bind (msock, sa, sockaddr_len (sa)) < 0)
{
CLOSE (msock);
msock = -1;
@ -327,15 +363,16 @@ bindport (unsigned short *port, int family)
DEBUGP (("Master socket fd %d bound.\n", msock));
if (!*port)
{
socklen_t sa_len = sockaddr_len ();
if (getsockname (msock, &srv.sa, &sa_len) < 0)
socklen_t sa_len = sockaddr_len (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));
*port = sockaddr_get_port (sa);
DEBUGP (("binding to address %s using port %i.\n",
pretty_print_address (bind_address), *port));
}
if (listen (msock, 1) < 0)
{
@ -389,13 +426,15 @@ select_fd (int fd, double maxtime, int writep)
uerr_t
acceptport (int *sock)
{
socklen_t 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)
return ACCEPTERR;
#endif
if ((*sock = accept (msock, addr, &addrlen)) < 0)
if ((*sock = accept (msock, sa, &addrlen)) < 0)
return ACCEPTERR;
DEBUGP (("Created socket fd %d.\n", *sock));
return ACCEPTOK;
@ -419,24 +458,34 @@ closeport (int sock)
int
conaddr (int fd, ip_address *ip)
{
wget_sockaddr mysrv;
socklen_t addrlen = sizeof (mysrv);
if (getsockname (fd, &mysrv.sa, &addrlen) < 0)
struct sockaddr_storage ss;
struct sockaddr *sa = (struct sockaddr *)&ss;
socklen_t addrlen = sizeof (ss);
if (getsockname (fd, sa, &addrlen) < 0)
return 0;
switch (mysrv.sa.sa_family)
switch (sa->sa_family)
{
#ifdef ENABLE_IPV6
case AF_INET6:
memcpy (ip, &mysrv.sin6.sin6_addr, 16);
ip->type = IPv6_ADDRESS;
ip->addr.ipv6.addr = ((struct sockaddr_in6 *)sa)->sin6_addr;
#ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
ip->addr.ipv6.scope_id = ((struct sockaddr_in6 *)sa)->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);
ip->type = IPv4_ADDRESS;
ip->addr.ipv4.addr = ((struct sockaddr_in *)sa)->sin_addr;
DEBUGP (("conaddr is: %s\n", pretty_print_address (ip)));
return 1;
default:
abort ();
}
return 0;
}

View File

@ -32,6 +32,12 @@ so, delete this exception statement from your version. */
#include "host.h"
/* bindport flags */
#define BIND_ON_IPV4_ONLY LH_IPv4_ONLY
#ifdef ENABLE_IPV6
#define BIND_ON_IPV6_ONLY LH_IPv6_ONLY
#endif /* ENABLE_IPV6 */
/* Function declarations */
int connect_to_one PARAMS ((ip_address *, unsigned short, int));
@ -40,7 +46,7 @@ void set_connection_host_name PARAMS ((const char *));
int test_socket_open PARAMS ((int));
int select_fd PARAMS ((int, double, int));
uerr_t bindport PARAMS ((unsigned short *, int));
uerr_t bindport PARAMS ((const ip_address *, unsigned short *));
uerr_t acceptport PARAMS ((int *));
void closeport PARAMS ((int));
int conaddr PARAMS ((int, ip_address *));

View File

@ -29,6 +29,7 @@ so, delete this exception statement from your version. */
#include <config.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
@ -126,7 +127,7 @@ ftp_request (const char *command, const char *value)
if (strncmp (res, "PASS", 4) != 0)
logprintf (LOG_ALWAYS, "--> %s\n", res);
else
logputs (LOG_ALWAYS, "--> PASS Turtle Power!\n");
logputs (LOG_ALWAYS, "--> PASS Turtle Power!\n\n");
}
else
DEBUGP (("\n--> %s\n", res));
@ -252,61 +253,23 @@ ftp_login (struct rbuf *rbuf, const char *acc, const char *pass)
return FTPOK;
}
#ifdef ENABLE_IPV6
uerr_t
ftp_eprt (struct rbuf *rbuf)
static void
ip_address_to_port_repr (const ip_address *addr, unsigned short port, char *buf,
size_t buflen)
{
uerr_t err;
unsigned char *ptr;
char *request, *respline;
ip_address in_addr;
unsigned short port;
assert (addr != NULL);
assert (addr->type == IPv4_ADDRESS);
assert (buf != NULL);
/* buf must contain the argument of PORT (of the form a,b,c,d,e,f). */
assert (buflen >= 6 * 4);
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;
ptr = (unsigned char *)(&addr->addr.ipv4.addr.s_addr);
snprintf (buf, buflen, "%d,%d,%d,%d,%d,%d", ptr[0], ptr[1],
ptr[2], ptr[3], (unsigned) (port & 0xff00) >> 8, port & 0xff);
buf[buflen - 1] = '\0';
}
#endif
/* Bind a port and send the appropriate PORT command to the FTP
server. Use acceptport after RETR, to get the socket of data
@ -316,53 +279,136 @@ ftp_port (struct rbuf *rbuf)
{
uerr_t err;
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;
ip_address addr;
int nwritten;
unsigned short port;
#ifdef ENABLE_IPV6
/*
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
/* Must contain the argument of PORT (of the form a,b,c,d,e,f). */
char bytes[6 * 4 + 1];
assert (rbuf != NULL);
assert (rbuf_initialized_p (rbuf));
/* Get the address of this side of the connection. */
if (!conaddr (RBUF_FD (rbuf), &addr))
return BINDERR;
assert (addr.type == IPv4_ADDRESS);
/* Setting port to 0 lets the system choose a free port. */
port = 0;
err = bindport (&port, AF_INET);
/* Bind the port. */
err = bindport (&addr, &port);
if (err != BINDOK)
return err;
/* 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). */
ip_address_to_port_repr (&addr, port, bytes, sizeof (bytes));
/* 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));
if (nwritten < 0)
{
xfree (request);
closeport (-1);
return WRITEFAILED;
}
xfree (request);
/* Get appropriate response. */
err = ftp_response (rbuf, &respline);
if (err != FTPOK)
{
xfree (respline);
closeport (-1);
return err;
}
if (*respline != '2')
{
xfree (respline);
closeport (-1);
return FTPPORTERR;
}
xfree (respline);
return FTPOK;
}
#ifdef ENABLE_IPV6
static void
ip_address_to_lprt_repr (const ip_address *addr, unsigned short port, char *buf,
size_t buflen)
{
unsigned char *ptr;
assert (addr != NULL);
assert (addr->type == IPv4_ADDRESS || addr->type == IPv6_ADDRESS);
assert (buf != NULL);
/* buf must contain the argument of LPRT (of the form af,n,h1,h2,...,hn,p1,p2). */
assert (buflen >= 21 * 4);
/* Construct the argument of LPRT (of the form af,n,h1,h2,...,hn,p1,p2). */
switch (addr->type)
{
case IPv4_ADDRESS:
ptr = (unsigned char *)(&addr->addr.ipv4.addr);
snprintf (buf, buflen, "%d,%d,%d,%d,%d,%d,%d,%d,%d", 4, 4,
ptr[0], ptr[1], ptr[2], ptr[3], 2,
(unsigned) (port & 0xff00) >> 8, port & 0xff);
buf[buflen - 1] = '\0';
break;
case IPv6_ADDRESS:
ptr = (unsigned char *)(&addr->addr.ipv6.addr);
snprintf (buf, buflen, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
6, 16, ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5], ptr[6], ptr[7],
ptr[8], ptr[9], ptr[10], ptr[11], ptr[12], ptr[13], ptr[14], ptr[15], 2,
(unsigned) (port & 0xff00) >> 8, port & 0xff);
buf[buflen - 1] = '\0';
break;
}
}
/* Bind a port and send the appropriate PORT command to the FTP
server. Use acceptport after RETR, to get the socket of data
connection. */
uerr_t
ftp_lprt (struct rbuf *rbuf)
{
uerr_t err;
char *request, *respline;
ip_address addr;
int nwritten;
unsigned short port;
/* Must contain the argument of LPRT (of the form af,n,h1,h2,...,hn,p1,p2). */
char bytes[21 * 4 + 1];
assert (rbuf != NULL);
assert (rbuf_initialized_p (rbuf));
/* Get the address of this side of the connection. */
if (!conaddr (RBUF_FD (rbuf), &addr))
return BINDERR;
assert (addr.type == IPv4_ADDRESS || addr.type == IPv6_ADDRESS);
/* Setting port to 0 lets the system choose a free port. */
port = 0;
/* Bind the port. */
err = bindport (&addr, &port);
if (err != BINDOK)
return err;
/* Construct the argument of LPRT (of the form af,n,h1,h2,...,hn,p1,p2). */
ip_address_to_lprt_repr (&addr, port, bytes, sizeof (bytes));
/* Send PORT request. */
request = ftp_request ("LPRT", bytes);
nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
if (nwritten < 0)
{
xfree (request);
closeport (-1);
return WRITEFAILED;
}
xfree (request);
@ -371,77 +417,104 @@ ftp_port (struct rbuf *rbuf)
if (err != FTPOK)
{
xfree (respline);
closeport (-1);
return err;
}
if (*respline != '2')
{
xfree (respline);
closeport (-1);
return FTPPORTERR;
}
xfree (respline);
return FTPOK;
}
#ifdef ENABLE_IPV6
uerr_t
ftp_epsv (struct rbuf *rbuf, ip_address *addr, unsigned short *port,
char *typ)
static void
ip_address_to_eprt_repr (const ip_address *addr, unsigned short port, char *buf,
size_t buflen)
{
int err;
char *s, *respline;
char *request = ftp_request ("EPSV", typ);
if (0 > iwrite (RBUF_FD (rbuf), request, strlen (request)))
int afnum;
assert (addr != NULL);
assert (addr->type == IPv4_ADDRESS || addr->type == IPv6_ADDRESS);
assert (buf != NULL);
/* buf must contain the argument of EPRT (of the form |af|addr|port|).
* 4 chars for the | separators, INET6_ADDRSTRLEN chars for addr
* 1 char for af (1-2) and 5 chars for port (0-65535) */
assert (buflen >= 4 + INET6_ADDRSTRLEN + 1 + 5);
/* Construct the argument of EPRT (of the form |af|addr|port|). */
afnum = (addr->type == IPv4_ADDRESS ? 1 : 2);
snprintf (buf, buflen, "|%d|%s|%d|", afnum, pretty_print_address (addr), port);
buf[buflen - 1] = '\0';
}
/* Bind a port and send the appropriate PORT command to the FTP
server. Use acceptport after RETR, to get the socket of data
connection. */
uerr_t
ftp_eprt (struct rbuf *rbuf)
{
uerr_t err;
char *request, *respline;
ip_address addr;
int nwritten;
unsigned short port;
/* Must contain the argument of EPRT (of the form |af|addr|port|).
* 4 chars for the | separators, ENABLE_IPV6_ADDRSTRLEN chars for addr
* 1 char for af (1-2) and 5 chars for port (0-65535) */
char bytes[4 + INET6_ADDRSTRLEN + 1 + 5 + 1];
assert (rbuf != NULL);
assert (rbuf_initialized_p(rbuf));
/* Get the address of this side of the connection. */
if (!conaddr (RBUF_FD (rbuf), &addr))
return BINDERR;
assert (addr.type == IPv4_ADDRESS || addr.type == IPv6_ADDRESS);
/* Setting port to 0 lets the system choose a free port. */
port = 0;
/* Bind the port. */
err = bindport (&addr, &port);
if (err != BINDOK)
return err;
/* Construct the argument of LPRT (of the form af,n,h1,h2,...,hn,p1,p2). */
ip_address_to_eprt_repr (&addr, port, bytes, sizeof (bytes));
/* Send PORT request. */
request = ftp_request ("EPRT", bytes);
nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
if (nwritten < 0)
{
xfree (request);
closeport (-1);
return WRITEFAILED;
}
/* Get the server response. */
xfree (request);
/* Get appropriate response. */
err = ftp_response (rbuf, &respline);
if (err != FTPOK)
{
xfree (respline);
closeport (-1);
return err;
}
if (*respline != '2')
{
xfree (respline);
return FTPNOPASV;
closeport (-1);
return FTPPORTERR;
}
/* 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;
socklen_t addrlen = sizeof (remote);
struct sockaddr_in *ipv4_sock = (struct sockaddr_in *)&remote;
getpeername (RBUF_FD (rbuf), (struct sockaddr *)&remote, &addrlen);
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. */
@ -451,19 +524,15 @@ 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];
unsigned char tmp[6];
assert (rbuf != NULL);
assert (rbuf_initialized_p(rbuf));
assert (addr != NULL);
assert (port != NULL);
memset (addr, 0, sizeof (ip_address));
#ifdef ENABLE_IPV6
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. */
@ -487,16 +556,132 @@ ftp_pasv (struct rbuf *rbuf, ip_address *addr, unsigned short *port)
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 < 4; i++)
for (i = 0; i < 6; i++)
{
addr4[i] = 0;
tmp[i] = 0;
for (; ISDIGIT (*s); s++)
addr4[i] = (*s - '0') + 10 * addr4[i];
tmp[i] = (*s - '0') + 10 * tmp[i];
if (*s == ',')
s++;
else if (i < 5)
{
/* When on the last number, anything can be a terminator. */
xfree (respline);
return FTPINVPASV;
}
}
xfree (respline);
addr->type = IPv4_ADDRESS;
/* Mauro Tortonesi: is this safe and/or elegant enough? */
memcpy (&addr->addr.ipv4.addr, tmp, 4);
*port = ((tmp[4] << 8) & 0xff00) + tmp[5];
return FTPOK;
}
#ifdef ENABLE_IPV6
/* Similar to ftp_lprt, but uses `LPSV' 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_lpsv (struct rbuf *rbuf, ip_address *addr, unsigned short *port)
{
char *request, *respline, *s;
int nwritten, i, af, addrlen, portlen;
uerr_t err;
unsigned char tmp[16];
unsigned char tmpprt[2];
assert (rbuf != NULL);
assert (rbuf_initialized_p(rbuf));
assert (addr != NULL);
assert (port != NULL);
memset (addr, 0, sizeof (ip_address));
/* Form the request. */
request = ftp_request ("LPSV", NULL);
/* And send it. */
nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
if (nwritten < 0)
{
xfree (request);
return WRITEFAILED;
}
xfree (request);
/* 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 response. */
s = respline;
for (s += 4; *s && !ISDIGIT (*s); s++);
if (!*s)
return FTPINVPASV;
/* First, get the address family */
af = 0;
for (; ISDIGIT (*s); s++)
af = (*s - '0') + 10 * af;
if (af != 4 && af != 6)
{
xfree (respline);
return FTPINVPASV;
}
if (!*s || *s++ != ',')
{
xfree (respline);
return FTPINVPASV;
}
/* Then, get the address length */
addrlen = 0;
for (; ISDIGIT (*s); s++)
addrlen = (*s - '0') + 10 * addrlen;
if (!*s || *s++ != ',')
{
xfree (respline);
return FTPINVPASV;
}
if (addrlen > 16)
{
xfree (respline);
return FTPINVPASV;
}
if ((af == 4 && addrlen != 4)
|| (af == 6 && addrlen != 16))
{
xfree (respline);
return FTPINVPASV;
}
/* Now, we get the actual address */
for (i = 0; i < addrlen; i++)
{
tmp[i] = 0;
for (; ISDIGIT (*s); s++)
tmp[i] = (*s - '0') + 10 * tmp[i];
if (*s == ',')
s++;
else
@ -506,30 +691,185 @@ ftp_pasv (struct rbuf *rbuf, ip_address *addr, unsigned short *port)
}
}
/* Eventually make an IPv4 in IPv6 adress if needed */
map_ipv4_to_ip ((ip4_address *)addr4, addr);
*port=0;
/* Now, get the port length */
portlen = 0;
for (; ISDIGIT (*s); s++)
*port = (*s - '0') + 10 * (*port);
if (*s == ',')
s++;
else
portlen = (*s - '0') + 10 * portlen;
if (!*s || *s++ != ',')
{
xfree (respline);
return FTPINVPASV;
}
{
unsigned short port2 = 0;
for (; ISDIGIT (*s); s++)
port2 = (*s - '0') + 10 * port2;
*port = (*port) * 256 + port2;
}
if (portlen > 2)
{
xfree (respline);
return FTPINVPASV;
}
/* Finally, we get the port number */
tmpprt[0] = 0;
for (; ISDIGIT (*s); s++)
tmpprt[0] = (*s - '0') + 10 * tmpprt[0];
if (!*s || *s++ != ',')
{
xfree (respline);
return FTPINVPASV;
}
tmpprt[1] = 0;
for (; ISDIGIT (*s); s++)
tmpprt[1] = (*s - '0') + 10 * tmpprt[1];
assert (s != NULL);
if (af == 4)
{
addr->type = IPv4_ADDRESS;
memcpy (&addr->addr.ipv4.addr, tmp, 4);
*port = ((tmpprt[0] << 8) & 0xff00) + tmpprt[1];
DEBUGP (("lpsv addr is: %s\n", pretty_print_address(addr)));
DEBUGP (("tmpprt[0] is: %d\n", tmpprt[0]));
DEBUGP (("tmpprt[1] is: %d\n", tmpprt[1]));
DEBUGP (("*port is: %d\n", *port));
}
else
{
assert (af == 6);
addr->type = IPv6_ADDRESS;
memcpy (&addr->addr.ipv6.addr, tmp, 16);
*port = ((tmpprt[0] << 8) & 0xff00) + tmpprt[1];
DEBUGP (("lpsv addr is: %s\n", pretty_print_address(addr)));
DEBUGP (("tmpprt[0] is: %d\n", tmpprt[0]));
DEBUGP (("tmpprt[1] is: %d\n", tmpprt[1]));
DEBUGP (("*port is: %d\n", *port));
}
xfree (respline);
return FTPOK;
}
/* Similar to ftp_eprt, but uses `EPSV' 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_epsv (struct rbuf *rbuf, ip_address *addr, unsigned short *port)
{
char *request, *respline, *start, delim, *s;
int nwritten, i;
uerr_t err;
unsigned short tport;
socklen_t addrlen;
struct sockaddr_storage ss;
struct sockaddr *sa = (struct sockaddr *)&ss;
assert (rbuf != NULL);
assert (rbuf_initialized_p(rbuf));
assert (addr != NULL);
assert (port != NULL);
addrlen = sizeof (ss);
if (getpeername (rbuf->fd, sa, &addrlen) < 0)
/* Mauro Tortonesi: HOW DO WE HANDLE THIS ERROR? */
return CONPORTERR;
assert (sa->sa_family == AF_INET || sa->sa_family == AF_INET6);
sockaddr_get_address (sa, NULL, addr);
/* Form the request. */
/* EPSV 1 means that we ask for IPv4 and EPSV 2 means that we ask for IPv6. */
request = ftp_request ("EPSV", (sa->sa_family == AF_INET ? "1" : "2"));
/* And send it. */
nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
if (nwritten < 0)
{
xfree (request);
return WRITEFAILED;
}
xfree (request);
/* Get the server response. */
err = ftp_response (rbuf, &respline);
if (err != FTPOK)
{
xfree (respline);
return err;
}
if (*respline != '2')
{
xfree (respline);
return FTPNOPASV;
}
assert (respline != NULL);
DEBUGP(("respline is %s\n", respline));
/* Parse the response. */
s = respline;
/* Skip the useless stuff and get what's inside the parentheses */
start = strchr (respline, '(');
if (start == NULL)
{
xfree (respline);
return FTPINVPASV;
}
/* Skip the first two void fields */
s = start + 1;
delim = *s++;
if (delim < 33 || delim > 126)
{
xfree (respline);
return FTPINVPASV;
}
for (i = 0; i < 2; i++)
{
if (*s++ != delim)
{
xfree (respline);
return FTPINVPASV;
}
}
/* Finally, get the port number */
tport = 0;
for (i = 1; ISDIGIT (*s); s++)
{
if (i > 5)
{
xfree (respline);
return FTPINVPASV;
}
tport = (*s - '0') + 10 * tport;
}
/* Make sure that the response terminates correcty */
if (*s++ != delim)
{
xfree (respline);
return FTPINVPASV;
}
if (*s++ != ')')
{
xfree (respline);
return FTPINVPASV;
}
*port = tport;
xfree (respline);
return FTPOK;
}
#endif
/* Sends the TYPE request to the server. */
uerr_t
ftp_type (struct rbuf *rbuf, int type)
@ -755,7 +1095,7 @@ ftp_syst (struct rbuf *rbuf, enum stype *server_type)
/* Skip the number (215, but 200 (!!!) in case of VMS) */
strtok (respline, " ");
/* Which system type has been reported (we are interested just in the
first word of the server response)? */
request = strtok (NULL, " ");
@ -812,7 +1152,7 @@ ftp_pwd (struct rbuf *rbuf, char **pwd)
and everything following it. */
strtok (respline, "\"");
request = strtok (NULL, "\"");
/* Has the `pwd' been already allocated? Free! */
FREE_MAYBE (*pwd);

117
src/ftp.c
View File

@ -121,6 +121,110 @@ ftp_expected_bytes (const char *s)
return res;
}
#ifdef ENABLE_IPV6
static int
getfamily (int fd)
{
struct sockaddr_storage ss;
struct sockaddr *sa = (struct sockaddr *)&ss;
socklen_t len = sizeof (ss);
assert (fd >= 0);
if (getpeername (fd, sa, &len) < 0)
/* Mauro Tortonesi: HOW DO WE HANDLE THIS ERROR? */
abort ();
return sa->sa_family;
}
/*
* This function sets up a passive data connection with the FTP server.
* It is merely a wrapper around ftp_epsv, ftp_lpsv and ftp_pasv.
*/
static uerr_t
ftp_do_pasv (struct rbuf *rbuf, ip_address *addr, unsigned short *port)
{
uerr_t err;
int family;
family = getfamily (rbuf->fd);
assert (family == AF_INET || family == AF_INET6);
/* If our control connection is over IPv6, then we first try EPSV and then
* LPSV if the former is not supported. If the control connection is over
* IPv4, we simply issue the good old PASV request. */
if (family == AF_INET6)
{
if (!opt.server_response)
logputs (LOG_VERBOSE, "==> EPSV ... ");
err = ftp_epsv (rbuf, addr, port);
/* If EPSV is not supported try LPSV */
if (err == FTPNOPASV)
{
if (!opt.server_response)
logputs (LOG_VERBOSE, "==> LPSV ... ");
err = ftp_lpsv (rbuf, addr, port);
}
}
else
{
if (!opt.server_response)
logputs (LOG_VERBOSE, "==> PASV ... ");
err = ftp_pasv (rbuf, addr, port);
}
return err;
}
/*
* This function sets up an active data connection with the FTP server.
* It is merely a wrapper around ftp_eprt, ftp_lprt and ftp_port.
*/
static uerr_t
ftp_do_port (struct rbuf *rbuf)
{
uerr_t err;
int family;
assert (rbuf != NULL);
assert (rbuf_initialized_p (rbuf));
family = getfamily (rbuf->fd);
assert (family == AF_INET || family == AF_INET6);
/* If our control connection is over IPv6, then we first try EPRT and then
* LPRT if the former is not supported. If the control connection is over
* IPv4, we simply issue the good old PORT request. */
if (family == AF_INET6)
{
if (!opt.server_response)
logputs (LOG_VERBOSE, "==> EPRT ... ");
err = ftp_eprt (rbuf);
/* If EPRT is not supported try LPRT */
if (err == FTPPORTERR)
{
if (!opt.server_response)
logputs (LOG_VERBOSE, "==> LPRT ... ");
err = ftp_lprt (rbuf);
}
}
else
{
if (!opt.server_response)
logputs (LOG_VERBOSE, "==> PORT ... ");
err = ftp_port (rbuf);
}
return err;
}
#else
#define ftp_do_pasv ftp_pasv
#define ftp_do_port ftp_port
#endif
/* Retrieves a file with denoted parameters through opening an FTP
connection to the server. It always closes the data connection,
and closes the control connection in case of error. */
@ -542,7 +646,7 @@ Error in server response, closing control connection.\n"));
unsigned short passive_port;
if (!opt.server_response)
logputs (LOG_VERBOSE, "==> PASV ... ");
err = ftp_pasv (&con->rbuf, &passive_addr, &passive_port);
err = ftp_do_pasv (&con->rbuf, &passive_addr, &passive_port);
/* FTPRERR, WRITEFAILED, FTPNOPASV, FTPINVPASV */
switch (err)
{
@ -579,13 +683,16 @@ Error in server response, closing control connection.\n"));
} /* switch(err) */
if (err==FTPOK)
{
DEBUGP (("trying to connect to %s port %d\n",
pretty_print_address (&passive_addr),
passive_port));
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"),
logprintf (LOG_VERBOSE, _("couldn't connect to %s port %hu: %s\n"),
pretty_print_address (&passive_addr), passive_port,
strerror (save_errno));
return CONNECT_ERROR (save_errno);
@ -601,7 +708,7 @@ Error in server response, closing control connection.\n"));
{
if (!opt.server_response)
logputs (LOG_VERBOSE, "==> PORT ... ");
err = ftp_port (&con->rbuf);
err = ftp_do_port (&con->rbuf);
/* FTPRERR, WRITEFAILED, bindport (CONSOCKERR, CONPORTERR, BINDERR,
LISTENERR), HOSTERR, FTPPORTERR */
switch (err)
@ -739,6 +846,7 @@ Error in server response, closing control connection.\n"));
logprintf (LOG_VERBOSE, "==> RETR %s ... ", u->file);
}
}
err = ftp_retr (&con->rbuf, u->file);
/* FTPRERR, WRITEFAILED, FTPNSFOD */
switch (err)
@ -930,6 +1038,7 @@ Error in server response, closing control connection.\n"));
if (flush_res == EOF)
res = -2;
}
/* If get_contents couldn't write to fp, bail out. */
if (res == -2)
{
@ -1224,7 +1333,7 @@ ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con)
logprintf (LOG_NOTQUIET, "unlink: %s\n", strerror (errno));
}
}
/* Restore the original leave-pendingness. */
if (orig_lp)
con->cmd |= LEAVE_PENDING;

View File

@ -51,8 +51,10 @@ 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 *, ip_address *, unsigned short *));
#ifdef ENABLE_IPV6
uerr_t ftp_epsv PARAMS ((struct rbuf *, ip_address *, unsigned short *,
char *));
uerr_t ftp_lprt PARAMS ((struct rbuf *));
uerr_t ftp_lpsv PARAMS ((struct rbuf *, ip_address *, unsigned short *));
uerr_t ftp_eprt PARAMS ((struct rbuf *));
uerr_t ftp_epsv PARAMS ((struct rbuf *, ip_address *, unsigned short *));
#endif
uerr_t ftp_type PARAMS ((struct rbuf *, int));
uerr_t ftp_cwd PARAMS ((struct rbuf *, const char *));

View File

@ -45,7 +45,7 @@ so, delete this exception statement from your version. */
#ifdef WINDOWS
# include <winsock.h>
# define SET_H_ERRNO(err) WSASetLastError(err)
# define SET_H_ERRNO(err) WSASetLastError (err)
#else
# include <sys/socket.h>
# include <netinet/in.h>
@ -82,9 +82,9 @@ extern int h_errno;
#endif
#ifdef ENABLE_IPV6
int ip_default_family = AF_INET6;
int ip_default_family = AF_UNSPEC;
#else
int ip_default_family = AF_INET;
int ip_default_family = AF_INET;
#endif
/* Mapping between known hosts and to lists of their addresses. */
@ -105,7 +105,7 @@ struct address_list {
/* Get the bounds of the address list. */
void
address_list_get_bounds (struct address_list *al, int *start, int *end)
address_list_get_bounds (const struct address_list *al, int *start, int *end)
{
*start = al->faulty;
*end = al->count;
@ -114,7 +114,7 @@ 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, ip_address *ip_store)
address_list_copy_one (const struct address_list *al, int index, ip_address *ip_store)
{
assert (index >= al->faulty && index < al->count);
memcpy (ip_store, al->addresses + index, sizeof (ip_address));
@ -123,14 +123,43 @@ address_list_copy_one (struct address_list *al, int index, ip_address *ip_store)
/* Check whether two address lists have all their IPs in common. */
int
address_list_match_all (struct address_list *al1, struct address_list *al2)
address_list_match_all (const struct address_list *al1, const struct address_list *al2)
{
int i;
if (al1 == al2)
return 1;
if (al1->count != al2->count)
return 0;
return 0 == memcmp (al1->addresses, al2->addresses,
al1->count * sizeof (ip_address));
for (i = 0; i < al1->count; ++i)
{
#ifdef ENABLE_IPv6
if (al1->addresses[i].type != al2->addresses[i].type)
return 0;
if (al1->addresses[i].type == IPv6_ADDRESS)
{
const struct in6_addr *addr1 = &al1->addresses[i].addr.ipv6.addr;
const struct in6_addr *addr2 = &al2->addresses[i].addr.ipv6.addr;
#ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
if ((al1->addresses[i].address.scope_id
!= al2->addresses[i].address.scope_id)
|| !IN6_ARE_ADDR_EQUAL (addr1, addr2))
#else
if (!IN6_ARE_ADDR_EQUAL (addr1, addr2))
#endif
return 0;
}
else
#endif
{
const struct in_addr *addr1 = (const struct in_addr *)&al1->addresses[i].addr.ipv4.addr;
const struct in_addr *addr2 = (const struct in_addr *)&al2->addresses[i].addr.ipv4.addr;
if (addr1->s_addr != addr2->s_addr)
return 0;
}
}
return 1;
}
/* Mark the INDEXth element of AL as faulty, so that the next time
@ -153,7 +182,7 @@ address_list_set_faulty (struct address_list *al, int index)
al->faulty = 0;
}
#ifdef HAVE_GETADDRINFO
#ifdef ENABLE_IPV6
/**
* address_list_from_addrinfo
*
@ -166,15 +195,15 @@ address_list_set_faulty (struct address_list *al, int index)
* address_list* New allocated address_list
*/
static struct address_list *
address_list_from_addrinfo (struct addrinfo *ai)
address_list_from_addrinfo (const struct addrinfo *ai)
{
struct address_list *al;
struct addrinfo *ai_head = ai;
const struct addrinfo *ptr;
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)
for (ptr = ai; ptr != NULL ; ptr = ptr->ai_next)
if (ptr->ai_family == AF_INET || ptr->ai_family == AF_INET6)
++cnt;
if (cnt == 0)
return NULL;
@ -185,17 +214,24 @@ address_list_from_addrinfo (struct addrinfo *ai)
al->faulty = 0;
al->refcount = 1;
for (i = 0, ai = ai_head; ai; ai = ai->ai_next)
if (ai->ai_family == AF_INET6)
for (i = 0, ptr = ai; ptr != NULL; ptr = ptr->ai_next)
if (ptr->ai_family == AF_INET6)
{
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ai->ai_addr;
memcpy (al->addresses + i, &sin6->sin6_addr, 16);
const struct sockaddr_in6 *sin6 =
(const struct sockaddr_in6 *)ptr->ai_addr;
al->addresses[i].addr.ipv6.addr = sin6->sin6_addr;
al->addresses[i].type = IPv6_ADDRESS;
#ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
al->addresses[i].addr.ipv6.scope_id = sin6->sin6_scope_id;
#endif
++i;
}
else if (ai->ai_family == AF_INET)
else if (ptr->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);
const struct sockaddr_in *sin =
(const struct sockaddr_in *)ptr->ai_addr;
al->addresses[i].addr.ipv4.addr = sin->sin_addr;
al->addresses[i].type = IPv4_ADDRESS;
++i;
}
assert (i == cnt);
@ -219,18 +255,20 @@ address_list_from_vector (char **h_addr_list)
al->addresses = xmalloc (count * sizeof (ip_address));
al->refcount = 1;
for (i = 0; i < count; i++)
map_ipv4_to_ip ((ip4_address *)h_addr_list[i], al->addresses + i);
for (i = 0; i < count; i++) {
/* Mauro Tortonesi: is this safe? */
memcpy (&((al->addresses + i)->addr.ipv4.addr.s_addr), h_addr_list[i], 4);
(al->addresses + i)->type = IPv4_ADDRESS;
}
return al;
}
#endif
/* Like address_list_from_vector, but initialized with a single
address. */
static struct address_list *
address_list_from_single (ip_address *addr)
address_list_from_single (const ip_address *addr)
{
struct address_list *al = xmalloc (sizeof (struct address_list));
al->count = 1;
@ -241,6 +279,7 @@ address_list_from_single (ip_address *addr)
return al;
}
#endif
static void
address_list_delete (struct address_list *al)
@ -262,58 +301,93 @@ address_list_release (struct address_list *al)
}
/**
* wget_sockaddr_set_address
* 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.
* This function takes a sockaddr struct and fills in the protocol type,
* the port number and the address. If ENABLE_IPV6 is defined, the sa
* parameter should point to a sockaddr_storage structure; if not, it
* should point to a sockaddr_in structure.
* If the address parameter is NULL, the function will use the unspecified
* address (0.0.0.0 for IPv4 and :: for IPv6).
* Unsupported address family will abort the whole programm.
*
* Input:
* wget_sockaddr* The space to be filled
* int The wished protocol
* struct sockaddr* The space to be filled
* unsigned short The port
* const ip_address The Binary IP adress
* const ip_address The IP address
*
* Return:
* - Only modify 1. param
* - Only modifies 1st parameter.
*/
void
wget_sockaddr_set_address (wget_sockaddr *sa,
int ip_family, unsigned short port, ip_address *addr)
sockaddr_set_address (struct sockaddr *sa, unsigned short port,
const ip_address *addr)
{
if (ip_family == AF_INET)
if (addr->type == IPv4_ADDRESS)
{
sa->sin.sin_family = ip_family;
sa->sin.sin_port = htons (port);
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
sin->sin_family = AF_INET;
sin->sin_port = htons (port);
if (addr == NULL)
memset (&sa->sin.sin_addr, 0, sizeof(ip4_address));
sin->sin_addr.s_addr = INADDR_ANY;
else
{
ip4_address addr4;
if (!map_ip_to_ipv4 (addr, &addr4))
/* should the callers have prevented this? */
abort ();
memcpy (&sa->sin.sin_addr, &addr4, sizeof(ip4_address));
}
return;
sin->sin_addr = addr->addr.ipv4.addr;
}
#ifdef ENABLE_IPV6
if (ip_family == AF_INET6)
else if (addr->type == IPv6_ADDRESS)
{
sa->sin6.sin6_family = ip_family;
sa->sin6.sin6_port = htons (port);
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
sin6->sin6_family = AF_INET6;
sin6->sin6_port = htons (port);
if (addr == NULL)
memset (&sa->sin6.sin6_addr, 0 , 16);
else
memcpy (&sa->sin6.sin6_addr, addr, 16);
return;
sin6->sin6_addr = in6addr_any;
else
sin6->sin6_addr = addr->addr.ipv6.addr;
#ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
sin6->sin6_scope_id = addr->addr.ipv6.scope_id;
#endif /* HAVE_SOCKADDR_IN6_SCOPE_ID */
}
#endif
abort();
#endif /* ENABLE_IPV6 */
else
abort ();
}
void
sockaddr_get_address (const struct sockaddr *sa, unsigned short *port,
ip_address *addr)
{
if (sa->sa_family == AF_INET)
{
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
addr->type = IPv4_ADDRESS;
addr->addr.ipv4.addr = sin->sin_addr;
if (port != NULL)
*port = ntohs (sin->sin_port);
}
#ifdef ENABLE_IPV6
else if (sa->sa_family == AF_INET6)
{
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
addr->type = IPv6_ADDRESS;
addr->addr.ipv6.addr = sin6->sin6_addr;
#ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
addr->addr.ipv6.scope_id = sin6->sin6_scope_id;
#endif
if (port != NULL)
*port = ntohs (sin6->sin6_port);
}
#endif
else
abort ();
}
#if 0 /* currently unused */
/**
* wget_sockaddr_set_port
* sockaddr_set_port
*
* This funtion only fill the port of the socket information.
* If the protocol is not supported nothing is done.
@ -330,54 +404,27 @@ wget_sockaddr_set_address (wget_sockaddr *sa,
* - Only modify 1. param
*/
void
wget_sockaddr_set_port (wget_sockaddr *sa, unsigned short port)
sockaddr_set_port (struct sockaddr *sa, unsigned short port)
{
if (sa->sa.sa_family == AF_INET)
if (sa->sa_family == AF_INET)
{
sa->sin.sin_port = htons (port);
return;
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
sin->sin_port = htons (port);
}
#ifdef ENABLE_IPV6
if (sa->sa.sa_family == AF_INET6)
else if (sa->sa_family == AF_INET6)
{
sa->sin6.sin6_port = htons (port);
return;
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
sin6->sin6_port = htons (port);
}
#endif
abort();
else
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 ENABLE_IPV6
if (sa->sa.sa_family == AF_INET6)
return &sa->sin6.sin6_addr;
#endif
abort();
/* unreached */
return NULL;
}
/**
* wget_sockaddr_get_port
* sockaddr_get_port
*
* This function only return the port from the input structure
* Unsuported adress family will abort the whole programm.
@ -392,15 +439,18 @@ wget_sockaddr_get_addr (wget_sockaddr *sa)
* unsigned short Port Number in host order.
*/
unsigned short
wget_sockaddr_get_port (const wget_sockaddr *sa)
sockaddr_get_port (const struct sockaddr *sa)
{
if (sa->sa.sa_family == AF_INET)
return htons (sa->sin.sin_port);
if (sa->sa_family == AF_INET) {
const struct sockaddr_in *sin = (const struct sockaddr_in *)sa;
return htons (sin->sin_port);
#ifdef ENABLE_IPV6
if (sa->sa.sa_family == AF_INET6)
return htons (sa->sin6.sin6_port);
} else if (sa->sa_family == AF_INET6) {
const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa;
return htons (sin6->sin6_port);
#endif
abort();
} else
abort ();
/* do not complain about return nothing */
return -1;
}
@ -419,58 +469,26 @@ wget_sockaddr_get_port (const wget_sockaddr *sa)
* - Public IP-Family Information
*
* Output:
* socklen_t structure length for socket options
* int structure length for socket options
*/
socklen_t
sockaddr_len ()
sockaddr_len (const struct sockaddr *sa)
{
if (ip_default_family == AF_INET)
return sizeof (struct sockaddr_in);
if (sa->sa_family == AF_INET)
{
return sizeof (struct sockaddr_in);
}
#ifdef ENABLE_IPV6
if (ip_default_family == AF_INET6)
return sizeof (struct sockaddr_in6);
else if (sa->sa_family == AF_INET6)
{
return sizeof (struct sockaddr_in6);
}
#endif
abort();
else
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 ENABLE_IPV6
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 ENABLE_IPV6
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;
}
/* Versions of gethostbyname and getaddrinfo that support timeout. */
@ -555,21 +573,33 @@ getaddrinfo_with_timeout (const char *node, const char *service,
inet_ntoa. With IPv6, it either prints an IPv6 address or an IPv4
address. */
char *
pretty_print_address (ip_address *addr)
const char *
pretty_print_address (const ip_address *addr)
{
switch (addr->type)
{
case IPv4_ADDRESS:
return inet_ntoa (addr->addr.ipv4.addr);
#ifdef ENABLE_IPV6
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;
case IPv6_ADDRESS:
{
int len;
static char buf[128];
inet_ntop (AF_INET6, &addr->addr.ipv6.addr, buf, sizeof (buf));
#if 0
#ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
/* print also scope_id for all ?non-global? addresses */
snprintf (buf + len, sizeof (buf) - len, "%%%d", addr->addr.ipv6.scope_id);
#endif
return inet_ntoa (*(struct in_addr *)addr);
#endif
len = strlen (buf);
buf[sizeof (buf) - 1] = '\0';
return buf;
}
#endif
}
abort ();
return NULL;
}
/* Add host name HOST with the address ADDR_TEXT to the cache.
@ -598,28 +628,66 @@ cache_host_lookup (const char *host, struct address_list *al)
}
struct address_list *
lookup_host (const char *host, int silent)
lookup_host (const char *host, int flags)
{
struct address_list *al = NULL;
uint32_t addr_ipv4;
ip_address addr;
/* First, try to check whether the address is already a numeric
address. */
#ifdef ENABLE_IPV6
if (inet_pton (AF_INET6, host, &addr) > 0)
return address_list_from_single (&addr);
int err, family;
struct addrinfo hints, *res;
/* This ip_default_family+flags business looks like bad design to
me. This function should rather accept a FAMILY argument. */
if (flags & LH_IPv4_ONLY)
family = AF_INET;
else if (flags & LH_IPv6_ONLY)
family = AF_INET6;
else
family = ip_default_family;
#endif
/* First, try to check whether the address is already a numeric
address. Where getaddrinfo is available, we do it using the
AI_NUMERICHOST flag. Without IPv6, we check whether inet_addr
succeeds. */
#ifdef ENABLE_IPV6
memset (&hints, 0, sizeof (hints));
hints.ai_family = family;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_NUMERICHOST;
if (flags & LH_PASSIVE)
hints.ai_flags = AI_PASSIVE;
/* no need to call getaddrinfo_with_timeout here, as we're not
* relying on the DNS, but we're only doing an address translation
* from presentation (ASCII) to network format */
err = getaddrinfo (host, NULL, &hints, &res);
if (err == 0 && res != NULL)
{
al = address_list_from_addrinfo (res);
freeaddrinfo (res);
return al;
}
#else
{
uint32_t addr_ipv4 = (uint32_t)inet_addr (host);
if (addr_ipv4 != (uint32_t) -1)
{
/* The return value of inet_addr is in network byte order, so
we can just copy it to ADDR. */
ip_address addr;
/* This has a huge number of dereferences because C doesn't
support anonymous unions and because struct in_addr adds a
cast. */
addr.addr.ipv4.addr.s_addr = addr_ipv4;
addr.type = IPv4_ADDRESS;
return address_list_from_single (&addr);
}
}
#endif
addr_ipv4 = (uint32_t)inet_addr (host);
if (addr_ipv4 != (uint32_t)-1)
{
/* ADDR is defined to be in network byte order, which is what
this returns, so we can just copy it to STORE_IP. */
map_ipv4_to_ip ((ip4_address *)&addr_ipv4, &addr);
return address_list_from_single (&addr);
}
/* Then, try to find the host in the cache. */
if (host_name_addresses_map)
{
@ -632,41 +700,37 @@ lookup_host (const char *host, int silent)
}
}
if (!silent)
if (!(flags & LH_SILENT))
logprintf (LOG_VERBOSE, _("Resolving %s... "), host);
/* Host name lookup goes on below. */
#ifdef HAVE_GETADDRINFO
#ifdef ENABLE_IPV6
{
struct addrinfo hints, *ai;
int err;
memset (&hints, 0, sizeof (hints));
if (ip_default_family == AF_INET)
hints.ai_family = AF_INET;
else
hints.ai_family = PF_UNSPEC;
hints.ai_family = family;
hints.ai_socktype = SOCK_STREAM;
err = getaddrinfo_with_timeout (host, NULL, &hints, &ai, opt.dns_timeout);
if (flags & LH_PASSIVE)
hints.ai_flags = AI_PASSIVE;
if (err != 0 || ai == NULL)
err = getaddrinfo_with_timeout (host, NULL, &hints, &res, opt.dns_timeout);
if (err != 0 || res == NULL)
{
if (!silent)
if (!(flags & LH_SILENT))
logprintf (LOG_VERBOSE, _("failed: %s.\n"),
err != EAI_SYSTEM ? gai_strerror (err) : strerror (errno));
return NULL;
}
al = address_list_from_addrinfo (ai);
freeaddrinfo (ai);
al = address_list_from_addrinfo (res);
freeaddrinfo (res);
}
#else
{
struct hostent *hptr;
hptr = gethostbyname_with_timeout (host, opt.dns_timeout);
struct hostent *hptr = gethostbyname_with_timeout (host, opt.dns_timeout);
if (!hptr)
{
if (!silent)
if (!(flags & LH_SILENT))
{
if (errno != ETIMEDOUT)
logprintf (LOG_VERBOSE, _("failed: %s.\n"), herrmsg (h_errno));
@ -675,6 +739,7 @@ lookup_host (const char *host, int silent)
}
return NULL;
}
assert (hptr->h_length == 4);
/* Do all systems have h_addr_list, or is it a newer thing? If
the latter, use address_list_from_single. */
al = address_list_from_vector (hptr->h_addr_list);
@ -683,7 +748,7 @@ lookup_host (const char *host, int silent)
/* Print the addresses determined by DNS lookup, but no more than
three. */
if (!silent)
if (!(flags & LH_SILENT))
{
int i;
int printmax = al->count <= 3 ? al->count : 3;

View File

@ -6,7 +6,7 @@ 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
(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
@ -44,64 +44,71 @@ so, delete this exception statement from your version. */
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 ENABLE_IPV6
struct sockaddr_in6 sin6; /* IPv6 socket address */
#endif
} wget_sockaddr;
extern int ip_default_family; /* defined in host.c */
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>. */
enum {
IPv4_ADDRESS,
#ifdef ENABLE_IPV6
# define MAX_IP_ADDRESS_SIZE 16
#else
# define MAX_IP_ADDRESS_SIZE 4
#endif
typedef struct {
unsigned char bytes[MAX_IP_ADDRESS_SIZE];
IPv6_ADDRESS
#endif /* ENABLE_IPV6 */
} type;
union {
#ifdef ENABLE_IPV6
struct {
struct in6_addr addr;
#ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
unsigned int scope_id;
#endif /* HAVE_SOCKADDR_IN6_SCOPE_ID */
} ipv6;
#endif /* ENABLE_IPV6 */
struct {
struct in_addr addr;
} ipv4;
} addr;
} ip_address;
#ifndef ENABLE_IPV6
#ifndef HAVE_SOCKADDR_STORAGE
#define sockaddr_storage sockaddr_in
#endif
#endif /* ENABLE_IPV6 */
/* Flags for lookup_host */
#define LH_SILENT 0x0001
#define LH_PASSIVE 0x0002
#define LH_IPv4_ONLY 0x0004
#ifdef ENABLE_IPV6
#define LH_IPv6_ONLY 0x0008
#endif /* ENABLE_IPV6 */
/* 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,
void address_list_get_bounds PARAMS ((const struct address_list *,
int *, int *));
void address_list_copy_one PARAMS ((const struct address_list *, int,
ip_address *));
int address_list_match_all PARAMS ((struct address_list *,
struct address_list *));
int address_list_match_all PARAMS ((const struct address_list *,
const struct address_list *));
void address_list_set_faulty PARAMS ((struct address_list *, int));
void address_list_release PARAMS ((struct address_list *));
char *pretty_print_address PARAMS ((ip_address *));
const char *pretty_print_address PARAMS ((const ip_address *));
int accept_domain PARAMS ((struct url *));
int sufmatch PARAMS ((const char **, const char *));
void sockaddr_set_address PARAMS ((struct sockaddr *, unsigned short,
const ip_address *));
void sockaddr_get_address PARAMS ((const struct sockaddr *, unsigned short *,
ip_address *));
unsigned short sockaddr_get_port PARAMS ((const struct sockaddr *));
socklen_t sockaddr_len PARAMS ((const struct sockaddr *sa));
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 *));
socklen_t 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 */

View File

@ -441,7 +441,7 @@ register_persistent (const char *host, unsigned short port, int fd)
/* This lookup_host cannot fail, because it has the results in the
cache. */
pc_last_host_ip = lookup_host (host, 1);
pc_last_host_ip = lookup_host (host, LH_SILENT);
assert (pc_last_host_ip != NULL);
pc_last_port = port;
@ -496,7 +496,7 @@ persistent_available_p (const char *host, unsigned short port)
return 0;
#endif /* HAVE_SSL */
this_host_ip = lookup_host (host, 1);
this_host_ip = lookup_host (host, LH_SILENT);
if (!this_host_ip)
return 0;