(root)/
glibc-2.38/
nscd/
aicache.c
       1  /* Cache handling for host lookup.
       2     Copyright (C) 2004-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     This program is free software; you can redistribute it and/or modify
       6     it under the terms of the GNU General Public License as published
       7     by the Free Software Foundation; version 2 of the License, or
       8     (at your option) any later version.
       9  
      10     This program 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
      13     GNU General Public License for more details.
      14  
      15     You should have received a copy of the GNU General Public License
      16     along with this program; if not, see <https://www.gnu.org/licenses/>.  */
      17  
      18  #include <assert.h>
      19  #include <errno.h>
      20  #include <libintl.h>
      21  #include <netdb.h>
      22  #include <nss.h>
      23  #include <string.h>
      24  #include <time.h>
      25  #include <unistd.h>
      26  #include <sys/mman.h>
      27  #include <resolv/resolv-internal.h>
      28  #include <resolv/resolv_context.h>
      29  #include <scratch_buffer.h>
      30  
      31  #include "dbg_log.h"
      32  #include "nscd.h"
      33  
      34  
      35  static const ai_response_header notfound =
      36  {
      37    .version = NSCD_VERSION,
      38    .found = 0,
      39    .naddrs = 0,
      40    .addrslen = 0,
      41    .canonlen = 0,
      42    .error = 0
      43  };
      44  
      45  
      46  static time_t
      47  addhstaiX (struct database_dyn *db, int fd, request_header *req,
      48  	   void *key, uid_t uid, struct hashentry *const he,
      49  	   struct datahead *dh)
      50  {
      51    /* Search for the entry matching the key.  Please note that we don't
      52       look again in the table whether the dataset is now available.  We
      53       simply insert it.  It does not matter if it is in there twice.  The
      54       pruning function only will look at the timestamp.  */
      55  
      56    /* We allocate all data in one memory block: the iov vector,
      57       the response header and the dataset itself.  */
      58    struct dataset
      59    {
      60      struct datahead head;
      61      ai_response_header resp;
      62      char strdata[0];
      63    } *dataset = NULL;
      64  
      65    if (__glibc_unlikely (debug_level > 0))
      66      {
      67        if (he == NULL)
      68  	dbg_log (_("Haven't found \"%s\" in hosts cache!"), (char *) key);
      69        else
      70  	dbg_log (_("Reloading \"%s\" in hosts cache!"), (char *) key);
      71      }
      72  
      73    nss_action_list nip;
      74    int no_more;
      75    int rc6 = 0;
      76    int rc4 = 0;
      77    int herrno = 0;
      78  
      79    no_more = !__nss_database_get (nss_database_hosts, &nip);
      80  
      81    /* Initialize configurations.  */
      82    struct resolv_context *ctx = __resolv_context_get ();
      83    if (ctx == NULL)
      84      no_more = 1;
      85  
      86    struct scratch_buffer tmpbuf6;
      87    scratch_buffer_init (&tmpbuf6);
      88    struct scratch_buffer tmpbuf4;
      89    scratch_buffer_init (&tmpbuf4);
      90    struct scratch_buffer canonbuf;
      91    scratch_buffer_init (&canonbuf);
      92  
      93    int32_t ttl = INT32_MAX;
      94    ssize_t total = 0;
      95    char *key_copy = NULL;
      96    bool alloca_used = false;
      97    time_t timeout = MAX_TIMEOUT_VALUE;
      98  
      99    while (!no_more)
     100      {
     101        void *cp;
     102        int status[2] = { NSS_STATUS_UNAVAIL, NSS_STATUS_UNAVAIL };
     103        int naddrs = 0;
     104        size_t addrslen = 0;
     105  
     106        char *canon = NULL;
     107        size_t canonlen;
     108  
     109        nss_gethostbyname4_r *fct4 = __nss_lookup_function (nip,
     110  							  "gethostbyname4_r");
     111        if (fct4 != NULL)
     112  	{
     113  	  struct gaih_addrtuple *at;
     114  	  while (1)
     115  	    {
     116  	      at = NULL;
     117  	      rc6 = 0;
     118  	      herrno = 0;
     119  	      status[1] = DL_CALL_FCT (fct4, (key, &at,
     120  					      tmpbuf6.data, tmpbuf6.length,
     121  					      &rc6, &herrno, &ttl));
     122  	      if (rc6 != ERANGE || (herrno != NETDB_INTERNAL
     123  				    && herrno != TRY_AGAIN))
     124  		break;
     125  	      if (!scratch_buffer_grow (&tmpbuf6))
     126  		{
     127  		  rc6 = ENOMEM;
     128  		  break;
     129  		}
     130  	    }
     131  
     132  	  if (rc6 != 0 && herrno == NETDB_INTERNAL)
     133  	    goto out;
     134  
     135  	  if (status[1] != NSS_STATUS_SUCCESS)
     136  	    goto next_nip;
     137  
     138  	  /* We found the data.  Count the addresses and the size.  */
     139  	  for (const struct gaih_addrtuple *at2 = at; at2 != NULL;
     140  	       at2 = at2->next)
     141  	    {
     142  	      ++naddrs;
     143  	      /* We do not handle anything other than IPv4 and IPv6
     144  		 addresses.  The getaddrinfo implementation does not
     145  		 either so it is not worth trying to do more.  */
     146  	      if (at2->family == AF_INET)
     147  		addrslen += INADDRSZ;
     148  	      else if (at2->family == AF_INET6)
     149  		addrslen += IN6ADDRSZ;
     150  	    }
     151  	  canon = at->name;
     152  	  canonlen = strlen (canon) + 1;
     153  
     154  	  total = sizeof (*dataset) + naddrs + addrslen + canonlen;
     155  
     156  	  /* Now we can allocate the data structure.  If the TTL of the
     157  	     entry is reported as zero do not cache the entry at all.  */
     158  	  if (ttl != 0 && he == NULL)
     159  	    dataset = (struct dataset *) mempool_alloc (db, total
     160  							+ req->key_len, 1);
     161  
     162  	  if (dataset == NULL)
     163  	    {
     164  	      /* We cannot permanently add the result in the moment.  But
     165  		 we can provide the result as is.  Store the data in some
     166  		 temporary memory.  */
     167  	      dataset = (struct dataset *) alloca (total + req->key_len);
     168  
     169  	      /* We cannot add this record to the permanent database.  */
     170  	      alloca_used = true;
     171  	    }
     172  
     173  	  /* Fill in the address and address families.  */
     174  	  char *addrs = dataset->strdata;
     175  	  uint8_t *family = (uint8_t *) (addrs + addrslen);
     176  
     177  	  for (const struct gaih_addrtuple *at2 = at; at2 != NULL;
     178  	       at2 = at2->next)
     179  	    {
     180  	      *family++ = at2->family;
     181  	      if (at2->family == AF_INET)
     182  		addrs = mempcpy (addrs, at2->addr, INADDRSZ);
     183  	      else if (at2->family == AF_INET6)
     184  		addrs = mempcpy (addrs, at2->addr, IN6ADDRSZ);
     185  	    }
     186  
     187  	  cp = family;
     188  	}
     189        else
     190  	{
     191  	  /* Prefer the function which also returns the TTL and
     192  	     canonical name.  */
     193  	  nss_gethostbyname3_r *fct
     194  	    = __nss_lookup_function (nip, "gethostbyname3_r");
     195  	  if (fct == NULL)
     196  	    fct = __nss_lookup_function (nip, "gethostbyname2_r");
     197  
     198  	  if (fct == NULL)
     199  	    goto next_nip;
     200  
     201  	  struct hostent th[2];
     202  
     203  	  /* Collect IPv6 information first.  */
     204  	  while (1)
     205  	    {
     206  	      rc6 = 0;
     207  	      status[0] = DL_CALL_FCT (fct, (key, AF_INET6, &th[0],
     208  					     tmpbuf6.data, tmpbuf6.length,
     209  					     &rc6, &herrno, &ttl,
     210  					     &canon));
     211  	      if (rc6 != ERANGE || herrno != NETDB_INTERNAL)
     212  		break;
     213  	      if (!scratch_buffer_grow (&tmpbuf6))
     214  		{
     215  		  rc6 = ENOMEM;
     216  		  break;
     217  		}
     218  	    }
     219  
     220  	  if (rc6 != 0 && herrno == NETDB_INTERNAL)
     221  	    goto out;
     222  
     223  	  /* Next collect IPv4 information.  */
     224  	  while (1)
     225  	    {
     226  	      rc4 = 0;
     227  	      status[1] = DL_CALL_FCT (fct, (key, AF_INET, &th[1],
     228  					     tmpbuf4.data, tmpbuf4.length,
     229  					     &rc4, &herrno,
     230  					     ttl == INT32_MAX ? &ttl : NULL,
     231  					     canon == NULL ? &canon : NULL));
     232  	      if (rc4 != ERANGE || herrno != NETDB_INTERNAL)
     233  		break;
     234  	      if (!scratch_buffer_grow (&tmpbuf4))
     235  		{
     236  		  rc4 = ENOMEM;
     237  		  break;
     238  		}
     239  	    }
     240  
     241  	  if (rc4 != 0 && herrno == NETDB_INTERNAL)
     242  	    goto out;
     243  
     244  	  if (status[0] != NSS_STATUS_SUCCESS
     245  	      && status[1] != NSS_STATUS_SUCCESS)
     246  	    goto next_nip;
     247  
     248  	  /* We found the data.  Count the addresses and the size.  */
     249  	  for (int j = 0; j < 2; ++j)
     250  	    if (status[j] == NSS_STATUS_SUCCESS)
     251  	      for (int i = 0; th[j].h_addr_list[i] != NULL; ++i)
     252  		{
     253  		  ++naddrs;
     254  		  addrslen += th[j].h_length;
     255  		}
     256  
     257  	  if (canon == NULL)
     258  	    {
     259  	      /* Determine the canonical name.  */
     260  	      nss_getcanonname_r *cfct;
     261  	      cfct = __nss_lookup_function (nip, "getcanonname_r");
     262  	      if (cfct != NULL)
     263  		{
     264  		  char *s;
     265  		  int rc;
     266  
     267  		  if (DL_CALL_FCT (cfct, (key, canonbuf.data, canonbuf.length,
     268  					  &s, &rc, &herrno))
     269  		      == NSS_STATUS_SUCCESS)
     270  		    canon = s;
     271  		  else
     272  		    /* Set to name now to avoid using gethostbyaddr.  */
     273  		    canon = key;
     274  		}
     275  	      else
     276  		{
     277  		  struct hostent *hstent = NULL;
     278  		  int herrno;
     279  		  struct hostent hstent_mem;
     280  		  void *addr;
     281  		  size_t addrlen;
     282  		  int addrfamily;
     283  
     284  		  if (status[1] == NSS_STATUS_SUCCESS)
     285  		    {
     286  		      addr = th[1].h_addr_list[0];
     287  		      addrlen = sizeof (struct in_addr);
     288  		      addrfamily = AF_INET;
     289  		    }
     290  		  else
     291  		    {
     292  		      addr = th[0].h_addr_list[0];
     293  		      addrlen = sizeof (struct in6_addr);
     294  		      addrfamily = AF_INET6;
     295  		    }
     296  
     297  		  int rc;
     298  		  while (1)
     299  		    {
     300  		      rc = __gethostbyaddr2_r (addr, addrlen, addrfamily,
     301  					       &hstent_mem,
     302  					       canonbuf.data, canonbuf.length,
     303  					       &hstent, &herrno, NULL);
     304  		      if (rc != ERANGE || herrno != NETDB_INTERNAL)
     305  			break;
     306  		      if (!scratch_buffer_grow (&canonbuf))
     307  			{
     308  			  rc = ENOMEM;
     309  			  break;
     310  			}
     311  		    }
     312  
     313  		  if (rc == 0)
     314  		    {
     315  		      if (hstent != NULL)
     316  			canon = hstent->h_name;
     317  		      else
     318  			canon = key;
     319  		    }
     320  		}
     321  	    }
     322  
     323  	  canonlen = canon == NULL ? 0 : (strlen (canon) + 1);
     324  
     325  	  total = sizeof (*dataset) + naddrs + addrslen + canonlen;
     326  
     327  
     328  	  /* Now we can allocate the data structure.  If the TTL of the
     329  	     entry is reported as zero do not cache the entry at all.  */
     330  	  if (ttl != 0 && he == NULL)
     331  	    dataset = (struct dataset *) mempool_alloc (db, total
     332  							+ req->key_len, 1);
     333  
     334  	  if (dataset == NULL)
     335  	    {
     336  	      /* We cannot permanently add the result in the moment.  But
     337  		 we can provide the result as is.  Store the data in some
     338  		 temporary memory.  */
     339  	      dataset = (struct dataset *) alloca (total + req->key_len);
     340  
     341  	      /* We cannot add this record to the permanent database.  */
     342  	      alloca_used = true;
     343  	    }
     344  
     345  	  /* Fill in the address and address families.  */
     346  	  char *addrs = dataset->strdata;
     347  	  uint8_t *family = (uint8_t *) (addrs + addrslen);
     348  
     349  	  for (int j = 0; j < 2; ++j)
     350  	    if (status[j] == NSS_STATUS_SUCCESS)
     351  	      for (int i = 0; th[j].h_addr_list[i] != NULL; ++i)
     352  		{
     353  		  addrs = mempcpy (addrs, th[j].h_addr_list[i],
     354  				   th[j].h_length);
     355  		  *family++ = th[j].h_addrtype;
     356  		}
     357  
     358  	  cp = family;
     359  	}
     360  
     361        timeout = datahead_init_pos (&dataset->head, total + req->key_len,
     362  				   total - offsetof (struct dataset, resp),
     363  				   he == NULL ? 0 : dh->nreloads + 1,
     364  				   ttl == INT32_MAX ? db->postimeout : ttl);
     365  
     366        /* Fill in the rest of the dataset.  */
     367        dataset->resp.version = NSCD_VERSION;
     368        dataset->resp.found = 1;
     369        dataset->resp.naddrs = naddrs;
     370        dataset->resp.addrslen = addrslen;
     371        dataset->resp.canonlen = canonlen;
     372        dataset->resp.error = NETDB_SUCCESS;
     373  
     374        if (canon != NULL)
     375  	cp = mempcpy (cp, canon, canonlen);
     376  
     377        key_copy = memcpy (cp, key, req->key_len);
     378  
     379        assert (cp == (char *) dataset + total);
     380  
     381        /* Now we can determine whether on refill we have to create a
     382  	 new record or not.  */
     383        if (he != NULL)
     384  	{
     385  	  assert (fd == -1);
     386  
     387  	  if (total + req->key_len == dh->allocsize
     388  	      && total - offsetof (struct dataset, resp) == dh->recsize
     389  	      && memcmp (&dataset->resp, dh->data,
     390  			 dh->allocsize - offsetof (struct dataset,
     391  						   resp)) == 0)
     392  	    {
     393  	      /* The data has not changed.  We will just bump the
     394  		 timeout value.  Note that the new record has been
     395  		 allocated on the stack and need not be freed.  */
     396  	      dh->timeout = dataset->head.timeout;
     397  	      dh->ttl = dataset->head.ttl;
     398  	      ++dh->nreloads;
     399  	    }
     400  	  else
     401  	    {
     402  	      /* We have to create a new record.  Just allocate
     403  		 appropriate memory and copy it.  */
     404  	      struct dataset *newp
     405  		= (struct dataset *) mempool_alloc (db, total + req->key_len,
     406  						    1);
     407  	      if (__glibc_likely (newp != NULL))
     408  		{
     409  		  /* Adjust pointer into the memory block.  */
     410  		  key_copy = (char *) newp + (key_copy - (char *) dataset);
     411  
     412  		  dataset = memcpy (newp, dataset, total + req->key_len);
     413  		  alloca_used = false;
     414  		}
     415  
     416  	      /* Mark the old record as obsolete.  */
     417  	      dh->usable = false;
     418  	    }
     419  	}
     420        else
     421  	{
     422  	  /* We write the dataset before inserting it to the database
     423  	     since while inserting this thread might block and so
     424  	     would unnecessarily let the receiver wait.  */
     425  	  assert (fd != -1);
     426  
     427  	  writeall (fd, &dataset->resp, dataset->head.recsize);
     428  	}
     429  
     430        goto out;
     431  
     432  next_nip:
     433        if (nss_next_action (nip, status[1]) == NSS_ACTION_RETURN)
     434  	break;
     435  
     436        if (nip[1].module == NULL)
     437  	no_more = -1;
     438        else
     439  	++nip;
     440      }
     441  
     442    /* No result found.  Create a negative result record.  */
     443    if (he != NULL && rc4 == EAGAIN)
     444      {
     445        /* If we have an old record available but cannot find one now
     446  	 because the service is not available we keep the old record
     447  	 and make sure it does not get removed.  */
     448        if (reload_count != UINT_MAX && dh->nreloads == reload_count)
     449  	/* Do not reset the value if we never not reload the record.  */
     450  	dh->nreloads = reload_count - 1;
     451  
     452        /* Reload with the same time-to-live value.  */
     453        timeout = dh->timeout = time (NULL) + dh->ttl;
     454      }
     455    else
     456      {
     457        /* We have no data.  This means we send the standard reply for
     458  	 this case.  */
     459        total = sizeof (notfound);
     460  
     461        if (fd != -1)
     462  	TEMP_FAILURE_RETRY (send (fd, &notfound, total, MSG_NOSIGNAL));
     463  
     464        /* If we have a transient error or cannot permanently store the
     465  	 result, so be it.  */
     466        if (rc4 == EAGAIN || __builtin_expect (db->negtimeout == 0, 0))
     467  	{
     468  	  /* Mark the old entry as obsolete.  */
     469  	  if (dh != NULL)
     470  	    dh->usable = false;
     471  	  dataset = NULL;
     472  	}
     473        else if ((dataset = mempool_alloc (db, (sizeof (struct dataset)
     474  					      + req->key_len), 1)) != NULL)
     475  	{
     476  	  timeout = datahead_init_neg (&dataset->head,
     477  				       sizeof (struct dataset) + req->key_len,
     478  				       total, db->negtimeout);
     479  
     480  	  /* This is the reply.  */
     481  	  memcpy (&dataset->resp, &notfound, total);
     482  
     483  	  /* Copy the key data.  */
     484  	  key_copy = memcpy (dataset->strdata, key, req->key_len);
     485  	}
     486     }
     487  
     488   out:
     489    __resolv_context_put (ctx);
     490  
     491    if (dataset != NULL && !alloca_used)
     492      {
     493        /* If necessary, we also propagate the data to disk.  */
     494        if (db->persistent)
     495  	{
     496  	  // XXX async OK?
     497  	  uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
     498  	  msync ((void *) pval,
     499  		 ((uintptr_t) dataset & pagesize_m1) + total + req->key_len,
     500  		 MS_ASYNC);
     501  	}
     502  
     503        (void) cache_add (req->type, key_copy, req->key_len, &dataset->head,
     504  			true, db, uid, he == NULL);
     505  
     506        pthread_rwlock_unlock (&db->lock);
     507  
     508        /* Mark the old entry as obsolete.  */
     509        if (dh != NULL)
     510  	dh->usable = false;
     511      }
     512  
     513    scratch_buffer_free (&tmpbuf6);
     514    scratch_buffer_free (&tmpbuf4);
     515    scratch_buffer_free (&canonbuf);
     516  
     517    return timeout;
     518  }
     519  
     520  
     521  void
     522  addhstai (struct database_dyn *db, int fd, request_header *req, void *key,
     523  	  uid_t uid)
     524  {
     525    addhstaiX (db, fd, req, key, uid, NULL, NULL);
     526  }
     527  
     528  
     529  time_t
     530  readdhstai (struct database_dyn *db, struct hashentry *he, struct datahead *dh)
     531  {
     532    request_header req =
     533      {
     534        .type = GETAI,
     535        .key_len = he->len
     536      };
     537  
     538    return addhstaiX (db, -1, &req, db->data + he->key, he->owner, he, dh);
     539  }