(root)/
glibc-2.38/
nss/
nss_files/
files-hosts.c
       1  /* Hosts file parser in nss_files module.
       2     Copyright (C) 1996-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <assert.h>
      20  #include <netinet/in.h>
      21  #include <arpa/inet.h>
      22  #include <arpa/nameser.h>
      23  #include <netdb.h>
      24  #include <resolv/resolv-internal.h>
      25  #include <scratch_buffer.h>
      26  #include <alloc_buffer.h>
      27  #include <nss.h>
      28  
      29  /* Get implementation for some internal functions.  */
      30  #include "../resolv/mapv4v6addr.h"
      31  #include "../resolv/res_hconf.h"
      32  
      33  
      34  #define ENTNAME		hostent
      35  #define DATABASE	"hosts"
      36  #define NEED_H_ERRNO
      37  
      38  #define EXTRA_ARGS	 , af, flags
      39  #define EXTRA_ARGS_DECL	 , int af, int flags
      40  
      41  #define ENTDATA hostent_data
      42  struct hostent_data
      43    {
      44      unsigned char host_addr[16]; /* IPv4 or IPv6 address.  */
      45      char *h_addr_ptrs[2];	/* Points to that and null terminator.  */
      46    };
      47  
      48  #define TRAILING_LIST_MEMBER		h_aliases
      49  #define TRAILING_LIST_SEPARATOR_P	isspace
      50  #include "files-parse.c"
      51  LINE_PARSER
      52  ("#",
      53   {
      54     char *addr;
      55  
      56     STRING_FIELD (addr, isspace, 1);
      57  
      58     /* Parse address.  */
      59     if (__inet_pton (af == AF_UNSPEC ? AF_INET : af, addr, entdata->host_addr)
      60         > 0)
      61       af = af == AF_UNSPEC ? AF_INET : af;
      62     else
      63       {
      64         if (af == AF_INET6 && (flags & AI_V4MAPPED) != 0
      65  	   && __inet_pton (AF_INET, addr, entdata->host_addr) > 0)
      66  	 map_v4v6_address ((char *) entdata->host_addr,
      67  			   (char *) entdata->host_addr);
      68         else if (af == AF_INET
      69  		&& __inet_pton (AF_INET6, addr, entdata->host_addr) > 0)
      70  	 {
      71  	   if (IN6_IS_ADDR_V4MAPPED (entdata->host_addr))
      72  	     memcpy (entdata->host_addr, entdata->host_addr + 12, INADDRSZ);
      73  	   else if (IN6_IS_ADDR_LOOPBACK (entdata->host_addr))
      74  	     {
      75  	       in_addr_t localhost = htonl (INADDR_LOOPBACK);
      76  	       memcpy (entdata->host_addr, &localhost, sizeof (localhost));
      77  	     }
      78  	   else
      79  	     /* Illegal address: ignore line.  */
      80  	     return 0;
      81  	 }
      82         else if (af == AF_UNSPEC
      83  		&& __inet_pton (AF_INET6, addr, entdata->host_addr) > 0)
      84  	 af = AF_INET6;
      85         else
      86  	 /* Illegal address: ignore line.  */
      87  	 return 0;
      88       }
      89  
      90     /* We always return entries of the requested form.  */
      91     result->h_addrtype = af;
      92     result->h_length = af == AF_INET ? INADDRSZ : IN6ADDRSZ;
      93  
      94     /* Store a pointer to the address in the expected form.  */
      95     entdata->h_addr_ptrs[0] = (char *) entdata->host_addr;
      96     entdata->h_addr_ptrs[1] = NULL;
      97     result->h_addr_list = entdata->h_addr_ptrs;
      98  
      99     STRING_FIELD (result->h_name, isspace, 1);
     100   })
     101  
     102  #define EXTRA_ARGS_VALUE , AF_INET, 0
     103  #include "files-XXX.c"
     104  #undef EXTRA_ARGS_VALUE
     105  
     106  /* We only need to consider IPv4 mapped addresses if the input to the
     107     gethostbyaddr() function is an IPv6 address.  */
     108  #define EXTRA_ARGS_VALUE \
     109    , af, (len == IN6ADDRSZ ? AI_V4MAPPED : 0)
     110  DB_LOOKUP (hostbyaddr, ,,,
     111  	   {
     112  	     if (result->h_length == (int) len
     113  		 && ! memcmp (addr, result->h_addr_list[0], len))
     114  	       break;
     115  	   }, const void *addr, socklen_t len, int af)
     116  #undef EXTRA_ARGS_VALUE
     117  
     118  /* Type of the address and alias arrays.  */
     119  #define DYNARRAY_STRUCT array
     120  #define DYNARRAY_ELEMENT char *
     121  #define DYNARRAY_PREFIX array_
     122  #include <malloc/dynarray-skeleton.c>
     123  
     124  static enum nss_status
     125  gethostbyname3_multi (FILE * stream, const char *name, int af,
     126  		      struct hostent *result, char *buffer, size_t buflen,
     127  		      int *errnop, int *herrnop)
     128  {
     129    assert (af == AF_INET || af == AF_INET6);
     130  
     131    /* We have to get all host entries from the file.  */
     132    struct scratch_buffer tmp_buffer;
     133    scratch_buffer_init (&tmp_buffer);
     134    struct hostent tmp_result_buf;
     135    struct array addresses;
     136    array_init (&addresses);
     137    struct array aliases;
     138    array_init (&aliases);
     139    enum nss_status status;
     140  
     141    /* Preserve the addresses and aliases encountered so far.  */
     142    for (size_t i = 0; result->h_addr_list[i] != NULL; ++i)
     143      array_add (&addresses, result->h_addr_list[i]);
     144    for (size_t i = 0; result->h_aliases[i] != NULL; ++i)
     145      array_add (&aliases, result->h_aliases[i]);
     146  
     147    /* The output buffer re-uses now-unused space at the end of the
     148       buffer, starting with the aliases array.  It comes last in the
     149       data produced by internal_getent.  (The alias names themselves
     150       are still located in the line read in internal_getent, which is
     151       stored at the beginning of the buffer.)  */
     152    struct alloc_buffer outbuf;
     153    {
     154      char *bufferend = (char *) result->h_aliases;
     155      outbuf = alloc_buffer_create (bufferend, buffer + buflen - bufferend);
     156    }
     157  
     158    while (true)
     159      {
     160        status = internal_getent (stream, &tmp_result_buf, tmp_buffer.data,
     161  				tmp_buffer.length, errnop, herrnop, af, 0);
     162        /* Enlarge the buffer if necessary.  */
     163        if (status == NSS_STATUS_TRYAGAIN && *herrnop == NETDB_INTERNAL
     164  	  && *errnop == ERANGE)
     165  	{
     166  	  if (!scratch_buffer_grow (&tmp_buffer))
     167  	    {
     168  	      *errnop = ENOMEM;
     169  	      /* *herrnop and status already have the right value.  */
     170  	      break;
     171  	    }
     172  	  /* Loop around and retry with a larger buffer.  */
     173  	}
     174        else if (status == NSS_STATUS_SUCCESS)
     175  	{
     176  	  /* A line was read.  Check that it matches the search
     177  	     criteria.  */
     178  
     179  	  int matches = 1;
     180  	  struct hostent *old_result = result;
     181  	  result = &tmp_result_buf;
     182  	  /* The following piece is a bit clumsy but we want to use
     183  	     the `LOOKUP_NAME_CASE' value.  The optimizer should do
     184  	     its job.  */
     185  	  do
     186  	    {
     187  	      LOOKUP_NAME_CASE (h_name, h_aliases)
     188  		result = old_result;
     189  	    }
     190  	  while ((matches = 0));
     191  
     192  	  /* If the line matches, we need to copy the addresses and
     193  	     aliases, so that we can reuse tmp_buffer for the next
     194  	     line.  */
     195  	  if (matches)
     196  	    {
     197  	      /* Record the addresses.  */
     198  	      for (size_t i = 0; tmp_result_buf.h_addr_list[i] != NULL; ++i)
     199  		{
     200  		  /* Allocate the target space in the output buffer,
     201  		     depending on the address family.  */
     202  		  void *target;
     203  		  if (af == AF_INET)
     204  		    {
     205  		      assert (tmp_result_buf.h_length == 4);
     206  		      target = alloc_buffer_alloc (&outbuf, struct in_addr);
     207  		    }
     208  		  else if (af == AF_INET6)
     209  		    {
     210  		      assert (tmp_result_buf.h_length == 16);
     211  		      target = alloc_buffer_alloc (&outbuf, struct in6_addr);
     212  		    }
     213  		  else
     214  		    __builtin_unreachable ();
     215  
     216  		  if (target == NULL)
     217  		    {
     218  		      /* Request a larger output buffer.  */
     219  		      *errnop = ERANGE;
     220  		      *herrnop = NETDB_INTERNAL;
     221  		      status = NSS_STATUS_TRYAGAIN;
     222  		      break;
     223  		    }
     224  		  memcpy (target, tmp_result_buf.h_addr_list[i],
     225  			  tmp_result_buf.h_length);
     226  		  array_add (&addresses, target);
     227  		}
     228  
     229  	      /* Record the aliases.  */
     230  	      for (size_t i = 0; tmp_result_buf.h_aliases[i] != NULL; ++i)
     231  		{
     232  		  char *alias = tmp_result_buf.h_aliases[i];
     233  		  array_add (&aliases,
     234  			     alloc_buffer_copy_string (&outbuf, alias));
     235  		}
     236  
     237  	      /* If the real name is different add, it also to the
     238  		 aliases.  This means that there is a duplication in
     239  		 the alias list but this is really the user's
     240  		 problem.  */
     241  	      {
     242  		char *new_name = tmp_result_buf.h_name;
     243  		if (strcmp (old_result->h_name, new_name) != 0)
     244  		  array_add (&aliases,
     245  			     alloc_buffer_copy_string (&outbuf, new_name));
     246  	      }
     247  
     248  	      /* Report memory allocation failures during the
     249  		 expansion of the temporary arrays.  */
     250  	      if (array_has_failed (&addresses) || array_has_failed (&aliases))
     251  		{
     252  		  *errnop = ENOMEM;
     253  		  *herrnop = NETDB_INTERNAL;
     254  		  status = NSS_STATUS_UNAVAIL;
     255  		  break;
     256  		}
     257  
     258  	      /* Request a larger output buffer if we ran out of room.  */
     259  	      if (alloc_buffer_has_failed (&outbuf))
     260  		{
     261  		  *errnop = ERANGE;
     262  		  *herrnop = NETDB_INTERNAL;
     263  		  status = NSS_STATUS_TRYAGAIN;
     264  		  break;
     265  		}
     266  
     267  	      result = old_result;
     268  	    } /* If match was found.  */
     269  
     270  	  /* If no match is found, loop around and fetch another
     271  	     line.  */
     272  
     273  	} /* status == NSS_STATUS_SUCCESS.  */
     274        else
     275  	/* internal_getent returned an error.  */
     276  	break;
     277      } /* while (true) */
     278  
     279    /* Propagate the NSS_STATUS_TRYAGAIN error to the caller.  It means
     280       that we may not have loaded the complete result.
     281       NSS_STATUS_NOTFOUND, however, means that we reached the end of
     282       the file successfully.  */
     283    if (status != NSS_STATUS_TRYAGAIN)
     284      status = NSS_STATUS_SUCCESS;
     285  
     286    if (status == NSS_STATUS_SUCCESS)
     287      {
     288        /* Copy the address and alias arrays into the output buffer and
     289  	 add NULL terminators.  The pointed-to elements were directly
     290  	 written into the output buffer above and do not need to be
     291  	 copied again.  */
     292        size_t addresses_count = array_size (&addresses);
     293        size_t aliases_count = array_size (&aliases);
     294        char **out_addresses = alloc_buffer_alloc_array
     295  	(&outbuf, char *, addresses_count + 1);
     296        char **out_aliases = alloc_buffer_alloc_array
     297  	(&outbuf, char *, aliases_count + 1);
     298        if (out_addresses == NULL || out_aliases == NULL)
     299  	{
     300  	  /* The output buffer is not large enough.  */
     301  	  *errnop = ERANGE;
     302  	  *herrnop = NETDB_INTERNAL;
     303  	  status = NSS_STATUS_TRYAGAIN;
     304  	  /* Fall through to function exit.  */
     305  	}
     306        else
     307  	{
     308  	  /* Everything is allocated in place.  Make the copies and
     309  	     adjust the array pointers.  */
     310  	  memcpy (out_addresses, array_begin (&addresses),
     311  		  addresses_count * sizeof (char *));
     312  	  out_addresses[addresses_count] = NULL;
     313  	  memcpy (out_aliases, array_begin (&aliases),
     314  		  aliases_count * sizeof (char *));
     315  	  out_aliases[aliases_count] = NULL;
     316  
     317  	  result->h_addr_list = out_addresses;
     318  	  result->h_aliases = out_aliases;
     319  
     320  	  status = NSS_STATUS_SUCCESS;
     321  	}
     322      }
     323  
     324    scratch_buffer_free (&tmp_buffer);
     325    array_free (&addresses);
     326    array_free (&aliases);
     327    return status;
     328  }
     329  
     330  enum nss_status
     331  _nss_files_gethostbyname3_r (const char *name, int af, struct hostent *result,
     332  			     char *buffer, size_t buflen, int *errnop,
     333  			     int *herrnop, int32_t *ttlp, char **canonp)
     334  {
     335    FILE *stream = NULL;
     336    uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct hostent_data);
     337    buffer += pad;
     338    buflen = buflen > pad ? buflen - pad : 0;
     339  
     340    /* Open file.  */
     341    enum nss_status status = internal_setent (&stream);
     342  
     343    if (status == NSS_STATUS_SUCCESS)
     344      {
     345        while ((status = internal_getent (stream, result, buffer, buflen, errnop,
     346  					herrnop, af, 0))
     347  	     == NSS_STATUS_SUCCESS)
     348  	{
     349  	  LOOKUP_NAME_CASE (h_name, h_aliases)
     350  	}
     351  
     352        if (status == NSS_STATUS_SUCCESS
     353  	  && _res_hconf.flags & HCONF_FLAG_MULTI)
     354  	status = gethostbyname3_multi
     355  	  (stream, name, af, result, buffer, buflen, errnop, herrnop);
     356  
     357        fclose (stream);
     358      }
     359  
     360    if (canonp && status == NSS_STATUS_SUCCESS)
     361      *canonp = result->h_name;
     362  
     363    return status;
     364  }
     365  libc_hidden_def (_nss_files_gethostbyname3_r)
     366  
     367  enum nss_status
     368  _nss_files_gethostbyname_r (const char *name, struct hostent *result,
     369  			    char *buffer, size_t buflen, int *errnop,
     370  			    int *herrnop)
     371  {
     372    return _nss_files_gethostbyname3_r (name, AF_INET, result, buffer, buflen,
     373  				      errnop, herrnop, NULL, NULL);
     374  }
     375  libc_hidden_def (_nss_files_gethostbyname_r)
     376  
     377  enum nss_status
     378  _nss_files_gethostbyname2_r (const char *name, int af, struct hostent *result,
     379  			     char *buffer, size_t buflen, int *errnop,
     380  			     int *herrnop)
     381  {
     382    return _nss_files_gethostbyname3_r (name, af, result, buffer, buflen,
     383  				      errnop, herrnop, NULL, NULL);
     384  }
     385  libc_hidden_def (_nss_files_gethostbyname2_r)
     386  
     387  enum nss_status
     388  _nss_files_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
     389  			     char *buffer, size_t buflen, int *errnop,
     390  			     int *herrnop, int32_t *ttlp)
     391  {
     392    FILE *stream = NULL;
     393  
     394    /* Open file.  */
     395    enum nss_status status = internal_setent (&stream);
     396  
     397    if (status == NSS_STATUS_SUCCESS)
     398      {
     399        bool any = false;
     400        bool got_canon = false;
     401        while (1)
     402  	{
     403  	  /* Align the buffer for the next record.  */
     404  	  uintptr_t pad = (-(uintptr_t) buffer
     405  			   % __alignof__ (struct hostent_data));
     406  	  buffer += pad;
     407  	  buflen = buflen > pad ? buflen - pad : 0;
     408  
     409  	  struct hostent result;
     410  	  status = internal_getent (stream, &result, buffer, buflen, errnop,
     411  				    herrnop, AF_UNSPEC, 0);
     412  	  if (status != NSS_STATUS_SUCCESS)
     413  	    break;
     414  
     415  	  int naliases = 0;
     416  	  if (__strcasecmp (name, result.h_name) != 0)
     417  	    {
     418  	      for (; result.h_aliases[naliases] != NULL; ++naliases)
     419  		if (! __strcasecmp (name, result.h_aliases[naliases]))
     420  		  break;
     421  	      if (result.h_aliases[naliases] == NULL)
     422  		continue;
     423  
     424  	      /* We know this alias exist.  Count it.  */
     425  	      ++naliases;
     426  	    }
     427  
     428  	  /* Determine how much memory has been used so far.  */
     429  	  // XXX It is not necessary to preserve the aliases array
     430  	  while (result.h_aliases[naliases] != NULL)
     431  	    ++naliases;
     432  	  char *bufferend = (char *) &result.h_aliases[naliases + 1];
     433  	  assert (buflen >= bufferend - buffer);
     434  	  buflen -= bufferend - buffer;
     435  	  buffer = bufferend;
     436  
     437  	  /* We found something.  */
     438  	  any = true;
     439  
     440  	  /* Create the record the caller expects.  There is only one
     441  	     address.  */
     442  	  assert (result.h_addr_list[1] == NULL);
     443  	  if (*pat == NULL)
     444  	    {
     445  	      uintptr_t pad = (-(uintptr_t) buffer
     446  			       % __alignof__ (struct gaih_addrtuple));
     447  	      buffer += pad;
     448  	      buflen = buflen > pad ? buflen - pad : 0;
     449  
     450  	      if (__builtin_expect (buflen < sizeof (struct gaih_addrtuple),
     451  				    0))
     452  		{
     453  		  *errnop = ERANGE;
     454  		  *herrnop = NETDB_INTERNAL;
     455  		  status = NSS_STATUS_TRYAGAIN;
     456  		  break;
     457  		}
     458  
     459  	      *pat = (struct gaih_addrtuple *) buffer;
     460  	      buffer += sizeof (struct gaih_addrtuple);
     461  	      buflen -= sizeof (struct gaih_addrtuple);
     462  	    }
     463  
     464  	  (*pat)->next = NULL;
     465  	  (*pat)->name = got_canon ? NULL : result.h_name;
     466  	  got_canon = true;
     467  	  (*pat)->family = result.h_addrtype;
     468  	  memcpy ((*pat)->addr, result.h_addr_list[0], result.h_length);
     469  	  (*pat)->scopeid = 0;
     470  
     471  	  pat = &((*pat)->next);
     472  
     473  	  /* If we only look for the first matching entry we are done.  */
     474  	  if ((_res_hconf.flags & HCONF_FLAG_MULTI) == 0)
     475  	    break;
     476  	}
     477  
     478        /* If we have to look for multiple records and found one, this
     479  	 is a success.  */
     480        if (status == NSS_STATUS_NOTFOUND && any)
     481  	{
     482  	  assert ((_res_hconf.flags & HCONF_FLAG_MULTI) != 0);
     483  	  status = NSS_STATUS_SUCCESS;
     484  	}
     485  
     486        fclose (stream);
     487      }
     488    else if (status == NSS_STATUS_TRYAGAIN)
     489      {
     490        *errnop = errno;
     491        *herrnop = TRY_AGAIN;
     492      }
     493    else
     494      {
     495        *errnop = errno;
     496        *herrnop = NO_DATA;
     497      }
     498  
     499    return status;
     500  }
     501  libc_hidden_def (_nss_files_gethostbyname4_r)