ares_parse_txt_reply: return a ares_txt_reply node for each sub-string
[platform/upstream/c-ares.git] / ares_expand_name.c
index 40b9f2e..2aa12bc 100644 (file)
@@ -1,4 +1,5 @@
-/* Copyright 1998 by the Massachusetts Institute of Technology.
+
+/* Copyright 1998, 2011 by the Massachusetts Institute of Technology.
  *
  * Permission to use, copy, modify, and distribute this
  * software and its documentation for any purpose and without
  * without express or implied warranty.
  */
 
-#include "setup.h"
-#include <sys/types.h>
+#include "ares_setup.h"
 
-#ifdef WIN32
-#include "nameser.h"
+#ifdef HAVE_NETINET_IN_H
+#  include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_NAMESER_H
+#  include <arpa/nameser.h>
 #else
-#include <netinet/in.h>
-#include <arpa/nameser.h>
+#  include "nameser.h"
+#endif
+#ifdef HAVE_ARPA_NAMESER_COMPAT_H
+#  include <arpa/nameser_compat.h>
 #endif
 
-#include <stdlib.h>
 #include "ares.h"
+#include "ares_nowarn.h"
 #include "ares_private.h" /* for the memdebug */
 
 static int name_length(const unsigned char *encoded, const unsigned char *abuf,
-                      int alen);
+                       int alen);
 
 /* Expand an RFC1035-encoded domain name given by encoded.  The
  * containing message is given by abuf and alen.  The result given by
@@ -55,54 +60,76 @@ static int name_length(const unsigned char *encoded, const unsigned char *abuf,
  */
 
 int ares_expand_name(const unsigned char *encoded, const unsigned char *abuf,
-                    int alen, char **s, long *enclen)
+                     int alen, char **s, long *enclen)
 {
   int len, indir = 0;
   char *q;
   const unsigned char *p;
+  union {
+    ssize_t sig;
+     size_t uns;
+  } nlen;
 
-  len = name_length(encoded, abuf, alen);
-  if (len == -1)
+  nlen.sig = name_length(encoded, abuf, alen);
+  if (nlen.sig < 0)
     return ARES_EBADNAME;
 
-  *s = malloc(len + 1);
+  *s = malloc(nlen.uns + 1);
   if (!*s)
     return ARES_ENOMEM;
   q = *s;
 
+  if (nlen.uns == 0) {
+    /* RFC2181 says this should be ".": the root of the DNS tree.
+     * Since this function strips trailing dots though, it becomes ""
+     */
+    q[0] = '\0';
+
+    /* indirect root label (like 0xc0 0x0c) is 2 bytes long (stupid, but
+       valid) */
+    if ((*encoded & INDIR_MASK) == INDIR_MASK)
+      *enclen = 2L;
+    else
+      *enclen = 1L;  /* the caller should move one byte to get past this */
+
+    return ARES_SUCCESS;
+  }
+
   /* No error-checking necessary; it was all done by name_length(). */
   p = encoded;
   while (*p)
     {
       if ((*p & INDIR_MASK) == INDIR_MASK)
-       {
-         if (!indir)
-           {
-             *enclen = p + 2 - encoded;
-             indir = 1;
-           }
-         p = abuf + ((*p & ~INDIR_MASK) << 8 | *(p + 1));
-       }
+        {
+          if (!indir)
+            {
+              *enclen = aresx_uztosl(p + 2U - encoded);
+              indir = 1;
+            }
+          p = abuf + ((*p & ~INDIR_MASK) << 8 | *(p + 1));
+        }
       else
-       {
-         len = *p;
-         p++;
-         while (len--)
-           {
-             if (*p == '.' || *p == '\\')
-               *q++ = '\\';
-             *q++ = *p;
-             p++;
-           }
-         *q++ = '.';
-       }
+        {
+          len = *p;
+          p++;
+          while (len--)
+            {
+              if (*p == '.' || *p == '\\')
+                *q++ = '\\';
+              *q++ = *p;
+              p++;
+            }
+          *q++ = '.';
+        }
     }
   if (!indir)
-    *enclen = p + 1 - encoded;
+    *enclen = aresx_uztosl(p + 1U - encoded);
 
   /* Nuke the trailing period if we wrote one. */
   if (q > *s)
     *(q - 1) = 0;
+  else
+    *q = 0; /* zero terminate */
 
   return ARES_SUCCESS;
 }
@@ -111,45 +138,45 @@ int ares_expand_name(const unsigned char *encoded, const unsigned char *abuf,
  * -1 if the encoding is invalid.
  */
 static int name_length(const unsigned char *encoded, const unsigned char *abuf,
-                      int alen)
+                       int alen)
 {
   int n = 0, offset, indir = 0;
 
   /* Allow the caller to pass us abuf + alen and have us check for it. */
-  if (encoded == abuf + alen)
+  if (encoded >= abuf + alen)
     return -1;
 
   while (*encoded)
     {
       if ((*encoded & INDIR_MASK) == INDIR_MASK)
-       {
-         /* Check the offset and go there. */
-         if (encoded + 1 >= abuf + alen)
-           return -1;
-         offset = (*encoded & ~INDIR_MASK) << 8 | *(encoded + 1);
-         if (offset >= alen)
-           return -1;
-         encoded = abuf + offset;
-
-         /* If we've seen more indirects than the message length,
-          * then there's a loop.
-          */
-         if (++indir > alen)
-           return -1;
-       }
+        {
+          /* Check the offset and go there. */
+          if (encoded + 1 >= abuf + alen)
+            return -1;
+          offset = (*encoded & ~INDIR_MASK) << 8 | *(encoded + 1);
+          if (offset >= alen)
+            return -1;
+          encoded = abuf + offset;
+
+          /* If we've seen more indirects than the message length,
+           * then there's a loop.
+           */
+          if (++indir > alen)
+            return -1;
+        }
       else
-       {
-         offset = *encoded;
-         if (encoded + offset + 1 >= abuf + alen)
-           return -1;
-         encoded++;
-         while (offset--)
-           {
-             n += (*encoded == '.' || *encoded == '\\') ? 2 : 1;
-             encoded++;
-           }
-         n++;
-       }
+        {
+          offset = *encoded;
+          if (encoded + offset + 1 >= abuf + alen)
+            return -1;
+          encoded++;
+          while (offset--)
+            {
+              n += (*encoded == '.' || *encoded == '\\') ? 2 : 1;
+              encoded++;
+            }
+          n++;
+        }
     }
 
   /* If there were any labels at all, then the number of dots is one
@@ -157,3 +184,14 @@ static int name_length(const unsigned char *encoded, const unsigned char *abuf,
    */
   return (n) ? n - 1 : n;
 }
+
+/* Like ares_expand_name but returns EBADRESP in case of invalid input. */
+int ares__expand_name_for_response(const unsigned char *encoded,
+                                   const unsigned char *abuf, int alen,
+                                   char **s, long *enclen)
+{
+  int status = ares_expand_name(encoded, abuf, alen, s, enclen);
+  if (status == ARES_EBADNAME)
+    status = ARES_EBADRESP;
+  return status;
+}