about summary refs log tree commit diff
path: root/nscd/hstcache.c
diff options
context:
space:
mode:
authorUlrich Drepper <drepper@redhat.com>2004-08-26 18:35:05 +0000
committerUlrich Drepper <drepper@redhat.com>2004-08-26 18:35:05 +0000
commita95a08b4af38992cbcf3d1da97199ef19528fbde (patch)
tree669bba61a337bf8b9c9c15857877bc589792435c /nscd/hstcache.c
parent1114ffff54dfbd35cbff9c845376b8221c2c9ced (diff)
downloadglibc-a95a08b4af38992cbcf3d1da97199ef19528fbde.tar.gz
glibc-a95a08b4af38992cbcf3d1da97199ef19528fbde.tar.xz
glibc-a95a08b4af38992cbcf3d1da97199ef19528fbde.zip
Update.
2004-08-26  Ulrich Drepper  <drepper@redhat.com>

	* nscd/cache.c: Major rewrite.  The data is now optionally kept in
	a mmaped memory region which is automatically mirrored on disk.
	This implements persistent data storage.  The Memory handled
	needed to be completely revamped, it now uses a garbage collection
	mechanism instead of malloc.
	* nscd/connections.c: Likewise.
	* nscd/nscd.c: Likewise.
	* nscd/nscd.h: Likewise.
	* nscd/nscd_conf.c: Likewise.
	* nscd/nscd_stat.c: Likewise.
	* nscd/grpcache.c: Likewise.
	* nscd/hstcache.c:: Likewise.
	* nscd/pwdcache.c:: Likewise.
	* nscd/Makefile: Add rules to build mem.c.
	* nscd/mem.c: New file.
	* nscd/nscd.conf: Describe new configuration options.
Diffstat (limited to 'nscd/hstcache.c')
-rw-r--r--nscd/hstcache.c706
1 files changed, 418 insertions, 288 deletions
diff --git a/nscd/hstcache.c b/nscd/hstcache.c
index 44b76aa7c0..5a536b3346 100644
--- a/nscd/hstcache.c
+++ b/nscd/hstcache.c
@@ -22,6 +22,7 @@
 #include <assert.h>
 #include <errno.h>
 #include <error.h>
+#include <libintl.h>
 #include <netdb.h>
 #include <stdbool.h>
 #include <stddef.h>
@@ -30,9 +31,9 @@
 #include <string.h>
 #include <time.h>
 #include <unistd.h>
-#include <libintl.h>
 #include <arpa/inet.h>
 #include <arpa/nameser.h>
+#include <sys/mman.h>
 #include <stackinfo.h>
 
 #include "nscd.h"
@@ -74,51 +75,88 @@ static const hst_response_header notfound =
 };
 
 
-struct hostdata
-{
-  hst_response_header resp;
-  char strdata[0];
-};
-
-
 static void
-cache_addhst (struct database *db, int fd, request_header *req, void *key,
-	      struct hostent *hst, uid_t owner, int add_addr)
+cache_addhst (struct database_dyn *db, int fd, request_header *req,
+	      const void *key, struct hostent *hst, uid_t owner, int add_addr,
+	      struct hashentry *he, struct datahead *dh, int errval)
 {
   ssize_t total;
   ssize_t written;
   time_t t = time (NULL);
 
+  /* We allocate all data in one memory block: the iov vector,
+     the response header and the dataset itself.  */
+  struct dataset
+  {
+    struct datahead head;
+    hst_response_header resp;
+    char strdata[0];
+  } *dataset;
+
+  assert (offsetof (struct dataset, resp) == offsetof (struct datahead, data));
+
   if (hst == NULL)
     {
-      /* We have no data.  This means we send the standard reply for this
-	 case.  */
-      total = sizeof (notfound);
+      if (he != NULL && errval == EAGAIN)
+	{
+	  /* If we have an old record available but cannot find one
+	     now because the service is not available we keep the old
+	     record and make sure it does not get removed.  */
+	  if (reload_count != UINT_MAX)
+	    /* Do not reset the value if we never not reload the record.  */
+	    dh->nreloads = reload_count - 1;
+
+	  written = total = 0;
+	}
+      else
+	{
+	  /* We have no data.  This means we send the standard reply for this
+	     case.  */
+	  written = total = sizeof (notfound);
 
-      written = TEMP_FAILURE_RETRY (write (fd, &notfound, total));
+	  if (fd != -1)
+	    written = TEMP_FAILURE_RETRY (write (fd, &notfound, total));
 
-      void *copy = malloc (req->key_len);
-      /* If we cannot allocate memory simply do not cache the information.  */
-      if (copy != NULL)
-	{
-	  memcpy (copy, key, req->key_len);
+	  dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len);
+	  /* If we cannot permanently store the result, so be it.  */
+	  if (dataset != NULL)
+	    {
+	      dataset->head.allocsize = sizeof (struct dataset) + req->key_len;
+	      dataset->head.recsize = total;
+	      dataset->head.notfound = true;
+	      dataset->head.nreloads = 0;
+	      dataset->head.usable = true;
 
-	  /* Compute the timeout time.  */
-	  t += db->negtimeout;
+	      /* Compute the timeout time.  */
+	      dataset->head.timeout = t + db->negtimeout;
 
-	  /* Now get the lock to safely insert the records.  */
-	  pthread_rwlock_rdlock (&db->lock);
+	      /* This is the reply.  */
+	      memcpy (&dataset->resp, &notfound, total);
 
-	  cache_add (req->type, copy, req->key_len, &notfound,
-		     sizeof (notfound), (void *) -1, 0, t, db, owner);
+	      /* Copy the key data.  */
+	      memcpy (dataset->strdata, key, req->key_len);
 
-	  pthread_rwlock_unlock (&db->lock);
+	      /* Now get the lock to safely insert the records.  */
+	      pthread_rwlock_rdlock (&db->lock);
+
+	      if (cache_add (req->type, &dataset->strdata, req->key_len,
+			     &dataset->head, true, db, owner) < 0)
+		/* Ensure the data can be recovered.  */
+		dataset->head.usable = false;
+
+	      pthread_rwlock_unlock (&db->lock);
+
+	      /* Mark the old entry as obsolete.  */
+	      if (dh != NULL)
+		dh->usable = false;
+	    }
+	  else
+	    ++db->head->addfailed;
 	}
     }
   else
     {
       /* Determine the I/O structure.  */
-      struct hostdata *data;
       size_t h_name_len = strlen (hst->h_name) + 1;
       size_t h_aliases_cnt;
       uint32_t *h_aliases_len;
@@ -148,28 +186,66 @@ cache_addhst (struct database *db, int fd, request_header *req, void *key,
       for (cnt = 0; hst->h_addr_list[cnt]; ++cnt)
 	++h_addr_list_cnt;
 
-      /* We allocate all data in one memory block: the iov vector,
-	 the response header and the dataset itself.  */
-      total += (sizeof (struct hostdata)
+      if (h_addr_list_cnt == 0)
+	/* Invalid entry.  */
+	return;
+
+      total += (sizeof (struct dataset)
 		+ h_name_len
 		+ h_aliases_cnt * sizeof (uint32_t)
 		+ h_addr_list_cnt * hst->h_length);
+      written = total;
 
-      data = (struct hostdata *) malloc (total + req->key_len);
-      if (data == NULL)
-	/* There is no reason to go on.  */
-	error (EXIT_FAILURE, errno, _("while allocating cache entry"));
+      /* If we refill the cache, first assume the reconrd did not
+	 change.  Allocate memory on the cache since it is likely
+	 discarded anyway.  If it turns out to be necessary to have a
+	 new record we can still allocate real memory.  */
+      bool alloca_used = false;
+      dataset = NULL;
 
-      data->resp.version = NSCD_VERSION;
-      data->resp.found = 1;
-      data->resp.h_name_len = h_name_len;
-      data->resp.h_aliases_cnt = h_aliases_cnt;
-      data->resp.h_addrtype = hst->h_addrtype;
-      data->resp.h_length = hst->h_length;
-      data->resp.h_addr_list_cnt = h_addr_list_cnt;
-      data->resp.error = NETDB_SUCCESS;
+      /* If the record contains more than one IP address (used for
+	 load balancing etc) don't cache the entry.  This is something
+	 the current cache handling cannot handle and it is more than
+	 questionable whether it is worthwhile complicating the cache
+	 handling just for handling such a special case. */
+      if (he == NULL && (add_addr || hst->h_addr_list[1] == NULL))
+	{
+	  dataset = (struct dataset *) mempool_alloc (db,
+						      total + req->key_len);
+	  if (dataset == NULL)
+	    ++db->head->addfailed;
+	}
 
-      cp = data->strdata;
+      if (dataset == NULL)
+	{
+	  /* We cannot permanently add the result in the moment.  But
+	     we can provide the result as is.  Store the data in some
+	     temporary memory.  */
+	  dataset = (struct dataset *) alloca (total + req->key_len);
+
+	  /* We cannot add this record to the permanent database.  */
+	  alloca_used = true;
+	}
+
+      dataset->head.allocsize = total + req->key_len;
+      dataset->head.recsize = total - offsetof (struct dataset, resp);
+      dataset->head.notfound = false;
+      dataset->head.nreloads = he == NULL ? 0 : (dh->nreloads + 1);
+      dataset->head.usable = true;
+
+      /* Compute the timeout time.  */
+      dataset->head.timeout = t + db->postimeout;
+
+      dataset->resp.version = NSCD_VERSION;
+      dataset->resp.found = 1;
+      dataset->resp.h_name_len = h_name_len;
+      dataset->resp.h_aliases_cnt = h_aliases_cnt;
+      dataset->resp.h_addrtype = hst->h_addrtype;
+      dataset->resp.h_length = hst->h_length;
+      dataset->resp.h_addr_list_cnt = h_addr_list_cnt;
+      dataset->resp.error = NETDB_SUCCESS;
+
+      cp = dataset->strdata;
 
       cp = mempcpy (cp, hst->h_name, h_name_len);
       cp = mempcpy (cp, h_aliases_len, h_aliases_cnt * sizeof (uint32_t));
@@ -184,7 +260,9 @@ cache_addhst (struct database *db, int fd, request_header *req, void *key,
       for (cnt = 0; cnt < h_aliases_cnt; ++cnt)
 	cp = mempcpy (cp, hst->h_aliases[cnt], h_aliases_len[cnt]);
 
-      assert (cp == data->strdata + total - sizeof (hst_response_header));
+      assert (cp
+	      == dataset->strdata + total - offsetof (struct dataset,
+						      strdata));
 
       /* If we are adding a GETHOSTBYNAME{,v6} entry we must be prepared
 	 that the answer we get from the NSS does not contain the key
@@ -193,90 +271,221 @@ cache_addhst (struct database *db, int fd, request_header *req, void *key,
 	 we explicitly add the name here.  */
       if (req->type == GETHOSTBYNAME || req->type == GETHOSTBYNAMEv6)
 	key_copy = memcpy (cp, key, req->key_len);
+      else
+	memset (cp, '\0', req->key_len);
 
-      /* We write the dataset before inserting it to the database
-	 since while inserting this thread might block and so would
-	 unnecessarily let the receiver wait.  */
-      written = TEMP_FAILURE_RETRY (write (fd, data, total));
+      /* Now we can determine whether on refill we have to create a new
+	 record or not.  */
+      if (he != NULL)
+	{
+	  assert (fd == -1);
 
-      /* If the record contains more than one IP address (used for
-         load balancing etc) don't cache the entry.  This is something
-         the current cache handling cannot handle and it is more than
-         questionable whether it is worthwhile complicating the cache
-         handling just for handling such a special case.  */
-      if (!add_addr && hst->h_addr_list[1] != NULL)
+	  if (total + req->key_len == dh->allocsize
+	      && total - offsetof (struct dataset, resp) == dh->recsize
+	      && memcmp (&dataset->resp, dh->data,
+			 dh->allocsize - offsetof (struct dataset, resp)) == 0)
+	    {
+	      /* The sata has not changed.  We will just bump the
+		 timeout value.  Note that the new record has been
+		 allocated on the stack and need not be freed.  */
+	      dh->timeout = dataset->head.timeout;
+	      ++dh->nreloads;
+	    }
+	  else
+	    {
+	      /* We have to create a new record.  Just allocate
+		 appropriate memory and copy it.  */
+	      struct dataset *newp
+		= (struct dataset *) mempool_alloc (db, total + req->key_len);
+	      if (newp != NULL)
+		{
+		  /* Adjust pointers into the memory block.  */
+		  addresses = (char *) newp + (addresses - (char *) dataset);
+		  aliases = (char *) newp + (aliases - (char *) dataset);
+		  if (key_copy != NULL)
+		    key_copy = (char *) newp + (key_copy - (char *) dataset);
+
+		  dataset = memcpy (newp, dataset, total + req->key_len);
+		  alloca_used = false;
+		}
+
+	      /* Mark the old record as obsolete.  */
+	      dh->usable = false;
+	    }
+	}
+      else
 	{
-	  free (data);
-	  return;
+	  /* We write the dataset before inserting it to the database
+	     since while inserting this thread might block and so would
+	     unnecessarily keep the receiver waiting.  */
+	  assert (fd != -1);
+
+	  written = TEMP_FAILURE_RETRY (write (fd, &dataset->resp, total));
 	}
 
-      addr_list_type = (hst->h_length == NS_INADDRSZ
-			? GETHOSTBYADDR : GETHOSTBYADDRv6);
+      /* Add the record to the database.  But only if it has not been
+	 stored on the stack.
 
-      /* Compute the timeout time.  */
-      t += db->postimeout;
-
-      /* Now get the lock to safely insert the records.  */
-      pthread_rwlock_rdlock (&db->lock);
-
-      /* First add all the aliases.  */
-      assert (add_addr || hst->h_addr_list[1] == NULL);
-      if (!add_addr)
-	for (cnt = 0; cnt < h_aliases_cnt; ++cnt)
-	  {
-	    if (addr_list_type == GETHOSTBYADDR)
-	      cache_add (GETHOSTBYNAME, aliases, h_aliases_len[cnt], data,
-			 total, data, 0, t, db, owner);
-
-	    cache_add (GETHOSTBYNAMEv6, aliases, h_aliases_len[cnt], data,
-		       total, data, 0, t, db, owner);
-
-	    aliases += h_aliases_len[cnt];
-	  }
-
-      /* Next the normal addresses.  */
-      if (add_addr)
-	for (cnt = 0; cnt < h_addr_list_cnt; ++cnt)
-	  {
-	    cache_add (addr_list_type, addresses, hst->h_length, data, total,
-		       data, cnt + 1 == h_addr_list_cnt, t, db, owner);
-	    addresses += hst->h_length;
-	  }
-
-      /* If necessary the IPv6 addresses.  */
-      if (add_addr && addr_list_type == GETHOSTBYADDR)
-	for (cnt = 0; cnt < h_addr_list_cnt; ++cnt)
-	  {
-	    cache_add (GETHOSTBYADDRv6, addresses, IN6ADDRSZ, data, total,
-		       data, 0, t, db, owner);
-	    addresses += IN6ADDRSZ;
-	  }
-
-      /* Avoid adding names if more than one address is available.  See
-	 above for more info.  */
-      if (!add_addr)
+	 If the record contains more than one IP address (used for
+	 load balancing etc) don't cache the entry.  This is something
+	 the current cache handling cannot handle and it is more than
+	 questionable whether it is worthwhile complicating the cache
+	 handling just for handling such a special case. */
+      if (! alloca_used)
 	{
-	  /* If necessary add the key for this request.
+	  /* If necessary, we also propagate the data to disk.  */
+	  if (db->persistent)
+	    // XXX async OK?
+	    msync (dataset, total + req->key_len, MS_ASYNC);
+
+	  addr_list_type = (hst->h_length == NS_INADDRSZ
+			    ? GETHOSTBYADDR : GETHOSTBYADDRv6);
+
+	  /* Now get the lock to safely insert the records.  */
+	  pthread_rwlock_rdlock (&db->lock);
+
+	  /* NB: the following code is really complicated.  It has
+	     seemlingly duplicated code paths which do the same.  The
+	     problem is that we always must add the hash table entry
+	     with the FIRST flag set first.  Otherwise we get dangling
+	     pointers in case memory allocation fails.  */
+	  assert (add_addr || hst->h_addr_list[1] == NULL);
 
-	     Note: hst->h_addr_list[1] == NULL.  */
-	  if (req->type == GETHOSTBYNAME || req->type == GETHOSTBYNAMEv6)
+	  /* Add the normal addresses.  */
+	  if (add_addr)
 	    {
+	      for (cnt = 0; cnt < h_addr_list_cnt; ++cnt)
+		{
+		  if (cache_add (addr_list_type, addresses, hst->h_length,
+				 &dataset->head, cnt == 0, db, owner) < 0)
+		    {
+		      /* Ensure the data can be recovered.  */
+		      if (cnt == 0)
+			dataset->head.usable = false;
+		      goto out;
+		    }
+		  addresses += hst->h_length;
+		}
+
+	      /* If necessary the IPv6 addresses.  */
 	      if (addr_list_type == GETHOSTBYADDR)
-		cache_add (GETHOSTBYNAME, key_copy, req->key_len, data, total,
-			   data, 0, t, db, owner);
-	      cache_add (GETHOSTBYNAMEv6, key_copy, req->key_len, data,
-			 total, data, 0, t, db, owner);
+		for (cnt = 0; cnt < h_addr_list_cnt; ++cnt)
+		  {
+		    if (cache_add (GETHOSTBYADDRv6, addresses, IN6ADDRSZ,
+				   &dataset->head, false, db, owner) < 0)
+		      goto out;
+
+		    addresses += IN6ADDRSZ;
+		  }
+	    }
+	  /* Avoid adding names if more than one address is available.  See
+	     above for more info.  */
+	  else
+	    {
+	      assert (req->type == GETHOSTBYNAME
+		      || req->type == GETHOSTBYNAMEv6
+		      || req->type == GETHOSTBYADDR
+		      || req->type == GETHOSTBYADDRv6);
+
+	      /* If necessary add the key for this request.  */
+	      if (req->type == GETHOSTBYNAME)
+		{
+		  bool first = true;
+		  if (addr_list_type == GETHOSTBYADDR)
+		    {
+		      if (cache_add (GETHOSTBYNAME, key_copy, req->key_len,
+				     &dataset->head, true, db, owner) < 0)
+			{
+			  /* Could not allocate memory.  Make sure the
+			     data gets discarded.  */
+			  dataset->head.usable = false;
+			  goto out;
+			}
+
+		      first = false;
+		    }
+		  if (cache_add (GETHOSTBYNAMEv6, key_copy, req->key_len,
+				 &dataset->head, first, db, owner) < 0)
+		    {
+		      /* Could not allocate memory.  Make sure the
+			 data gets discarded.  */
+		      if (first)
+			dataset->head.usable = false;
+		      goto out;
+		    }
+		}
+	      else if (req->type == GETHOSTBYNAMEv6)
+		{
+		  if (cache_add (GETHOSTBYNAMEv6, key_copy, req->key_len,
+				 &dataset->head, true, db, owner) < 0)
+		    {
+		      /* Could not allocate memory.  Make sure the
+			 data gets discarded.  */
+		      dataset->head.usable = false;
+		      goto out;
+		    }
+
+		  if (addr_list_type == GETHOSTBYADDR
+		      && cache_add (GETHOSTBYNAME, key_copy, req->key_len,
+				    &dataset->head, false, db, owner) < 0)
+		    goto out;
+		}
+
+	      /* And finally the name.  We mark this as the last entry.  */
+	      if (addr_list_type == GETHOSTBYADDR
+		  && req->type == GETHOSTBYADDR
+		  && cache_add (GETHOSTBYNAME, dataset->strdata, h_name_len,
+				&dataset->head, true, db, owner) < 0)
+		{
+		  /* Could not allocate memory.  Make sure the
+		     data gets discarded.  */
+		  dataset->head.usable = false;
+		  goto out;
+		}
+
+	      if (cache_add (GETHOSTBYNAMEv6, dataset->strdata,
+			     h_name_len, &dataset->head,
+			     ((req->type == GETHOSTBYADDR
+			       && addr_list_type != GETHOSTBYADDR)
+			      || req->type == GETHOSTBYADDRv6), db,
+			     owner) < 0)
+		{
+		  /* Could not allocate memory.  Make sure the
+		     data gets discarded.  */
+		  if ((req->type == GETHOSTBYADDR
+		       && addr_list_type != GETHOSTBYADDR)
+		      || req->type == GETHOSTBYADDRv6)
+		    dataset->head.usable = false;
+		  goto out;
+		}
+
+	      if (addr_list_type == GETHOSTBYADDR
+		  && req->type != GETHOSTBYADDR
+		  && cache_add (GETHOSTBYNAME, dataset->strdata, h_name_len,
+				&dataset->head, false, db, owner) < 0)
+		goto out;
+
+	      /* First add all the aliases.  */
+	      for (cnt = 0; cnt < h_aliases_cnt; ++cnt)
+		{
+		  if (addr_list_type == GETHOSTBYADDR)
+		    if (cache_add (GETHOSTBYNAME, aliases,
+				   h_aliases_len[cnt], &dataset->head,
+				   false, db, owner) < 0)
+		      break;
+
+		  if (cache_add (GETHOSTBYNAMEv6, aliases,
+				 h_aliases_len[cnt], &dataset->head,
+				 false, db, owner) < 0)
+		    break;
+
+		  aliases += h_aliases_len[cnt];
+		}
 	    }
 
-	  /* And finally the name.  We mark this as the last entry.  */
-	  if (addr_list_type == GETHOSTBYADDR)
-	    cache_add (GETHOSTBYNAME, data->strdata, h_name_len, data, total,
-		       data, 0, t, db, owner);
-	  cache_add (GETHOSTBYNAMEv6, data->strdata, h_name_len, data,
-		     total, data, 1, t, db, owner);
+	out:
+	  pthread_rwlock_unlock (&db->lock);
 	}
-
-      pthread_rwlock_unlock (&db->lock);
     }
 
   if (__builtin_expect (written != total, 0) && debug_level > 0)
@@ -288,9 +497,28 @@ cache_addhst (struct database *db, int fd, request_header *req, void *key,
 }
 
 
-void
-addhstbyname (struct database *db, int fd, request_header *req,
-	      void *key, uid_t uid)
+static int
+lookup (int type, void *key, struct hostent *resultbufp, char *buffer,
+	size_t buflen, struct hostent **hst)
+{
+  if (type == GETHOSTBYNAME)
+    return __gethostbyname2_r (key, AF_INET, resultbufp, buffer, buflen, hst,
+			       &h_errno);
+  else if (type == GETHOSTBYNAMEv6)
+    return __gethostbyname2_r (key, AF_INET6, resultbufp, buffer, buflen, hst,
+			       &h_errno);
+  else if (type == GETHOSTBYADDR)
+    return __gethostbyaddr_r (key, NS_INADDRSZ, AF_INET, resultbufp, buffer,
+			      buflen, hst, &h_errno);
+  else
+    return __gethostbyaddr_r (key, NS_IN6ADDRSZ, AF_INET6, resultbufp, buffer,
+			      buflen, hst, &h_errno);
+}
+
+
+static void
+addhstbyX (struct database_dyn *db, int fd, request_header *req,
+	   void *key, uid_t uid, struct hashentry *he, struct datahead *dh)
 {
   /* Search for the entry matching the key.  Please note that we don't
      look again in the table whether the dataset is now available.  We
@@ -302,20 +530,25 @@ addhstbyname (struct database *db, int fd, request_header *req,
   struct hostent *hst;
   uid_t oldeuid = 0;
   bool use_malloc = false;
+  int errval = 0;
 
   if (__builtin_expect (debug_level > 0, 0))
-    dbg_log (_("Haven't found \"%s\" in hosts cache!"), (char *) key);
+    {
+      if (he == NULL)
+	dbg_log (_("Haven't found \"%s\" in hosts cache!"), (char *) key);
+      else
+	dbg_log (_("Reloading \"%s\" in hosts cache!"), (char *) key);
+    }
 
-  if (secure[hstdb])
+  if (db->secure)
     {
       oldeuid = geteuid ();
       seteuid (uid);
     }
 
-  while (__gethostbyname2_r (key, AF_INET, &resultbuf, buffer, buflen,
-  			     &hst, &h_errno) != 0
+  while (lookup (req->type, key, &resultbuf, buffer, buflen, &hst) != 0
 	 && h_errno == NETDB_INTERNAL
-	 && errno == ERANGE)
+	 && (errval = errno) == ERANGE)
     {
       char *old_buffer = buffer;
       errno = 0;
@@ -332,6 +565,11 @@ addhstbyname (struct database *db, int fd, request_header *req,
 		 never happen.  */
 	      hst = NULL;
 	      buffer = old_buffer;
+
+	      /* We set the error to indicate this is (possibly) a
+		 temporary error and that it does not mean the entry
+		 is not available at all.  */
+	      errval = EAGAIN;
 	      break;
 	    }
 	  use_malloc = true;
@@ -342,10 +580,11 @@ addhstbyname (struct database *db, int fd, request_header *req,
 	buffer = (char *) extend_alloca (buffer, buflen, buflen + INCR);
     }
 
-  if (secure[hstdb])
+  if (db->secure)
     seteuid (oldeuid);
 
-  cache_addhst (db, fd, req, key, hst, uid, 0);
+  cache_addhst (db, fd, req, key, hst, uid, 0, he, dh,
+		h_errno == TRY_AGAIN ? errval : 0);
 
   if (use_malloc)
     free (buffer);
@@ -353,197 +592,88 @@ addhstbyname (struct database *db, int fd, request_header *req,
 
 
 void
-addhstbyaddr (struct database *db, int fd, request_header *req,
+addhstbyname (struct database_dyn *db, int fd, request_header *req,
 	      void *key, uid_t uid)
 {
-  /* Search for the entry matching the key.  Please note that we don't
-     look again in the table whether the dataset is now available.  We
-     simply insert it.  It does not matter if it is in there twice.  The
-     pruning function only will look at the timestamp.  */
-  int buflen = 1024;
-  char *buffer = (char *) alloca (buflen);
-  struct hostent resultbuf;
-  struct hostent *hst;
-  uid_t oldeuid = 0;
-  bool use_malloc = false;
+  addhstbyX (db, fd, req, key, uid, NULL, NULL);
+}
 
-  if (__builtin_expect (debug_level > 0, 0))
-    {
-      char buf[INET_ADDRSTRLEN];
-      dbg_log (_("Haven't found \"%s\" in hosts cache!"),
-	       inet_ntop (AF_INET, key, buf, sizeof (buf)));
-    }
 
-  if (secure[hstdb])
+void
+readdhstbyname (struct database_dyn *db, struct hashentry *he,
+		struct datahead *dh)
+{
+  request_header req =
     {
-      oldeuid = geteuid ();
-      seteuid (uid);
-    }
+      .type = GETHOSTBYNAME,
+      .key_len = he->len
+    };
 
-  while (__gethostbyaddr_r (key, NS_INADDRSZ, AF_INET, &resultbuf, buffer,
-  			    buflen, &hst, &h_errno) != 0
-	 && h_errno == NETDB_INTERNAL
-	 && errno == ERANGE)
-    {
-      char *old_buffer = buffer;
-      errno = 0;
+  addhstbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
+}
 
-      if (__builtin_expect (buflen > 32768, 0))
-	{
-	  buflen += INCR;
-	  buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
-	  if (buffer == NULL)
-	    {
-	      /* We ran out of memory.  We cannot do anything but
-		 sending a negative response.  In reality this should
-		 never happen.  */
-	      hst = NULL;
-	      buffer = old_buffer;
-	      break;
-	    }
-	  use_malloc = true;
-	}
-      else
-	/* Allocate a new buffer on the stack.  If possible combine it
-	   with the previously allocated buffer.  */
-	buffer = (char *) extend_alloca (buffer, buflen, buflen + INCR);
-    }
 
-  if (secure[hstdb])
-    seteuid (oldeuid);
+void
+addhstbyaddr (struct database_dyn *db, int fd, request_header *req,
+	      void *key, uid_t uid)
+{
+  addhstbyX (db, fd, req, key, uid, NULL, NULL);
+}
 
-  cache_addhst (db, fd, req, key, hst, uid, 1);
 
-  if (use_malloc)
-    free (buffer);
+void
+readdhstbyaddr (struct database_dyn *db, struct hashentry *he,
+		struct datahead *dh)
+{
+  request_header req =
+    {
+      .type = GETHOSTBYADDR,
+      .key_len = he->len
+    };
+
+  addhstbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
 }
 
 
 void
-addhstbynamev6 (struct database *db, int fd, request_header *req,
+addhstbynamev6 (struct database_dyn *db, int fd, request_header *req,
 		void *key, uid_t uid)
 {
-  /* Search for the entry matching the key.  Please note that we don't
-     look again in the table whether the dataset is now available.  We
-     simply insert it.  It does not matter if it is in there twice.  The
-     pruning function only will look at the timestamp.  */
-  int buflen = 1024;
-  char *buffer = (char *) alloca (buflen);
-  struct hostent resultbuf;
-  struct hostent *hst;
-  uid_t oldeuid = 0;
-  bool use_malloc = false;
-
-  if (__builtin_expect (debug_level > 0, 0))
-    dbg_log (_("Haven't found \"%s\" in hosts cache!"), (char *) key);
+  addhstbyX (db, fd, req, key, uid, NULL, NULL);
+}
 
-  if (secure[hstdb])
-    {
-      oldeuid = geteuid ();
-      seteuid (uid);
-    }
 
-  while (__gethostbyname2_r (key, AF_INET6, &resultbuf, buffer, buflen,
-  			     &hst, &h_errno) != 0
-	 && h_errno == NETDB_INTERNAL
-	 && errno == ERANGE)
+void
+readdhstbynamev6 (struct database_dyn *db, struct hashentry *he,
+		  struct datahead *dh)
+{
+  request_header req =
     {
-      char *old_buffer = buffer;
-      errno = 0;
+      .type = GETHOSTBYNAMEv6,
+      .key_len = he->len
+    };
 
-      if (__builtin_expect (buflen > 32768, 0))
-	{
-	  buflen += INCR;
-	  buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
-	  if (buffer == NULL)
-	    {
-	      /* We ran out of memory.  We cannot do anything but
-		 sending a negative response.  In reality this should
-		 never happen.  */
-	      hst = NULL;
-	      buffer = old_buffer;
-	      break;
-	    }
-	  use_malloc = true;
-	}
-      else
-	/* Allocate a new buffer on the stack.  If possible combine it
-	   with the previously allocated buffer.  */
-	buffer = (char *) extend_alloca (buffer, buflen, buflen + INCR);
-    }
-
-  if (secure[hstdb])
-    seteuid (oldeuid);
-
-  cache_addhst (db, fd, req, key, hst, uid, 0);
-
-  if (use_malloc)
-    free (buffer);
+  addhstbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
 }
 
 
 void
-addhstbyaddrv6 (struct database *db, int fd, request_header *req,
+addhstbyaddrv6 (struct database_dyn *db, int fd, request_header *req,
 		void *key, uid_t uid)
 {
-  /* Search for the entry matching the key.  Please note that we don't
-     look again in the table whether the dataset is now available.  We
-     simply insert it.  It does not matter if it is in there twice.  The
-     pruning function only will look at the timestamp.  */
-  int buflen = 1024;
-  char *buffer = (char *) alloca (buflen);
-  struct hostent resultbuf;
-  struct hostent *hst;
-  uid_t oldeuid = 0;
-  bool use_malloc = false;
+  addhstbyX (db, fd, req, key, uid, NULL, NULL);
+}
 
-  if (__builtin_expect (debug_level > 0, 0))
-    {
-      char buf[INET6_ADDRSTRLEN];
-      dbg_log (_("Haven't found \"%s\" in hosts cache!"),
-	       inet_ntop (AF_INET6, key, buf, sizeof (buf)));
-    }
 
-  if (secure[hstdb])
-    {
-      oldeuid = geteuid ();
-      seteuid (uid);
-    }
-
-  while (__gethostbyaddr_r (key, NS_IN6ADDRSZ, AF_INET6, &resultbuf,
-  			    buffer, buflen, &hst, &h_errno) != 0
-	 && h_errno == NETDB_INTERNAL
-	 && errno == ERANGE)
+void
+readdhstbyaddrv6 (struct database_dyn *db, struct hashentry *he,
+		  struct datahead *dh)
+{
+  request_header req =
     {
-      char *old_buffer = buffer;
-      errno = 0;
+      .type = GETHOSTBYADDRv6,
+      .key_len = he->len
+    };
 
-      if (__builtin_expect (buflen > 32768, 0))
-	{
-	  buflen += INCR;
-	  buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
-	  if (buffer == NULL)
-	    {
-	      /* We ran out of memory.  We cannot do anything but
-		 sending a negative response.  In reality this should
-		 never happen.  */
-	      hst = NULL;
-	      buffer = old_buffer;
-	      break;
-	    }
-	  use_malloc = true;
-	}
-      else
-	/* Allocate a new buffer on the stack.  If possible combine it
-	   with the previously allocated buffer.  */
-	buffer = (char *) extend_alloca (buffer, buflen, buflen + INCR);
-    }
-
-  if (secure[hstdb])
-    seteuid (oldeuid);
-
-  cache_addhst (db, fd, req, key, hst, uid, 1);
-
-  if (use_malloc)
-    free (buffer);
+  addhstbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
 }