RELEASE-NOTES: two more bug fixes
[platform/upstream/c-ares.git] / ares__get_hostent.c
index bf99627..4497d60 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 <sys/types.h>
-
-#ifdef WIN32
+#include "ares_setup.h"
 
-#else
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <netdb.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
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
 
 #include "ares.h"
+#include "ares_inet_net_pton.h"
+#include "ares_nowarn.h"
 #include "ares_private.h"
 
-int ares__get_hostent(FILE *fp, struct hostent **host)
+int ares__get_hostent(FILE *fp, int family, struct hostent **host)
 {
-  char *line = NULL, *p, *q, *canonical, **alias;
-  int status, linesize, end_at_hostname, naliases;
-  struct in_addr addr;
+  char *line = NULL, *p, *q, **alias;
+  char *txtaddr, *txthost, *txtalias;
+  int status;
+  size_t addrlen, linesize, naliases;
+  struct ares_addr addr;
   struct hostent *hostent = NULL;
 
+  *host = NULL; /* Assume failure */
+
+  /* Validate family */
+  switch (family) {
+    case AF_INET:
+    case AF_INET6:
+    case AF_UNSPEC:
+      break;
+    default:
+      return ARES_EBADFAMILY;
+  }
+
   while ((status = ares__read_line(fp, &line, &linesize)) == ARES_SUCCESS)
     {
-      /* Skip comment lines; terminate line at comment character. */
-      if (*line == '#' || !*line)
-       continue;
-      p = strchr(line, '#');
-      if (p)
-       *p = 0;
-
-      /* Get the address part. */
+
+      /* Trim line comment. */
+      p = line;
+      while (*p && (*p != '#'))
+        p++;
+      *p = '\0';
+
+      /* Trim trailing whitespace. */
+      q = p - 1;
+      while ((q >= line) && ISSPACE(*q))
+        q--;
+      *++q = '\0';
+
+      /* Skip leading whitespace. */
       p = line;
-      while (*p && !isspace((unsigned char)*p))
-       p++;
+      while (*p && ISSPACE(*p))
+        p++;
+      if (!*p)
+        /* Ignore line if empty. */
+        continue;
+
+      /* Pointer to start of IPv4 or IPv6 address part. */
+      txtaddr = p;
+
+      /* Advance past address part. */
+      while (*p && !ISSPACE(*p))
+        p++;
       if (!*p)
-       continue;
-      *p = 0;
-      addr.s_addr = inet_addr(line);
-      if (addr.s_addr == INADDR_NONE)
-       continue;
+        /* Ignore line if reached end of line. */
+        continue;
 
-      /* Get the canonical hostname. */
+      /* Null terminate address part. */
+      *p = '\0';
+
+      /* Advance to host name */
       p++;
-      while (isspace((unsigned char)*p))
-       p++;
+      while (*p && ISSPACE(*p))
+        p++;
       if (!*p)
-       continue;
-      q = p;
-      while (*q && !isspace((unsigned char)*q))
-       q++;
-      end_at_hostname = (*q == 0);
-      *q = 0;
-      canonical = p;
+        /* Ignore line if reached end of line. */
+        continue;
+
+      /* Pointer to start of host name. */
+      txthost = p;
+
+      /* Advance past host name. */
+      while (*p && !ISSPACE(*p))
+        p++;
+
+      /* Pointer to start of first alias. */
+      txtalias = NULL;
+      if (*p)
+        {
+          q = p + 1;
+          while (*q && ISSPACE(*q))
+            q++;
+          if (*q)
+            txtalias = q;
+        }
 
+      /* Null terminate host name. */
+      *p = '\0';
+
+      /* find out number of aliases. */
       naliases = 0;
-      if (!end_at_hostname)
-       {
-         /* Count the aliases. */
-         p = q + 1;
-         while (isspace((unsigned char)*p))
-           p++;
-         while (*p)
-           {
-             while (*p && !isspace((unsigned char)*p))
-               p++;
-             while (isspace((unsigned char)*p))
-               p++;
-             naliases++;
-           }
-       }
-
-      /* Allocate memory for the host structure. */
+      if (txtalias)
+        {
+          p = txtalias;
+          while (*p)
+            {
+              while (*p && !ISSPACE(*p))
+                p++;
+              while (*p && ISSPACE(*p))
+                p++;
+              naliases++;
+            }
+        }
+
+      /* Convert address string to network address for the requested family. */
+      addrlen = 0;
+      addr.family = AF_UNSPEC;
+      addr.addrV4.s_addr = INADDR_NONE;
+      if ((family == AF_INET) || (family == AF_UNSPEC))
+        {
+          addr.addrV4.s_addr = inet_addr(txtaddr);
+          if (addr.addrV4.s_addr != INADDR_NONE)
+            {
+              /* Actual network address family and length. */
+              addr.family = AF_INET;
+              addrlen = sizeof(addr.addrV4);
+            }
+        }
+      if ((family == AF_INET6) || ((family == AF_UNSPEC) && (!addrlen)))
+        {
+          if (ares_inet_pton(AF_INET6, txtaddr, &addr.addrV6) > 0)
+            {
+              /* Actual network address family and length. */
+              addr.family = AF_INET6;
+              addrlen = sizeof(addr.addrV6);
+            }
+        }
+      if (!addrlen)
+        /* Ignore line if invalid address string for the requested family. */
+        continue;
+
+      /*
+      ** Actual address family possible values are AF_INET and AF_INET6 only.
+      */
+
+      /* Allocate memory for the hostent structure. */
       hostent = malloc(sizeof(struct hostent));
       if (!hostent)
-       break;
+        break;
+
+      /* Initialize fields for out of memory condition. */
       hostent->h_aliases = NULL;
       hostent->h_addr_list = NULL;
-      hostent->h_name = strdup(canonical);
+
+      /* Copy official host name. */
+      hostent->h_name = strdup(txthost);
       if (!hostent->h_name)
-       break;
+        break;
+
+      /* Copy network address. */
       hostent->h_addr_list = malloc(2 * sizeof(char *));
       if (!hostent->h_addr_list)
-       break;
-      hostent->h_addr_list[0] = malloc(sizeof(struct in_addr));
+        break;
+      hostent->h_addr_list[1] = NULL;
+      hostent->h_addr_list[0] = malloc(addrlen);
       if (!hostent->h_addr_list[0])
-       break;
+        break;
+      if (addr.family == AF_INET)
+        memcpy(hostent->h_addr_list[0], &addr.addrV4, sizeof(addr.addrV4));
+      else
+        memcpy(hostent->h_addr_list[0], &addr.addrV6, sizeof(addr.addrV6));
+
+      /* Copy aliases. */
       hostent->h_aliases = malloc((naliases + 1) * sizeof(char *));
       if (!hostent->h_aliases)
-       break;
+        break;
+      alias = hostent->h_aliases;
+      while (naliases)
+        *(alias + naliases--) = NULL;
+      *alias = NULL;
+      while (txtalias)
+        {
+          p = txtalias;
+          while (*p && !ISSPACE(*p))
+            p++;
+          q = p;
+          while (*q && ISSPACE(*q))
+            q++;
+          *p = '\0';
+          if ((*alias = strdup(txtalias)) == NULL)
+            break;
+          alias++;
+          txtalias = *q ? q : NULL;
+        }
+      if (txtalias)
+        /* Alias memory allocation failure. */
+        break;
 
-      /* Copy in aliases. */
-      naliases = 0;
-      if (!end_at_hostname)
-       {
-         p = canonical + strlen(canonical) + 1;
-         while (isspace((unsigned char)*p))
-           p++;
-         while (*p)
-           {
-             q = p;
-             while (*q && !isspace((unsigned char)*q))
-               q++;
-             hostent->h_aliases[naliases] = malloc(q - p + 1);
-             if (hostent->h_aliases[naliases] == NULL)
-               break;
-             memcpy(hostent->h_aliases[naliases], p, q - p);
-             hostent->h_aliases[naliases][q - p] = 0;
-             p = q;
-             while (isspace((unsigned char)*p))
-               p++;
-             naliases++;
-           }
-         if (*p)
-           break;
-       }
-      hostent->h_aliases[naliases] = NULL;
-
-      hostent->h_addrtype = AF_INET;
-      hostent->h_length = sizeof(struct in_addr);
-      memcpy(hostent->h_addr_list[0], &addr, sizeof(struct in_addr));
-      hostent->h_addr_list[1] = NULL;
-      *host = hostent;
+      /* Copy actual network address family and length. */
+      hostent->h_addrtype = aresx_sitoss(addr.family);
+      hostent->h_length = aresx_uztoss(addrlen);
+
+      /* Free line buffer. */
       free(line);
+
+      /* Return hostent successfully */
+      *host = hostent;
       return ARES_SUCCESS;
+
     }
-  if(line)
+
+  /* If allocated, free line buffer. */
+  if (line)
     free(line);
 
   if (status == ARES_SUCCESS)
     {
       /* Memory allocation failure; clean up. */
       if (hostent)
-       {
-          if(hostent->h_name)
+        {
+          if (hostent->h_name)
             free((char *) hostent->h_name);
-         if (hostent->h_aliases)
-           {
-             for (alias = hostent->h_aliases; *alias; alias++)
-               free(*alias);
-           }
-          if(hostent->h_aliases)
-            free(hostent->h_aliases);
-         if (hostent->h_addr_list && hostent->h_addr_list[0])
-           free(hostent->h_addr_list[0]);
-          if(hostent->h_addr_list)
-            free(hostent->h_addr_list);
+          if (hostent->h_aliases)
+            {
+              for (alias = hostent->h_aliases; *alias; alias++)
+                free(*alias);
+              free(hostent->h_aliases);
+            }
+          if (hostent->h_addr_list)
+            {
+              if (hostent->h_addr_list[0])
+                free(hostent->h_addr_list[0]);
+              free(hostent->h_addr_list);
+            }
           free(hostent);
-       }
+        }
       return ARES_ENOMEM;
     }