]> sjero.net Git - wget/commitdiff
[svn] Added reordering of addresses to try IPv4 first and the associated
authorhniksic <devnull@localhost>
Sun, 24 Apr 2005 20:00:19 +0000 (13:00 -0700)
committerhniksic <devnull@localhost>
Sun, 24 Apr 2005 20:00:19 +0000 (13:00 -0700)
--prefer-family switch.

doc/ChangeLog
doc/wget.texi
src/ChangeLog
src/host.c
src/init.c
src/main.c
src/options.h
src/utils.c
src/utils.h

index 33954e1c5f677453266d98959b52556b6f366104..28cc49e8be68b374558aeab90ac90eab9f89e273 100644 (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>
 
        * wget.texi (Download Options): Don't claim that -6 accepts mapped
index e2fb09f80859a7d412c60ba6660ffc21b7d01e22..bb59b4d689993609e4f20521bba7e931edd8a7cc 100644 (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
 same command.  Neither option is available in Wget compiled without
 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
 
 @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
 @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}
 Set the type of the progress indicator.  Legal types are ``dot'' and
 ``bar''.
@@ -3548,6 +3575,12 @@ Mauro Tortonesi,
 Dave Turner,
 Gisle Vanem,
 Russell Vincent,
+@iftex
+@v{Z}eljko Vrba,
+@end iftex
+@ifnottex
+Zeljko Vrba,
+@end ifnottex
 Charles G Waldman,
 Douglas E. Wegscheid,
 YAMAZAKI Makoto,
index dbc1b27c2d3fbbee10a75a80b254992f217ddc22..80df1b498797717d33cc641e6190943155df9f45 100644 (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>
 
        * connect.c (retryable_socket_connect_error): Return 0 for
index 585b93c7ea8ffa73834fbeb8e16087e1bddf8269..5082e179558412c16f4291bd58d506a195d40b01 100644 (file)
@@ -239,6 +239,32 @@ address_list_from_addrinfo (const struct addrinfo *ai)
   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 */
 
 /* 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
 is_valid_ipv6_address (const char *str, const char *end)
 {
+  /* Use lower-case for these to avoid clash with system headers.  */
   enum {
-    NS_INADDRSZ  = 4,
-    NS_IN6ADDRSZ = 16,
-    NS_INT16SZ   = 2
+    ns_inaddrsz  = 4,
+    ns_in6addrsz = 16,
+    ns_int16sz   = 2
   };
 
   const char *curtok;
@@ -531,19 +558,19 @@ is_valid_ipv6_address (const char *str, const char *end)
            }
          else if (str == end)
            return 0;
-         if (tp > NS_IN6ADDRSZ - NS_INT16SZ)
+         if (tp > ns_in6addrsz - ns_int16sz)
            return 0;
-         tp += NS_INT16SZ;
+         tp += ns_int16sz;
          saw_xdigit = 0;
          val = 0;
          continue;
        }
 
       /* 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)
        {
-         tp += NS_INADDRSZ;
+         tp += ns_inaddrsz;
          saw_xdigit = 0;
          break;
        }
@@ -553,19 +580,19 @@ is_valid_ipv6_address (const char *str, const char *end)
 
   if (saw_xdigit == 1)
     {
-      if (tp > NS_IN6ADDRSZ - NS_INT16SZ
+      if (tp > ns_in6addrsz - ns_int16sz
        return 0;
-      tp += NS_INT16SZ;
+      tp += ns_int16sz;
     }
 
   if (colonp != NULL)
     {
-      if (tp == NS_IN6ADDRSZ
+      if (tp == ns_in6addrsz
        return 0;
-      tp = NS_IN6ADDRSZ;
+      tp = ns_in6addrsz;
     }
 
-  if (tp != NS_IN6ADDRSZ)
+  if (tp != ns_in6addrsz)
     return 0;
 
   return 1;
@@ -640,15 +667,20 @@ cache_remove (const char *host)
     }
 }
 \f
-/* Look up HOST in DNS and return a list of IP addresses.  The
-   addresses in the list are in the same order in which
-   gethostbyname/getaddrinfo returned them.
+/* Look up HOST in DNS and return a list of IP addresses.
 
    This function caches its result so that, if the same host is passed
    the second time, the addresses are returned without DNS lookup.
    (Use LH_REFRESH to force lookup, or set opt.dns_cache to 0 to
    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:
      LH_SILENT  - don't print the "resolving ... done" messages.
      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"));
        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 */
   {
index 3fb9aa23948cef0f1121f7c4bc797b699199dedf..8ab1bec3bfd43eed6d4ed7ffe0b96ed9c2c9747a 100644 (file)
@@ -88,6 +88,7 @@ CMD_DECLARE (cmd_spec_dirstruct);
 CMD_DECLARE (cmd_spec_header);
 CMD_DECLARE (cmd_spec_htmlify);
 CMD_DECLARE (cmd_spec_mirror);
+CMD_DECLARE (cmd_spec_prefer_family);
 CMD_DECLARE (cmd_spec_progress);
 CMD_DECLARE (cmd_spec_recursive);
 CMD_DECLARE (cmd_spec_restrict_file_names);
@@ -177,6 +178,7 @@ static struct {
   { "passiveftp",      &opt.ftp_pasv,          cmd_lockable_boolean },
   { "postdata",                &opt.post_data,         cmd_string },
   { "postfile",                &opt.post_file_name,    cmd_file },
+  { "preferfamily",    NULL,                   cmd_spec_prefer_family },
   { "preservepermissions", &opt.preserve_perm, cmd_boolean },
   { "progress",                &opt.progress_type,     cmd_spec_progress },
   { "protocoldirectories", &opt.protocol_directories, cmd_boolean },
@@ -1072,6 +1074,32 @@ cmd_spec_mirror (const char *com, const char *val, void *closure)
   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
    implementation before that.  */
 
index 718abc9e8e5195eea516e371fc1142ffe237119c..4d2163abfca5fd96e1f637c770c6457b594b5496 100644 (file)
@@ -214,6 +214,7 @@ struct cmdline_option option_data[] =
     { "passive-ftp", 0, OPT_BOOLEAN, "passiveftp", -1 },
     { "post-data", 0, OPT_VALUE, "postdata", -1 },
     { "post-file", 0, OPT_VALUE, "postfile", -1 },
+    { "prefer-family", 0, OPT_VALUE, "preferfamily", -1 },
     { "preserve-permissions", 0, OPT_BOOLEAN, "preservepermissions", -1 },
     { "progress", 0, OPT_VALUE, "progress", -1 },
     { "protocol-directories", 0, OPT_BOOLEAN, "protocoldirectories", -1 },
@@ -465,6 +466,9 @@ Download:\n"),
   -4,  --inet4-only              connect only to IPv4 addresses.\n"),
     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
     "\n",
 
index 90f9fc3a1f95ba94a608feb5217764a3bda67fd7..d4140a0c534cdd39990b8b95cb10b5b4d60c9993 100644 (file)
@@ -197,6 +197,12 @@ struct options
   int ipv4_only;               /* IPv4 connections have been requested. */
   int ipv6_only;               /* IPv4 connections have been requested. */
 #endif
+  enum {
+    prefer_ipv4,
+    prefer_ipv6,
+    prefer_none
+  } prefer_family;             /* preferred address family when more
+                                  than one type is available */
 };
 
 extern struct options opt;
index 38700e04d22a68f9f5f5ec25326645297e2da685..1afb8429edac906df2beb0ea32b7f182bfffc16a 100644 (file)
@@ -1975,3 +1975,49 @@ base64_decode (const char *base64, char *to)
 #undef IS_ASCII
 #undef IS_BASE64
 #undef NEXT_BASE64_CHAR
+\f
+/* 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);
+    }
+}
index 57992a607672aba224de7b63403a578b2f1c63bf..51816a08abd10c394a4ec30d8269f6260f83d6ba 100644 (file)
@@ -119,4 +119,7 @@ void xsleep PARAMS ((double));
 int base64_encode PARAMS ((const char *, int, 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 */