Back to index

glibc  2.9
nisplus-hosts.c
Go to the documentation of this file.
00001 /* Copyright (C) 1997-2003, 2005, 2006, 2008 Free Software Foundation, Inc.
00002    This file is part of the GNU C Library.
00003    Contributed by Thorsten Kukuk <kukuk@suse.de>, 1997.
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 <atomic.h>
00022 #include <ctype.h>
00023 #include <errno.h>
00024 #include <netdb.h>
00025 #include <nss.h>
00026 #include <string.h>
00027 #include <arpa/inet.h>
00028 #include <netinet/in.h>
00029 #include <rpcsvc/nis.h>
00030 #include <bits/libc-lock.h>
00031 
00032 #include "nss-nisplus.h"
00033 
00034 __libc_lock_define_initialized (static, lock)
00035 
00036 static nis_result *result;
00037 static nis_name tablename_val;
00038 static u_long tablename_len;
00039 
00040 #define NISENTRYVAL(idx, col, res) \
00041         (NIS_RES_OBJECT (res)[idx].EN_data.en_cols.en_cols_val[col].ec_value.ec_value_val)
00042 
00043 #define NISENTRYLEN(idx, col, res) \
00044         (NIS_RES_OBJECT (res)[idx].EN_data.en_cols.en_cols_val[col].ec_value.ec_value_len)
00045 
00046 /* Get implementation for some internal functions. */
00047 #include <resolv/mapv4v6addr.h>
00048 
00049 
00050 static int
00051 _nss_nisplus_parse_hostent (nis_result *result, int af, struct hostent *host,
00052                          char *buffer, size_t buflen, int *errnop,
00053                          int flags)
00054 {
00055   unsigned int i;
00056   char *first_unused = buffer;
00057   size_t room_left = buflen;
00058 
00059   if (result == NULL)
00060     return 0;
00061 
00062   if ((result->status != NIS_SUCCESS && result->status != NIS_S_SUCCESS)
00063       || __type_of (NIS_RES_OBJECT (result)) != NIS_ENTRY_OBJ
00064       || strcmp (NIS_RES_OBJECT (result)[0].EN_data.en_type, "hosts_tbl") != 0
00065       || NIS_RES_OBJECT (result)[0].EN_data.en_cols.en_cols_len < 4)
00066     return 0;
00067 
00068   char *data = first_unused;
00069 
00070   if (room_left < (af != AF_INET || (flags & AI_V4MAPPED) != 0
00071                  ? IN6ADDRSZ : INADDRSZ))
00072     {
00073     no_more_room:
00074       *errnop = ERANGE;
00075       return -1;
00076     }
00077 
00078   /* Parse address.  */
00079   if (af != AF_INET6
00080       && inet_pton (AF_INET, NISENTRYVAL (0, 2, result), data) > 0)
00081     {
00082       assert ((flags & AI_V4MAPPED) == 0 || af != AF_UNSPEC);
00083       if (flags & AI_V4MAPPED)
00084        {
00085          map_v4v6_address (data, data);
00086          host->h_addrtype = AF_INET6;
00087          host->h_length = IN6ADDRSZ;
00088        }
00089       else
00090        {
00091          host->h_addrtype = AF_INET;
00092          host->h_length = INADDRSZ;
00093        }
00094     }
00095   else if (af != AF_INET
00096           && inet_pton (AF_INET6, NISENTRYVAL (0, 2, result), data) > 0)
00097     {
00098       host->h_addrtype = AF_INET6;
00099       host->h_length = IN6ADDRSZ;
00100     }
00101   else
00102     /* Illegal address: ignore line.  */
00103     return 0;
00104 
00105   first_unused += host->h_length;
00106   room_left -= host->h_length;
00107 
00108   if (NISENTRYLEN (0, 0, result) + 1 > room_left)
00109     goto no_more_room;
00110 
00111   host->h_name = first_unused;
00112   first_unused = __stpncpy (first_unused, NISENTRYVAL (0, 0, result),
00113                          NISENTRYLEN (0, 0, result));
00114   *first_unused++ = '\0';
00115 
00116   room_left -= NISENTRYLEN (0, 0, result) + 1;
00117   char *line = first_unused;
00118 
00119   /* When this is a call to gethostbyname4_r we do not need the aliases.  */
00120   if (af != AF_UNSPEC)
00121     {
00122       /* XXX Rewrite at some point to allocate the array first and then
00123         copy the strings.  It is wasteful to first concatenate the strings
00124         to just split them again later.  */
00125       for (i = 0; i < NIS_RES_NUMOBJ (result); ++i)
00126        {
00127          if (strcmp (NISENTRYVAL (i, 1, result), host->h_name) != 0)
00128            {
00129              if (NISENTRYLEN (i, 1, result) + 2 > room_left)
00130               goto no_more_room;
00131 
00132              *first_unused++ = ' ';
00133              first_unused = __stpncpy (first_unused,
00134                                    NISENTRYVAL (i, 1, result),
00135                                    NISENTRYLEN (i, 1, result));
00136              *first_unused = '\0';
00137              room_left -= NISENTRYLEN (i, 1, result) + 1;
00138            }
00139        }
00140       *first_unused++ = '\0';
00141     }
00142 
00143   /* Adjust the pointer so it is aligned for
00144      storing pointers.  */
00145   size_t adjust = ((__alignof__ (char *)
00146                   - (first_unused - (char *) 0) % __alignof__ (char *))
00147                  % __alignof__ (char *));
00148   if (room_left < adjust + 3 * sizeof (char *))
00149     goto no_more_room;
00150   first_unused += adjust;
00151   room_left -= adjust;
00152   host->h_addr_list = (char **) first_unused;
00153 
00154   room_left -= 3 * sizeof (char *);
00155   host->h_addr_list[0] = data;
00156   host->h_addr_list[1] = NULL;
00157   host->h_aliases = &host->h_addr_list[2];
00158 
00159   /* When this is a call to gethostbyname4_r we do not need the aliases.  */
00160   if (af != AF_UNSPEC)
00161     {
00162       i = 0;
00163       while (*line != '\0')
00164        {
00165          /* Skip leading blanks.  */
00166          while (isspace (*line))
00167            ++line;
00168 
00169          if (*line == '\0')
00170            break;
00171 
00172          if (room_left < sizeof (char *))
00173            goto no_more_room;
00174 
00175          room_left -= sizeof (char *);
00176          host->h_aliases[i++] = line;
00177 
00178          while (*line != '\0' && *line != ' ')
00179            ++line;
00180 
00181          if (*line == ' ')
00182            *line++ = '\0';
00183        }
00184 
00185       host->h_aliases[i] = NULL;
00186     }
00187 
00188   return 1;
00189 }
00190 
00191 
00192 static enum nss_status
00193 _nss_create_tablename (int *errnop)
00194 {
00195   if (tablename_val == NULL)
00196     {
00197       const char *local_dir = nis_local_directory ();
00198       size_t local_dir_len = strlen (local_dir);
00199       static const char prefix[] = "hosts.org_dir.";
00200 
00201       char *p = malloc (sizeof (prefix) + local_dir_len);
00202       if (p == NULL)
00203        {
00204          *errnop = errno;
00205          return NSS_STATUS_TRYAGAIN;
00206        }
00207 
00208       memcpy (__stpcpy (p, prefix), local_dir, local_dir_len + 1);
00209 
00210       tablename_len = sizeof (prefix) - 1 + local_dir_len;
00211 
00212       atomic_write_barrier ();
00213 
00214       tablename_val = p;
00215     }
00216 
00217   return NSS_STATUS_SUCCESS;
00218 }
00219 
00220 
00221 enum nss_status
00222 _nss_nisplus_sethostent (int stayopen)
00223 {
00224   enum nss_status status = NSS_STATUS_SUCCESS;
00225   int err;
00226 
00227   __libc_lock_lock (lock);
00228 
00229   if (result != NULL)
00230     {
00231       nis_freeresult (result);
00232       result = NULL;
00233     }
00234 
00235   if (tablename_val == NULL)
00236     status = _nss_create_tablename (&err);
00237 
00238   __libc_lock_unlock (lock);
00239 
00240   return status;
00241 }
00242 
00243 
00244 enum nss_status
00245 _nss_nisplus_endhostent (void)
00246 {
00247   __libc_lock_lock (lock);
00248 
00249   if (result != NULL)
00250     {
00251       nis_freeresult (result);
00252       result = NULL;
00253     }
00254 
00255   __libc_lock_unlock (lock);
00256 
00257   return NSS_STATUS_SUCCESS;
00258 }
00259 
00260 
00261 static enum nss_status
00262 internal_nisplus_gethostent_r (struct hostent *host, char *buffer,
00263                             size_t buflen, int *errnop, int *herrnop)
00264 {
00265   int parse_res;
00266 
00267   /* Get the next entry until we found a correct one. */
00268   do
00269     {
00270       nis_result *saved_res;
00271 
00272       if (result == NULL)
00273        {
00274          saved_res = NULL;
00275          if (tablename_val == NULL)
00276            {
00277              enum nss_status status = _nss_create_tablename (errnop);
00278 
00279              if (status != NSS_STATUS_SUCCESS)
00280               return status;
00281            }
00282 
00283          result = nis_first_entry (tablename_val);
00284          if (result == NULL)
00285            {
00286              *errnop = errno;
00287              return NSS_STATUS_TRYAGAIN;
00288            }
00289          if (niserr2nss (result->status) != NSS_STATUS_SUCCESS)
00290             {
00291               enum nss_status retval = niserr2nss (result->status);
00292               if (retval == NSS_STATUS_TRYAGAIN)
00293                 {
00294                   *herrnop = NETDB_INTERNAL;
00295                   *errnop = errno;
00296                 }
00297               return retval;
00298             }
00299 
00300        }
00301       else
00302        {
00303          saved_res = result;
00304          result = nis_next_entry (tablename_val, &result->cookie);
00305          if (result == NULL)
00306            {
00307              *errnop = errno;
00308              return NSS_STATUS_TRYAGAIN;
00309            }
00310          if (niserr2nss (result->status) != NSS_STATUS_SUCCESS)
00311             {
00312               enum nss_status retval= niserr2nss (result->status);
00313 
00314              nis_freeresult (result);
00315              result = saved_res;
00316               if (retval == NSS_STATUS_TRYAGAIN)
00317                 {
00318                   *herrnop = NETDB_INTERNAL;
00319                 *errnop = errno;
00320                 }
00321               return retval;
00322             }
00323        }
00324 
00325       if (_res.options & RES_USE_INET6)
00326        parse_res = _nss_nisplus_parse_hostent (result, AF_INET6, host, buffer,
00327                                           buflen, errnop, AI_V4MAPPED);
00328       else
00329        parse_res = _nss_nisplus_parse_hostent (result, AF_INET, host, buffer,
00330                                           buflen, errnop, 0);
00331 
00332       if (parse_res == -1)
00333         {
00334          nis_freeresult (result);
00335          result = saved_res;
00336           *herrnop = NETDB_INTERNAL;
00337          *errnop = ERANGE;
00338           return NSS_STATUS_TRYAGAIN;
00339         }
00340       if (saved_res != NULL)
00341        nis_freeresult (saved_res);
00342 
00343     } while (!parse_res);
00344 
00345   return NSS_STATUS_SUCCESS;
00346 }
00347 
00348 
00349 enum nss_status
00350 _nss_nisplus_gethostent_r (struct hostent *result, char *buffer,
00351                         size_t buflen, int *errnop, int *herrnop)
00352 {
00353   int status;
00354 
00355   __libc_lock_lock (lock);
00356 
00357   status = internal_nisplus_gethostent_r (result, buffer, buflen, errnop,
00358                                      herrnop);
00359 
00360   __libc_lock_unlock (lock);
00361 
00362   return status;
00363 }
00364 
00365 
00366 static enum nss_status
00367 get_tablename (int *herrnop)
00368 {
00369   __libc_lock_lock (lock);
00370 
00371   enum nss_status status = _nss_create_tablename (herrnop);
00372 
00373   __libc_lock_unlock (lock);
00374 
00375   if (status != NSS_STATUS_SUCCESS)
00376     *herrnop = NETDB_INTERNAL;
00377 
00378   return status;
00379 }
00380 
00381 
00382 static enum nss_status
00383 internal_gethostbyname2_r (const char *name, int af, struct hostent *host,
00384                         char *buffer, size_t buflen, int *errnop,
00385                         int *herrnop, int flags)
00386 {
00387   if (tablename_val == NULL)
00388     {
00389       enum nss_status status = get_tablename (herrnop);
00390       if (status != NSS_STATUS_SUCCESS)
00391        return status;
00392     }
00393 
00394   if (name == NULL)
00395     {
00396       *errnop = EINVAL;
00397       *herrnop = NETDB_INTERNAL;
00398       return NSS_STATUS_NOTFOUND;
00399     }
00400 
00401   char buf[strlen (name) + 10 + tablename_len];
00402   int olderr = errno;
00403 
00404   /* Search at first in the alias list, and use the correct name
00405      for the next search.  */
00406   snprintf (buf, sizeof (buf), "[name=%s],%s", name, tablename_val);
00407   nis_result *result = nis_list (buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
00408 
00409   if (result != NULL)
00410     {
00411       /* If we did not find it, try it as original name. But if the
00412         database is correct, we should find it in the first case, too.  */
00413       char *bufptr = buf;
00414       size_t buflen = sizeof (buf);
00415 
00416       if ((result->status == NIS_SUCCESS || result->status == NIS_S_SUCCESS)
00417          && __type_of (result->objects.objects_val) == NIS_ENTRY_OBJ
00418          && strcmp (result->objects.objects_val->EN_data.en_type,
00419                    "hosts_tbl") == 0
00420          && result->objects.objects_val->EN_data.en_cols.en_cols_len >= 3)
00421        {
00422          /* We need to allocate a new buffer since there is no
00423             guarantee the returned alias name has a length limit.  */
00424          name = NISENTRYVAL(0, 0, result);
00425          size_t buflen = strlen (name) + 10 + tablename_len;
00426          bufptr = alloca (buflen);
00427        }
00428 
00429       snprintf (bufptr, buflen, "[cname=%s],%s", name, tablename_val);
00430 
00431       nis_freeresult (result);
00432       result = nis_list (bufptr, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
00433     }
00434 
00435   if (result == NULL)
00436     {
00437       *errnop = ENOMEM;
00438       return NSS_STATUS_TRYAGAIN;
00439     }
00440 
00441   int retval = niserr2nss (result->status);
00442   if (__builtin_expect (retval != NSS_STATUS_SUCCESS, 0))
00443     {
00444       if (retval == NSS_STATUS_TRYAGAIN)
00445        {
00446          *errnop = errno;
00447          *herrnop = NETDB_INTERNAL;
00448        }
00449       else
00450        __set_errno (olderr);
00451       nis_freeresult (result);
00452       return retval;
00453     }
00454 
00455   int parse_res = _nss_nisplus_parse_hostent (result, af, host, buffer,
00456                                          buflen, errnop, flags);
00457 
00458   nis_freeresult (result);
00459 
00460   if (parse_res > 0)
00461     return NSS_STATUS_SUCCESS;
00462 
00463   *herrnop = NETDB_INTERNAL;
00464   if (parse_res == -1)
00465     {
00466       *errnop = ERANGE;
00467       return NSS_STATUS_TRYAGAIN;
00468     }
00469 
00470   __set_errno (olderr);
00471   return NSS_STATUS_NOTFOUND;
00472 }
00473 
00474 
00475 enum nss_status
00476 _nss_nisplus_gethostbyname2_r (const char *name, int af, struct hostent *host,
00477                             char *buffer, size_t buflen, int *errnop,
00478                             int *herrnop)
00479 {
00480   if (af != AF_INET && af != AF_INET6)
00481     {
00482       *herrnop = HOST_NOT_FOUND;
00483       return NSS_STATUS_NOTFOUND;
00484     }
00485 
00486   return internal_gethostbyname2_r (name, af, host, buffer, buflen, errnop,
00487                                 herrnop,
00488                       ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0));
00489 }
00490 
00491 
00492 enum nss_status
00493 _nss_nisplus_gethostbyname_r (const char *name, struct hostent *host,
00494                            char *buffer, size_t buflen, int *errnop,
00495                            int *h_errnop)
00496 {
00497   if (_res.options & RES_USE_INET6)
00498     {
00499       enum nss_status status;
00500 
00501       status = internal_gethostbyname2_r (name, AF_INET6, host, buffer,
00502                                      buflen, errnop, h_errnop,
00503                                      AI_V4MAPPED);
00504       if (status == NSS_STATUS_SUCCESS)
00505         return status;
00506     }
00507 
00508   return internal_gethostbyname2_r (name, AF_INET, host, buffer,
00509                                buflen, errnop, h_errnop, 0);
00510 }
00511 
00512 
00513 enum nss_status
00514 _nss_nisplus_gethostbyaddr_r (const void *addr, socklen_t addrlen, int af,
00515                            struct hostent *host, char *buffer,
00516                            size_t buflen, int *errnop, int *herrnop)
00517 {
00518   if (tablename_val == NULL)
00519     {
00520       enum nss_status status = get_tablename (herrnop);
00521       if (status != NSS_STATUS_SUCCESS)
00522        return status;
00523     }
00524 
00525   if (addr == NULL)
00526     return NSS_STATUS_NOTFOUND;
00527 
00528   char buf[24 + tablename_len];
00529   int retval, parse_res;
00530   int olderr = errno;
00531 
00532   snprintf (buf, sizeof (buf), "[addr=%s],%s",
00533           inet_ntoa (*(const struct in_addr *) addr), tablename_val);
00534   nis_result *result = nis_list (buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
00535 
00536   if (result == NULL)
00537     {
00538       __set_errno (ENOMEM);
00539       return NSS_STATUS_TRYAGAIN;
00540     }
00541 
00542   retval = niserr2nss (result->status);
00543   if (__builtin_expect (retval != NSS_STATUS_SUCCESS, 0))
00544     {
00545       if (retval == NSS_STATUS_TRYAGAIN)
00546        {
00547          *errnop = errno;
00548          *herrnop = NETDB_INTERNAL;
00549        }
00550       else
00551        __set_errno (olderr);
00552       nis_freeresult (result);
00553       return retval;
00554     }
00555 
00556   parse_res = _nss_nisplus_parse_hostent (result, af, host,
00557                                      buffer, buflen, errnop,
00558                                      ((_res.options & RES_USE_INET6)
00559                                       ? AI_V4MAPPED : 0));
00560   nis_freeresult (result);
00561 
00562   if (parse_res > 0)
00563     return NSS_STATUS_SUCCESS;
00564 
00565   *herrnop = NETDB_INTERNAL;
00566   if (parse_res == -1)
00567     {
00568       *errnop = ERANGE;
00569       return NSS_STATUS_TRYAGAIN;
00570     }
00571 
00572   __set_errno (olderr);
00573   return NSS_STATUS_NOTFOUND;
00574 }
00575 
00576 
00577 enum nss_status
00578 _nss_nisplus_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
00579                             char *buffer, size_t buflen, int *errnop,
00580                             int *herrnop, int32_t *ttlp)
00581 {
00582   struct hostent host;
00583 
00584   enum nss_status status = internal_gethostbyname2_r (name, AF_UNSPEC, &host,
00585                                                 buffer, buflen,
00586                                                 errnop, herrnop, 0);
00587   if (__builtin_expect (status == NSS_STATUS_SUCCESS, 1))
00588     {
00589       if (*pat == NULL)
00590        {
00591          uintptr_t pad = (-(uintptr_t) buffer
00592                         % __alignof__ (struct gaih_addrtuple));
00593          buffer += pad;
00594          buflen = buflen > pad ? buflen - pad : 0;
00595 
00596          if (__builtin_expect (buflen < sizeof (struct gaih_addrtuple), 0))
00597            {
00598              free (result);
00599              *errnop = ERANGE;
00600              *herrnop = NETDB_INTERNAL;
00601              return NSS_STATUS_TRYAGAIN;
00602            }
00603        }
00604 
00605       (*pat)->next = NULL;
00606       (*pat)->name = host.h_name;
00607       (*pat)->family = host.h_addrtype;
00608 
00609       memcpy ((*pat)->addr, host.h_addr_list[0], host.h_length);
00610       (*pat)->scopeid = 0;
00611       assert (host.h_addr_list[1] == NULL);
00612     }
00613 
00614   return status;
00615 }