Back to index

glibc  2.9
files-hosts.c
Go to the documentation of this file.
00001 /* Hosts file parser in nss_files module.
00002    Copyright (C) 1996-2001, 2003-2007, 2008 Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
00004 
00005    The GNU C Library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Lesser General Public
00007    License as published by the Free Software Foundation; either
00008    version 2.1 of the License, or (at your option) any later version.
00009 
00010    The GNU C Library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Lesser General Public License for more details.
00014 
00015    You should have received a copy of the GNU Lesser General Public
00016    License along with the GNU C Library; if not, write to the Free
00017    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00018    02111-1307 USA.  */
00019 
00020 #include <assert.h>
00021 #include <netinet/in.h>
00022 #include <arpa/inet.h>
00023 #include <arpa/nameser.h>
00024 #include <netdb.h>
00025 #include <resolv.h>
00026 
00027 
00028 /* Get implementation for some internal functions.  */
00029 #include "../resolv/mapv4v6addr.h"
00030 #include "../resolv/res_hconf.h"
00031 
00032 
00033 #define ENTNAME             hostent
00034 #define DATABASE     "hosts"
00035 #define NEED_H_ERRNO
00036 
00037 #define EXTRA_ARGS    , af, flags
00038 #define EXTRA_ARGS_DECL      , int af, int flags
00039 
00040 #define ENTDATA hostent_data
00041 struct hostent_data
00042   {
00043     unsigned char host_addr[16]; /* IPv4 or IPv6 address.  */
00044     char *h_addr_ptrs[2];   /* Points to that and null terminator.  */
00045   };
00046 
00047 #define TRAILING_LIST_MEMBER              h_aliases
00048 #define TRAILING_LIST_SEPARATOR_P  isspace
00049 #include "files-parse.c"
00050 LINE_PARSER
00051 ("#",
00052  {
00053    char *addr;
00054 
00055    STRING_FIELD (addr, isspace, 1);
00056 
00057    /* Parse address.  */
00058    if (inet_pton (af == AF_UNSPEC ? AF_INET : af, addr, entdata->host_addr)
00059        > 0)
00060      af = af == AF_UNSPEC ? AF_INET : af;
00061    else
00062      {
00063        if (af == AF_INET6 && (flags & AI_V4MAPPED) != 0
00064           && inet_pton (AF_INET, addr, entdata->host_addr) > 0)
00065         map_v4v6_address ((char *) entdata->host_addr,
00066                         (char *) entdata->host_addr);
00067        else if (af == AF_INET
00068               && inet_pton (AF_INET6, addr, entdata->host_addr) > 0)
00069         {
00070           if (IN6_IS_ADDR_V4MAPPED (entdata->host_addr))
00071             memcpy (entdata->host_addr, entdata->host_addr + 12, INADDRSZ);
00072           else if (IN6_IS_ADDR_LOOPBACK (entdata->host_addr))
00073             {
00074               in_addr_t localhost = htonl (INADDR_LOOPBACK);
00075               memcpy (entdata->host_addr, &localhost, sizeof (localhost));
00076             }
00077           else
00078             /* Illegal address: ignore line.  */
00079             return 0;
00080         }
00081        else if (af == AF_UNSPEC
00082               && inet_pton (AF_INET6, addr, entdata->host_addr) > 0)
00083         af = AF_INET6;
00084        else
00085         /* Illegal address: ignore line.  */
00086         return 0;
00087      }
00088 
00089    /* We always return entries of the requested form.  */
00090    result->h_addrtype = af;
00091    result->h_length = af == AF_INET ? INADDRSZ : IN6ADDRSZ;
00092 
00093    /* Store a pointer to the address in the expected form.  */
00094    entdata->h_addr_ptrs[0] = (char *) entdata->host_addr;
00095    entdata->h_addr_ptrs[1] = NULL;
00096    result->h_addr_list = entdata->h_addr_ptrs;
00097 
00098    STRING_FIELD (result->h_name, isspace, 1);
00099  })
00100 
00101 
00102 
00103 #define HOST_DB_LOOKUP(name, keysize, keypattern, break_if_match, proto...) \
00104 enum nss_status                                                             \
00105 _nss_files_get##name##_r (proto,                                     \
00106                        struct STRUCTURE *result, char *buffer,       \
00107                        size_t buflen, int *errnop H_ERRNO_PROTO)            \
00108 {                                                                    \
00109   uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct hostent_data);    \
00110   buffer += pad;                                                     \
00111   buflen = buflen > pad ? buflen - pad : 0;                                 \
00112 									      \
00113   __libc_lock_lock (lock);                                           \
00114                                                                      \
00115   /* Reset file pointer to beginning or open file.  */                      \
00116   enum nss_status status = internal_setent (keep_stream);                   \
00117                                                                      \
00118   if (status == NSS_STATUS_SUCCESS)                                         \
00119     {                                                                \
00120       /* Tell getent function that we have repositioned the file pointer.  */ \
00121       last_use = getby;                                                     \
00122                                                                      \
00123       while ((status = internal_getent (result, buffer, buflen, errnop             \
00124                                    H_ERRNO_ARG EXTRA_ARGS_VALUE))           \
00125             == NSS_STATUS_SUCCESS)                                   \
00126        { break_if_match }                                            \
00127                                                                      \
00128       if (status == NSS_STATUS_SUCCESS                                      \
00129          && _res_hconf.flags & HCONF_FLAG_MULTI)                     \
00130        {                                                             \
00131          /* We have to get all host entries from the file.  */              \
00132          const size_t tmp_buflen = MIN (buflen, 4096);                      \
00133          char tmp_buffer[tmp_buflen]                                        \
00134 	    __attribute__ ((__aligned__ (__alignof__ (struct hostent_data))));\
00135          struct hostent tmp_result_buf;                              \
00136          int naddrs = 1;                                             \
00137          int naliases = 0;                                           \
00138          char *bufferend;                                            \
00139                                                                      \
00140          while (result->h_aliases[naliases] != NULL)                        \
00141            ++naliases;                                                      \
00142                                                                      \
00143          bufferend = (char *) &result->h_aliases[naliases + 1];             \
00144                                                                      \
00145          while ((status = internal_getent (&tmp_result_buf, tmp_buffer,      \
00146                                        tmp_buflen, errnop H_ERRNO_ARG    \
00147                                        EXTRA_ARGS_VALUE))                   \
00148                == NSS_STATUS_SUCCESS)                                       \
00149            {                                                         \
00150              int matches = 1;                                               \
00151              struct hostent *old_result = result;                           \
00152              result = &tmp_result_buf;                                      \
00153              /* The following piece is a bit clumsy but we want to use the   \
00154                `break_if_match' value.  The optimizer should do its         \
00155                job.  */                                              \
00156              do                                                      \
00157               {                                                      \
00158                 break_if_match                                       \
00159                 result = old_result;                                        \
00160               }                                                      \
00161              while ((matches = 0));                                         \
00162                                                                      \
00163              if (matches)                                            \
00164               {                                                      \
00165                 /* We could be very clever and try to recycle a few bytes   \
00166                    in the buffer instead of generating new arrays.  But     \
00167                    we are not doing this here since it's more work than     \
00168                    it's worth.  Simply let the user provide a bit bigger    \
00169                    buffer.  */                                       \
00170                 char **new_h_addr_list;                              \
00171                 char **new_h_aliases;                                       \
00172                 int newaliases = 0;                                         \
00173                 size_t newstrlen = 0;                                       \
00174                 int cnt;                                             \
00175                                                                      \
00176                 /* Count the new aliases and the length of the strings.  */ \
00177                 while (tmp_result_buf.h_aliases[newaliases] != NULL)        \
00178                   {                                                  \
00179                     char *cp = tmp_result_buf.h_aliases[newaliases];        \
00180                     ++newaliases;                                    \
00181                     newstrlen += strlen (cp) + 1;                           \
00182                   }                                                  \
00183                 /* If the real name is different add it also to the         \
00184                    aliases.  This means that there is a duplication         \
00185                    in the alias list but this is really the users           \
00186                    problem.  */                                      \
00187                 if (strcmp (old_result->h_name,                      \
00188                            tmp_result_buf.h_name) != 0)              \
00189                   {                                                  \
00190                     ++newaliases;                                    \
00191                     newstrlen += strlen (tmp_result_buf.h_name) + 1;        \
00192                   }                                                  \
00193                                                                      \
00194                 /* Make sure bufferend is aligned.  */               \
00195                 assert ((bufferend - (char *) 0) % sizeof (char *) == 0);   \
00196                                                                      \
00197                 /* Now we can check whether the buffer is large enough.     \
00198                    16 is the maximal size of the IP address.  */            \
00199                 if (bufferend + 16 + (naddrs + 2) * sizeof (char *)         \
00200                     + roundup (newstrlen, sizeof (char *))                  \
00201                     + (naliases + newaliases + 1) * sizeof (char *)         \
00202                     >= buffer + buflen)                              \
00203                   {                                                  \
00204                     *errnop = ERANGE;                                       \
00205                     *herrnop = NETDB_INTERNAL;                       \
00206                     status = NSS_STATUS_TRYAGAIN;                           \
00207                     break;                                           \
00208                   }                                                  \
00209                                                                      \
00210                 new_h_addr_list =                                    \
00211                   (char **) (bufferend                               \
00212                             + roundup (newstrlen, sizeof (char *))          \
00213                             + 16);                                   \
00214                 new_h_aliases =                                      \
00215                   (char **) ((char *) new_h_addr_list                       \
00216                             + (naddrs + 2) * sizeof (char *));       \
00217                                                                      \
00218                 /* Copy the old data in the new arrays.  */                 \
00219                 for (cnt = 0; cnt < naddrs; ++cnt)                          \
00220                   new_h_addr_list[cnt] = old_result->h_addr_list[cnt];      \
00221                                                                      \
00222                 for (cnt = 0; cnt < naliases; ++cnt)                        \
00223                   new_h_aliases[cnt] = old_result->h_aliases[cnt];          \
00224                                                                      \
00225                 /* Store the new strings.  */                               \
00226                 cnt = 0;                                             \
00227                 while (tmp_result_buf.h_aliases[cnt] != NULL)               \
00228                   {                                                  \
00229                     new_h_aliases[naliases++] = bufferend;                  \
00230                     bufferend = (__stpcpy (bufferend,                       \
00231                                         tmp_result_buf.h_aliases[cnt])   \
00232                                + 1);                                 \
00233                     ++cnt;                                           \
00234                   }                                                  \
00235                                                                      \
00236                 if (cnt < newaliases)                                       \
00237                   {                                                  \
00238                     new_h_aliases[naliases++] = bufferend;                  \
00239                     bufferend = __stpcpy (bufferend,                        \
00240                                        tmp_result_buf.h_name) + 1;          \
00241                   }                                                  \
00242                                                                      \
00243                 /* Final NULL pointer.  */                                  \
00244                 new_h_aliases[naliases] = NULL;                      \
00245                                                                      \
00246                 /* Round up the buffer end address.  */              \
00247                 bufferend += (sizeof (char *)                               \
00248                             - ((bufferend - (char *) 0)              \
00249                                % sizeof (char *))) % sizeof (char *);     \
00250                                                                      \
00251                 /* Now the new address.  */                                 \
00252                 new_h_addr_list[naddrs++] =                                 \
00253                   memcpy (bufferend, tmp_result_buf.h_addr,                 \
00254                          tmp_result_buf.h_length);                          \
00255                                                                      \
00256                 /* Also here a final NULL pointer.  */               \
00257                 new_h_addr_list[naddrs] = NULL;                      \
00258                                                                      \
00259                 /* Store the new array pointers.  */                        \
00260                 old_result->h_aliases = new_h_aliases;               \
00261                 old_result->h_addr_list = new_h_addr_list;                  \
00262                                                                      \
00263                 /* Compute the new buffer end.  */                          \
00264                 bufferend = (char *) &new_h_aliases[naliases + 1];          \
00265                 assert (bufferend <= buffer + buflen);               \
00266                                                                      \
00267                 result = old_result;                                        \
00268               }                                                      \
00269            }                                                         \
00270                                                                      \
00271          if (status != NSS_STATUS_TRYAGAIN)                                 \
00272            status = NSS_STATUS_SUCCESS;                              \
00273        }                                                             \
00274                                                                      \
00275                                                                      \
00276       if (! keep_stream)                                             \
00277        internal_endent ();                                           \
00278     }                                                                \
00279 									      \
00280   __libc_lock_unlock (lock);                                                \
00281                                                                      \
00282   return status;                                                     \
00283 }
00284 
00285 
00286 #define EXTRA_ARGS_VALUE \
00287   , ((_res.options & RES_USE_INET6) ? AF_INET6 : AF_INET),                  \
00288   ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0)
00289 #include "files-XXX.c"
00290 HOST_DB_LOOKUP (hostbyname, ,,
00291               {
00292                 LOOKUP_NAME_CASE (h_name, h_aliases)
00293               }, const char *name)
00294 #undef EXTRA_ARGS_VALUE
00295 
00296 
00297 /* XXX Is using _res to determine whether we want to convert IPv4 addresses
00298    to IPv6 addresses really the right thing to do?  */
00299 #define EXTRA_ARGS_VALUE \
00300   , af, ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0)
00301 HOST_DB_LOOKUP (hostbyname2, ,,
00302               {
00303                 LOOKUP_NAME_CASE (h_name, h_aliases)
00304               }, const char *name, int af)
00305 #undef EXTRA_ARGS_VALUE
00306 
00307 
00308 /* We only need to consider IPv4 mapped addresses if the input to the
00309    gethostbyaddr() function is an IPv6 address.  */
00310 #define EXTRA_ARGS_VALUE \
00311   , af, (len == IN6ADDRSZ ? AI_V4MAPPED : 0)
00312 DB_LOOKUP (hostbyaddr, ,,
00313           {
00314             if (result->h_length == (int) len
00315                && ! memcmp (addr, result->h_addr_list[0], len))
00316               break;
00317           }, const void *addr, socklen_t len, int af)
00318 #undef EXTRA_ARGS_VALUE
00319 
00320 
00321 enum nss_status
00322 _nss_files_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
00323                           char *buffer, size_t buflen, int *errnop,
00324                           int *herrnop, int32_t *ttlp)
00325 {
00326   __libc_lock_lock (lock);
00327 
00328   /* Reset file pointer to beginning or open file.  */
00329   enum nss_status status = internal_setent (keep_stream);
00330 
00331   if (status == NSS_STATUS_SUCCESS)
00332     {
00333       /* Tell getent function that we have repositioned the file pointer.  */
00334       last_use = getby;
00335 
00336       bool any = false;
00337       bool got_canon = false;
00338       while (1)
00339        {
00340          /* Align the buffer for the next record.  */
00341          uintptr_t pad = (-(uintptr_t) buffer
00342                         % __alignof__ (struct hostent_data));
00343          buffer += pad;
00344          buflen = buflen > pad ? buflen - pad : 0;
00345 
00346          struct hostent result;
00347          status = internal_getent (&result, buffer, buflen, errnop
00348                                 H_ERRNO_ARG, AF_UNSPEC, 0);
00349          if (status != NSS_STATUS_SUCCESS)
00350            break;
00351 
00352          int naliases = 0;
00353          if (__strcasecmp (name, result.h_name) != 0)
00354            {
00355              for (; result.h_aliases[naliases] != NULL; ++naliases)
00356               if (! __strcasecmp (name, result.h_aliases[naliases]))
00357                 break;
00358              if (result.h_aliases[naliases] == NULL)
00359               continue;
00360 
00361              /* We know this alias exist.  Count it.  */
00362              ++naliases;
00363            }
00364 
00365          /* Determine how much memory has been used so far.  */
00366          // XXX It is not necessary to preserve the aliases array
00367          while (result.h_aliases[naliases] != NULL)
00368            ++naliases;
00369          char *bufferend = (char *) &result.h_aliases[naliases + 1];
00370          assert (buflen >= bufferend - buffer);
00371          buflen -= bufferend - buffer;
00372          buffer = bufferend;
00373 
00374          /* We found something.  */
00375          any = true;
00376 
00377          /* Create the record the caller expects.  There is only one
00378             address.  */
00379          assert (result.h_addr_list[1] == NULL);
00380          if (*pat == NULL)
00381            {
00382              uintptr_t pad = (-(uintptr_t) buffer
00383                             % __alignof__ (struct gaih_addrtuple));
00384              buffer += pad;
00385              buflen = buflen > pad ? buflen - pad : 0;
00386 
00387              if (__builtin_expect (buflen < sizeof (struct gaih_addrtuple),
00388                                 0))
00389               {
00390                 *errnop = ERANGE;
00391                 *herrnop = NETDB_INTERNAL;
00392                 status = NSS_STATUS_TRYAGAIN;
00393                 break;
00394               }
00395 
00396              *pat = (struct gaih_addrtuple *) buffer;
00397              buffer += sizeof (struct gaih_addrtuple);
00398              buflen -= sizeof (struct gaih_addrtuple);
00399            }
00400 
00401          (*pat)->next = NULL;
00402          (*pat)->name = got_canon ? NULL : result.h_name;
00403          got_canon = true;
00404          (*pat)->family = result.h_addrtype;
00405          memcpy ((*pat)->addr, result.h_addr_list[0], result.h_length);
00406          (*pat)->scopeid = 0;
00407 
00408          pat = &((*pat)->next);
00409 
00410          /* If we only look for the first matching entry we are done.  */
00411          if ((_res_hconf.flags & HCONF_FLAG_MULTI) == 0)
00412            break;
00413        }
00414 
00415       /* If we have to look for multiple records and found one, this
00416         is a success.  */
00417       if (status == NSS_STATUS_NOTFOUND && any)
00418        {
00419          assert ((_res_hconf.flags & HCONF_FLAG_MULTI) != 0);
00420          status = NSS_STATUS_SUCCESS;
00421        }
00422 
00423       if (! keep_stream)
00424        internal_endent ();
00425     }
00426 
00427   __libc_lock_unlock (lock);
00428 
00429   return status;
00430 }