library init: be recursive
[platform/upstream/c-ares.git] / ares_parse_a_reply.c
index 54b5151..a3ed69e 100644 (file)
@@ -1,4 +1,3 @@
-/* $Id$ */
 
 /* Copyright 1998 by the Massachusetts Institute of Technology.
  *
  * without express or implied warranty.
  */
 
-#include "setup.h"
-#include <sys/types.h>
+#include "ares_setup.h"
 
-#if defined(WIN32) && !defined(WATT32)
-#include "nameser.h"
+#ifdef HAVE_NETINET_IN_H
+#  include <netinet/in.h>
+#endif
+#ifdef HAVE_NETDB_H
+#  include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#  include <arpa/inet.h>
+#endif
+#ifdef HAVE_ARPA_NAMESER_H
+#  include <arpa/nameser.h>
 #else
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-#include <arpa/nameser.h>
+#  include "nameser.h"
+#endif
 #ifdef HAVE_ARPA_NAMESER_COMPAT_H
-#include <arpa/nameser_compat.h>
+#  include <arpa/nameser_compat.h>
 #endif
+
+#ifdef HAVE_STRINGS_H
+#  include <strings.h>
+#endif
+
+#ifdef HAVE_LIMITS_H
+#  include <limits.h>
 #endif
 
-#include <stdlib.h>
-#include <string.h>
 #include "ares.h"
 #include "ares_dns.h"
 #include "ares_private.h"
 
 int ares_parse_a_reply(const unsigned char *abuf, int alen,
-                       struct hostent **host)
+                       struct hostent **host,
+                       struct ares_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)
@@ -64,7 +80,7 @@ int ares_parse_a_reply(const unsigned char *abuf, int alen,
 
   /* Expand the name from the question, and skip past the question. */
   aptr = abuf + HFIXEDSZ;
-  status = ares_expand_name(aptr, abuf, alen, &hostname, &len);
+  status = ares__expand_name_for_response(aptr, abuf, alen, &hostname, &len);
   if (status != ARES_SUCCESS)
     return status;
   if (aptr + len + QFIXEDSZ > abuf + alen)
@@ -74,20 +90,30 @@ 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;
 
@@ -95,25 +121,54 @@ int ares_parse_a_reply(const unsigned char *abuf, int alen,
   for (i = 0; i < (int)ancount; i++)
     {
       /* Decode the RR up to the data field. */
-      status = ares_expand_name(aptr, abuf, alen, &rr_name, &len);
+      status = ares__expand_name_for_response(aptr, abuf, alen, &rr_name, &len);
       if (status != ARES_SUCCESS)
         break;
       aptr += len;
       if (aptr + RRFIXEDSZ > abuf + alen)
         {
+          free(rr_name);
           status = ARES_EBADRESP;
           break;
         }
       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 (aptr + rr_len > abuf + alen)
+        {
+          free(rr_name);
+          status = ARES_EBADRESP;
+          break;
+        }
 
       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)
+              {
+                free(rr_name);
+                status = ARES_EBADRESP;
+                break;
+              }
+              memcpy(&addrs[naddrs], aptr, sizeof(struct in_addr));
+            }
+          if (naddrs < max_addr_ttls)
+            {
+              struct ares_addrttl * const at = &addrttls[naddrs];
+              if (aptr + sizeof(struct in_addr) > abuf + alen)
+              {
+                free(rr_name);
+                status = ARES_EBADRESP;
+                break;
+              }
+              memcpy(&at->ipaddr, aptr,  sizeof(struct in_addr));
+              at->ttl = rr_ttl;
+            }
           naddrs++;
           status = ARES_SUCCESS;
         }
@@ -121,15 +176,23 @@ 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. */
-          status = ares_expand_name(aptr, abuf, alen, &rr_data, &len);
+          status = ares__expand_name_for_response(aptr, abuf, alen, &rr_data,
+                                                  &len);
           if (status != ARES_SUCCESS)
             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);
@@ -142,36 +205,59 @@ int ares_parse_a_reply(const unsigned char *abuf, int alen,
         }
     }
 
-  if (status == ARES_SUCCESS && naddrs == 0)
+  if (status == ARES_SUCCESS && naddrs == 0 && naliases == 0)
+    /* the check for naliases to be zero is to make sure CNAME responses
+       don't get caught here */
     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;
+                  if (!naddrs && addrs)
+                    free(addrs);
+                  *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;