- ares_gethostbyname() now accepts 'AF_UNSPEC' as a family for resolving
authorDaniel Stenberg <daniel@haxx.se>
Sat, 31 Jan 2009 20:17:41 +0000 (20:17 +0000)
committerDaniel Stenberg <daniel@haxx.se>
Sat, 31 Jan 2009 20:17:41 +0000 (20:17 +0000)
  either AF_INET6 or AF_INET. It works by accepting any of the looksups in the
  hosts file, and it resolves the AAAA field with a fallback to A.

ares/CHANGES
ares/RELEASE-NOTES
ares/ares__get_hostent.c
ares/ares_gethostbyname.c

index 2321472..ec20226 100644 (file)
@@ -1,5 +1,10 @@
   Changelog for the c-ares project
 
+* January 31 2009 (Daniel Stenberg)
+- ares_gethostbyname() now accepts 'AF_UNSPEC' as a family for resolving
+  either AF_INET6 or AF_INET. It works by accepting any of the looksups in the
+  hosts file, and it resolves the AAAA field with a fallback to A.
+
 * January 14 2009 (Daniel Stenberg)
 - ares.h no longer uses the HAVE_STRUCT_IN6_ADDR define check, but instead it
   now declares the private struct ares_in6_addr for all systems instead of
index 8b5aa49..761121b 100644 (file)
@@ -2,8 +2,10 @@ This is what's new and changed in the c-ares 1.6.1 release:
 
 Changed:
 
- o in6_addr is not used in ares.h anymore, but a private ares_in6_addr is instead
-   declared and used
+ o in6_addr is not used in ares.h anymore, but a private ares_in6_addr is
+   instead declared and used
+ p ares_gethostbyname() now supports 'AF_UNSPEC' as a family for resolving
+   either AF_INET6 or AF_INET
 
 Fixed:
 
index 40dad5d..c14919a 100644 (file)
@@ -68,17 +68,21 @@ int ares__get_hostent(FILE *fp, int family, struct hostent **host)
       *p = 0;
       addr.s_addr = inet_addr(line);
       if (addr.s_addr == INADDR_NONE)
-       {
-          if (ares_inet_pton(AF_INET6, line, &addr6) > 0)
-            {
-              if (family != AF_INET6)
-                continue;
-              addrlen = sizeof(struct in6_addr);
-            }
-          else
-            continue;
-       }
+      {
+        /* It wasn't an AF_INET dotted address, then AF_UNSPEC and AF_INET6
+           families are subject for this further check */
+        if ((family != AF_INET) &&
+            (ares_inet_pton(AF_INET6, line, &addr6) > 0)) {
+          addrlen = sizeof(struct in6_addr);
+          family = AF_INET6;
+        }
+        else
+          continue;
+      }
+      else if (family == AF_UNSPEC)
+        family = AF_INET; /* now confirmed! */
       else if (family != AF_INET)
+        /* unknown, keep moving */
         continue;
 
       /* Get the canonical hostname. */
index 55bfb30..e6b9f7f 100644 (file)
@@ -61,7 +61,8 @@ struct host_query {
   char *name;
   ares_host_callback callback;
   void *arg;
-  int family;
+  int sent_family; /* this family is what was is being used */
+  int want_family; /* this family is what is asked for in the API */
   const char *remaining_lookups;
   int timeouts;
 };
@@ -71,29 +72,34 @@ static void host_callback(void *arg, int status, int timeouts,
                           unsigned char *abuf, int alen);
 static void end_hquery(struct host_query *hquery, int status,
                        struct hostent *host);
-static int fake_hostent(const char *name, int family, ares_host_callback callback,
-                        void *arg);
+static int fake_hostent(const char *name, int family,
+                        ares_host_callback callback, void *arg);
 static int file_lookup(const char *name, int family, struct hostent **host);
-static void sort_addresses(struct hostent *host, const struct apattern *sortlist,
-                           int nsort);
-static void sort6_addresses(struct hostent *host, const struct apattern *sortlist,
-                           int nsort);
-static int get_address_index(const struct in_addr *addr, const struct apattern *sortlist,
-                             int nsort);
-static int get6_address_index(const struct in6_addr *addr, const struct apattern *sortlist,
-                             int nsort);
+static void sort_addresses(struct hostent *host,
+                           const struct apattern *sortlist, int nsort);
+static void sort6_addresses(struct hostent *host,
+                            const struct apattern *sortlist, int nsort);
+static int get_address_index(const struct in_addr *addr,
+                             const struct apattern *sortlist, int nsort);
+static int get6_address_index(const struct in6_addr *addr,
+                              const struct apattern *sortlist, int nsort);
 
 void ares_gethostbyname(ares_channel channel, const char *name, int family,
                         ares_host_callback callback, void *arg)
 {
   struct host_query *hquery;
 
-  /* Right now we only know how to look up Internet addresses. */
-  if (family != AF_INET && family != AF_INET6)
-    {
-      callback(arg, ARES_ENOTIMP, 0, NULL);
-      return;
-    }
+  /* Right now we only know how to look up Internet addresses - and unspec
+     means try both basically. */
+  switch (family) {
+  case AF_INET:
+  case AF_INET6:
+  case AF_UNSPEC:
+    break;
+  default:
+    callback(arg, ARES_ENOTIMP, 0, NULL);
+    return;
+  }
 
   if (fake_hostent(name, family, callback, arg))
     return;
@@ -107,13 +113,13 @@ void ares_gethostbyname(ares_channel channel, const char *name, int family,
     }
   hquery->channel = channel;
   hquery->name = strdup(name);
-  hquery->family = family;
-  if (!hquery->name)
-    {
-      free(hquery);
-      callback(arg, ARES_ENOMEM, 0, NULL);
-      return;
-    }
+  hquery->want_family = family;
+  hquery->sent_family = -1; /* nothing is sent yet */
+  if (!hquery->name) {
+    free(hquery);
+    callback(arg, ARES_ENOMEM, 0, NULL);
+    return;
+  }
   hquery->callback = callback;
   hquery->arg = arg;
   hquery->remaining_lookups = channel->lookups;
@@ -136,17 +142,23 @@ static void next_lookup(struct host_query *hquery, int status_code)
         case 'b':
           /* DNS lookup */
           hquery->remaining_lookups = p + 1;
-          if (hquery->family == AF_INET6)
+          if ((hquery->want_family == AF_INET6) ||
+              (hquery->want_family == AF_UNSPEC)) {
+            /* if inet6 or unspec, start out with AAAA */
+            hquery->sent_family = AF_INET6;
             ares_search(hquery->channel, hquery->name, C_IN, T_AAAA,
                         host_callback, hquery);
-          else
+          }
+          else {
+            hquery->sent_family = AF_INET;
             ares_search(hquery->channel, hquery->name, C_IN, T_A, host_callback,
                         hquery);
+          }
           return;
 
         case 'f':
           /* Host file lookup */
-          status = file_lookup(hquery->name, hquery->family, &host);
+          status = file_lookup(hquery->name, hquery->want_family, &host);
 
           /* this status check below previously checked for !ARES_ENOTFOUND,
              but we should not assume that this single error code is the one
@@ -173,33 +185,34 @@ static void host_callback(void *arg, int status, int timeouts,
   hquery->timeouts += timeouts;
   if (status == ARES_SUCCESS)
     {
-      if (hquery->family == AF_INET)
+      if (hquery->sent_family == AF_INET)
         {
           status = ares_parse_a_reply(abuf, alen, &host, NULL, NULL);
           if (host && channel->nsort)
             sort_addresses(host, channel->sortlist, channel->nsort);
         }
-      else if (hquery->family == AF_INET6)
+      else if (hquery->sent_family == AF_INET6)
         {
           status = ares_parse_aaaa_reply(abuf, alen, &host, NULL, NULL);
-          if (status == ARES_ENODATA)
-            {
-              /* The query returned something (e.g. CNAME) but there were no
-                 AAAA records.  Try looking up A instead.  */
-              hquery->family = AF_INET;
-              ares_search(hquery->channel, hquery->name, C_IN, T_A, host_callback,
-                          hquery);
-              return;
-            }
+          if (status == ARES_ENODATA) {
+            /* The query returned something (e.g. CNAME) but there were no
+               AAAA records.  Try looking up A instead.  We should possibly
+               limit this attempt-next logic to AF_UNSPEC lookups only. */
+            hquery->sent_family = AF_INET;
+            ares_search(hquery->channel, hquery->name, C_IN, T_A,
+                        host_callback, hquery);
+            return;
+          }
           if (host && channel->nsort)
             sort6_addresses(host, channel->sortlist, channel->nsort);
         }
       end_hquery(hquery, status, host);
     }
-  else if (status == ARES_ENODATA && hquery->family == AF_INET6)
+  else if (status == ARES_ENODATA && hquery->sent_family == AF_INET6)
     {
-      /* There was no AAAA. Now lookup an A */
-      hquery->family = AF_INET;
+      /* There was no AAAA. Now lookup an A.  We should possibly limit this
+         attempt-next logic to AF_UNSPEC lookups only. */
+      hquery->sent_family = AF_INET;
       ares_search(hquery->channel, hquery->name, C_IN, T_A, host_callback,
                   hquery);
     }