]> sjero.net Git - wget/blobdiff - src/host.c
[svn] Added reordering of addresses to try IPv4 first and the associated
[wget] / src / host.c
index 272ed4f1629160e454650a93057cb506d6f7757d..5082e179558412c16f4291bd58d506a195d40b01 100644 (file)
@@ -55,10 +55,6 @@ so, delete this exception statement from your version.  */
 # define SET_H_ERRNO(err) WSASetLastError (err)
 #endif /* WINDOWS */
 
-#ifndef NO_ADDRESS
-# define NO_ADDRESS NO_DATA
-#endif
-
 #include <errno.h>
 
 #include "wget.h"
@@ -78,6 +74,10 @@ extern int h_errno;
 # endif
 #endif
 
+#ifndef NO_ADDRESS
+# define NO_ADDRESS NO_DATA
+#endif
+
 /* Lists of IP addresses that result from running DNS queries.  See
    lookup_host for details.  */
 
@@ -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 */
   {