Blame nscd/aicache.c

Packit 6c4009
/* Cache handling for host lookup.
Packit 6c4009
   Copyright (C) 2004-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Contributed by Ulrich Drepper <drepper@redhat.com>, 2004.
Packit 6c4009
Packit 6c4009
   This program is free software; you can redistribute it and/or modify
Packit 6c4009
   it under the terms of the GNU General Public License as published
Packit 6c4009
   by the Free Software Foundation; version 2 of the License, or
Packit 6c4009
   (at your option) any later version.
Packit 6c4009
Packit 6c4009
   This program is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 6c4009
   GNU General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU General Public License
Packit 6c4009
   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <assert.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <libintl.h>
Packit 6c4009
#include <netdb.h>
Packit 6c4009
#include <nss.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <time.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <sys/mman.h>
Packit 6c4009
#include <resolv/resolv-internal.h>
Packit 6c4009
#include <resolv/resolv_context.h>
Packit 6c4009
#include <resolv/res_use_inet6.h>
Packit 6c4009
#include <scratch_buffer.h>
Packit 6c4009
Packit 6c4009
#include "dbg_log.h"
Packit 6c4009
#include "nscd.h"
Packit 6c4009
Packit 6c4009
Packit 6c4009
typedef enum nss_status (*nss_gethostbyname4_r)
Packit 6c4009
  (const char *name, struct gaih_addrtuple **pat,
Packit 6c4009
   char *buffer, size_t buflen, int *errnop,
Packit 6c4009
   int *h_errnop, int32_t *ttlp);
Packit 6c4009
typedef enum nss_status (*nss_gethostbyname3_r)
Packit 6c4009
  (const char *name, int af, struct hostent *host,
Packit 6c4009
   char *buffer, size_t buflen, int *errnop,
Packit 6c4009
   int *h_errnop, int32_t *, char **);
Packit 6c4009
typedef enum nss_status (*nss_getcanonname_r)
Packit 6c4009
  (const char *name, char *buffer, size_t buflen, char **result,
Packit 6c4009
   int *errnop, int *h_errnop);
Packit 6c4009
Packit 6c4009
Packit 6c4009
static const ai_response_header notfound =
Packit 6c4009
{
Packit 6c4009
  .version = NSCD_VERSION,
Packit 6c4009
  .found = 0,
Packit 6c4009
  .naddrs = 0,
Packit 6c4009
  .addrslen = 0,
Packit 6c4009
  .canonlen = 0,
Packit 6c4009
  .error = 0
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
Packit 6c4009
static time_t
Packit 6c4009
addhstaiX (struct database_dyn *db, int fd, request_header *req,
Packit 6c4009
	   void *key, uid_t uid, struct hashentry *const he,
Packit 6c4009
	   struct datahead *dh)
Packit 6c4009
{
Packit 6c4009
  /* Search for the entry matching the key.  Please note that we don't
Packit 6c4009
     look again in the table whether the dataset is now available.  We
Packit 6c4009
     simply insert it.  It does not matter if it is in there twice.  The
Packit 6c4009
     pruning function only will look at the timestamp.  */
Packit 6c4009
Packit 6c4009
  /* We allocate all data in one memory block: the iov vector,
Packit 6c4009
     the response header and the dataset itself.  */
Packit 6c4009
  struct dataset
Packit 6c4009
  {
Packit 6c4009
    struct datahead head;
Packit 6c4009
    ai_response_header resp;
Packit 6c4009
    char strdata[0];
Packit 6c4009
  } *dataset = NULL;
Packit 6c4009
Packit 6c4009
  if (__glibc_unlikely (debug_level > 0))
Packit 6c4009
    {
Packit 6c4009
      if (he == NULL)
Packit 6c4009
	dbg_log (_("Haven't found \"%s\" in hosts cache!"), (char *) key);
Packit 6c4009
      else
Packit 6c4009
	dbg_log (_("Reloading \"%s\" in hosts cache!"), (char *) key);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  static service_user *hosts_database;
Packit 6c4009
  service_user *nip;
Packit 6c4009
  int no_more;
Packit 6c4009
  int rc6 = 0;
Packit 6c4009
  int rc4 = 0;
Packit 6c4009
  int herrno = 0;
Packit 6c4009
Packit 6c4009
  if (hosts_database == NULL)
Packit Service 8fc214
    no_more = __nss_database_lookup2 ("hosts", NULL,
Packit Service 8fc214
				      "dns [!UNAVAIL=return] files",
Packit Service 8fc214
				      &hosts_database);
Packit 6c4009
  else
Packit 6c4009
    no_more = 0;
Packit 6c4009
  nip = hosts_database;
Packit 6c4009
Packit 6c4009
  /* Initialize configurations.  If we are looking for both IPv4 and
Packit 6c4009
     IPv6 address we don't want the lookup functions to automatically
Packit 6c4009
     promote IPv4 addresses to IPv6 addresses.  Therefore, use the
Packit 6c4009
     _no_inet6 variant.  */
Packit 6c4009
  struct resolv_context *ctx = __resolv_context_get ();
Packit 6c4009
  bool enable_inet6 = __resolv_context_disable_inet6 (ctx);
Packit 6c4009
  if (ctx == NULL)
Packit 6c4009
    no_more = 1;
Packit 6c4009
Packit 6c4009
  struct scratch_buffer tmpbuf6;
Packit 6c4009
  scratch_buffer_init (&tmpbuf6);
Packit 6c4009
  struct scratch_buffer tmpbuf4;
Packit 6c4009
  scratch_buffer_init (&tmpbuf4);
Packit 6c4009
  struct scratch_buffer canonbuf;
Packit 6c4009
  scratch_buffer_init (&canonbuf);
Packit 6c4009
Packit 6c4009
  int32_t ttl = INT32_MAX;
Packit 6c4009
  ssize_t total = 0;
Packit 6c4009
  char *key_copy = NULL;
Packit 6c4009
  bool alloca_used = false;
Packit 6c4009
  time_t timeout = MAX_TIMEOUT_VALUE;
Packit 6c4009
Packit 6c4009
  while (!no_more)
Packit 6c4009
    {
Packit 6c4009
      void *cp;
Packit 6c4009
      int status[2] = { NSS_STATUS_UNAVAIL, NSS_STATUS_UNAVAIL };
Packit 6c4009
      int naddrs = 0;
Packit 6c4009
      size_t addrslen = 0;
Packit 6c4009
Packit 6c4009
      char *canon = NULL;
Packit 6c4009
      size_t canonlen;
Packit 6c4009
Packit 6c4009
      nss_gethostbyname4_r fct4 = __nss_lookup_function (nip,
Packit 6c4009
							 "gethostbyname4_r");
Packit 6c4009
      if (fct4 != NULL)
Packit 6c4009
	{
Packit 6c4009
	  struct gaih_addrtuple atmem;
Packit 6c4009
	  struct gaih_addrtuple *at;
Packit 6c4009
	  while (1)
Packit 6c4009
	    {
Packit 6c4009
	      at = &atmem;
Packit 6c4009
	      rc6 = 0;
Packit 6c4009
	      herrno = 0;
Packit 6c4009
	      status[1] = DL_CALL_FCT (fct4, (key, &at,
Packit 6c4009
					      tmpbuf6.data, tmpbuf6.length,
Packit 6c4009
					      &rc6, &herrno, &ttl));
Packit 6c4009
	      if (rc6 != ERANGE || (herrno != NETDB_INTERNAL
Packit 6c4009
				    && herrno != TRY_AGAIN))
Packit 6c4009
		break;
Packit 6c4009
	      if (!scratch_buffer_grow (&tmpbuf6))
Packit 6c4009
		{
Packit 6c4009
		  rc6 = ENOMEM;
Packit 6c4009
		  break;
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  if (rc6 != 0 && herrno == NETDB_INTERNAL)
Packit 6c4009
	    goto out;
Packit 6c4009
Packit 6c4009
	  if (status[1] != NSS_STATUS_SUCCESS)
Packit 6c4009
	    goto next_nip;
Packit 6c4009
Packit 6c4009
	  /* We found the data.  Count the addresses and the size.  */
Packit 6c4009
	  for (const struct gaih_addrtuple *at2 = at = &atmem; at2 != NULL;
Packit 6c4009
	       at2 = at2->next)
Packit 6c4009
	    {
Packit 6c4009
	      ++naddrs;
Packit 6c4009
	      /* We do not handle anything other than IPv4 and IPv6
Packit 6c4009
		 addresses.  The getaddrinfo implementation does not
Packit 6c4009
		 either so it is not worth trying to do more.  */
Packit 6c4009
	      if (at2->family == AF_INET)
Packit 6c4009
		addrslen += INADDRSZ;
Packit 6c4009
	      else if (at2->family == AF_INET6)
Packit 6c4009
		addrslen += IN6ADDRSZ;
Packit 6c4009
	    }
Packit 6c4009
	  canon = at->name;
Packit 6c4009
	  canonlen = strlen (canon) + 1;
Packit 6c4009
Packit 6c4009
	  total = sizeof (*dataset) + naddrs + addrslen + canonlen;
Packit 6c4009
Packit 6c4009
	  /* Now we can allocate the data structure.  If the TTL of the
Packit 6c4009
	     entry is reported as zero do not cache the entry at all.  */
Packit 6c4009
	  if (ttl != 0 && he == NULL)
Packit 6c4009
	    dataset = (struct dataset *) mempool_alloc (db, total
Packit 6c4009
							+ req->key_len, 1);
Packit 6c4009
Packit 6c4009
	  if (dataset == NULL)
Packit 6c4009
	    {
Packit 6c4009
	      /* We cannot permanently add the result in the moment.  But
Packit 6c4009
		 we can provide the result as is.  Store the data in some
Packit 6c4009
		 temporary memory.  */
Packit 6c4009
	      dataset = (struct dataset *) alloca (total + req->key_len);
Packit 6c4009
Packit 6c4009
	      /* We cannot add this record to the permanent database.  */
Packit 6c4009
	      alloca_used = true;
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  /* Fill in the address and address families.  */
Packit 6c4009
	  char *addrs = dataset->strdata;
Packit 6c4009
	  uint8_t *family = (uint8_t *) (addrs + addrslen);
Packit 6c4009
Packit 6c4009
	  for (const struct gaih_addrtuple *at2 = at; at2 != NULL;
Packit 6c4009
	       at2 = at2->next)
Packit 6c4009
	    {
Packit 6c4009
	      *family++ = at2->family;
Packit 6c4009
	      if (at2->family == AF_INET)
Packit 6c4009
		addrs = mempcpy (addrs, at2->addr, INADDRSZ);
Packit 6c4009
	      else if (at2->family == AF_INET6)
Packit 6c4009
		addrs = mempcpy (addrs, at2->addr, IN6ADDRSZ);
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  cp = family;
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
	{
Packit 6c4009
	  /* Prefer the function which also returns the TTL and
Packit 6c4009
	     canonical name.  */
Packit 6c4009
	  nss_gethostbyname3_r fct = __nss_lookup_function (nip,
Packit 6c4009
							    "gethostbyname3_r");
Packit 6c4009
	  if (fct == NULL)
Packit 6c4009
	    fct = __nss_lookup_function (nip, "gethostbyname2_r");
Packit 6c4009
Packit 6c4009
	  if (fct == NULL)
Packit 6c4009
	    goto next_nip;
Packit 6c4009
Packit 6c4009
	  struct hostent th[2];
Packit 6c4009
Packit 6c4009
	  /* Collect IPv6 information first.  */
Packit 6c4009
	  while (1)
Packit 6c4009
	    {
Packit 6c4009
	      rc6 = 0;
Packit 6c4009
	      status[0] = DL_CALL_FCT (fct, (key, AF_INET6, &th[0],
Packit 6c4009
					     tmpbuf6.data, tmpbuf6.length,
Packit 6c4009
					     &rc6, &herrno, &ttl,
Packit 6c4009
					     &canon));
Packit 6c4009
	      if (rc6 != ERANGE || herrno != NETDB_INTERNAL)
Packit 6c4009
		break;
Packit 6c4009
	      if (!scratch_buffer_grow (&tmpbuf6))
Packit 6c4009
		{
Packit 6c4009
		  rc6 = ENOMEM;
Packit 6c4009
		  break;
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  if (rc6 != 0 && herrno == NETDB_INTERNAL)
Packit 6c4009
	    goto out;
Packit 6c4009
Packit 6c4009
	  /* Next collect IPv4 information.  */
Packit 6c4009
	  while (1)
Packit 6c4009
	    {
Packit 6c4009
	      rc4 = 0;
Packit 6c4009
	      status[1] = DL_CALL_FCT (fct, (key, AF_INET, &th[1],
Packit 6c4009
					     tmpbuf4.data, tmpbuf4.length,
Packit 6c4009
					     &rc4, &herrno,
Packit 6c4009
					     ttl == INT32_MAX ? &ttl : NULL,
Packit 6c4009
					     canon == NULL ? &canon : NULL));
Packit 6c4009
	      if (rc4 != ERANGE || herrno != NETDB_INTERNAL)
Packit 6c4009
		break;
Packit 6c4009
	      if (!scratch_buffer_grow (&tmpbuf4))
Packit 6c4009
		{
Packit 6c4009
		  rc4 = ENOMEM;
Packit 6c4009
		  break;
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  if (rc4 != 0 && herrno == NETDB_INTERNAL)
Packit 6c4009
	    goto out;
Packit 6c4009
Packit 6c4009
	  if (status[0] != NSS_STATUS_SUCCESS
Packit 6c4009
	      && status[1] != NSS_STATUS_SUCCESS)
Packit 6c4009
	    goto next_nip;
Packit 6c4009
Packit 6c4009
	  /* We found the data.  Count the addresses and the size.  */
Packit 6c4009
	  for (int j = 0; j < 2; ++j)
Packit 6c4009
	    if (status[j] == NSS_STATUS_SUCCESS)
Packit 6c4009
	      for (int i = 0; th[j].h_addr_list[i] != NULL; ++i)
Packit 6c4009
		{
Packit 6c4009
		  ++naddrs;
Packit 6c4009
		  addrslen += th[j].h_length;
Packit 6c4009
		}
Packit 6c4009
Packit 6c4009
	  if (canon == NULL)
Packit 6c4009
	    {
Packit 6c4009
	      /* Determine the canonical name.  */
Packit 6c4009
	      nss_getcanonname_r cfct;
Packit 6c4009
	      cfct = __nss_lookup_function (nip, "getcanonname_r");
Packit 6c4009
	      if (cfct != NULL)
Packit 6c4009
		{
Packit 6c4009
		  char *s;
Packit 6c4009
		  int rc;
Packit 6c4009
Packit 6c4009
		  if (DL_CALL_FCT (cfct, (key, canonbuf.data, canonbuf.length,
Packit 6c4009
					  &s, &rc, &herrno))
Packit 6c4009
		      == NSS_STATUS_SUCCESS)
Packit 6c4009
		    canon = s;
Packit 6c4009
		  else
Packit 6c4009
		    /* Set to name now to avoid using gethostbyaddr.  */
Packit 6c4009
		    canon = key;
Packit 6c4009
		}
Packit 6c4009
	      else
Packit 6c4009
		{
Packit 6c4009
		  struct hostent *hstent = NULL;
Packit 6c4009
		  int herrno;
Packit 6c4009
		  struct hostent hstent_mem;
Packit 6c4009
		  void *addr;
Packit 6c4009
		  size_t addrlen;
Packit 6c4009
		  int addrfamily;
Packit 6c4009
Packit 6c4009
		  if (status[1] == NSS_STATUS_SUCCESS)
Packit 6c4009
		    {
Packit 6c4009
		      addr = th[1].h_addr_list[0];
Packit 6c4009
		      addrlen = sizeof (struct in_addr);
Packit 6c4009
		      addrfamily = AF_INET;
Packit 6c4009
		    }
Packit 6c4009
		  else
Packit 6c4009
		    {
Packit 6c4009
		      addr = th[0].h_addr_list[0];
Packit 6c4009
		      addrlen = sizeof (struct in6_addr);
Packit 6c4009
		      addrfamily = AF_INET6;
Packit 6c4009
		    }
Packit 6c4009
Packit 6c4009
		  int rc;
Packit 6c4009
		  while (1)
Packit 6c4009
		    {
Packit 6c4009
		      rc = __gethostbyaddr2_r (addr, addrlen, addrfamily,
Packit 6c4009
					       &hstent_mem,
Packit 6c4009
					       canonbuf.data, canonbuf.length,
Packit 6c4009
					       &hstent, &herrno, NULL);
Packit 6c4009
		      if (rc != ERANGE || herrno != NETDB_INTERNAL)
Packit 6c4009
			break;
Packit 6c4009
		      if (!scratch_buffer_grow (&canonbuf))
Packit 6c4009
			{
Packit 6c4009
			  rc = ENOMEM;
Packit 6c4009
			  break;
Packit 6c4009
			}
Packit 6c4009
		    }
Packit 6c4009
Packit 6c4009
		  if (rc == 0)
Packit 6c4009
		    {
Packit 6c4009
		      if (hstent != NULL)
Packit 6c4009
			canon = hstent->h_name;
Packit 6c4009
		      else
Packit 6c4009
			canon = key;
Packit 6c4009
		    }
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  canonlen = canon == NULL ? 0 : (strlen (canon) + 1);
Packit 6c4009
Packit 6c4009
	  total = sizeof (*dataset) + naddrs + addrslen + canonlen;
Packit 6c4009
Packit 6c4009
Packit 6c4009
	  /* Now we can allocate the data structure.  If the TTL of the
Packit 6c4009
	     entry is reported as zero do not cache the entry at all.  */
Packit 6c4009
	  if (ttl != 0 && he == NULL)
Packit 6c4009
	    dataset = (struct dataset *) mempool_alloc (db, total
Packit 6c4009
							+ req->key_len, 1);
Packit 6c4009
Packit 6c4009
	  if (dataset == NULL)
Packit 6c4009
	    {
Packit 6c4009
	      /* We cannot permanently add the result in the moment.  But
Packit 6c4009
		 we can provide the result as is.  Store the data in some
Packit 6c4009
		 temporary memory.  */
Packit 6c4009
	      dataset = (struct dataset *) alloca (total + req->key_len);
Packit 6c4009
Packit 6c4009
	      /* We cannot add this record to the permanent database.  */
Packit 6c4009
	      alloca_used = true;
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  /* Fill in the address and address families.  */
Packit 6c4009
	  char *addrs = dataset->strdata;
Packit 6c4009
	  uint8_t *family = (uint8_t *) (addrs + addrslen);
Packit 6c4009
Packit 6c4009
	  for (int j = 0; j < 2; ++j)
Packit 6c4009
	    if (status[j] == NSS_STATUS_SUCCESS)
Packit 6c4009
	      for (int i = 0; th[j].h_addr_list[i] != NULL; ++i)
Packit 6c4009
		{
Packit 6c4009
		  addrs = mempcpy (addrs, th[j].h_addr_list[i],
Packit 6c4009
				   th[j].h_length);
Packit 6c4009
		  *family++ = th[j].h_addrtype;
Packit 6c4009
		}
Packit 6c4009
Packit 6c4009
	  cp = family;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      timeout = datahead_init_pos (&dataset->head, total + req->key_len,
Packit 6c4009
				   total - offsetof (struct dataset, resp),
Packit 6c4009
				   he == NULL ? 0 : dh->nreloads + 1,
Packit 6c4009
				   ttl == INT32_MAX ? db->postimeout : ttl);
Packit 6c4009
Packit 6c4009
      /* Fill in the rest of the dataset.  */
Packit 6c4009
      dataset->resp.version = NSCD_VERSION;
Packit 6c4009
      dataset->resp.found = 1;
Packit 6c4009
      dataset->resp.naddrs = naddrs;
Packit 6c4009
      dataset->resp.addrslen = addrslen;
Packit 6c4009
      dataset->resp.canonlen = canonlen;
Packit 6c4009
      dataset->resp.error = NETDB_SUCCESS;
Packit 6c4009
Packit 6c4009
      if (canon != NULL)
Packit 6c4009
	cp = mempcpy (cp, canon, canonlen);
Packit 6c4009
Packit 6c4009
      key_copy = memcpy (cp, key, req->key_len);
Packit 6c4009
Packit 6c4009
      assert (cp == (char *) dataset + total);
Packit 6c4009
Packit 6c4009
      /* Now we can determine whether on refill we have to create a
Packit 6c4009
	 new record or not.  */
Packit 6c4009
      if (he != NULL)
Packit 6c4009
	{
Packit 6c4009
	  assert (fd == -1);
Packit 6c4009
Packit 6c4009
	  if (total + req->key_len == dh->allocsize
Packit 6c4009
	      && total - offsetof (struct dataset, resp) == dh->recsize
Packit 6c4009
	      && memcmp (&dataset->resp, dh->data,
Packit 6c4009
			 dh->allocsize - offsetof (struct dataset,
Packit 6c4009
						   resp)) == 0)
Packit 6c4009
	    {
Packit 6c4009
	      /* The data has not changed.  We will just bump the
Packit 6c4009
		 timeout value.  Note that the new record has been
Packit 6c4009
		 allocated on the stack and need not be freed.  */
Packit 6c4009
	      dh->timeout = dataset->head.timeout;
Packit 6c4009
	      dh->ttl = dataset->head.ttl;
Packit 6c4009
	      ++dh->nreloads;
Packit 6c4009
	    }
Packit 6c4009
	  else
Packit 6c4009
	    {
Packit 6c4009
	      /* We have to create a new record.  Just allocate
Packit 6c4009
		 appropriate memory and copy it.  */
Packit 6c4009
	      struct dataset *newp
Packit 6c4009
		= (struct dataset *) mempool_alloc (db, total + req->key_len,
Packit 6c4009
						    1);
Packit 6c4009
	      if (__glibc_likely (newp != NULL))
Packit 6c4009
		{
Packit 6c4009
		  /* Adjust pointer into the memory block.  */
Packit 6c4009
		  key_copy = (char *) newp + (key_copy - (char *) dataset);
Packit 6c4009
Packit 6c4009
		  dataset = memcpy (newp, dataset, total + req->key_len);
Packit 6c4009
		  alloca_used = false;
Packit 6c4009
		}
Packit 6c4009
Packit 6c4009
	      /* Mark the old record as obsolete.  */
Packit 6c4009
	      dh->usable = false;
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
	{
Packit 6c4009
	  /* We write the dataset before inserting it to the database
Packit 6c4009
	     since while inserting this thread might block and so
Packit 6c4009
	     would unnecessarily let the receiver wait.  */
Packit 6c4009
	  assert (fd != -1);
Packit 6c4009
Packit 6c4009
	  writeall (fd, &dataset->resp, dataset->head.recsize);
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      goto out;
Packit 6c4009
Packit 6c4009
next_nip:
Packit 6c4009
      if (nss_next_action (nip, status[1]) == NSS_ACTION_RETURN)
Packit 6c4009
	break;
Packit 6c4009
Packit 6c4009
      if (nip->next == NULL)
Packit 6c4009
	no_more = -1;
Packit 6c4009
      else
Packit 6c4009
	nip = nip->next;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* No result found.  Create a negative result record.  */
Packit 6c4009
  if (he != NULL && rc4 == EAGAIN)
Packit 6c4009
    {
Packit 6c4009
      /* If we have an old record available but cannot find one now
Packit 6c4009
	 because the service is not available we keep the old record
Packit 6c4009
	 and make sure it does not get removed.  */
Packit 6c4009
      if (reload_count != UINT_MAX && dh->nreloads == reload_count)
Packit 6c4009
	/* Do not reset the value if we never not reload the record.  */
Packit 6c4009
	dh->nreloads = reload_count - 1;
Packit 6c4009
Packit 6c4009
      /* Reload with the same time-to-live value.  */
Packit 6c4009
      timeout = dh->timeout = time (NULL) + dh->ttl;
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      /* We have no data.  This means we send the standard reply for
Packit 6c4009
	 this case.  */
Packit 6c4009
      total = sizeof (notfound);
Packit 6c4009
Packit 6c4009
      if (fd != -1)
Packit 6c4009
	TEMP_FAILURE_RETRY (send (fd, &notfound, total, MSG_NOSIGNAL));
Packit 6c4009
Packit 6c4009
      /* If we have a transient error or cannot permanently store the
Packit 6c4009
	 result, so be it.  */
Packit 6c4009
      if (rc4 == EAGAIN || __builtin_expect (db->negtimeout == 0, 0))
Packit 6c4009
	{
Packit 6c4009
	  /* Mark the old entry as obsolete.  */
Packit 6c4009
	  if (dh != NULL)
Packit 6c4009
	    dh->usable = false;
Packit 6c4009
	  dataset = NULL;
Packit 6c4009
	}
Packit 6c4009
      else if ((dataset = mempool_alloc (db, (sizeof (struct dataset)
Packit 6c4009
					      + req->key_len), 1)) != NULL)
Packit 6c4009
	{
Packit 6c4009
	  timeout = datahead_init_neg (&dataset->head,
Packit 6c4009
				       sizeof (struct dataset) + req->key_len,
Packit 6c4009
				       total, db->negtimeout);
Packit 6c4009
Packit 6c4009
	  /* This is the reply.  */
Packit 6c4009
	  memcpy (&dataset->resp, &notfound, total);
Packit 6c4009
Packit 6c4009
	  /* Copy the key data.  */
Packit 6c4009
	  key_copy = memcpy (dataset->strdata, key, req->key_len);
Packit 6c4009
	}
Packit 6c4009
   }
Packit 6c4009
Packit 6c4009
 out:
Packit 6c4009
  __resolv_context_enable_inet6 (ctx, enable_inet6);
Packit 6c4009
  __resolv_context_put (ctx);
Packit 6c4009
Packit 6c4009
  if (dataset != NULL && !alloca_used)
Packit 6c4009
    {
Packit 6c4009
      /* If necessary, we also propagate the data to disk.  */
Packit 6c4009
      if (db->persistent)
Packit 6c4009
	{
Packit 6c4009
	  // XXX async OK?
Packit 6c4009
	  uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
Packit 6c4009
	  msync ((void *) pval,
Packit 6c4009
		 ((uintptr_t) dataset & pagesize_m1) + total + req->key_len,
Packit 6c4009
		 MS_ASYNC);
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      (void) cache_add (req->type, key_copy, req->key_len, &dataset->head,
Packit 6c4009
			true, db, uid, he == NULL);
Packit 6c4009
Packit 6c4009
      pthread_rwlock_unlock (&db->lock);
Packit 6c4009
Packit 6c4009
      /* Mark the old entry as obsolete.  */
Packit 6c4009
      if (dh != NULL)
Packit 6c4009
	dh->usable = false;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  scratch_buffer_free (&tmpbuf6);
Packit 6c4009
  scratch_buffer_free (&tmpbuf4);
Packit 6c4009
  scratch_buffer_free (&canonbuf);
Packit 6c4009
Packit 6c4009
  return timeout;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
addhstai (struct database_dyn *db, int fd, request_header *req, void *key,
Packit 6c4009
	  uid_t uid)
Packit 6c4009
{
Packit 6c4009
  addhstaiX (db, fd, req, key, uid, NULL, NULL);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
time_t
Packit 6c4009
readdhstai (struct database_dyn *db, struct hashentry *he, struct datahead *dh)
Packit 6c4009
{
Packit 6c4009
  request_header req =
Packit 6c4009
    {
Packit 6c4009
      .type = GETAI,
Packit 6c4009
      .key_len = he->len
Packit 6c4009
    };
Packit 6c4009
Packit 6c4009
  return addhstaiX (db, -1, &req, db->data + he->key, he->owner, he, dh);
Packit 6c4009
}