1
0
mirror of https://github.com/moparisthebest/wget synced 2024-07-03 16:38:41 -04:00

[svn] Commit IPv6 support by Thomas Lussnig.

Published in <sxsk7u8715e.fsf@florida.arsdigita.de>.
This commit is contained in:
hniksic 2002-01-23 21:45:54 -08:00
parent e59a7ee8ac
commit cb40034035
11 changed files with 668 additions and 146 deletions

View File

@ -1,3 +1,7 @@
2002-01-24 Hrvoje Niksic <hniksic@arsdigita.com>
* source: Integrated IPv6 support by Thomas Lussnig.
2002-01-15 Ian Abbott <abbotti@mev.co.uk> 2002-01-15 Ian Abbott <abbotti@mev.co.uk>
* init.c (cmd_file): Change `\' to `/' for Windows (yes, really!) * init.c (cmd_file): Change `\' to `/' for Windows (yes, really!)

View File

@ -76,15 +76,13 @@ set_connection_host_name (const char *host)
/* Connect to a remote host whose address has been resolved. */ /* Connect to a remote host whose address has been resolved. */
int int
connect_to_one (const unsigned char *addr, unsigned short port, int silent) connect_to_one (ip_address *addr, unsigned short port, int silent)
{ {
struct sockaddr_in sock_name; wget_sockaddr sa;
int sock, save_errno; int sock, save_errno;
/* Set port and protocol */ /* Set port and protocol */
sock_name.sin_family = AF_INET; wget_sockaddr_set_address (&sa, ip_default_family, port, addr);
sock_name.sin_port = htons (port);
memcpy ((unsigned char *)&sock_name.sin_addr, addr, 4);
if (!silent) if (!silent)
{ {
@ -99,15 +97,14 @@ connect_to_one (const unsigned char *addr, unsigned short port, int silent)
} }
/* Make an internet socket, stream type. */ /* Make an internet socket, stream type. */
sock = socket (AF_INET, SOCK_STREAM, 0); sock = socket (ip_default_family, SOCK_STREAM, 0);
if (sock < 0) if (sock < 0)
goto out; goto out;
if (opt.bind_address) if (opt.bind_address)
{ {
/* Bind the client side to the requested address. */ /* Bind the client side to the requested address. */
if (bind (sock, (struct sockaddr *)opt.bind_address, if (bind (sock, (struct sockaddr *)opt.bind_address, sockaddr_len ()))
sizeof (*opt.bind_address)))
{ {
close (sock); close (sock);
sock = -1; sock = -1;
@ -116,7 +113,7 @@ connect_to_one (const unsigned char *addr, unsigned short port, int silent)
} }
/* Connect the socket to the remote host. */ /* Connect the socket to the remote host. */
if (connect (sock, (struct sockaddr *)&sock_name, sizeof (sock_name)) < 0) if (connect (sock, &sa.sa, sockaddr_len ()) < 0)
{ {
close (sock); close (sock);
sock = -1; sock = -1;
@ -151,11 +148,11 @@ connect_to_many (struct address_list *al, unsigned short port, int silent)
address_list_get_bounds (al, &start, &end); address_list_get_bounds (al, &start, &end);
for (i = start; i < end; i++) for (i = start; i < end; i++)
{ {
unsigned char addr[4]; ip_address addr;
int sock; int sock;
address_list_copy_one (al, i, addr); address_list_copy_one (al, i, &addr);
sock = connect_to_one (addr, port, silent); sock = connect_to_one (&addr, port, silent);
if (sock >= 0) if (sock >= 0)
/* Success. */ /* Success. */
return sock; return sock;
@ -207,29 +204,26 @@ test_socket_open (int sock)
internal variable MPORT is set to the value of the ensuing master internal variable MPORT is set to the value of the ensuing master
socket. Call acceptport() to block for and accept a connection. */ socket. Call acceptport() to block for and accept a connection. */
uerr_t uerr_t
bindport (unsigned short *port) bindport (unsigned short *port, int family)
{ {
int optval = 1; int optval = 1;
static struct sockaddr_in srv; wget_sockaddr srv;
memset (&srv, 0, sizeof (wget_sockaddr));
msock = -1; 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; return CONSOCKERR;
if (setsockopt (msock, SOL_SOCKET, SO_REUSEADDR, if (setsockopt (msock, SOL_SOCKET, SO_REUSEADDR,
(char *)&optval, sizeof (optval)) < 0) (char *)&optval, sizeof (optval)) < 0)
return CONSOCKERR; return CONSOCKERR;
if (opt.bind_address == NULL) if (opt.bind_address == NULL)
{ wget_sockaddr_set_address (&srv, ip_default_family, htons (*port), NULL);
srv.sin_family = AF_INET;
srv.sin_addr.s_addr = htonl (INADDR_ANY);
}
else else
srv = *opt.bind_address; srv = *opt.bind_address;
wget_sockaddr_set_port (&srv, *port);
srv.sin_port = htons (*port); if (bind (msock, &srv.sa, sockaddr_len ()) < 0)
if (bind (msock, addr, sizeof (struct sockaddr_in)) < 0)
{ {
CLOSE (msock); CLOSE (msock);
msock = -1; msock = -1;
@ -241,14 +235,15 @@ bindport (unsigned short *port)
/* #### addrlen should be a 32-bit type, which int is not /* #### 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, guaranteed to be. Oh, and don't try to make it a size_t,
because that can be 64-bit. */ because that can be 64-bit. */
int addrlen = sizeof (struct sockaddr_in); int sa_len = sockaddr_len ();
if (getsockname (msock, addr, &addrlen) < 0) if (getsockname (msock, &srv.sa, &sa_len) < 0)
{ {
CLOSE (msock); CLOSE (msock);
msock = -1; msock = -1;
return CONPORTERR; return CONPORTERR;
} }
*port = ntohs (srv.sin_port); *port = wget_sockaddr_get_port (&srv);
DEBUGP (("using port %i.\n", *port));
} }
if (listen (msock, 1) < 0) if (listen (msock, 1) < 0)
{ {
@ -292,7 +287,7 @@ select_fd (int fd, int maxtime, int writep)
uerr_t uerr_t
acceptport (int *sock) acceptport (int *sock)
{ {
int addrlen = sizeof (struct sockaddr_in); int addrlen = sockaddr_len ();
#ifdef HAVE_SELECT #ifdef HAVE_SELECT
if (select_fd (msock, opt.timeout, 0) <= 0) if (select_fd (msock, opt.timeout, 0) <= 0)
@ -317,22 +312,33 @@ closeport (int sock)
msock = -1; msock = -1;
} }
/* Return the local IP address associated with the connection on FD. /* Return the local IP address associated with the connection on FD. */
It is returned in a static buffer. */
unsigned char *
conaddr (int fd)
{
static unsigned char res[4];
struct sockaddr_in mysrv;
struct sockaddr *myaddr;
int addrlen = sizeof (mysrv); /* see bindport() for discussion of
using `int' here. */
myaddr = (struct sockaddr *) (&mysrv); int
if (getsockname (fd, myaddr, (int *)&addrlen) < 0) conaddr (int fd, ip_address *ip)
return NULL; {
memcpy (res, &mysrv.sin_addr, 4); wget_sockaddr mysrv;
return res;
/* 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 /* Read at most LEN bytes from FD, storing them to BUF. This is

View File

@ -24,16 +24,16 @@ struct address_list;
/* Function declarations */ /* Function declarations */
int connect_to_one PARAMS ((const unsigned char *, unsigned short, int)); int connect_to_one PARAMS ((ip_address *, unsigned short, int));
int connect_to_many PARAMS ((struct address_list *, unsigned short, int)); int connect_to_many PARAMS ((struct address_list *, unsigned short, int));
void set_connection_host_name PARAMS ((const char *)); void set_connection_host_name PARAMS ((const char *));
int test_socket_open PARAMS ((int)); int test_socket_open PARAMS ((int));
int select_fd PARAMS ((int, int, int)); int select_fd PARAMS ((int, int, int));
uerr_t bindport PARAMS ((unsigned short *)); uerr_t bindport PARAMS ((unsigned short *, int));
uerr_t acceptport PARAMS ((int *)); uerr_t acceptport PARAMS ((int *));
void closeport PARAMS ((int)); void closeport PARAMS ((int));
unsigned char *conaddr PARAMS ((int)); int conaddr PARAMS ((int, ip_address *));
int iread PARAMS ((int, char *, int)); int iread PARAMS ((int, char *, int));
int iwrite PARAMS ((int, char *, int)); int iwrite PARAMS ((int, char *, int));

View File

@ -33,6 +33,10 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#endif #endif
#include <sys/types.h> #include <sys/types.h>
/* For inet_ntop. */
#include <sys/socket.h>
#include <arpa/inet.h>
#ifdef WINDOWS #ifdef WINDOWS
# include <winsock.h> # include <winsock.h>
#endif #endif
@ -235,6 +239,62 @@ ftp_login (struct rbuf *rbuf, const char *acc, const char *pass)
return FTPOK; return FTPOK;
} }
#ifdef INET6
uerr_t
ftp_eprt (struct rbuf *rbuf)
{
uerr_t err;
char *request, *respline;
ip_address in_addr;
unsigned short port;
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;
}
#endif
/* Bind a port and send the appropriate PORT command to the FTP /* Bind a port and send the appropriate PORT command to the FTP
server. Use acceptport after RETR, to get the socket of data server. Use acceptport after RETR, to get the socket of data
connection. */ connection. */
@ -242,25 +302,48 @@ uerr_t
ftp_port (struct rbuf *rbuf) ftp_port (struct rbuf *rbuf)
{ {
uerr_t err; uerr_t err;
char *request, *respline, *bytes; char *request, *respline;
unsigned char *in_addr; char bytes[6 * 4 +1];
ip_address in_addr;
ip4_address in_addr_4;
unsigned char *in_addr4_ptr = (unsigned char *)&in_addr_4;
int nwritten; int nwritten;
unsigned short port; unsigned short port;
#ifdef INET6
/*
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
/* Setting port to 0 lets the system choose a free port. */ /* Setting port to 0 lets the system choose a free port. */
port = 0; port = 0;
/* Bind the port. */
err = bindport (&port); err = bindport (&port, AF_INET);
if (err != BINDOK) if (err != BINDOK)
return err; return err;
/* Get the address of this side of the connection. */
if (!(in_addr = conaddr (RBUF_FD (rbuf)))) /* 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; return BINDERR;
/* Construct the argument of PORT (of the form a,b,c,d,e,f). */ if (!map_ip_to_ipv4 (&in_addr, &in_addr_4))
bytes = (char *)alloca (6 * 4 + 1); return BINDERR;
sprintf (bytes, "%d,%d,%d,%d,%d,%d", in_addr[0], in_addr[1],
in_addr[2], in_addr[3], (unsigned) (port & 0xff00) >> 8, /* Construct the argument of PORT (of the form a,b,c,d,e,f). Port
port & 0xff); 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. */ /* Send PORT request. */
request = ftp_request ("PORT", bytes); request = ftp_request ("PORT", bytes);
nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request)); nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
@ -286,16 +369,88 @@ ftp_port (struct rbuf *rbuf)
return FTPOK; return FTPOK;
} }
#ifdef INET6
uerr_t
ftp_epsv (struct rbuf *rbuf, ip_address *addr, unsigned short *port,
char *typ)
{
int err;
char *s, *respline;
char *request = ftp_request ("EPSV", typ);
if (0 > iwrite (RBUF_FD (rbuf), request, strlen (request)))
{
xfree (request);
return WRITEFAILED;
}
/* 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 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;
int len = sizeof (remote);
struct sockaddr_in *ipv4_sock = ( struct sockaddr_in *)&remote;
getpeername (RBUF_FD (rbuf), (struct sockaddr*)&remote, &len);
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 /* Similar to ftp_port, but uses `PASV' to initiate the passive FTP
transfer. Reads the response from server and parses it. Reads the transfer. Reads the response from server and parses it. Reads the
host and port addresses and returns them. */ host and port addresses and returns them. */
uerr_t uerr_t
ftp_pasv (struct rbuf *rbuf, unsigned char *addr) ftp_pasv (struct rbuf *rbuf, ip_address *addr, unsigned short *port)
{ {
char *request, *respline, *s; char *request, *respline, *s;
int nwritten, i; int nwritten, i;
uerr_t err; uerr_t err;
unsigned char addr4[4];
#ifdef INET6
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. */ /* Form the request. */
request = ftp_request ("PASV", NULL); request = ftp_request ("PASV", NULL);
/* And send it. */ /* And send it. */
@ -319,24 +474,45 @@ ftp_pasv (struct rbuf *rbuf, unsigned char *addr)
return FTPNOPASV; return FTPNOPASV;
} }
/* Parse the request. */ /* Parse the request. */
/* respline::=227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). */
s = respline; s = respline;
for (s += 4; *s && !ISDIGIT (*s); s++); for (s += 4; *s && !ISDIGIT (*s); s++);
if (!*s) if (!*s)
return FTPINVPASV; return FTPINVPASV;
for (i = 0; i < 6; i++) for (i = 0; i < 4; i++)
{ {
addr[i] = 0; addr4[i] = 0;
for (; ISDIGIT (*s); s++) for (; ISDIGIT (*s); s++)
addr[i] = (*s - '0') + 10 * addr[i]; addr4[i] = (*s - '0') + 10 * addr4[i];
if (*s == ',') if (*s == ',')
s++; s++;
else if (i < 5) else
{ {
/* When on the last number, anything can be a terminator. */
xfree (respline); xfree (respline);
return FTPINVPASV; return FTPINVPASV;
} }
} }
/* Eventually make an IPv4 in IPv6 adress if needed */
map_ipv4_to_ip ((ip4_address *)addr4, addr);
*port=0;
for (; ISDIGIT (*s); s++)
*port = (*s - '0') + 10 * (*port);
if (*s == ',')
s++;
else
{
xfree (respline);
return FTPINVPASV;
}
{
unsigned short port2 = 0;
for (; ISDIGIT (*s); s++)
port2 = (*s - '0') + 10 * port2;
*port = (*port) * 256 + port2;
}
xfree (respline); xfree (respline);
return FTPOK; return FTPOK;
} }

View File

@ -119,9 +119,8 @@ getftp (struct url *u, long *len, long restval, ccon *con)
FILE *fp; FILE *fp;
char *user, *passwd, *respline; char *user, *passwd, *respline;
char *tms, *tmrate; char *tms, *tmrate;
unsigned char pasv_addr[6];
int cmd = con->cmd; int cmd = con->cmd;
int passive_mode_open = 0; int pasv_mode_open = 0;
long expected_bytes = 0L; long expected_bytes = 0L;
assert (con != NULL); assert (con != NULL);
@ -489,9 +488,11 @@ Error in server response, closing control connection.\n"));
{ {
if (opt.ftp_pasv > 0) if (opt.ftp_pasv > 0)
{ {
ip_address passive_addr;
unsigned short passive_port;
if (!opt.server_response) if (!opt.server_response)
logputs (LOG_VERBOSE, "==> PASV ... "); logputs (LOG_VERBOSE, "==> PASV ... ");
err = ftp_pasv (&con->rbuf, pasv_addr); err = ftp_pasv (&con->rbuf, &passive_addr, &passive_port);
/* FTPRERR, WRITEFAILED, FTPNOPASV, FTPINVPASV */ /* FTPRERR, WRITEFAILED, FTPNOPASV, FTPINVPASV */
switch (err) switch (err)
{ {
@ -525,32 +526,28 @@ Error in server response, closing control connection.\n"));
default: default:
abort (); abort ();
break; break;
} } /* switch(err) */
if (err==FTPOK) if (err==FTPOK)
{ {
unsigned short tport; dtsock = connect_to_one (&passive_addr, passive_port, 1);
tport = (pasv_addr[4] << 8) + pasv_addr[5];
dtsock = connect_to_one (pasv_addr, tport, 1);
if (dtsock < 0) if (dtsock < 0)
{ {
int save_errno = errno; int save_errno = errno;
CLOSE (csock); CLOSE (csock);
rbuf_uninitialize (&con->rbuf); rbuf_uninitialize (&con->rbuf);
logprintf (LOG_VERBOSE, _("couldn't connect to %s:%hu: %s\n"), logprintf (LOG_VERBOSE, _("couldn't connect to %s:%hu: %s\n"),
pretty_print_address (pasv_addr), tport, pretty_print_address (&passive_addr), passive_port,
strerror (save_errno)); strerror (save_errno));
return save_errno == ECONNREFUSED ? CONREFUSED : CONERROR; return save_errno == ECONNREFUSED ? CONREFUSED : CONERROR;
} }
passive_mode_open = 1; /* Flag to avoid accept port */ pasv_mode_open = 1; /* Flag to avoid accept port */
if (!opt.server_response) if (!opt.server_response)
logputs (LOG_VERBOSE, _("done. ")); logputs (LOG_VERBOSE, _("done. "));
} /* err==FTP_OK */ } /* err==FTP_OK */
} }
if (!passive_mode_open) /* Try to use a port command if PASV failed */ if (!pasv_mode_open) /* Try to use a port command if PASV failed */
{ {
if (!opt.server_response) if (!opt.server_response)
logputs (LOG_VERBOSE, "==> PORT ... "); logputs (LOG_VERBOSE, "==> PORT ... ");
@ -782,7 +779,7 @@ Error in server response, closing control connection.\n"));
if (!(cmd & (DO_LIST | DO_RETR))) if (!(cmd & (DO_LIST | DO_RETR)))
return RETRFINISHED; return RETRFINISHED;
if (!passive_mode_open) /* we are not using pasive mode so we need if (!pasv_mode_open) /* we are not using pasive mode so we need
to accept */ to accept */
{ {
/* Open the data transmission socket by calling acceptport(). */ /* Open the data transmission socket by calling acceptport(). */

View File

@ -36,7 +36,11 @@ enum stype
uerr_t ftp_response PARAMS ((struct rbuf *, char **)); uerr_t ftp_response PARAMS ((struct rbuf *, char **));
uerr_t ftp_login PARAMS ((struct rbuf *, const char *, const char *)); uerr_t ftp_login PARAMS ((struct rbuf *, const char *, const char *));
uerr_t ftp_port PARAMS ((struct rbuf *)); uerr_t ftp_port PARAMS ((struct rbuf *));
uerr_t ftp_pasv PARAMS ((struct rbuf *, unsigned char *)); uerr_t ftp_pasv PARAMS ((struct rbuf *, ip_address *, unsigned short *));
#ifdef INET6
uerr_t ftp_epsv PARAMS ((struct rbuf *, ip_address *, unsigned short *,
char *));
#endif
uerr_t ftp_type PARAMS ((struct rbuf *, int)); uerr_t ftp_type PARAMS ((struct rbuf *, int));
uerr_t ftp_cwd PARAMS ((struct rbuf *, const char *)); uerr_t ftp_cwd PARAMS ((struct rbuf *, const char *));
uerr_t ftp_retr PARAMS ((struct rbuf *, const char *)); uerr_t ftp_retr PARAMS ((struct rbuf *, const char *));

View File

@ -125,7 +125,8 @@ init_ssl (SSL_CTX **ctx)
meth = SSLv23_client_method (); meth = SSLv23_client_method ();
*ctx = SSL_CTX_new (meth); *ctx = SSL_CTX_new (meth);
SSL_CTX_set_verify (*ctx, verify, verify_callback); SSL_CTX_set_verify (*ctx, verify, verify_callback);
if (*ctx == NULL) return SSLERRCTXCREATE; if (*ctx == NULL)
return SSLERRCTXCREATE;
if (opt.sslcertfile) if (opt.sslcertfile)
{ {
if (SSL_CTX_use_certificate_file (*ctx, opt.sslcertfile, if (SSL_CTX_use_certificate_file (*ctx, opt.sslcertfile,

View File

@ -18,6 +18,7 @@ along with Wget; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include <config.h> #include <config.h>
#include <netdb.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -65,8 +66,11 @@ extern int h_errno;
# endif # endif
#endif #endif
/* An IPv4 address is simply a 4-byte quantity. */ #ifdef INET6
typedef unsigned char ipv4_address[4]; int ip_default_family = AF_INET6;
#else
int ip_default_family = AF_INET;
#endif
/* Mapping between known hosts and to lists of their addresses. */ /* Mapping between known hosts and to lists of their addresses. */
@ -77,12 +81,10 @@ static struct hash_table *host_name_addresses_map;
struct address_list { struct address_list {
int count; /* number of adrresses */ int count; /* number of adrresses */
ipv4_address *addresses; /* pointer to the string of addresses */ ip_address *addresses; /* pointer to the string of addresses */
int faulty; /* number of addresses known not to int faulty; /* number of addresses known not to work. */
work. */ int refcount; /* so we know whether to free it or not. */
int refcount; /* so we know whether to free it or
not. */
}; };
/* Get the bounds of the address list. */ /* Get the bounds of the address list. */
@ -97,11 +99,10 @@ address_list_get_bounds (struct address_list *al, int *start, int *end)
/* Copy address number INDEX to IP_STORE. */ /* Copy address number INDEX to IP_STORE. */
void void
address_list_copy_one (struct address_list *al, int index, address_list_copy_one (struct address_list *al, int index, ip_address *ip_store)
unsigned char *ip_store)
{ {
assert (index >= al->faulty && index < al->count); assert (index >= al->faulty && index < al->count && ip_store!=NULL );
memcpy (ip_store, al->addresses + index, sizeof (ipv4_address)); memcpy (ip_store, al->addresses + index, sizeof (ip_address));
} }
/* Check whether two address lists have all their IPs in common. */ /* Check whether two address lists have all their IPs in common. */
@ -114,7 +115,7 @@ address_list_match_all (struct address_list *al1, struct address_list *al2)
if (al1->count != al2->count) if (al1->count != al2->count)
return 0; return 0;
return 0 == memcmp (al1->addresses, al2->addresses, return 0 == memcmp (al1->addresses, al2->addresses,
al1->count * sizeof (ipv4_address)); al1->count * sizeof (ip_address));
} }
/* Mark the INDEXth element of AL as faulty, so that the next time /* Mark the INDEXth element of AL as faulty, so that the next time
@ -137,9 +138,57 @@ address_list_set_faulty (struct address_list *al, int index)
al->faulty = 0; al->faulty = 0;
} }
#ifdef INET6
/**
* address_list_from_addrinfo
*
* This function transform an addrinfo links list in and address_list.
*
* Input:
* addrinfo* Linkt list of addrinfo
*
* Output:
* address_list* New allocated address_list
*/
static struct address_list *
address_list_from_addrinfo (struct addrinfo *ai)
{
struct address_list *al;
struct addrinfo *ai_head = ai;
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)
++cnt;
if (cnt == 0)
return NULL;
al = xmalloc (sizeof (struct address_list));
al->addresses = xmalloc (cnt * sizeof (ip_address));
al->count = cnt;
al->faulty = 0;
al->refcount = 1;
for (i = 0, ai = ai_head; ai; ai = ai->ai_next)
if (ai->ai_family == AF_INET6)
{
struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)ai->ai_addr;
memcpy (al->addresses + i, &sin6->sin6_addr, 16);
++i;
}
else if (ai->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);
++i;
}
assert (i == cnt);
return al;
}
#else
/* Create an address_list out of a NULL-terminated list of addresses, /* Create an address_list out of a NULL-terminated list of addresses,
as returned by gethostbyname. */ as returned by gethostbyname. */
static struct address_list * static struct address_list *
address_list_new (char **h_addr_list) address_list_new (char **h_addr_list)
{ {
@ -152,26 +201,27 @@ address_list_new (char **h_addr_list)
assert (count > 0); assert (count > 0);
al->count = count; al->count = count;
al->faulty = 0; al->faulty = 0;
al->addresses = xmalloc (count * sizeof (ipv4_address)); al->addresses = xmalloc (count * sizeof (ip_address));
al->refcount = 1; al->refcount = 1;
for (i = 0; i < count; i++) for (i = 0; i < count; i++)
memcpy (al->addresses + i, h_addr_list[i], sizeof (ipv4_address)); memcpy (al->addresses + i, h_addr_list[i], sizeof (ip_address));
return al; return al;
} }
#endif
/* Like address_list_new, but initialized with only one address. */ /* Like address_list_new, but initialized with only one address. */
static struct address_list * static struct address_list *
address_list_new_one (const char *addr) address_list_new_one (ip_address *addr)
{ {
struct address_list *al = xmalloc (sizeof (struct address_list)); struct address_list *al = xmalloc (sizeof (struct address_list));
al->count = 1; al->count = 1;
al->faulty = 0; al->faulty = 0;
al->addresses = xmalloc (sizeof (ipv4_address)); al->addresses = xmalloc (sizeof (ip_address));
al->refcount = 1; al->refcount = 1;
memcpy (al->addresses, addr, sizeof (ipv4_address)); memcpy (al->addresses, addr, sizeof (ip_address));
return al; return al;
} }
@ -195,12 +245,229 @@ address_list_release (struct address_list *al)
} }
} }
/* The same as inet_ntoa, but without the need for a cast, or for /**
#including the netinet stuff. */ * wget_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.
*
* Input:
* wget_sockaddr* The space to be filled
* int The wished protocol
* unsigned short The port
* const ip_address The Binary IP adress
*
* Return:
* - Only modify 1. param
*/
void
wget_sockaddr_set_address (wget_sockaddr *sa,
int ip_family, unsigned short port, ip_address *addr)
{
if (ip_family == AF_INET)
{
sa->sin.sin_family = ip_family;
sa->sin.sin_port = htons (port);
if (addr == NULL)
memset ((unsigned char*)&sa->sin.sin_addr, 0, sizeof(ip_address));
else
memcpy ((unsigned char*)&sa->sin.sin_addr, addr, sizeof(ip_address));
return;
}
#ifdef INET6
if (ip_family == AF_INET6)
{
sa->sin6.sin6_family = ip_family;
sa->sin6.sin6_port = htons (port);
if (addr == NULL)
memset (&sa->sin6.sin6_addr, 0 , sizeof(ip_address));
else
memcpy (&sa->sin6.sin6_addr, addr, sizeof(ip_address));
return;
}
#endif
abort();
}
/**
* wget_sockaddr_set_port
*
* This funtion only fill the port of the socket information.
* If the protocol is not supported nothing is done.
* Unsuported adress family will abort the whole programm.
*
* Require:
* that the IP-Protocol already is set.
*
* Input:
* wget_sockaddr* The space there port should be entered
* unsigned int The port that should be entered in host order
*
* Return:
* - Only modify 1. param
*/
void
wget_sockaddr_set_port (wget_sockaddr *sa, unsigned short port)
{
if (sa->sa.sa_family == AF_INET)
{
sa->sin.sin_port = htons (port);
return;
}
#ifdef INET6
if (sa->sa.sa_family == AF_INET6)
{
sa->sin6.sin6_port = htons (port);
return;
}
#endif
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 INET6
if (sa->sa.sa_family == AF_INET6)
return &sa->sin6.sin6_addr;
#endif
abort();
/* unreached */
return NULL;
}
/**
* wget_sockaddr_get_port
*
* This function only return the port from the input structure
* Unsuported adress family will abort the whole programm.
*
* Require:
* that the IP-Protocol already is set.
*
* Input:
* wget_sockaddr* Information where to get the port
*
* Output:
* unsigned short Port Number in host order.
*/
unsigned short
wget_sockaddr_get_port (const wget_sockaddr *sa)
{
if (sa->sa.sa_family == AF_INET)
return htons (sa->sin.sin_port);
#ifdef INET6
if (sa->sa.sa_family == AF_INET6)
return htons (sa->sin6.sin6_port);
#endif
abort();
/* do not complain about return nothing */
return -1;
}
/**
* sockaddr_len
*
* This function return the length of the sockaddr corresponding to
* the acutall prefered protocol for (bind, connect etc...)
* Unsuported adress family will abort the whole programm.
*
* Require:
* that the IP-Protocol already is set.
*
* Input:
* - Public IP-Family Information
*
* Output:
* int structure length for socket options
*/
int
sockaddr_len ()
{
if (ip_default_family == AF_INET)
return sizeof (struct sockaddr_in);
#ifdef INET6
if (ip_default_family == AF_INET6)
return sizeof (struct sockaddr_in6);
#endif
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 INET6
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 INET6
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;
}
/* Pretty-print ADDR. When compiled without IPv6, this is the same as
inet_ntoa. With IPv6, it either prints an IPv6 address or an IPv4
address. */
char * char *
pretty_print_address (const void *addr) pretty_print_address (ip_address *addr)
{ {
#ifdef INET6
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;
#endif
return inet_ntoa (*(struct in_addr *)addr); return inet_ntoa (*(struct in_addr *)addr);
} }
@ -233,59 +500,85 @@ struct address_list *
lookup_host (const char *host, int silent) lookup_host (const char *host, int silent)
{ {
struct address_list *al = NULL; struct address_list *al = NULL;
unsigned long addr; unsigned long addr_ipv4; /* #### use a 32-bit type here. */
struct hostent *hptr; ip_address addr;
/* If the address is of the form d.d.d.d, no further lookup is /* First, try to check whether the address is already a numeric
needed. */ address. */
addr = (unsigned long)inet_addr (host);
if ((int)addr != -1) #ifdef INET6
if (inet_pton (AF_INET6, host, &addr) > 0)
return address_list_new_one (&addr);
#endif
addr_ipv4 = (unsigned long)inet_addr (host);
if ((int)addr_ipv4 != -1)
{ {
/* ADDR is defined to be in network byte order, which is what /* ADDR is defined to be in network byte order, which is what
this returns, so we can just copy it to STORE_IP. However, this returns, so we can just copy it to STORE_IP. However,
on big endian 64-bit architectures the value will be stored on big endian 64-bit architectures the value will be stored
in the *last*, not first four bytes. OFFSET makes sure that in the *last*, not first four bytes. OFFSET makes sure that
we copy the correct four bytes. */ we copy the correct four bytes. */
int offset; int offset = 0;
#ifdef WORDS_BIGENDIAN #ifdef WORDS_BIGENDIAN
offset = sizeof (unsigned long) - sizeof (ipv4_address); offset = sizeof (unsigned long) - sizeof (ip_address);
#else
offset = 0;
#endif #endif
return address_list_new_one ((char *)&addr + offset); map_ipv4_to_ip ((ip4_address *)((char *)&addr_ipv4 + offset), &addr);
return address_list_new_one (&addr);
} }
/* By now we know that the host name we got is not of the form
d.d.d.d. Try to find it in our cache of host names. */
if (host_name_addresses_map) if (host_name_addresses_map)
al = hash_table_get (host_name_addresses_map, host);
if (al)
{ {
DEBUGP (("Found %s in host_name_addresses_map (%p)\n", host, al)); al = hash_table_get (host_name_addresses_map, host);
++al->refcount;
return al; if (al)
{
DEBUGP (("Found %s in host_name_addresses_map (%p)\n", host, al));
++al->refcount;
return al;
}
} }
if (!silent) if (!silent)
logprintf (LOG_VERBOSE, _("Resolving %s... "), host); logprintf (LOG_VERBOSE, _("Resolving %s... "), host);
/* Look up the host using gethostbyname(). */ #ifdef INET6
hptr = gethostbyname (host); {
if (!hptr) struct addrinfo hints, *ai;
{ int err;
if (!silent)
logprintf (LOG_VERBOSE, _("failed: %s.\n"), herrmsg (h_errno)); memset (&hints, 0, sizeof (hints));
return NULL; hints.ai_family = PF_UNSPEC;
} hints.ai_socktype = SOCK_STREAM;
err = getaddrinfo (host, NULL, &hints, &ai);
if (err != 0 || ai == NULL)
{
if (!silent)
logprintf (LOG_VERBOSE, _("failed: %s.\n"), gai_strerror (err));
return NULL;
}
al = address_list_from_addrinfo (ai);
freeaddrinfo (ai);
}
#else
{
struct hostent *hptr = gethostbyname (host);
if (!hptr)
{
if (!silent)
logprintf (LOG_VERBOSE, _("failed: %s.\n"), herrmsg (h_errno));
return NULL;
}
/* Do all systems have h_addr_list, or is it a newer thing? If
the latter, use address_list_new_one. */
al = address_list_new (hptr->h_addr_list);
}
#endif
if (!silent) if (!silent)
logprintf (LOG_VERBOSE, _("done.\n")); logprintf (LOG_VERBOSE, _("done.\n"));
/* Do all systems have h_addr_list, or is it a newer thing? If the
latter, use address_list_new_one. */
al = address_list_new (hptr->h_addr_list);
/* Cache the lookup information. */ /* Cache the lookup information. */
cache_host_lookup (host, al); cache_host_lookup (host, al);

View File

@ -20,28 +20,71 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#ifndef HOST_H #ifndef HOST_H
#define HOST_H #define HOST_H
#include <netdb.h>
#undef INET6
struct url; struct url;
struct address_list; 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 INET6
struct sockaddr_in6 sin6; /* IPv6 socket address */
#endif
} wget_sockaddr;
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>. */
#ifdef INET6
# define MAX_IP_ADDRESS_SIZE 16
#else
# define MAX_IP_ADDRESS_SIZE 4
#endif
typedef struct {
unsigned char bytes[MAX_IP_ADDRESS_SIZE];
} ip_address;
/* Function declarations */ /* Function declarations */
struct address_list *lookup_host PARAMS ((const char *, int)); struct address_list *lookup_host PARAMS ((const char *, int));
char *herrmsg PARAMS ((int)); char *herrmsg PARAMS ((int));
void address_list_get_bounds PARAMS ((struct address_list *, int *, 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_copy_one PARAMS ((struct address_list *, int,
unsigned char *)); ip_address *));
int address_list_match_all PARAMS ((struct address_list *, int address_list_match_all PARAMS ((struct address_list *,
struct address_list *)); struct address_list *));
void address_list_set_faulty PARAMS ((struct address_list *, int)); void address_list_set_faulty PARAMS ((struct address_list *, int));
void address_list_release PARAMS ((struct address_list *)); void address_list_release PARAMS ((struct address_list *));
/* This was originally going to be a macro, but then every caller char *pretty_print_address PARAMS ((ip_address *));
would have to #include the netinet stuff. */
char *pretty_print_address PARAMS ((const void *));
int accept_domain PARAMS ((struct url *)); int accept_domain PARAMS ((struct url *));
int sufmatch PARAMS ((const char **, const char *)); int sufmatch PARAMS ((const char **, const char *));
void host_cleanup PARAMS ((void)); 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 *));
int 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 */ #endif /* HOST_H */

View File

@ -527,10 +527,10 @@ static int
cmd_address (const char *com, const char *val, void *closure) cmd_address (const char *com, const char *val, void *closure)
{ {
struct address_list *al; struct address_list *al;
struct sockaddr_in sin; wget_sockaddr sa;
struct sockaddr_in **target = (struct sockaddr_in **)closure; wget_sockaddr **target = (wget_sockaddr **)closure;
memset (&sin, '\0', sizeof (sin)); memset (&sa, '\0', sizeof (sa));
al = lookup_host (val, 1); al = lookup_host (val, 1);
if (!al) if (!al)
@ -539,16 +539,15 @@ cmd_address (const char *com, const char *val, void *closure)
exec_name, com, val); exec_name, com, val);
return 0; return 0;
} }
address_list_copy_one (al, 0, (unsigned char *)&sin.sin_addr); sa.sa.sa_family = ip_default_family;
wget_sockaddr_set_port (&sa, 0);
address_list_copy_one (al, 0, wget_sockaddr_get_addr (&sa));
address_list_release (al); address_list_release (al);
sin.sin_family = AF_INET;
sin.sin_port = 0;
FREE_MAYBE (*target); FREE_MAYBE (*target);
*target = xmalloc (sizeof (sin)); *target = xmalloc (sizeof (sa));
memcpy (*target, &sin, sizeof (sin)); memcpy (*target, &sa, sizeof (sa));
return 1; return 1;
} }

View File

@ -19,6 +19,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/* Needed for FDP. */ /* Needed for FDP. */
#include <stdio.h> #include <stdio.h>
#include "host.h"
struct options struct options
{ {
@ -152,9 +153,7 @@ struct options
int page_requisites; /* Whether we need to download all files int page_requisites; /* Whether we need to download all files
necessary to display a page properly. */ necessary to display a page properly. */
wget_sockaddr *bind_address; /* What local IP address to bind to. */
struct sockaddr_in *bind_address; /* What local IP address to bind to. */
#ifdef HAVE_SSL #ifdef HAVE_SSL
char *sslcertfile; /* external client cert to use. */ char *sslcertfile; /* external client cert to use. */
char *sslcertkey; /* the keyfile for this certificate char *sslcertkey; /* the keyfile for this certificate