Hi, NetBSD, and I suspect others BSDs, and maybe other systems too, have v4-mapped addresses support disabled by default (because it can lead to misconfigurations, with security issues, search draft-itojun-v6ops-v4mapped-harmful on google for details). This means that, on these systems, a v6-enabled wget can't talk to v4 servers.
The attached patches are what I commited to the NetBSD pkgsrc system. They make wget use INET4 sockets for ipv4 addresses. I tested this with both v6 and v4, http and ftp active and passive) servers. I don't think it would cause problems on others systems either. -- Manuel Bouyer <[EMAIL PROTECTED]> NetBSD: 26 ans d'experience feront toujours la difference --
$NetBSD: patch-aa,v 1.7 2005/01/25 20:07:25 bouyer Exp $ --- src/connect.c.orig Sat Nov 1 15:08:22 2003 +++ src/connect.c Sun Jan 23 22:11:24 2005 @@ -155,11 +155,11 @@ int sock, save_errno; /* Set port and protocol */ - wget_sockaddr_set_address (&sa, ip_default_family, port, addr); + wget_sockaddr_set_address (&sa, port, addr, addr->family); if (!silent) { - char *pretty_addr = pretty_print_address (addr); + char *pretty_addr = pretty_print_address (addr->bytes); if (connection_host_name && 0 != strcmp (connection_host_name, pretty_addr)) logprintf (LOG_VERBOSE, _("Connecting to %s[%s]:%hu... "), @@ -170,7 +170,7 @@ } /* Make an internet socket, stream type. */ - sock = socket (ip_default_family, SOCK_STREAM, 0); + sock = socket (addr->family, SOCK_STREAM, 0); if (sock < 0) goto out; @@ -196,8 +196,8 @@ { /* 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 ())) + wget_sockaddr_set_address (&bsa, 0, &bind_address, bind_address.family); + if (bind (sock, &bsa.sa, sockaddr_len (bind_address.family))) { CLOSE (sock); sock = -1; @@ -206,7 +206,7 @@ } /* Connect the socket to the remote host. */ - if (connect_with_timeout (sock, &sa.sa, sockaddr_len (), + if (connect_with_timeout (sock, &sa.sa, sockaddr_len (addr->family), opt.connect_timeout) < 0) { CLOSE (sock); @@ -248,8 +248,11 @@ sock = connect_to_one (&addr, port, silent); if (sock >= 0) - /* Success. */ - return sock; + { + /* Success. */ + ip_default_family = addr.family; + return sock; + } address_list_set_faulty (al, i); @@ -316,9 +319,10 @@ #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) + wget_sockaddr_set_address (&srv, htons (*port), + bind_address_resolved ? &bind_address : NULL, + family); + if (bind (msock, &srv.sa, sockaddr_len (family)) < 0) { CLOSE (msock); msock = -1; @@ -327,7 +331,7 @@ DEBUGP (("Master socket fd %d bound.\n", msock)); if (!*port) { - socklen_t sa_len = sockaddr_len (); + socklen_t sa_len = sockaddr_len (family); if (getsockname (msock, &srv.sa, &sa_len) < 0) { CLOSE (msock); @@ -389,7 +393,7 @@ uerr_t acceptport (int *sock) { - socklen_t addrlen = sockaddr_len (); + socklen_t addrlen = sockaddr_len (ip_default_family); #ifdef HAVE_SELECT if (opt.connect_timeout) @@ -429,11 +433,13 @@ { #ifdef ENABLE_IPV6 case AF_INET6: - memcpy (ip, &mysrv.sin6.sin6_addr, 16); + memcpy (ip->bytes, &mysrv.sin6.sin6_addr, 16); + ip->family = AF_INET6; return 1; #endif case AF_INET: - map_ipv4_to_ip ((ip4_address *)&mysrv.sin.sin_addr, ip); + memcpy (ip->bytes, &mysrv.sin.sin_addr, 4); + ip->family = AF_INET; return 1; default: abort ();
$NetBSD: patch-ab,v 1.7 2005/01/25 20:07:25 bouyer Exp $ --- src/ftp-basic.c.orig Sat Nov 8 20:17:55 2003 +++ src/ftp-basic.c Sun Jan 23 22:20:35 2005 @@ -267,7 +267,7 @@ /* Setting port to 0 lets the system choose a free port. */ port = 0; - err = bindport (&port, ip_default_family); + err = bindport (&port, AF_INET6); if (err != BINDOK) /* Bind the port. */ return err; @@ -275,7 +275,8 @@ if (!conaddr (RBUF_FD (rbuf), &in_addr)) /* Huh? This is not BINDERR! */ return BINDERR; - inet_ntop (AF_INET6, &in_addr, ipv6, sizeof (ipv6)); + inet_ntop (AF_INET6, in_addr.bytes, ipv6, sizeof (ipv6)); + in_addr.family = AF_INET6; /* Construct the argument of EPRT (of the form |2|IPv6.ascii|PORT.ascii|). */ bytes = alloca (3 + strlen (ipv6) + 1 + numdigit (port) + 1 + 1); @@ -319,8 +320,6 @@ 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; @@ -347,16 +346,14 @@ 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). 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); + in_addr.bytes[0], in_addr.bytes[1], in_addr.bytes[2], + in_addr.bytes[3], port >> 8, port & 0xff); /* Send PORT request. */ request = ftp_request ("PORT", bytes); nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request)); @@ -426,10 +423,12 @@ switch(remote.sa.sa_family) { case AF_INET6: - memcpy (addr, &remote.sin6.sin6_addr, 16); + memcpy (&addr->bytes[0], &remote.sin6.sin6_addr, 16); + addr->family = AF_INET6; break; case AF_INET: - map_ipv4_to_ip ((ip4_address *)&ipv4_sock->sin_addr, addr); + memcpy (&addr->bytes[0], &remote.sin.sin_addr, 4); + addr->family = AF_INET; break; default: abort(); @@ -454,15 +453,12 @@ unsigned char addr4[4]; #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); @@ -505,9 +501,8 @@ return FTPINVPASV; } } - - /* Eventually make an IPv4 in IPv6 adress if needed */ - map_ipv4_to_ip ((ip4_address *)addr4, addr); + memcpy(addr->bytes, addr4, 4); + addr->family = AF_INET; *port=0; for (; ISDIGIT (*s); s++) @@ -526,7 +521,7 @@ port2 = (*s - '0') + 10 * port2; *port = (*port) * 256 + port2; } - xfree (respline); + xfree (respline); return FTPOK; }
$NetBSD: patch-ac,v 1.7 2005/01/25 20:07:25 bouyer Exp $ --- src/host.c.orig Sun Oct 26 02:38:25 2003 +++ src/host.c Sun Jan 23 21:23:28 2005 @@ -189,13 +189,15 @@ if (ai->ai_family == AF_INET6) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ai->ai_addr; - memcpy (al->addresses + i, &sin6->sin6_addr, 16); + memcpy (al->addresses[i].bytes, &sin6->sin6_addr, 16); + al->addresses[i].family = AF_INET6; ++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); + memcpy (al->addresses[i].bytes, &sin->sin_addr, 4); + al->addresses[i].family = AF_INET; ++i; } assert (i == cnt); @@ -219,8 +221,11 @@ 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++) { + memcpy (al->addresses[i].bytes, h_addr_list[i], 4); + al->addresses[i].family = AF_INET; + } + return al; } @@ -279,33 +284,29 @@ */ void wget_sockaddr_set_address (wget_sockaddr *sa, - int ip_family, unsigned short port, ip_address *addr) + unsigned short port, ip_address *addr, int family) { - if (ip_family == AF_INET) + if (family == AF_INET) { - sa->sin.sin_family = ip_family; + sa->sin.sin_family = AF_INET; sa->sin.sin_port = htons (port); if (addr == NULL) memset (&sa->sin.sin_addr, 0, sizeof(ip4_address)); 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)); + memcpy (&sa->sin.sin_addr, addr->bytes, sizeof(ip4_address)); } return; } #ifdef ENABLE_IPV6 - if (ip_family == AF_INET6) + if (family == AF_INET6) { - sa->sin6.sin6_family = ip_family; + sa->sin6.sin6_family = AF_INET6; sa->sin6.sin6_port = htons (port); if (addr == NULL) memset (&sa->sin6.sin6_addr, 0 , 16); else - memcpy (&sa->sin6.sin6_addr, addr, 16); + memcpy (&sa->sin6.sin6_addr, addr->bytes, 16); return; } #endif @@ -422,12 +423,12 @@ * socklen_t structure length for socket options */ socklen_t -sockaddr_len () +sockaddr_len (int family) { - if (ip_default_family == AF_INET) + if (family == AF_INET) return sizeof (struct sockaddr_in); #ifdef ENABLE_IPV6 - if (ip_default_family == AF_INET6) + if (family == AF_INET6) return sizeof (struct sockaddr_in6); #endif abort(); @@ -435,42 +436,6 @@ 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. */ @@ -559,17 +524,16 @@ pretty_print_address (ip_address *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; + if (addr->family == AF_INET6) + { + if (!inet_ntop (AF_INET6, addr->bytes, buf, sizeof (buf))) + return "<unknown>"; + return buf; + } #endif - return inet_ntoa (*(struct in_addr *)addr); + return inet_ntoa (*(struct in_addr *)addr->bytes); } /* Add host name HOST with the address ADDR_TEXT to the cache. @@ -601,23 +565,27 @@ lookup_host (const char *host, int silent) { struct address_list *al = NULL; - uint32_t addr_ipv4; ip_address addr; + u_int32_t addr4; /* 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); + if (inet_pton (AF_INET6, host, addr.bytes) > 0) + { + addr.family = AF_INET6; + return address_list_from_single (&addr); + } #endif - addr_ipv4 = (uint32_t)inet_addr (host); - if (addr_ipv4 != (uint32_t)-1) + addr4 = (uint32_t)inet_addr (host); + memcpy(addr.bytes, &addr4, 4); + addr.family = AF_INET; + if (addr4 != (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); }
$NetBSD: patch-ad,v 1.8 2005/01/25 20:07:25 bouyer Exp $ --- src/host.h.orig Sat Oct 11 03:39:07 2003 +++ src/host.h Sun Jan 23 21:22:57 2005 @@ -71,6 +71,7 @@ typedef struct { unsigned char bytes[MAX_IP_ADDRESS_SIZE]; + int family; } ip_address; /* Function declarations */ @@ -92,14 +93,12 @@ void host_cleanup PARAMS ((void)); -void wget_sockaddr_set_address PARAMS((wget_sockaddr *, int, - unsigned short, ip_address *)); +void wget_sockaddr_set_address PARAMS((wget_sockaddr *, + unsigned short, ip_address *, int)); 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 *)); +socklen_t sockaddr_len PARAMS((int)); extern int ip_default_family; /* defined in host.c */