Blame nscd/servicescache.c

Packit Service 82fcde
/* Cache handling for services lookup.
Packit Service 82fcde
   Copyright (C) 2007-2018 Free Software Foundation, Inc.
Packit Service 82fcde
   This file is part of the GNU C Library.
Packit Service 82fcde
   Contributed by Ulrich Drepper <drepper@drepper.com>, 2007.
Packit Service 82fcde
Packit Service 82fcde
   This program is free software; you can redistribute it and/or modify
Packit Service 82fcde
   it under the terms of the GNU General Public License as published
Packit Service 82fcde
   by the Free Software Foundation; version 2 of the License, or
Packit Service 82fcde
   (at your option) any later version.
Packit Service 82fcde
Packit Service 82fcde
   This program is distributed in the hope that it will be useful,
Packit Service 82fcde
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 82fcde
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service 82fcde
   GNU General Public License for more details.
Packit Service 82fcde
Packit Service 82fcde
   You should have received a copy of the GNU General Public License
Packit Service 82fcde
   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
Packit Service 82fcde
Packit Service 82fcde
#include <assert.h>
Packit Service 82fcde
#include <errno.h>
Packit Service 82fcde
#include <libintl.h>
Packit Service 82fcde
#include <netdb.h>
Packit Service 82fcde
#include <unistd.h>
Packit Service 82fcde
#include <stdint.h>
Packit Service 82fcde
#include <sys/mman.h>
Packit Service 82fcde
#include <kernel-features.h>
Packit Service 82fcde
#include <scratch_buffer.h>
Packit Service 82fcde
Packit Service 82fcde
#include "nscd.h"
Packit Service 82fcde
#include "dbg_log.h"
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* This is the standard reply in case the service is disabled.  */
Packit Service 82fcde
static const serv_response_header disabled =
Packit Service 82fcde
{
Packit Service 82fcde
  .version = NSCD_VERSION,
Packit Service 82fcde
  .found = -1,
Packit Service 82fcde
  .s_name_len = 0,
Packit Service 82fcde
  .s_proto_len = 0,
Packit Service 82fcde
  .s_aliases_cnt = 0,
Packit Service 82fcde
  .s_port = -1
Packit Service 82fcde
};
Packit Service 82fcde
Packit Service 82fcde
/* This is the struct describing how to write this record.  */
Packit Service 82fcde
const struct iovec serv_iov_disabled =
Packit Service 82fcde
{
Packit Service 82fcde
  .iov_base = (void *) &disabled,
Packit Service 82fcde
  .iov_len = sizeof (disabled)
Packit Service 82fcde
};
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* This is the standard reply in case we haven't found the dataset.  */
Packit Service 82fcde
static const serv_response_header notfound =
Packit Service 82fcde
{
Packit Service 82fcde
  .version = NSCD_VERSION,
Packit Service 82fcde
  .found = 0,
Packit Service 82fcde
  .s_name_len = 0,
Packit Service 82fcde
  .s_proto_len = 0,
Packit Service 82fcde
  .s_aliases_cnt = 0,
Packit Service 82fcde
  .s_port = -1
Packit Service 82fcde
};
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
static time_t
Packit Service 82fcde
cache_addserv (struct database_dyn *db, int fd, request_header *req,
Packit Service 82fcde
	       const void *key, struct servent *serv, uid_t owner,
Packit Service 82fcde
	       struct hashentry *const he, struct datahead *dh, int errval)
Packit Service 82fcde
{
Packit Service 82fcde
  bool all_written = true;
Packit Service 82fcde
  ssize_t total;
Packit Service 82fcde
  time_t t = time (NULL);
Packit Service 82fcde
Packit Service 82fcde
  /* We allocate all data in one memory block: the iov vector,
Packit Service 82fcde
     the response header and the dataset itself.  */
Packit Service 82fcde
  struct dataset
Packit Service 82fcde
  {
Packit Service 82fcde
    struct datahead head;
Packit Service 82fcde
    serv_response_header resp;
Packit Service 82fcde
    char strdata[0];
Packit Service 82fcde
  } *dataset;
Packit Service 82fcde
Packit Service 82fcde
  assert (offsetof (struct dataset, resp) == offsetof (struct datahead, data));
Packit Service 82fcde
Packit Service 82fcde
  time_t timeout = MAX_TIMEOUT_VALUE;
Packit Service 82fcde
  if (serv == NULL)
Packit Service 82fcde
    {
Packit Service 82fcde
      if (he != NULL && errval == EAGAIN)
Packit Service 82fcde
	{
Packit Service 82fcde
	  /* If we have an old record available but cannot find one
Packit Service 82fcde
	     now because the service is not available we keep the old
Packit Service 82fcde
	     record and make sure it does not get removed.  */
Packit Service 82fcde
	  if (reload_count != UINT_MAX)
Packit Service 82fcde
	    /* Do not reset the value if we never not reload the record.  */
Packit Service 82fcde
	    dh->nreloads = reload_count - 1;
Packit Service 82fcde
Packit Service 82fcde
	  /* Reload with the same time-to-live value.  */
Packit Service 82fcde
	  timeout = dh->timeout = t + db->postimeout;
Packit Service 82fcde
Packit Service 82fcde
	  total = 0;
Packit Service 82fcde
	}
Packit Service 82fcde
      else
Packit Service 82fcde
	{
Packit Service 82fcde
	  /* We have no data.  This means we send the standard reply for this
Packit Service 82fcde
	     case.  */
Packit Service 82fcde
	  total = sizeof (notfound);
Packit Service 82fcde
Packit Service 82fcde
	  if (fd != -1
Packit Service 82fcde
	      && TEMP_FAILURE_RETRY (send (fd, &notfound, total,
Packit Service 82fcde
					   MSG_NOSIGNAL)) != total)
Packit Service 82fcde
	    all_written = false;
Packit Service 82fcde
Packit Service 82fcde
	  /* If we have a transient error or cannot permanently store
Packit Service 82fcde
	     the result, so be it.  */
Packit Service 82fcde
	  if (errval == EAGAIN || __builtin_expect (db->negtimeout == 0, 0))
Packit Service 82fcde
	    {
Packit Service 82fcde
	      /* Mark the old entry as obsolete.  */
Packit Service 82fcde
	      if (dh != NULL)
Packit Service 82fcde
		dh->usable = false;
Packit Service 82fcde
	    }
Packit Service 82fcde
	  else if ((dataset = mempool_alloc (db, (sizeof (struct dataset)
Packit Service 82fcde
						  + req->key_len), 1)) != NULL)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      timeout = datahead_init_neg (&dataset->head,
Packit Service 82fcde
					   (sizeof (struct dataset)
Packit Service 82fcde
					    + req->key_len), total,
Packit Service 82fcde
					   db->negtimeout);
Packit Service 82fcde
Packit Service 82fcde
	      /* This is the reply.  */
Packit Service 82fcde
	      memcpy (&dataset->resp, &notfound, total);
Packit Service 82fcde
Packit Service 82fcde
	      /* Copy the key data.  */
Packit Service 82fcde
	      memcpy (dataset->strdata, key, req->key_len);
Packit Service 82fcde
Packit Service 82fcde
	      /* If necessary, we also propagate the data to disk.  */
Packit Service 82fcde
	      if (db->persistent)
Packit Service 82fcde
		{
Packit Service 82fcde
		  // XXX async OK?
Packit Service 82fcde
		  uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
Packit Service 82fcde
		  msync ((void *) pval,
Packit Service 82fcde
			 ((uintptr_t) dataset & pagesize_m1)
Packit Service 82fcde
			 + sizeof (struct dataset) + req->key_len, MS_ASYNC);
Packit Service 82fcde
		}
Packit Service 82fcde
Packit Service 82fcde
	      (void) cache_add (req->type, &dataset->strdata, req->key_len,
Packit Service 82fcde
				&dataset->head, true, db, owner, he == NULL);
Packit Service 82fcde
Packit Service 82fcde
	      pthread_rwlock_unlock (&db->lock);
Packit Service 82fcde
Packit Service 82fcde
	      /* Mark the old entry as obsolete.  */
Packit Service 82fcde
	      if (dh != NULL)
Packit Service 82fcde
		dh->usable = false;
Packit Service 82fcde
	    }
Packit Service 82fcde
	}
Packit Service 82fcde
    }
Packit Service 82fcde
  else
Packit Service 82fcde
    {
Packit Service 82fcde
      /* Determine the I/O structure.  */
Packit Service 82fcde
      size_t s_name_len = strlen (serv->s_name) + 1;
Packit Service 82fcde
      size_t s_proto_len = strlen (serv->s_proto) + 1;
Packit Service 82fcde
      uint32_t *s_aliases_len;
Packit Service 82fcde
      size_t s_aliases_cnt;
Packit Service 82fcde
      char *aliases;
Packit Service 82fcde
      char *cp;
Packit Service 82fcde
      size_t cnt;
Packit Service 82fcde
Packit Service 82fcde
      /* Determine the number of aliases.  */
Packit Service 82fcde
      s_aliases_cnt = 0;
Packit Service 82fcde
      for (cnt = 0; serv->s_aliases[cnt] != NULL; ++cnt)
Packit Service 82fcde
	++s_aliases_cnt;
Packit Service 82fcde
      /* Determine the length of all aliases.  */
Packit Service 82fcde
      s_aliases_len = (uint32_t *) alloca (s_aliases_cnt * sizeof (uint32_t));
Packit Service 82fcde
      total = 0;
Packit Service 82fcde
      for (cnt = 0; cnt < s_aliases_cnt; ++cnt)
Packit Service 82fcde
	{
Packit Service 82fcde
	  s_aliases_len[cnt] = strlen (serv->s_aliases[cnt]) + 1;
Packit Service 82fcde
	  total += s_aliases_len[cnt];
Packit Service 82fcde
	}
Packit Service 82fcde
Packit Service 82fcde
      total += (offsetof (struct dataset, strdata)
Packit Service 82fcde
		+ s_name_len
Packit Service 82fcde
		+ s_proto_len
Packit Service 82fcde
		+ s_aliases_cnt * sizeof (uint32_t));
Packit Service 82fcde
Packit Service 82fcde
      /* If we refill the cache, first assume the reconrd did not
Packit Service 82fcde
	 change.  Allocate memory on the cache since it is likely
Packit Service 82fcde
	 discarded anyway.  If it turns out to be necessary to have a
Packit Service 82fcde
	 new record we can still allocate real memory.  */
Packit Service 82fcde
      bool alloca_used = false;
Packit Service 82fcde
      dataset = NULL;
Packit Service 82fcde
Packit Service 82fcde
      if (he == NULL)
Packit Service 82fcde
	dataset = (struct dataset *) mempool_alloc (db, total + req->key_len,
Packit Service 82fcde
						    1);
Packit Service 82fcde
Packit Service 82fcde
      if (dataset == NULL)
Packit Service 82fcde
	{
Packit Service 82fcde
	  /* We cannot permanently add the result in the moment.  But
Packit Service 82fcde
	     we can provide the result as is.  Store the data in some
Packit Service 82fcde
	     temporary memory.  */
Packit Service 82fcde
	  dataset = (struct dataset *) alloca (total + req->key_len);
Packit Service 82fcde
Packit Service 82fcde
	  /* We cannot add this record to the permanent database.  */
Packit Service 82fcde
	  alloca_used = true;
Packit Service 82fcde
	}
Packit Service 82fcde
Packit Service 82fcde
      timeout = datahead_init_pos (&dataset->head, total + req->key_len,
Packit Service 82fcde
				   total - offsetof (struct dataset, resp),
Packit Service 82fcde
				   he == NULL ? 0 : dh->nreloads + 1,
Packit Service 82fcde
				   db->postimeout);
Packit Service 82fcde
Packit Service 82fcde
      dataset->resp.version = NSCD_VERSION;
Packit Service 82fcde
      dataset->resp.found = 1;
Packit Service 82fcde
      dataset->resp.s_name_len = s_name_len;
Packit Service 82fcde
      dataset->resp.s_proto_len = s_proto_len;
Packit Service 82fcde
      dataset->resp.s_port = serv->s_port;
Packit Service 82fcde
      dataset->resp.s_aliases_cnt = s_aliases_cnt;
Packit Service 82fcde
Packit Service 82fcde
      cp = dataset->strdata;
Packit Service 82fcde
Packit Service 82fcde
      cp = mempcpy (cp, serv->s_name, s_name_len);
Packit Service 82fcde
      cp = mempcpy (cp, serv->s_proto, s_proto_len);
Packit Service 82fcde
      cp = mempcpy (cp, s_aliases_len, s_aliases_cnt * sizeof (uint32_t));
Packit Service 82fcde
Packit Service 82fcde
      /* Then the aliases.  */
Packit Service 82fcde
      aliases = cp;
Packit Service 82fcde
      for (cnt = 0; cnt < s_aliases_cnt; ++cnt)
Packit Service 82fcde
	cp = mempcpy (cp, serv->s_aliases[cnt], s_aliases_len[cnt]);
Packit Service 82fcde
Packit Service 82fcde
      assert (cp
Packit Service 82fcde
	      == dataset->strdata + total - offsetof (struct dataset,
Packit Service 82fcde
						      strdata));
Packit Service 82fcde
Packit Service 82fcde
      char *key_copy = memcpy (cp, key, req->key_len);
Packit Service 82fcde
Packit Service 82fcde
      /* Now we can determine whether on refill we have to create a new
Packit Service 82fcde
	 record or not.  */
Packit Service 82fcde
      if (he != NULL)
Packit Service 82fcde
	{
Packit Service 82fcde
	  assert (fd == -1);
Packit Service 82fcde
Packit Service 82fcde
	  if (total + req->key_len == dh->allocsize
Packit Service 82fcde
	      && total - offsetof (struct dataset, resp) == dh->recsize
Packit Service 82fcde
	      && memcmp (&dataset->resp, dh->data,
Packit Service 82fcde
			 dh->allocsize - offsetof (struct dataset, resp)) == 0)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      /* The data has not changed.  We will just bump the
Packit Service 82fcde
		 timeout value.  Note that the new record has been
Packit Service 82fcde
		 allocated on the stack and need not be freed.  */
Packit Service 82fcde
	      dh->timeout = dataset->head.timeout;
Packit Service 82fcde
	      ++dh->nreloads;
Packit Service 82fcde
	    }
Packit Service 82fcde
	  else
Packit Service 82fcde
	    {
Packit Service 82fcde
	      /* We have to create a new record.  Just allocate
Packit Service 82fcde
		 appropriate memory and copy it.  */
Packit Service 82fcde
	      struct dataset *newp
Packit Service 82fcde
		= (struct dataset *) mempool_alloc (db, total + req->key_len,
Packit Service 82fcde
						    1);
Packit Service 82fcde
	      if (newp != NULL)
Packit Service 82fcde
		{
Packit Service 82fcde
		  /* Adjust pointers into the memory block.  */
Packit Service 82fcde
		  aliases = (char *) newp + (aliases - (char *) dataset);
Packit Service 82fcde
		  assert (key_copy != NULL);
Packit Service 82fcde
		  key_copy = (char *) newp + (key_copy - (char *) dataset);
Packit Service 82fcde
Packit Service 82fcde
		  dataset = memcpy (newp, dataset, total + req->key_len);
Packit Service 82fcde
		  alloca_used = false;
Packit Service 82fcde
		}
Packit Service 82fcde
Packit Service 82fcde
	      /* Mark the old record as obsolete.  */
Packit Service 82fcde
	      dh->usable = false;
Packit Service 82fcde
	    }
Packit Service 82fcde
	}
Packit Service 82fcde
      else
Packit Service 82fcde
	{
Packit Service 82fcde
	  /* We write the dataset before inserting it to the database
Packit Service 82fcde
	     since while inserting this thread might block and so would
Packit Service 82fcde
	     unnecessarily keep the receiver waiting.  */
Packit Service 82fcde
	  assert (fd != -1);
Packit Service 82fcde
Packit Service 82fcde
	  if (writeall (fd, &dataset->resp, dataset->head.recsize)
Packit Service 82fcde
	      != dataset->head.recsize)
Packit Service 82fcde
	    all_written = false;
Packit Service 82fcde
	}
Packit Service 82fcde
Packit Service 82fcde
      /* Add the record to the database.  But only if it has not been
Packit Service 82fcde
	 stored on the stack.  */
Packit Service 82fcde
      if (! alloca_used)
Packit Service 82fcde
	{
Packit Service 82fcde
	  /* If necessary, we also propagate the data to disk.  */
Packit Service 82fcde
	  if (db->persistent)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      // XXX async OK?
Packit Service 82fcde
	      uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
Packit Service 82fcde
	      msync ((void *) pval,
Packit Service 82fcde
		     ((uintptr_t) dataset & pagesize_m1)
Packit Service 82fcde
		     + total + req->key_len, MS_ASYNC);
Packit Service 82fcde
	    }
Packit Service 82fcde
Packit Service 82fcde
	  (void) cache_add (req->type, key_copy, req->key_len,
Packit Service 82fcde
			    &dataset->head, true, db, owner, he == NULL);
Packit Service 82fcde
Packit Service 82fcde
	  pthread_rwlock_unlock (&db->lock);
Packit Service 82fcde
	}
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  if (__builtin_expect (!all_written, 0) && debug_level > 0)
Packit Service 82fcde
    {
Packit Service 82fcde
      char buf[256];
Packit Service 82fcde
      dbg_log (_("short write in %s: %s"),  __FUNCTION__,
Packit Service 82fcde
	       strerror_r (errno, buf, sizeof (buf)));
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  return timeout;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
static int
Packit Service 82fcde
lookup (int type, char *key, struct servent *resultbufp, char *buffer,
Packit Service 82fcde
	size_t buflen, struct servent **serv)
Packit Service 82fcde
{
Packit Service 82fcde
  char *proto = strrchr (key, '/');
Packit Service 82fcde
  if (proto != NULL && proto != key)
Packit Service 82fcde
    {
Packit Service 82fcde
      key = strndupa (key, proto - key);
Packit Service 82fcde
      if (proto[1] == '\0')
Packit Service 82fcde
	proto = NULL;
Packit Service 82fcde
      else
Packit Service 82fcde
	++proto;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  if (type == GETSERVBYNAME)
Packit Service 82fcde
    return __getservbyname_r (key, proto, resultbufp, buffer, buflen, serv);
Packit Service 82fcde
Packit Service 82fcde
  assert (type == GETSERVBYPORT);
Packit Service 82fcde
  return __getservbyport_r (atol (key), proto, resultbufp, buffer, buflen,
Packit Service 82fcde
			    serv);
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
static time_t
Packit Service 82fcde
addservbyX (struct database_dyn *db, int fd, request_header *req,
Packit Service 82fcde
	    char *key, uid_t uid, struct hashentry *he, struct datahead *dh)
Packit Service 82fcde
{
Packit Service 82fcde
  /* Search for the entry matching the key.  Please note that we don't
Packit Service 82fcde
     look again in the table whether the dataset is now available.  We
Packit Service 82fcde
     simply insert it.  It does not matter if it is in there twice.  The
Packit Service 82fcde
     pruning function only will look at the timestamp.  */
Packit Service 82fcde
  struct servent resultbuf;
Packit Service 82fcde
  struct servent *serv;
Packit Service 82fcde
  int errval = 0;
Packit Service 82fcde
  struct scratch_buffer tmpbuf;
Packit Service 82fcde
  scratch_buffer_init (&tmpbuf);
Packit Service 82fcde
Packit Service 82fcde
  if (__glibc_unlikely (debug_level > 0))
Packit Service 82fcde
    {
Packit Service 82fcde
      if (he == NULL)
Packit Service 82fcde
	dbg_log (_("Haven't found \"%s\" in services cache!"), key);
Packit Service 82fcde
      else
Packit Service 82fcde
	dbg_log (_("Reloading \"%s\" in services cache!"), key);
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  while (lookup (req->type, key, &resultbuf,
Packit Service 82fcde
		 tmpbuf.data, tmpbuf.length, &serv) != 0
Packit Service 82fcde
	 && (errval = errno) == ERANGE)
Packit Service 82fcde
    if (!scratch_buffer_grow (&tmpbuf))
Packit Service 82fcde
      {
Packit Service 82fcde
	/* We ran out of memory.  We cannot do anything but sending a
Packit Service 82fcde
	   negative response.  In reality this should never
Packit Service 82fcde
	   happen.  */
Packit Service 82fcde
	serv = NULL;
Packit Service 82fcde
	/* We set the error to indicate this is (possibly) a temporary
Packit Service 82fcde
	   error and that it does not mean the entry is not available
Packit Service 82fcde
	   at all.  */
Packit Service 82fcde
	errval = EAGAIN;
Packit Service 82fcde
	break;
Packit Service 82fcde
      }
Packit Service 82fcde
Packit Service 82fcde
  time_t timeout = cache_addserv (db, fd, req, key, serv, uid, he, dh, errval);
Packit Service 82fcde
  scratch_buffer_free (&tmpbuf);
Packit Service 82fcde
  return timeout;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
void
Packit Service 82fcde
addservbyname (struct database_dyn *db, int fd, request_header *req,
Packit Service 82fcde
	       void *key, uid_t uid)
Packit Service 82fcde
{
Packit Service 82fcde
  addservbyX (db, fd, req, key, uid, NULL, NULL);
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
time_t
Packit Service 82fcde
readdservbyname (struct database_dyn *db, struct hashentry *he,
Packit Service 82fcde
		 struct datahead *dh)
Packit Service 82fcde
{
Packit Service 82fcde
  request_header req =
Packit Service 82fcde
    {
Packit Service 82fcde
      .type = GETSERVBYNAME,
Packit Service 82fcde
      .key_len = he->len
Packit Service 82fcde
    };
Packit Service 82fcde
Packit Service 82fcde
  return addservbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
void
Packit Service 82fcde
addservbyport (struct database_dyn *db, int fd, request_header *req,
Packit Service 82fcde
	       void *key, uid_t uid)
Packit Service 82fcde
{
Packit Service 82fcde
  addservbyX (db, fd, req, key, uid, NULL, NULL);
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
time_t
Packit Service 82fcde
readdservbyport (struct database_dyn *db, struct hashentry *he,
Packit Service 82fcde
		 struct datahead *dh)
Packit Service 82fcde
{
Packit Service 82fcde
  request_header req =
Packit Service 82fcde
    {
Packit Service 82fcde
      .type = GETSERVBYPORT,
Packit Service 82fcde
      .key_len = he->len
Packit Service 82fcde
    };
Packit Service 82fcde
Packit Service 82fcde
  return addservbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
Packit Service 82fcde
}