Commit 052dac0d authored by Yang Tse's avatar Yang Tse
Browse files

Overhauled ares__get_hostent()

- Fixing out of bounds memory overwrite triggered with malformed /etc/hosts file.
- Improving parsing of /etc/hosts file.
- Validating requested address family.
- Ensuring that failures always return a NULL pointer.
- Adjusting header inclusions.
parent 2eeafcf9
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
  Changelog for the c-ares project

* October 7, 2009 (Yang Tse)
- Overhauled ares__get_hostent() Fixing out of bounds memory overwrite
  triggered with malformed /etc/hosts file. Improving parsing of /etc/hosts
  file. Validating requested address family. Ensuring that failures always
  return a NULL pointer. Adjusting header inclusions.

* 4 Sep 2009 (Daniel Stenberg)
- Jakub Hrozek added ares_parse_srv_reply() for SRV parsing

+1 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ Fixed:
 o only expose/export symbols starting with 'ares_'
 o fix \Device\TCP handle leaks triggered by buggy iphlpapi.dll
 o init without internet gone no longer fails
 o out of bounds memory overwrite triggered with malformed /etc/hosts file

Thanks go to these friendly people for their efforts and contributions:

+156 −96
Original line number Diff line number Diff line
/* $Id$ */

/* Copyright 1998 by the Massachusetts Institute of Technology.
/* Copyright 1998, 2009 by the Massachusetts Institute of Technology.
 *
 * Permission to use, copy, modify, and distribute this
 * software and its documentation for any purpose and without
@@ -17,7 +17,6 @@

#include "setup.h"

#if !defined(WIN32) || defined(WATT32)
#ifdef HAVE_SYS_SOCKET_H
#  include <sys/socket.h>
#endif
@@ -30,12 +29,6 @@
#ifdef HAVE_ARPA_INET_H
#  include <arpa/inet.h>
#endif
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "ares.h"
#include "inet_net_pton.h"
@@ -43,135 +36,202 @@

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;
  char *line = NULL, *p, *q, **alias;
  char *txtaddr, *txthost, *txtalias;
  int status, linesize, addrfam, naliases;
  struct in_addr addr;
  struct in6_addr addr6;
  size_t addrlen = sizeof(struct in_addr);
  size_t addrlen;
  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)
      {
        /* It wasn't an AF_INET dotted address, then AF_UNSPEC and AF_INET6
           families are subject for this further check */
        if ((family != AF_INET) &&
            (ares_inet_pton(AF_INET6, line, &addr6) > 0)) {
          addrlen = sizeof(struct in6_addr);
          family = AF_INET6;
        }
        else
          continue;
      }
      else if (family == AF_UNSPEC)
        family = AF_INET; /* now confirmed! */
      else if (family != AF_INET)
        /* unknown, keep moving */

      /* 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))

      /* 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++;
      end_at_hostname = (*q == 0);
      *q = 0;
      canonical = p;
          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;
      addrfam = AF_UNSPEC;
      if ((family == AF_INET) || (family == AF_UNSPEC))
        {
          addr.s_addr = inet_addr(txtaddr);
          if (addr.s_addr != INADDR_NONE)
            {
              /* Actual network address family and length. */
              addrfam = AF_INET;
              addrlen = sizeof(struct in_addr);
            }
        }
      if ((family == AF_INET6) || ((family == AF_UNSPEC) && (!addrlen)))
        {
          if (ares_inet_pton(AF_INET6, txtaddr, &addr6) > 0)
            {
              /* Actual network address family and length. */
              addrfam = AF_INET6;
              addrlen = sizeof(struct in6_addr);
            }
        }
      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 (addrfam == AF_INET)
        memcpy(hostent->h_addr_list[0], &addr, addrlen);
      else
        memcpy(hostent->h_addr_list[0], &addr6, addrlen);

      /* 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 >= 0)
        *(alias + naliases--) = 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))
          while (*q && ISSPACE(*q))
            q++;
              hostent->h_aliases[naliases] = malloc(q - p + 1);
              if (hostent->h_aliases[naliases] == NULL)
          *p = '\0';
          if ((*alias = strdup(txtalias)) == NULL)
            break;
              memcpy(hostent->h_aliases[naliases], p, q - p);
              hostent->h_aliases[naliases][q - p] = 0;
              p = q;
              while (ISSPACE(*p))
                p++;
              naliases++;
          alias++;
          txtalias = *q ? q : NULL;
        }
          if (*p)
      if (txtalias)
        /* Alias memory allocation failure. */
        break;
        }
      hostent->h_aliases[naliases] = NULL;

      hostent->h_addrtype = family;
      /* Copy actual network address family and length. */
      hostent->h_addrtype = addrfam;
      hostent->h_length = (int)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;

      /* Free line buffer. */
      free(line);

      /* Return hostent successfully */
      *host = hostent;
      return ARES_SUCCESS;

    }

  /* If allocated, free line buffer. */
  if (line)
    free(line);

@@ -186,16 +246,16 @@ int ares__get_hostent(FILE *fp, int family, struct hostent **host)
            {
              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)
            {
              if (hostent->h_addr_list[0])
                free(hostent->h_addr_list[0]);
              free(hostent->h_addr_list);
            }
          free(hostent);
        }
      *host = NULL;
      return ARES_ENOMEM;
    }