RELEASE-NOTES: two more bug fixes
[platform/upstream/c-ares.git] / ares__get_hostent.c
index dc5f8ad..4497d60 100644 (file)
@@ -1,6 +1,5 @@
-/* $Id$ */
 
-/* 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 "ares_setup.h"
 
-#if !defined(WIN32) || defined(WATT32)
-#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"
-#include "inet_net_pton.h"
 
 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;
-  struct in6_addr addr6;
-  int addrlen = sizeof(struct in_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 && !ISSPACE(*p))
+      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(*p))
         p++;
       if (!*p)
+        /* Ignore line if empty. */
         continue;
-      *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;
-       }
-      else if (family != AF_INET)
+
+      /* Pointer to start of IPv4 or IPv6 address part. */
+      txtaddr = p;
+
+      /* Advance past address part. */
+      while (*p && !ISSPACE(*p))
+        p++;
+      if (!*p)
+        /* 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(*p))
+      while (*p && ISSPACE(*p))
         p++;
       if (!*p)
+        /* Ignore line if reached end of line. */
         continue;
-      q = p;
-      while (*q && !ISSPACE(*q))
-        q++;
-      end_at_hostname = (*q == 0);
-      *q = 0;
-      canonical = p;
 
+      /* 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)
+      if (txtalias)
         {
-          /* Count the aliases. */
-          p = q + 1;
-          while (ISSPACE(*p))
-            p++;
+          p = txtalias;
           while (*p)
             {
               while (*p && !ISSPACE(*p))
                 p++;
-              while (ISSPACE(*p))
+              while (*p && ISSPACE(*p))
                 p++;
               naliases++;
             }
         }
 
-      /* Allocate memory for the host structure. */
+      /* 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;
+
+      /* 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;
+
+      /* Copy network address. */
       hostent->h_addr_list = malloc(2 * sizeof(char *));
       if (!hostent->h_addr_list)
         break;
+      hostent->h_addr_list[1] = NULL;
       hostent->h_addr_list[0] = malloc(addrlen);
       if (!hostent->h_addr_list[0])
         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;
-
-      /* Copy in aliases. */
-      naliases = 0;
-      if (!end_at_hostname)
+      alias = hostent->h_aliases;
+      while (naliases)
+        *(alias + naliases--) = NULL;
+      *alias = NULL;
+      while (txtalias)
         {
-          p = canonical + strlen(canonical) + 1;
-          while (ISSPACE(*p))
+          p = txtalias;
+          while (*p && !ISSPACE(*p))
             p++;
-          while (*p)
-            {
-              q = p;
-              while (*q && !ISSPACE(*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(*p))
-                p++;
-              naliases++;
-            }
-          if (*p)
+          q = p;
+          while (*q && ISSPACE(*q))
+            q++;
+          *p = '\0';
+          if ((*alias = strdup(txtalias)) == NULL)
             break;
+          alias++;
+          txtalias = *q ? q : NULL;
         }
-      hostent->h_aliases[naliases] = NULL;
-
-      hostent->h_addrtype = family;
-      hostent->h_length = addrlen;
-      if (family == AF_INET)
-        memcpy(hostent->h_addr_list[0], &addr, addrlen);
-      else if (family == AF_INET6)
-        memcpy(hostent->h_addr_list[0], &addr6, addrlen);
-      hostent->h_addr_list[1] = NULL;
-      *host = hostent;
+      if (txtalias)
+        /* Alias memory allocation failure. */
+        break;
+
+      /* 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)
@@ -168,22 +238,22 @@ int ares__get_hostent(FILE *fp, int family, struct hostent **host)
       /* 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);
+              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);
             }
-          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);
           free(hostent);
         }
-      *host = NULL;
       return ARES_ENOMEM;
     }