Logo Search packages:      
Sourcecode: glibc version File versions

files-hosts.c

/* Hosts file parser in nss_files module.
   Copyright (C) 1996-2001, 2003-2007, 2008 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307 USA.  */

#include <assert.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <netdb.h>
#include <resolv.h>


/* Get implementation for some internal functions.  */
#include "../resolv/mapv4v6addr.h"
#include "../resolv/res_hconf.h"


#define ENTNAME         hostent
#define DATABASE  "hosts"
#define NEED_H_ERRNO

#define EXTRA_ARGS       , af, flags
#define EXTRA_ARGS_DECL  , int af, int flags

#define ENTDATA hostent_data
struct hostent_data
  {
    unsigned char host_addr[16]; /* IPv4 or IPv6 address.  */
    char *h_addr_ptrs[2];     /* Points to that and null terminator.  */
  };

#define TRAILING_LIST_MEMBER        h_aliases
#define TRAILING_LIST_SEPARATOR_P   isspace
#include "files-parse.c"
LINE_PARSER
("#",
 {
   char *addr;

   STRING_FIELD (addr, isspace, 1);

   /* Parse address.  */
   if (inet_pton (af == AF_UNSPEC ? AF_INET : af, addr, entdata->host_addr)
       > 0)
     af = af == AF_UNSPEC ? AF_INET : af;
   else
     {
       if (af == AF_INET6 && (flags & AI_V4MAPPED) != 0
         && inet_pton (AF_INET, addr, entdata->host_addr) > 0)
       map_v4v6_address ((char *) entdata->host_addr,
                     (char *) entdata->host_addr);
       else if (af == AF_INET
            && inet_pton (AF_INET6, addr, entdata->host_addr) > 0)
       {
         if (IN6_IS_ADDR_V4MAPPED (entdata->host_addr))
           memcpy (entdata->host_addr, entdata->host_addr + 12, INADDRSZ);
         else if (IN6_IS_ADDR_LOOPBACK (entdata->host_addr))
           {
             in_addr_t localhost = htonl (INADDR_LOOPBACK);
             memcpy (entdata->host_addr, &localhost, sizeof (localhost));
           }
         else
           /* Illegal address: ignore line.  */
           return 0;
       }
       else if (af == AF_UNSPEC
            && inet_pton (AF_INET6, addr, entdata->host_addr) > 0)
       af = AF_INET6;
       else
       /* Illegal address: ignore line.  */
       return 0;
     }

   /* We always return entries of the requested form.  */
   result->h_addrtype = af;
   result->h_length = af == AF_INET ? INADDRSZ : IN6ADDRSZ;

   /* Store a pointer to the address in the expected form.  */
   entdata->h_addr_ptrs[0] = (char *) entdata->host_addr;
   entdata->h_addr_ptrs[1] = NULL;
   result->h_addr_list = entdata->h_addr_ptrs;

   STRING_FIELD (result->h_name, isspace, 1);
 })



#define HOST_DB_LOOKUP(name, keysize, keypattern, break_if_match, proto...) \
enum nss_status                                                   \
_nss_files_get##name##_r (proto,                                  \
                    struct STRUCTURE *result, char *buffer,       \
                    size_t buflen, int *errnop H_ERRNO_PROTO)           \
{                                                           \
  uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct hostent_data);    \
  buffer += pad;                                            \
  buflen = buflen > pad ? buflen - pad : 0;                             \
                                                            \
  __libc_lock_lock (lock);                                        \
                                                            \
  /* Reset file pointer to beginning or open file.  */                        \
  enum nss_status status = internal_setent (keep_stream);               \
                                                            \
  if (status == NSS_STATUS_SUCCESS)                               \
    {                                                       \
      /* Tell getent function that we have repositioned the file pointer.  */ \
      last_use = getby;                                           \
                                                            \
      while ((status = internal_getent (result, buffer, buflen, errnop        \
                              H_ERRNO_ARG EXTRA_ARGS_VALUE))            \
           == NSS_STATUS_SUCCESS)                                 \
      { break_if_match }                                          \
                                                            \
      if (status == NSS_STATUS_SUCCESS                                  \
        && _res_hconf.flags & HCONF_FLAG_MULTI)                   \
      {                                                     \
        /* We have to get all host entries from the file.  */                 \
        const size_t tmp_buflen = MIN (buflen, 4096);                   \
        char tmp_buffer[tmp_buflen]                               \
          __attribute__ ((__aligned__ (__alignof__ (struct hostent_data))));\
        struct hostent tmp_result_buf;                            \
        int naddrs = 1;                                     \
        int naliases = 0;                                         \
        char *bufferend;                                          \
                                                            \
        while (result->h_aliases[naliases] != NULL)                     \
          ++naliases;                                             \
                                                            \
        bufferend = (char *) &result->h_aliases[naliases + 1];          \
                                                            \
        while ((status = internal_getent (&tmp_result_buf, tmp_buffer,      \
                                  tmp_buflen, errnop H_ERRNO_ARG    \
                                  EXTRA_ARGS_VALUE))              \
             == NSS_STATUS_SUCCESS)                               \
          {                                                 \
            int matches = 1;                                      \
            struct hostent *old_result = result;                        \
            result = &tmp_result_buf;                                   \
            /* The following piece is a bit clumsy but we want to use the   \
             `break_if_match' value.  The optimizer should do its       \
             job.  */                                       \
            do                                              \
            {                                               \
              break_if_match                                \
              result = old_result;                                \
            }                                               \
            while ((matches = 0));                                \
                                                            \
            if (matches)                                          \
            {                                               \
              /* We could be very clever and try to recycle a few bytes   \
                 in the buffer instead of generating new arrays.  But     \
                 we are not doing this here since it's more work than     \
                 it's worth.  Simply let the user provide a bit bigger    \
                 buffer.  */                                \
              char **new_h_addr_list;                             \
              char **new_h_aliases;                               \
              int newaliases = 0;                                 \
              size_t newstrlen = 0;                               \
              int cnt;                                      \
                                                            \
              /* Count the new aliases and the length of the strings.  */ \
              while (tmp_result_buf.h_aliases[newaliases] != NULL)            \
                {                                           \
                  char *cp = tmp_result_buf.h_aliases[newaliases];            \
                  ++newaliases;                                   \
                  newstrlen += strlen (cp) + 1;                   \
                }                                           \
              /* If the real name is different add it also to the       \
                 aliases.  This means that there is a duplication       \
                 in the alias list but this is really the users         \
                 problem.  */                               \
              if (strcmp (old_result->h_name,                     \
                        tmp_result_buf.h_name) != 0)              \
                {                                           \
                  ++newaliases;                                   \
                  newstrlen += strlen (tmp_result_buf.h_name) + 1;            \
                }                                           \
                                                            \
              /* Make sure bufferend is aligned.  */              \
              assert ((bufferend - (char *) 0) % sizeof (char *) == 0);   \
                                                            \
              /* Now we can check whether the buffer is large enough.     \
                 16 is the maximal size of the IP address.  */          \
              if (bufferend + 16 + (naddrs + 2) * sizeof (char *)       \
                  + roundup (newstrlen, sizeof (char *))                \
                  + (naliases + newaliases + 1) * sizeof (char *)       \
                  >= buffer + buflen)                             \
                {                                           \
                  *errnop = ERANGE;                               \
                  *herrnop = NETDB_INTERNAL;                      \
                  status = NSS_STATUS_TRYAGAIN;                   \
                  break;                                          \
                }                                           \
                                                            \
              new_h_addr_list =                                   \
                (char **) (bufferend                              \
                         + roundup (newstrlen, sizeof (char *))         \
                         + 16);                                   \
              new_h_aliases =                               \
                (char **) ((char *) new_h_addr_list                     \
                         + (naddrs + 2) * sizeof (char *));       \
                                                            \
              /* Copy the old data in the new arrays.  */               \
              for (cnt = 0; cnt < naddrs; ++cnt)                        \
                new_h_addr_list[cnt] = old_result->h_addr_list[cnt];      \
                                                            \
              for (cnt = 0; cnt < naliases; ++cnt)                      \
                new_h_aliases[cnt] = old_result->h_aliases[cnt];        \
                                                            \
              /* Store the new strings.  */                             \
              cnt = 0;                                      \
              while (tmp_result_buf.h_aliases[cnt] != NULL)             \
                {                                           \
                  new_h_aliases[naliases++] = bufferend;                \
                  bufferend = (__stpcpy (bufferend,                     \
                                   tmp_result_buf.h_aliases[cnt])   \
                           + 1);                            \
                  ++cnt;                                          \
                }                                           \
                                                            \
              if (cnt < newaliases)                               \
                {                                           \
                  new_h_aliases[naliases++] = bufferend;                \
                  bufferend = __stpcpy (bufferend,                      \
                                  tmp_result_buf.h_name) + 1;           \
                }                                           \
                                                            \
              /* Final NULL pointer.  */                          \
              new_h_aliases[naliases] = NULL;                     \
                                                            \
              /* Round up the buffer end address.  */             \
              bufferend += (sizeof (char *)                             \
                        - ((bufferend - (char *) 0)               \
                           % sizeof (char *))) % sizeof (char *);     \
                                                            \
              /* Now the new address.  */                         \
              new_h_addr_list[naddrs++] =                         \
                memcpy (bufferend, tmp_result_buf.h_addr,               \
                      tmp_result_buf.h_length);                   \
                                                            \
              /* Also here a final NULL pointer.  */              \
              new_h_addr_list[naddrs] = NULL;                     \
                                                            \
              /* Store the new array pointers.  */                      \
              old_result->h_aliases = new_h_aliases;              \
              old_result->h_addr_list = new_h_addr_list;                \
                                                            \
              /* Compute the new buffer end.  */                        \
              bufferend = (char *) &new_h_aliases[naliases + 1];        \
              assert (bufferend <= buffer + buflen);              \
                                                            \
              result = old_result;                                \
            }                                               \
          }                                                 \
                                                            \
        if (status != NSS_STATUS_TRYAGAIN)                              \
          status = NSS_STATUS_SUCCESS;                            \
      }                                                     \
                                                            \
                                                            \
      if (! keep_stream)                                          \
      internal_endent ();                                         \
    }                                                       \
                                                            \
  __libc_lock_unlock (lock);                                      \
                                                            \
  return status;                                            \
}


#define EXTRA_ARGS_VALUE \
  , ((_res.options & RES_USE_INET6) ? AF_INET6 : AF_INET),              \
  ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0)
#include "files-XXX.c"
HOST_DB_LOOKUP (hostbyname, ,,
            {
              LOOKUP_NAME_CASE (h_name, h_aliases)
            }, const char *name)
#undef EXTRA_ARGS_VALUE


/* XXX Is using _res to determine whether we want to convert IPv4 addresses
   to IPv6 addresses really the right thing to do?  */
#define EXTRA_ARGS_VALUE \
  , af, ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0)
HOST_DB_LOOKUP (hostbyname2, ,,
            {
              LOOKUP_NAME_CASE (h_name, h_aliases)
            }, const char *name, int af)
#undef EXTRA_ARGS_VALUE


/* We only need to consider IPv4 mapped addresses if the input to the
   gethostbyaddr() function is an IPv6 address.  */
#define EXTRA_ARGS_VALUE \
  , af, (len == IN6ADDRSZ ? AI_V4MAPPED : 0)
DB_LOOKUP (hostbyaddr, ,,
         {
           if (result->h_length == (int) len
             && ! memcmp (addr, result->h_addr_list[0], len))
             break;
         }, const void *addr, socklen_t len, int af)
#undef EXTRA_ARGS_VALUE


enum nss_status
_nss_files_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
                       char *buffer, size_t buflen, int *errnop,
                       int *herrnop, int32_t *ttlp)
{
  __libc_lock_lock (lock);

  /* Reset file pointer to beginning or open file.  */
  enum nss_status status = internal_setent (keep_stream);

  if (status == NSS_STATUS_SUCCESS)
    {
      /* Tell getent function that we have repositioned the file pointer.  */
      last_use = getby;

      bool any = false;
      bool got_canon = false;
      while (1)
      {
        /* Align the buffer for the next record.  */
        uintptr_t pad = (-(uintptr_t) buffer
                     % __alignof__ (struct hostent_data));
        buffer += pad;
        buflen = buflen > pad ? buflen - pad : 0;

        struct hostent result;
        status = internal_getent (&result, buffer, buflen, errnop
                            H_ERRNO_ARG, AF_UNSPEC, 0);
        if (status != NSS_STATUS_SUCCESS)
          break;

        int naliases = 0;
        if (__strcasecmp (name, result.h_name) != 0)
          {
            for (; result.h_aliases[naliases] != NULL; ++naliases)
            if (! __strcasecmp (name, result.h_aliases[naliases]))
              break;
            if (result.h_aliases[naliases] == NULL)
            continue;

            /* We know this alias exist.  Count it.  */
            ++naliases;
          }

        /* Determine how much memory has been used so far.  */
        // XXX It is not necessary to preserve the aliases array
        while (result.h_aliases[naliases] != NULL)
          ++naliases;
        char *bufferend = (char *) &result.h_aliases[naliases + 1];
        assert (buflen >= bufferend - buffer);
        buflen -= bufferend - buffer;
        buffer = bufferend;

        /* We found something.  */
        any = true;

        /* Create the record the caller expects.  There is only one
           address.  */
        assert (result.h_addr_list[1] == NULL);
        if (*pat == NULL)
          {
            uintptr_t pad = (-(uintptr_t) buffer
                         % __alignof__ (struct gaih_addrtuple));
            buffer += pad;
            buflen = buflen > pad ? buflen - pad : 0;

            if (__builtin_expect (buflen < sizeof (struct gaih_addrtuple),
                            0))
            {
              *errnop = ERANGE;
              *herrnop = NETDB_INTERNAL;
              status = NSS_STATUS_TRYAGAIN;
              break;
            }

            *pat = (struct gaih_addrtuple *) buffer;
            buffer += sizeof (struct gaih_addrtuple);
            buflen -= sizeof (struct gaih_addrtuple);
          }

        (*pat)->next = NULL;
        (*pat)->name = got_canon ? NULL : result.h_name;
        got_canon = true;
        (*pat)->family = result.h_addrtype;
        memcpy ((*pat)->addr, result.h_addr_list[0], result.h_length);
        (*pat)->scopeid = 0;

        pat = &((*pat)->next);

        /* If we only look for the first matching entry we are done.  */
        if ((_res_hconf.flags & HCONF_FLAG_MULTI) == 0)
          break;
      }

      /* If we have to look for multiple records and found one, this
       is a success.  */
      if (status == NSS_STATUS_NOTFOUND && any)
      {
        assert ((_res_hconf.flags & HCONF_FLAG_MULTI) != 0);
        status = NSS_STATUS_SUCCESS;
      }

      if (! keep_stream)
      internal_endent ();
    }

  __libc_lock_unlock (lock);

  return status;
}

Generated by  Doxygen 1.6.0   Back to index