Return TTL data from ares_parse_{a,aaaa}_reply, if the user is so inclined. Patch...
authorSteinar H. Gunderson <sesse@google.com>
Thu, 15 Nov 2007 08:36:25 +0000 (08:36 +0000)
committerSteinar H. Gunderson <sesse@google.com>
Thu, 15 Nov 2007 08:36:25 +0000 (08:36 +0000)
ares.h
ares_gethostbyname.c
ares_parse_a_reply.3
ares_parse_a_reply.c
ares_parse_aaaa_reply.3
ares_parse_aaaa_reply.c

diff --git a/ares.h b/ares.h
index af7df70..5e34310 100644 (file)
--- a/ares.h
+++ b/ares.h
@@ -240,10 +240,29 @@ int ares_expand_name(const unsigned char *encoded, const unsigned char *abuf,
                      int alen, char **s, long *enclen);
 int ares_expand_string(const unsigned char *encoded, const unsigned char *abuf,
                      int alen, unsigned char **s, long *enclen);
+
+struct addrttl {
+  struct in_addr ipaddr;
+  int            ttl;
+};
+struct addr6ttl {
+  struct in6_addr ip6addr;
+  int             ttl;
+};
+
+/*
+** Parse the buffer, starting at *abuf and of length alen bytes, previously
+** obtained from an ares_search call.  Put the results in *host, if nonnull.
+** Also, if addrttls is nonnull, put up to *naddrttls IPv4 addresses along with
+** their TTLs in that array, and set *naddrttls to the number of addresses
+** so written.
+*/
 int ares_parse_a_reply(const unsigned char *abuf, int alen,
-                       struct hostent **host);
+                       struct hostent **host,
+                       struct addrttl *addrttls, int *naddrttls);
 int ares_parse_aaaa_reply(const unsigned char *abuf, int alen,
-                       struct hostent **host);
+                       struct hostent **host,
+                       struct addr6ttl *addrttls, int *naddrttls);
 int ares_parse_ptr_reply(const unsigned char *abuf, int alen, const void *addr,
                          int addrlen, int family, struct hostent **host);
 int ares_parse_ns_reply(const unsigned char *abuf, int alen,
index bdc8791..b96ac6b 100644 (file)
@@ -160,13 +160,13 @@ static void host_callback(void *arg, int status, int timeouts,
     {
       if (hquery->family == AF_INET)
         {
-          status = ares_parse_a_reply(abuf, alen, &host);
+          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)
         {
-          status = ares_parse_aaaa_reply(abuf, alen, &host);
+          status = ares_parse_aaaa_reply(abuf, alen, &host, NULL, NULL);
           if (host && channel->nsort)
             sort6_addresses(host, channel->sortlist, channel->nsort);
         }
index 7acd70f..fff8e23 100644 (file)
@@ -22,24 +22,39 @@ ares_parse_a_reply \- Parse a reply to a DNS query of type A into a hostent
 .B #include <ares.h>
 .PP
 .B int ares_parse_a_reply(const unsigned char *\fIabuf\fP, int \fIalen\fP,
-.B     struct hostent **\fIhost\fP);
+.B     struct hostent **\fIhost\fP,
+.B      struct addrttl *\fIaddrttls\fB, int *\fInaddrttls\fB);
 .fi
 .SH DESCRIPTION
 The
 .B ares_parse_a_reply
 function parses the response to a query of type A into a
-.BR "struct hostent" .
+.BR "struct hostent"
+and/or an array of
+.BR "struct addrttls" . 
 The parameters
 .I abuf
 and
 .I alen
 give the contents of the response.  The result is stored in allocated
 memory and a pointer to it stored into the variable pointed to by
-.IR host .
+.IR host ,
+if host is nonnull.
 It is the caller's responsibility to free the resulting host structure
 using
 .BR ares_free_hostent (3)
 when it is no longer needed.
+.PP
+If
+.IR addrttls
+and
+.IR naddrttls
+are both nonnull,
+then up to *naddrttls
+.BR "struct addrttl"
+records are stored in the array pointed to by addrttls,
+and then *naddrttls is set to the number of records so stored.
+Note that the memory for these records is supplied by the caller.
 .SH RETURN VALUES
 .B ares_parse_a_reply
 can return any of the following values:
index 6f42750..4dea80e 100644 (file)
 #include "ares_private.h"
 
 int ares_parse_a_reply(const unsigned char *abuf, int alen,
-                       struct hostent **host)
+                       struct hostent **host,
+                       struct addrttl *addrttls, int *naddrttls)
 {
   unsigned int qdcount, ancount;
-  int status, i, rr_type, rr_class, rr_len, naddrs;
+  int status, i, rr_type, rr_class, rr_len, rr_ttl, naddrs;
+  int cname_ttl = INT_MAX;  /* the TTL imposed by the CNAME chain */
   int naliases;
   long len;
   const unsigned char *aptr;
   char *hostname, *rr_name, *rr_data, **aliases;
   struct in_addr *addrs;
   struct hostent *hostent;
+  const int max_addr_ttls = (addrttls && naddrttls) ? *naddrttls : 0;
 
   /* Set *host to NULL for all failure cases. */
-  *host = NULL;
+  if (host)
+    *host = NULL;
+  /* Same with *naddrttls. */
+  if (naddrttls)
+    *naddrttls = 0;
 
   /* Give up if abuf doesn't have room for a header. */
   if (alen < HFIXEDSZ)
@@ -73,20 +80,29 @@ int ares_parse_a_reply(const unsigned char *abuf, int alen,
     }
   aptr += len + QFIXEDSZ;
 
-  /* Allocate addresses and aliases; ancount gives an upper bound for both. */
-  addrs = malloc(ancount * sizeof(struct in_addr));
-  if (!addrs)
+  if (host)
     {
-      free(hostname);
-      return ARES_ENOMEM;
+      /* Allocate addresses and aliases; ancount gives an upper bound for both. */
+      addrs = malloc(ancount * sizeof(struct in_addr));
+      if (!addrs)
+        {
+          free(hostname);
+          return ARES_ENOMEM;
+        }
+      aliases = malloc((ancount + 1) * sizeof(char *));
+      if (!aliases)
+        {
+          free(hostname);
+          free(addrs);
+          return ARES_ENOMEM;
+        }
     }
-  aliases = malloc((ancount + 1) * sizeof(char *));
-  if (!aliases)
+  else
     {
-      free(hostname);
-      free(addrs);
-      return ARES_ENOMEM;
+      addrs = NULL;
+      aliases = NULL;
     }
+  
   naddrs = 0;
   naliases = 0;
 
@@ -106,13 +122,33 @@ int ares_parse_a_reply(const unsigned char *abuf, int alen,
       rr_type = DNS_RR_TYPE(aptr);
       rr_class = DNS_RR_CLASS(aptr);
       rr_len = DNS_RR_LEN(aptr);
+      rr_ttl = DNS_RR_TTL(aptr);
       aptr += RRFIXEDSZ;
 
       if (rr_class == C_IN && rr_type == T_A
           && rr_len == sizeof(struct in_addr)
           && strcasecmp(rr_name, hostname) == 0)
         {
-          memcpy(&addrs[naddrs], aptr, sizeof(struct in_addr));
+          if (addrs)
+            {
+              if (aptr + sizeof(struct in_addr) > abuf + alen)
+              {
+                status = ARES_EBADRESP;
+                break;
+              }
+              memcpy(&addrs[naddrs], aptr, sizeof(struct in_addr));
+            }
+          if (naddrs < max_addr_ttls)
+            {
+              struct addrttl * const at = &addrttls[naddrs];
+              if (aptr + sizeof(struct in_addr) > abuf + alen)
+              {
+                status = ARES_EBADRESP;
+                break;
+              }
+              memcpy(&at->ipaddr, aptr,  sizeof(struct in_addr));
+              at->ttl = rr_ttl;
+            }
           naddrs++;
           status = ARES_SUCCESS;
         }
@@ -120,7 +156,10 @@ int ares_parse_a_reply(const unsigned char *abuf, int alen,
       if (rr_class == C_IN && rr_type == T_CNAME)
         {
           /* Record the RR name as an alias. */
-          aliases[naliases] = rr_name;
+          if (aliases)
+            aliases[naliases] = rr_name;
+          else
+            free(rr_name);
           naliases++;
 
           /* Decode the RR data and replace the hostname with it. */
@@ -129,6 +168,10 @@ int ares_parse_a_reply(const unsigned char *abuf, int alen,
             break;
           free(hostname);
           hostname = rr_data;
+
+          /* Take the min of the TTLs we see in the CNAME chain. */
+          if (cname_ttl > rr_ttl)
+            cname_ttl = rr_ttl;
         }
       else
         free(rr_name);
@@ -145,32 +188,51 @@ int ares_parse_a_reply(const unsigned char *abuf, int alen,
     status = ARES_ENODATA;
   if (status == ARES_SUCCESS)
     {
-      /* We got our answer.  Allocate memory to build the host entry. */
-      aliases[naliases] = NULL;
-      hostent = malloc(sizeof(struct hostent));
-      if (hostent)
+      /* We got our answer. */
+      if (naddrttls)
         {
-          hostent->h_addr_list = malloc((naddrs + 1) * sizeof(char *));
-          if (hostent->h_addr_list)
+          const int n = naddrs < max_addr_ttls ? naddrs : max_addr_ttls;
+          for (i = 0; i < n; i++)
             {
-              /* Fill in the hostent and return successfully. */
-              hostent->h_name = hostname;
-              hostent->h_aliases = aliases;
-              hostent->h_addrtype = AF_INET;
-              hostent->h_length = sizeof(struct in_addr);
-              for (i = 0; i < naddrs; i++)
-                hostent->h_addr_list[i] = (char *) &addrs[i];
-              hostent->h_addr_list[naddrs] = NULL;
-              *host = hostent;
-              return ARES_SUCCESS;
+              /* Ensure that each A TTL is no larger than the CNAME TTL. */
+              if (addrttls[i].ttl > cname_ttl)
+                addrttls[i].ttl = cname_ttl;
             }
-          free(hostent);
+          *naddrttls = n;
         }
-      status = ARES_ENOMEM;
+      if (aliases)
+        aliases[naliases] = NULL;
+      if (host)
+        {
+          /* Allocate memory to build the host entry. */
+          hostent = malloc(sizeof(struct hostent));
+          if (hostent)
+            {
+              hostent->h_addr_list = malloc((naddrs + 1) * sizeof(char *));
+              if (hostent->h_addr_list)
+                {
+                  /* Fill in the hostent and return successfully. */
+                  hostent->h_name = hostname;
+                  hostent->h_aliases = aliases;
+                  hostent->h_addrtype = AF_INET;
+                  hostent->h_length = sizeof(struct in_addr);
+                  for (i = 0; i < naddrs; i++)
+                    hostent->h_addr_list[i] = (char *) &addrs[i];
+                  hostent->h_addr_list[naddrs] = NULL;
+                  *host = hostent;
+                  return ARES_SUCCESS;
+                }
+              free(hostent);
+            }
+          status = ARES_ENOMEM;
+        }
+     }
+  if (aliases)
+    {
+      for (i = 0; i < naliases; i++)
+        free(aliases[i]);
+      free(aliases);
     }
-  for (i = 0; i < naliases; i++)
-    free(aliases[i]);
-  free(aliases);
   free(addrs);
   free(hostname);
   return status;
index 15ecd5e..3b80295 100644 (file)
@@ -22,24 +22,39 @@ ares_parse_aaaa_reply \- Parse a reply to a DNS query of type AAAA into a hosten
 .B #include <ares.h>
 .PP
 .B int ares_parse_aaaa_reply(const unsigned char *\fIabuf\fP, int \fIalen\fP,
-.B     struct hostent **\fIhost\fP);
+.B     struct hostent **\fIhost\fP,         
+.B      struct addrttl *\fIaddrttls\fB, int *\fInaddrttls\fB);
 .fi
 .SH DESCRIPTION
 The
 .B ares_parse_aaaa_reply
 function parses the response to a query of type AAAA into a
-.BR "struct hostent" .
+.BR "struct hostent"
+and/or an array of
+.BR "struct addrttls" . 
 The parameters
 .I abuf
 and
 .I alen
 give the contents of the response.  The result is stored in allocated
 memory and a pointer to it stored into the variable pointed to by
-.IR host .
+.IR host ,
+if host is nonnull.
 It is the caller's responsibility to free the resulting host structure
 using
 .BR ares_free_hostent (3)
 when it is no longer needed.
+.PP
+If
+.IR addrttls
+and
+.IR naddrttls
+are both nonnull,
+then up to *naddrttls
+.BR "struct addr6ttl"
+records are stored in the array pointed to by addrttls,
+and then *naddrttls is set to the number of records so stored.
+Note that the memory for these records is supplied by the caller.
 .SH RETURN VALUES
 .B ares_parse_aaaa_reply
 can return any of the following values:
index 535d40e..3b578f1 100644 (file)
 #include "ares_private.h"
 
 int ares_parse_aaaa_reply(const unsigned char *abuf, int alen,
-                       struct hostent **host)
+                          struct hostent **host, struct addr6ttl *addrttls,
+                          int *naddrttls)
 {
   unsigned int qdcount, ancount;
-  int status, i, rr_type, rr_class, rr_len, naddrs;
+  int status, i, rr_type, rr_class, rr_len, rr_ttl, naddrs;
+  int cname_ttl = INT_MAX;  /* the TTL imposed by the CNAME chain */
   int naliases;
   long len;
   const unsigned char *aptr;
   char *hostname, *rr_name, *rr_data, **aliases;
   struct in6_addr *addrs;
   struct hostent *hostent;
+  const int max_addr_ttls = (addrttls && naddrttls) ? *naddrttls : 0;
 
   /* Set *host to NULL for all failure cases. */
-  *host = NULL;
+  if (host)
+    *host = NULL;
+  /* Same with *naddrttls. */
+  if (naddrttls)
+    *naddrttls = 0;
 
   /* Give up if abuf doesn't have room for a header. */
   if (alen < HFIXEDSZ)
@@ -77,18 +84,26 @@ int ares_parse_aaaa_reply(const unsigned char *abuf, int alen,
   aptr += len + QFIXEDSZ;
 
   /* Allocate addresses and aliases; ancount gives an upper bound for both. */
-  addrs = malloc(ancount * sizeof(struct in6_addr));
-  if (!addrs)
+  if (host)
     {
-      free(hostname);
-      return ARES_ENOMEM;
+      addrs = malloc(ancount * sizeof(struct in6_addr));
+      if (!addrs)
+        {
+          free(hostname);
+          return ARES_ENOMEM;
+        }
+      aliases = malloc((ancount + 1) * sizeof(char *));
+      if (!aliases)
+        {
+          free(hostname);
+          free(addrs);
+          return ARES_ENOMEM;
+        }
     }
-  aliases = malloc((ancount + 1) * sizeof(char *));
-  if (!aliases)
+  else
     {
-      free(hostname);
-      free(addrs);
-      return ARES_ENOMEM;
+      addrs = NULL;
+      aliases = NULL;
     }
   naddrs = 0;
   naliases = 0;
@@ -109,13 +124,33 @@ int ares_parse_aaaa_reply(const unsigned char *abuf, int alen,
       rr_type = DNS_RR_TYPE(aptr);
       rr_class = DNS_RR_CLASS(aptr);
       rr_len = DNS_RR_LEN(aptr);
+      rr_ttl = DNS_RR_TTL(aptr);
       aptr += RRFIXEDSZ;
 
       if (rr_class == C_IN && rr_type == T_AAAA
           && rr_len == sizeof(struct in6_addr)
           && strcasecmp(rr_name, hostname) == 0)
         {
-          memcpy(&addrs[naddrs], aptr, sizeof(struct in6_addr));
+          if (addrs)
+            {
+              if (aptr + sizeof(struct in6_addr) > abuf + alen)
+              {
+                status = ARES_EBADRESP;
+                break;
+              }
+              memcpy(&addrs[naddrs], aptr, sizeof(struct in6_addr));
+            }
+          if (naddrs < max_addr_ttls)
+            {
+              struct addr6ttl * const at = &addrttls[naddrs];
+              if (aptr + sizeof(struct in6_addr) > abuf + alen)
+              {
+                status = ARES_EBADRESP;
+                break;
+              }
+              memcpy(&at->ip6addr, aptr,  sizeof(struct in6_addr));
+              at->ttl = rr_ttl;
+            }
           naddrs++;
           status = ARES_SUCCESS;
         }
@@ -123,7 +158,10 @@ int ares_parse_aaaa_reply(const unsigned char *abuf, int alen,
       if (rr_class == C_IN && rr_type == T_CNAME)
         {
           /* Record the RR name as an alias. */
-          aliases[naliases] = rr_name;
+          if (aliases)
+            aliases[naliases] = rr_name;
+          else
+            free(rr_name);
           naliases++;
 
           /* Decode the RR data and replace the hostname with it. */
@@ -132,6 +170,10 @@ int ares_parse_aaaa_reply(const unsigned char *abuf, int alen,
             break;
           free(hostname);
           hostname = rr_data;
+
+          /* Take the min of the TTLs we see in the CNAME chain. */
+          if (cname_ttl > rr_ttl)
+            cname_ttl = rr_ttl;
         }
       else
         free(rr_name);
@@ -148,28 +190,50 @@ int ares_parse_aaaa_reply(const unsigned char *abuf, int alen,
     status = ARES_ENODATA;
   if (status == ARES_SUCCESS)
     {
-      /* We got our answer.  Allocate memory to build the host entry. */
-      aliases[naliases] = NULL;
-      hostent = malloc(sizeof(struct hostent));
-      if (hostent)
+      /* We got our answer. */
+      if (naddrttls)
         {
-          hostent->h_addr_list = malloc((naddrs + 1) * sizeof(char *));
-          if (hostent->h_addr_list)
+          const int n = naddrs < max_addr_ttls ? naddrs : max_addr_ttls;
+          for (i = 0; i < n; i++)
             {
-              /* Fill in the hostent and return successfully. */
-              hostent->h_name = hostname;
-              hostent->h_aliases = aliases;
-              hostent->h_addrtype = AF_INET6;
-              hostent->h_length = sizeof(struct in6_addr);
-              for (i = 0; i < naddrs; i++)
-                hostent->h_addr_list[i] = (char *) &addrs[i];
-              hostent->h_addr_list[naddrs] = NULL;
-              *host = hostent;
-              return ARES_SUCCESS;
+              /* Ensure that each A TTL is no larger than the CNAME TTL. */
+              if (addrttls[i].ttl > cname_ttl)
+                addrttls[i].ttl = cname_ttl;
             }
-          free(hostent);
+          *naddrttls = n;
         }
-      status = ARES_ENOMEM;
+      if (aliases)
+        aliases[naliases] = NULL;
+      if (host)
+        {
+          /* Allocate memory to build the host entry. */
+          hostent = malloc(sizeof(struct hostent));
+          if (hostent)
+            {
+              hostent->h_addr_list = malloc((naddrs + 1) * sizeof(char *));
+              if (hostent->h_addr_list)
+                {
+                  /* Fill in the hostent and return successfully. */
+                  hostent->h_name = hostname;
+                  hostent->h_aliases = aliases;
+                  hostent->h_addrtype = AF_INET6;
+                  hostent->h_length = sizeof(struct in6_addr);
+                  for (i = 0; i < naddrs; i++)
+                    hostent->h_addr_list[i] = (char *) &addrs[i];
+                  hostent->h_addr_list[naddrs] = NULL;
+                  *host = hostent;
+                  return ARES_SUCCESS;
+                }
+              free(hostent);
+            }
+          status = ARES_ENOMEM;
+        }
+    }
+  if (aliases)
+    {
+      for (i = 0; i < naliases; i++)
+        free(aliases[i]);
+      free(aliases);
     }
   for (i = 0; i < naliases; i++)
     free(aliases[i]);