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

[svn] Added reordering of addresses to try IPv4 first and the associated

--prefer-family switch.
This commit is contained in:
hniksic 2005-04-24 13:00:19 -07:00
parent e14d2b8115
commit cf994330a3
9 changed files with 194 additions and 15 deletions

View File

@ -1,3 +1,7 @@
2005-04-24 Hrvoje Niksic <hniksic@xemacs.org>
* wget.texi (Download Options): Document --prefer-family.
2005-04-24 Hrvoje Niksic <hniksic@xemacs.org> 2005-04-24 Hrvoje Niksic <hniksic@xemacs.org>
* wget.texi (Download Options): Don't claim that -6 accepts mapped * wget.texi (Download Options): Don't claim that -6 accepts mapped

View File

@ -900,6 +900,27 @@ or to deal with broken network configuration. Only one of
@samp{--inet6-only} and @samp{--inet4-only} may be specified in the @samp{--inet6-only} and @samp{--inet4-only} may be specified in the
same command. Neither option is available in Wget compiled without same command. Neither option is available in Wget compiled without
IPv6 support. IPv6 support.
@item --prefer-family=IPv4/IPv6/none
When given a choice of several addresses, connect to the addresses
with specified address family first. IPv4 addresses are preferred by
default.
This avoids spurious errors and correct attempts when accessing hosts
that resolve to both IPv6 and IPv4 addresses from IPv4 networks. For
example, @samp{www.kame.net} resolves to
@samp{2001:200:0:8002:203:47ff:fea5:3085} and to
@samp{203.178.141.194}. When the preferred family is @code{IPv4}, the
IPv4 address is used first; when the preferred family is @code{IPv6},
the IPv6 address is used first; if the specified value is @code{none},
the address order returned by DNS is used without change.
Unlike @samp{-4} and @samp{-6}, this option doesn't forbid access to
any address family, it only changes the @emph{order} in which the
addresses are accessed. Also note that the reordering performed by
this option is @dfn{stable}---it doesn't affect order of addresses of
the same family. That is, the relative order of all IPv4 addresses
and of all IPv6 addresses remains intact in all cases.
@end table @end table
@node Directory Options @node Directory Options
@ -2536,6 +2557,12 @@ the request body. The same as @samp{--post-data}.
Use POST as the method for all HTTP requests and send the contents of Use POST as the method for all HTTP requests and send the contents of
@var{file} in the request body. The same as @samp{--post-file}. @var{file} in the request body. The same as @samp{--post-file}.
@item prefer_family = IPv4/IPv6/none
When given a choice of several addresses, connect to the addresses
with specified address family first. IPv4 addresses are preferred by
default. The same as @samp{--prefer-family}, which see for a detailed
discussion of why this is useful.
@item progress = @var{string} @item progress = @var{string}
Set the type of the progress indicator. Legal types are ``dot'' and Set the type of the progress indicator. Legal types are ``dot'' and
``bar''. ``bar''.
@ -3548,6 +3575,12 @@ Mauro Tortonesi,
Dave Turner, Dave Turner,
Gisle Vanem, Gisle Vanem,
Russell Vincent, Russell Vincent,
@iftex
@v{Z}eljko Vrba,
@end iftex
@ifnottex
Zeljko Vrba,
@end ifnottex
Charles G Waldman, Charles G Waldman,
Douglas E. Wegscheid, Douglas E. Wegscheid,
YAMAZAKI Makoto, YAMAZAKI Makoto,

View File

@ -1,3 +1,18 @@
2005-04-24 Hrvoje Niksic <hniksic@xemacs.org>
* host.c (cmp_prefer_ipv4): New function.
(cmp_prefer_ipv6): New function.
(lookup_host): Use the appropriate comparator according to
opt.prefer_family.
* init.c: New option prefer_family.
* host.c (is_valid_ipv6_address): Spell NS_* constants in lower
case to avoid clash with system headers.
(lookup_host): Reorder the addresses so that IPv4 ones come first.
* utils.c (stable_sort): New function.
2005-04-24 Hrvoje Niksic <hniksic@xemacs.org> 2005-04-24 Hrvoje Niksic <hniksic@xemacs.org>
* connect.c (retryable_socket_connect_error): Return 0 for * connect.c (retryable_socket_connect_error): Return 0 for

View File

@ -239,6 +239,32 @@ address_list_from_addrinfo (const struct addrinfo *ai)
return al; return al;
} }
#define IS_IPV4(addr) (((const ip_address *) addr)->type == IPV4_ADDRESS)
/* Compare two IP addresses by type, giving preference to the IPv4
address (sorting it first). In other words, return -1 if ADDR1 is
IPv4 and ADDR2 is IPv6, +1 if ADDR1 is IPv6 and ADDR2 is IPv4, and
0 otherwise.
This is intended to be used as the comparator arg to a qsort-like
sorting function, which is why it accepts generic pointers. */
static int
cmp_prefer_ipv4 (const void *addr1, const void *addr2)
{
return !IS_IPV4 (addr1) - !IS_IPV4 (addr2);
}
#define IS_IPV6(addr) (((const ip_address *) addr)->type == IPV6_ADDRESS)
/* Like the above, but give preference to the IPv6 address. */
static int
cmp_prefer_ipv6 (const void *addr1, const void *addr2)
{
return !IS_IPV6 (addr1) - !IS_IPV6 (addr2);
}
#else /* not ENABLE_IPV6 */ #else /* not ENABLE_IPV6 */
/* Create an address_list from a NULL-terminated vector of IPv4 /* Create an address_list from a NULL-terminated vector of IPv4
@ -473,10 +499,11 @@ is_valid_ipv4_address (const char *str, const char *end)
int int
is_valid_ipv6_address (const char *str, const char *end) is_valid_ipv6_address (const char *str, const char *end)
{ {
/* Use lower-case for these to avoid clash with system headers. */
enum { enum {
NS_INADDRSZ = 4, ns_inaddrsz = 4,
NS_IN6ADDRSZ = 16, ns_in6addrsz = 16,
NS_INT16SZ = 2 ns_int16sz = 2
}; };
const char *curtok; const char *curtok;
@ -531,19 +558,19 @@ is_valid_ipv6_address (const char *str, const char *end)
} }
else if (str == end) else if (str == end)
return 0; return 0;
if (tp > NS_IN6ADDRSZ - NS_INT16SZ) if (tp > ns_in6addrsz - ns_int16sz)
return 0; return 0;
tp += NS_INT16SZ; tp += ns_int16sz;
saw_xdigit = 0; saw_xdigit = 0;
val = 0; val = 0;
continue; continue;
} }
/* if ch is a dot ... */ /* if ch is a dot ... */
if (ch == '.' && (tp <= NS_IN6ADDRSZ - NS_INADDRSZ) if (ch == '.' && (tp <= ns_in6addrsz - ns_inaddrsz)
&& is_valid_ipv4_address (curtok, end) == 1) && is_valid_ipv4_address (curtok, end) == 1)
{ {
tp += NS_INADDRSZ; tp += ns_inaddrsz;
saw_xdigit = 0; saw_xdigit = 0;
break; break;
} }
@ -553,19 +580,19 @@ is_valid_ipv6_address (const char *str, const char *end)
if (saw_xdigit == 1) if (saw_xdigit == 1)
{ {
if (tp > NS_IN6ADDRSZ - NS_INT16SZ) if (tp > ns_in6addrsz - ns_int16sz)
return 0; return 0;
tp += NS_INT16SZ; tp += ns_int16sz;
} }
if (colonp != NULL) if (colonp != NULL)
{ {
if (tp == NS_IN6ADDRSZ) if (tp == ns_in6addrsz)
return 0; return 0;
tp = NS_IN6ADDRSZ; tp = ns_in6addrsz;
} }
if (tp != NS_IN6ADDRSZ) if (tp != ns_in6addrsz)
return 0; return 0;
return 1; return 1;
@ -640,15 +667,20 @@ cache_remove (const char *host)
} }
} }
/* Look up HOST in DNS and return a list of IP addresses. The /* Look up HOST in DNS and return a list of IP addresses.
addresses in the list are in the same order in which
gethostbyname/getaddrinfo returned them.
This function caches its result so that, if the same host is passed This function caches its result so that, if the same host is passed
the second time, the addresses are returned without DNS lookup. the second time, the addresses are returned without DNS lookup.
(Use LH_REFRESH to force lookup, or set opt.dns_cache to 0 to (Use LH_REFRESH to force lookup, or set opt.dns_cache to 0 to
globally disable caching.) globally disable caching.)
The order of the returned addresses is affected by the setting of
opt.prefer_family: if it is set to prefer_ipv4, IPv4 addresses are
placed at the beginning; if it is prefer_ipv6, IPv6 ones are placed
at the beginning; otherwise, the order is left intact. The
relative order of addresses with the same family is left
undisturbed in either case.
FLAGS can be a combination of: FLAGS can be a combination of:
LH_SILENT - don't print the "resolving ... done" messages. LH_SILENT - don't print the "resolving ... done" messages.
LH_BIND - resolve addresses for use with bind, which under LH_BIND - resolve addresses for use with bind, which under
@ -778,6 +810,14 @@ lookup_host (const char *host, int flags)
_("failed: No IPv4/IPv6 addresses for host.\n")); _("failed: No IPv4/IPv6 addresses for host.\n"));
return NULL; return NULL;
} }
/* Reorder addresses so that IPv4 ones (or IPv6 ones, as per
--prefer-family) come first. Sorting is stable so the order of
the addresses with the same family is undisturbed. */
if (al->count > 1 && opt.prefer_family != prefer_none)
stable_sort (al->addresses, al->count, sizeof (ip_address),
opt.prefer_family == prefer_ipv4
? cmp_prefer_ipv4 : cmp_prefer_ipv6);
} }
#else /* not ENABLE_IPV6 */ #else /* not ENABLE_IPV6 */
{ {

View File

@ -88,6 +88,7 @@ CMD_DECLARE (cmd_spec_dirstruct);
CMD_DECLARE (cmd_spec_header); CMD_DECLARE (cmd_spec_header);
CMD_DECLARE (cmd_spec_htmlify); CMD_DECLARE (cmd_spec_htmlify);
CMD_DECLARE (cmd_spec_mirror); CMD_DECLARE (cmd_spec_mirror);
CMD_DECLARE (cmd_spec_prefer_family);
CMD_DECLARE (cmd_spec_progress); CMD_DECLARE (cmd_spec_progress);
CMD_DECLARE (cmd_spec_recursive); CMD_DECLARE (cmd_spec_recursive);
CMD_DECLARE (cmd_spec_restrict_file_names); CMD_DECLARE (cmd_spec_restrict_file_names);
@ -177,6 +178,7 @@ static struct {
{ "passiveftp", &opt.ftp_pasv, cmd_lockable_boolean }, { "passiveftp", &opt.ftp_pasv, cmd_lockable_boolean },
{ "postdata", &opt.post_data, cmd_string }, { "postdata", &opt.post_data, cmd_string },
{ "postfile", &opt.post_file_name, cmd_file }, { "postfile", &opt.post_file_name, cmd_file },
{ "preferfamily", NULL, cmd_spec_prefer_family },
{ "preservepermissions", &opt.preserve_perm, cmd_boolean }, { "preservepermissions", &opt.preserve_perm, cmd_boolean },
{ "progress", &opt.progress_type, cmd_spec_progress }, { "progress", &opt.progress_type, cmd_spec_progress },
{ "protocoldirectories", &opt.protocol_directories, cmd_boolean }, { "protocoldirectories", &opt.protocol_directories, cmd_boolean },
@ -1072,6 +1074,32 @@ cmd_spec_mirror (const char *com, const char *val, void *closure)
return 1; return 1;
} }
/* Validate --prefer-family and set the choice. Allowed values are
"IPv4", "IPv6", and "none". */
static int
cmd_spec_prefer_family (const char *com, const char *val, void *closure)
{
if (0 == strcasecmp (val, "ipv4"))
{
opt.prefer_family = prefer_ipv4;
return 1;
}
else if (0 == strcasecmp (val, "ipv6"))
{
opt.prefer_family = prefer_ipv6;
return 1;
}
else if (0 == strcasecmp (val, "none"))
{
opt.prefer_family = prefer_none;
return 1;
}
fprintf (stderr, _("%s: %s: Invalid preferred family `%s'.\n"),
exec_name, com, val);
return 0;
}
/* Set progress.type to VAL, but verify that it's a valid progress /* Set progress.type to VAL, but verify that it's a valid progress
implementation before that. */ implementation before that. */

View File

@ -214,6 +214,7 @@ struct cmdline_option option_data[] =
{ "passive-ftp", 0, OPT_BOOLEAN, "passiveftp", -1 }, { "passive-ftp", 0, OPT_BOOLEAN, "passiveftp", -1 },
{ "post-data", 0, OPT_VALUE, "postdata", -1 }, { "post-data", 0, OPT_VALUE, "postdata", -1 },
{ "post-file", 0, OPT_VALUE, "postfile", -1 }, { "post-file", 0, OPT_VALUE, "postfile", -1 },
{ "prefer-family", 0, OPT_VALUE, "preferfamily", -1 },
{ "preserve-permissions", 0, OPT_BOOLEAN, "preservepermissions", -1 }, { "preserve-permissions", 0, OPT_BOOLEAN, "preservepermissions", -1 },
{ "progress", 0, OPT_VALUE, "progress", -1 }, { "progress", 0, OPT_VALUE, "progress", -1 },
{ "protocol-directories", 0, OPT_BOOLEAN, "protocoldirectories", -1 }, { "protocol-directories", 0, OPT_BOOLEAN, "protocoldirectories", -1 },
@ -465,6 +466,9 @@ Download:\n"),
-4, --inet4-only connect only to IPv4 addresses.\n"), -4, --inet4-only connect only to IPv4 addresses.\n"),
N_("\ N_("\
-6, --inet6-only connect only to IPv6 addresses.\n"), -6, --inet6-only connect only to IPv6 addresses.\n"),
N_("\
--prefer-family=FAMILY connect first to addresses of specified family,\n\
one of IPv6, IPv4, or none.\n"),
#endif #endif
"\n", "\n",

View File

@ -197,6 +197,12 @@ struct options
int ipv4_only; /* IPv4 connections have been requested. */ int ipv4_only; /* IPv4 connections have been requested. */
int ipv6_only; /* IPv4 connections have been requested. */ int ipv6_only; /* IPv4 connections have been requested. */
#endif #endif
enum {
prefer_ipv4,
prefer_ipv6,
prefer_none
} prefer_family; /* preferred address family when more
than one type is available */
}; };
extern struct options opt; extern struct options opt;

View File

@ -1975,3 +1975,49 @@ base64_decode (const char *base64, char *to)
#undef IS_ASCII #undef IS_ASCII
#undef IS_BASE64 #undef IS_BASE64
#undef NEXT_BASE64_CHAR #undef NEXT_BASE64_CHAR
/* Simple merge sort for use by stable_sort. Implementation courtesy
Zeljko Vrba. */
static void
mergesort_internal (void *base, void *temp, size_t size, size_t from, size_t to,
int (*cmpfun) PARAMS ((const void *, const void *)))
{
#define ELT(array, pos) ((char *)(array) + (pos) * size)
if (from < to)
{
size_t i, j, k;
size_t mid = (to + from) / 2;
mergesort_internal (base, temp, size, from, mid, cmpfun);
mergesort_internal (base, temp, size, mid + 1, to, cmpfun);
i = from;
j = mid + 1;
for (k = from; (i <= mid) && (j <= to); k++)
if (cmpfun (ELT (base, i), ELT (base, j)) <= 0)
memcpy (ELT (temp, k), ELT (base, i++), size);
else
memcpy (ELT (temp, k), ELT (base, j++), size);
while (i <= mid)
memcpy (ELT (temp, k++), ELT (base, i++), size);
while (j <= to)
memcpy (ELT (temp, k++), ELT (base, j++), size);
for (k = from; k <= to; k++)
memcpy (ELT (base, k), ELT (temp, k), size);
}
#undef ELT
}
/* Stable sort with interface exactly like standard library's qsort.
Uses mergesort internally, allocating temporary storage with
alloca. */
void
stable_sort (void *base, size_t nmemb, size_t size,
int (*cmpfun) PARAMS ((const void *, const void *)))
{
if (size > 1)
{
void *temp = alloca (nmemb * size * sizeof (void *));
mergesort_internal (base, temp, size, 0, nmemb - 1, cmpfun);
}
}

View File

@ -119,4 +119,7 @@ void xsleep PARAMS ((double));
int base64_encode PARAMS ((const char *, int, char *)); int base64_encode PARAMS ((const char *, int, char *));
int base64_decode PARAMS ((const char *, char *)); int base64_decode PARAMS ((const char *, char *));
void stable_sort PARAMS ((void *, size_t, size_t,
int (*) (const void *, const void *)));
#endif /* UTILS_H */ #endif /* UTILS_H */