From cb4003403509b46d2f6ef6936baf969906ff1430 Mon Sep 17 00:00:00 2001 From: hniksic Date: Wed, 23 Jan 2002 21:45:54 -0800 Subject: [PATCH] [svn] Commit IPv6 support by Thomas Lussnig. Published in . --- src/ChangeLog | 4 + src/connect.c | 90 ++++++----- src/connect.h | 6 +- src/ftp-basic.c | 212 ++++++++++++++++++++++--- src/ftp.c | 23 ++- src/ftp.h | 6 +- src/gen_sslfunc.c | 3 +- src/host.c | 397 ++++++++++++++++++++++++++++++++++++++++------ src/host.h | 51 +++++- src/init.c | 17 +- src/options.h | 5 +- 11 files changed, 668 insertions(+), 146 deletions(-) diff --git a/src/ChangeLog b/src/ChangeLog index 1f40548a..75445b27 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,7 @@ +2002-01-24 Hrvoje Niksic + + * source: Integrated IPv6 support by Thomas Lussnig. + 2002-01-15 Ian Abbott * init.c (cmd_file): Change `\' to `/' for Windows (yes, really!) diff --git a/src/connect.c b/src/connect.c index 28d2f6aa..43ed0083 100644 --- a/src/connect.c +++ b/src/connect.c @@ -76,15 +76,13 @@ set_connection_host_name (const char *host) /* Connect to a remote host whose address has been resolved. */ 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; /* Set port and protocol */ - sock_name.sin_family = AF_INET; - sock_name.sin_port = htons (port); - memcpy ((unsigned char *)&sock_name.sin_addr, addr, 4); + wget_sockaddr_set_address (&sa, ip_default_family, port, addr); if (!silent) { @@ -99,15 +97,14 @@ connect_to_one (const unsigned char *addr, unsigned short port, int silent) } /* Make an internet socket, stream type. */ - sock = socket (AF_INET, SOCK_STREAM, 0); + sock = socket (ip_default_family, SOCK_STREAM, 0); if (sock < 0) goto out; if (opt.bind_address) { /* Bind the client side to the requested address. */ - if (bind (sock, (struct sockaddr *)opt.bind_address, - sizeof (*opt.bind_address))) + if (bind (sock, (struct sockaddr *)opt.bind_address, sockaddr_len ())) { close (sock); 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. */ - if (connect (sock, (struct sockaddr *)&sock_name, sizeof (sock_name)) < 0) + if (connect (sock, &sa.sa, sockaddr_len ()) < 0) { close (sock); 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); for (i = start; i < end; i++) { - unsigned char addr[4]; + ip_address addr; 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) /* Success. */ return sock; @@ -207,29 +204,26 @@ test_socket_open (int sock) internal variable MPORT is set to the value of the ensuing master socket. Call acceptport() to block for and accept a connection. */ uerr_t -bindport (unsigned short *port) +bindport (unsigned short *port, int family) { int optval = 1; - static struct sockaddr_in srv; + wget_sockaddr srv; + memset (&srv, 0, sizeof (wget_sockaddr)); msock = -1; - addr = (struct sockaddr *) &srv; - if ((msock = socket (AF_INET, SOCK_STREAM, 0)) < 0) + + if ((msock = socket (family, SOCK_STREAM, 0)) < 0) return CONSOCKERR; if (setsockopt (msock, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof (optval)) < 0) return CONSOCKERR; if (opt.bind_address == NULL) - { - srv.sin_family = AF_INET; - srv.sin_addr.s_addr = htonl (INADDR_ANY); - } + wget_sockaddr_set_address (&srv, ip_default_family, htons (*port), NULL); else srv = *opt.bind_address; - - srv.sin_port = htons (*port); - if (bind (msock, addr, sizeof (struct sockaddr_in)) < 0) + wget_sockaddr_set_port (&srv, *port); + if (bind (msock, &srv.sa, sockaddr_len ()) < 0) { CLOSE (msock); msock = -1; @@ -241,14 +235,15 @@ bindport (unsigned short *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 addrlen = sizeof (struct sockaddr_in); - if (getsockname (msock, addr, &addrlen) < 0) + int sa_len = sockaddr_len (); + if (getsockname (msock, &srv.sa, &sa_len) < 0) { CLOSE (msock); msock = -1; return CONPORTERR; } - *port = ntohs (srv.sin_port); + *port = wget_sockaddr_get_port (&srv); + DEBUGP (("using port %i.\n", *port)); } if (listen (msock, 1) < 0) { @@ -292,7 +287,7 @@ select_fd (int fd, int maxtime, int writep) uerr_t acceptport (int *sock) { - int addrlen = sizeof (struct sockaddr_in); + int addrlen = sockaddr_len (); #ifdef HAVE_SELECT if (select_fd (msock, opt.timeout, 0) <= 0) @@ -317,22 +312,33 @@ closeport (int sock) msock = -1; } -/* Return the local IP address associated with the connection on FD. - It is returned in a static buffer. */ -unsigned char * -conaddr (int fd) +/* Return the local IP address associated with the connection on FD. */ + +int +conaddr (int fd, ip_address *ip) { - static unsigned char res[4]; - struct sockaddr_in mysrv; - struct sockaddr *myaddr; - int addrlen = sizeof (mysrv); /* see bindport() for discussion of - using `int' here. */ - - myaddr = (struct sockaddr *) (&mysrv); - if (getsockname (fd, myaddr, (int *)&addrlen) < 0) - return NULL; - memcpy (res, &mysrv.sin_addr, 4); - return res; + wget_sockaddr mysrv; + + /* see bindport() for discussion of using `int' here. */ + int addrlen = sizeof (mysrv); + + if (getsockname (fd, &mysrv.sa, (int *)&addrlen) < 0) + return 0; + + switch (mysrv.sa.sa_family) + { +#ifdef INET6 + case AF_INET6: + memcpy (ip, &mysrv.sin6.sin6_addr, 16); + return 1; +#endif + case AF_INET: + map_ipv4_to_ip ((ip4_address *)&mysrv.sin.sin_addr, ip); + return 1; + default: + abort (); + } + return 0; } /* Read at most LEN bytes from FD, storing them to BUF. This is diff --git a/src/connect.h b/src/connect.h index 1924cba3..4b637ea0 100644 --- a/src/connect.h +++ b/src/connect.h @@ -24,16 +24,16 @@ struct address_list; /* 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)); void set_connection_host_name PARAMS ((const char *)); int test_socket_open PARAMS ((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 *)); void closeport PARAMS ((int)); -unsigned char *conaddr PARAMS ((int)); +int conaddr PARAMS ((int, ip_address *)); int iread PARAMS ((int, char *, int)); int iwrite PARAMS ((int, char *, int)); diff --git a/src/ftp-basic.c b/src/ftp-basic.c index 69e94f02..b3b14a7a 100644 --- a/src/ftp-basic.c +++ b/src/ftp-basic.c @@ -33,6 +33,10 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #endif #include +/* For inet_ntop. */ +#include +#include + #ifdef WINDOWS # include #endif @@ -235,6 +239,62 @@ ftp_login (struct rbuf *rbuf, const char *acc, const char *pass) 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 server. Use acceptport after RETR, to get the socket of data connection. */ @@ -242,25 +302,48 @@ uerr_t ftp_port (struct rbuf *rbuf) { uerr_t err; - char *request, *respline, *bytes; - unsigned char *in_addr; + 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; + int nwritten; 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. */ port = 0; - /* Bind the port. */ - err = bindport (&port); + + err = bindport (&port, AF_INET); if (err != BINDOK) 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; + 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). */ - bytes = (char *)alloca (6 * 4 + 1); - sprintf (bytes, "%d,%d,%d,%d,%d,%d", in_addr[0], in_addr[1], - in_addr[2], in_addr[3], (unsigned) (port & 0xff00) >> 8, - port & 0xff); + + /* 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)); @@ -286,16 +369,88 @@ ftp_port (struct rbuf *rbuf) 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 transfer. Reads the response from server and parses it. Reads the host and port addresses and returns them. */ 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; int nwritten, i; 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. */ request = ftp_request ("PASV", NULL); /* And send it. */ @@ -319,24 +474,45 @@ ftp_pasv (struct rbuf *rbuf, unsigned char *addr) 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 < 6; i++) + for (i = 0; i < 4; i++) { - addr[i] = 0; + addr4[i] = 0; for (; ISDIGIT (*s); s++) - addr[i] = (*s - '0') + 10 * addr[i]; + addr4[i] = (*s - '0') + 10 * addr4[i]; if (*s == ',') s++; - else if (i < 5) + else { - /* When on the last number, anything can be a terminator. */ xfree (respline); 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); return FTPOK; } diff --git a/src/ftp.c b/src/ftp.c index 1b78fd33..e02c018b 100644 --- a/src/ftp.c +++ b/src/ftp.c @@ -119,9 +119,8 @@ getftp (struct url *u, long *len, long restval, ccon *con) FILE *fp; char *user, *passwd, *respline; char *tms, *tmrate; - unsigned char pasv_addr[6]; int cmd = con->cmd; - int passive_mode_open = 0; + int pasv_mode_open = 0; long expected_bytes = 0L; assert (con != NULL); @@ -489,9 +488,11 @@ Error in server response, closing control connection.\n")); { if (opt.ftp_pasv > 0) { + ip_address passive_addr; + unsigned short passive_port; if (!opt.server_response) logputs (LOG_VERBOSE, "==> PASV ... "); - err = ftp_pasv (&con->rbuf, pasv_addr); + err = ftp_pasv (&con->rbuf, &passive_addr, &passive_port); /* FTPRERR, WRITEFAILED, FTPNOPASV, FTPINVPASV */ switch (err) { @@ -525,32 +526,28 @@ Error in server response, closing control connection.\n")); default: abort (); break; - } + } /* switch(err) */ if (err==FTPOK) { - unsigned short tport; - - tport = (pasv_addr[4] << 8) + pasv_addr[5]; - dtsock = connect_to_one (pasv_addr, tport, 1); - + 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"), - pretty_print_address (pasv_addr), tport, + pretty_print_address (&passive_addr), passive_port, strerror (save_errno)); 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) logputs (LOG_VERBOSE, _("done. ")); } /* 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) logputs (LOG_VERBOSE, "==> PORT ... "); @@ -782,7 +779,7 @@ Error in server response, closing control connection.\n")); if (!(cmd & (DO_LIST | DO_RETR))) 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 */ { /* Open the data transmission socket by calling acceptport(). */ diff --git a/src/ftp.h b/src/ftp.h index f988ca7f..0da39a36 100644 --- a/src/ftp.h +++ b/src/ftp.h @@ -36,7 +36,11 @@ enum stype uerr_t ftp_response PARAMS ((struct rbuf *, char **)); 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 *, 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_cwd PARAMS ((struct rbuf *, const char *)); uerr_t ftp_retr PARAMS ((struct rbuf *, const char *)); diff --git a/src/gen_sslfunc.c b/src/gen_sslfunc.c index 8af78895..da84492d 100644 --- a/src/gen_sslfunc.c +++ b/src/gen_sslfunc.c @@ -125,7 +125,8 @@ init_ssl (SSL_CTX **ctx) meth = SSLv23_client_method (); *ctx = SSL_CTX_new (meth); SSL_CTX_set_verify (*ctx, verify, verify_callback); - if (*ctx == NULL) return SSLERRCTXCREATE; + if (*ctx == NULL) + return SSLERRCTXCREATE; if (opt.sslcertfile) { if (SSL_CTX_use_certificate_file (*ctx, opt.sslcertfile, diff --git a/src/host.c b/src/host.c index ddc04445..e9df830d 100644 --- a/src/host.c +++ b/src/host.c @@ -18,6 +18,7 @@ along with Wget; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include +#include #include #include @@ -65,8 +66,11 @@ extern int h_errno; # endif #endif -/* An IPv4 address is simply a 4-byte quantity. */ -typedef unsigned char ipv4_address[4]; +#ifdef INET6 +int ip_default_family = AF_INET6; +#else +int ip_default_family = AF_INET; +#endif /* 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 { 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 - work. */ - int refcount; /* so we know whether to free it or - not. */ + int faulty; /* number of addresses known not to work. */ + int refcount; /* so we know whether to free it or not. */ }; /* 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. */ void -address_list_copy_one (struct address_list *al, int index, - unsigned char *ip_store) +address_list_copy_one (struct address_list *al, int index, ip_address *ip_store) { - assert (index >= al->faulty && index < al->count); - memcpy (ip_store, al->addresses + index, sizeof (ipv4_address)); + assert (index >= al->faulty && index < al->count && ip_store!=NULL ); + memcpy (ip_store, al->addresses + index, sizeof (ip_address)); } /* 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) return 0; 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 @@ -137,9 +138,57 @@ address_list_set_faulty (struct address_list *al, int index) 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, as returned by gethostbyname. */ - static struct address_list * address_list_new (char **h_addr_list) { @@ -152,26 +201,27 @@ address_list_new (char **h_addr_list) assert (count > 0); al->count = count; al->faulty = 0; - al->addresses = xmalloc (count * sizeof (ipv4_address)); + al->addresses = xmalloc (count * sizeof (ip_address)); al->refcount = 1; 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; } +#endif /* Like address_list_new, but initialized with only one address. */ 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)); al->count = 1; al->faulty = 0; - al->addresses = xmalloc (sizeof (ipv4_address)); + al->addresses = xmalloc (sizeof (ip_address)); al->refcount = 1; - memcpy (al->addresses, addr, sizeof (ipv4_address)); + memcpy (al->addresses, addr, sizeof (ip_address)); 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 * -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 ""; + return buf; +#endif return inet_ntoa (*(struct in_addr *)addr); } @@ -233,59 +500,85 @@ struct address_list * lookup_host (const char *host, int silent) { struct address_list *al = NULL; - unsigned long addr; - struct hostent *hptr; + unsigned long addr_ipv4; /* #### use a 32-bit type here. */ + ip_address addr; + + /* First, try to check whether the address is already a numeric + address. */ + +#ifdef INET6 + if (inet_pton (AF_INET6, host, &addr) > 0) + return address_list_new_one (&addr); +#endif - /* If the address is of the form d.d.d.d, no further lookup is - needed. */ - addr = (unsigned long)inet_addr (host); - if ((int)addr != -1) + addr_ipv4 = (unsigned long)inet_addr (host); + if ((int)addr_ipv4 != -1) { /* ADDR is defined to be in network byte order, which is what this returns, so we can just copy it to STORE_IP. However, on big endian 64-bit architectures the value will be stored in the *last*, not first four bytes. OFFSET makes sure that we copy the correct four bytes. */ - int offset; + int offset = 0; #ifdef WORDS_BIGENDIAN - offset = sizeof (unsigned long) - sizeof (ipv4_address); -#else - offset = 0; + offset = sizeof (unsigned long) - sizeof (ip_address); #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) - al = hash_table_get (host_name_addresses_map, host); - - if (al) { - DEBUGP (("Found %s in host_name_addresses_map (%p)\n", host, al)); - ++al->refcount; - return al; + al = hash_table_get (host_name_addresses_map, host); + + if (al) + { + DEBUGP (("Found %s in host_name_addresses_map (%p)\n", host, al)); + ++al->refcount; + return al; + } } if (!silent) logprintf (LOG_VERBOSE, _("Resolving %s... "), host); - /* Look up the host using gethostbyname(). */ - hptr = gethostbyname (host); - if (!hptr) - { - if (!silent) - logprintf (LOG_VERBOSE, _("failed: %s.\n"), herrmsg (h_errno)); - return NULL; - } +#ifdef INET6 + { + struct addrinfo hints, *ai; + int err; + + memset (&hints, 0, sizeof (hints)); + 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) 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_host_lookup (host, al); diff --git a/src/host.h b/src/host.h index f99c636c..4d89ecf5 100644 --- a/src/host.h +++ b/src/host.h @@ -20,28 +20,71 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef HOST_H #define HOST_H +#include + +#undef INET6 + 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 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:. */ + +#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 */ 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, - unsigned char *)); + ip_address *)); int address_list_match_all PARAMS ((struct address_list *, struct address_list *)); void address_list_set_faulty PARAMS ((struct address_list *, int)); void address_list_release PARAMS ((struct address_list *)); -/* This was originally going to be a macro, but then every caller - would have to #include the netinet stuff. */ -char *pretty_print_address PARAMS ((const void *)); +char *pretty_print_address PARAMS ((ip_address *)); int accept_domain PARAMS ((struct url *)); int sufmatch PARAMS ((const char **, const char *)); 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 */ diff --git a/src/init.c b/src/init.c index 03330775..20bbc6a2 100644 --- a/src/init.c +++ b/src/init.c @@ -527,10 +527,10 @@ static int cmd_address (const char *com, const char *val, void *closure) { struct address_list *al; - struct sockaddr_in sin; - struct sockaddr_in **target = (struct sockaddr_in **)closure; + wget_sockaddr sa; + wget_sockaddr **target = (wget_sockaddr **)closure; - memset (&sin, '\0', sizeof (sin)); + memset (&sa, '\0', sizeof (sa)); al = lookup_host (val, 1); if (!al) @@ -539,16 +539,15 @@ cmd_address (const char *com, const char *val, void *closure) exec_name, com, val); 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); - sin.sin_family = AF_INET; - sin.sin_port = 0; - FREE_MAYBE (*target); - *target = xmalloc (sizeof (sin)); - memcpy (*target, &sin, sizeof (sin)); + *target = xmalloc (sizeof (sa)); + memcpy (*target, &sa, sizeof (sa)); return 1; } diff --git a/src/options.h b/src/options.h index e8cd8fb2..a07c641e 100644 --- a/src/options.h +++ b/src/options.h @@ -19,6 +19,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* Needed for FDP. */ #include +#include "host.h" struct options { @@ -152,9 +153,7 @@ struct options int page_requisites; /* Whether we need to download all files necessary to display a page properly. */ - - struct sockaddr_in *bind_address; /* What local IP address to bind to. */ - + wget_sockaddr *bind_address; /* What local IP address to bind to. */ #ifdef HAVE_SSL char *sslcertfile; /* external client cert to use. */ char *sslcertkey; /* the keyfile for this certificate -- 2.39.2