Blame resolv/resolv_context.c

Packit 6c4009
/* Temporary, thread-local resolver state.
Packit 6c4009
   Copyright (C) 2017-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library 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 GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <resolv_context.h>
Packit 6c4009
#include <resolv_conf.h>
Packit 6c4009
#include <resolv-internal.h>
Packit 6c4009
Packit 6c4009
#include <assert.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <stdio.h>
Packit 6c4009
Packit 6c4009
/* Currently active struct resolv_context object.  This pointer forms
Packit 6c4009
   the start of a single-linked list, using the __next member of
Packit 6c4009
   struct resolv_context.  This list serves two purposes:
Packit 6c4009
Packit 6c4009
   (a) A subsequent call to __resolv_context_get will only increment
Packit 6c4009
       the reference counter and will not allocate a new object.  The
Packit 6c4009
       _res state freshness check is skipped in this case, too.
Packit 6c4009
Packit 6c4009
   (b) The per-thread cleanup function defined by the resolver calls
Packit 6c4009
       __resolv_context_freeres, which will deallocate all the context
Packit 6c4009
       objects.  This avoids the need for cancellation handlers and
Packit 6c4009
       the complexity they bring, but it requires heap allocation of
Packit 6c4009
       the context object because the per-thread cleanup functions run
Packit 6c4009
       only after the stack has been fully unwound (and all on-stack
Packit 6c4009
       objects have been deallocated at this point).
Packit 6c4009
Packit 6c4009
   The TLS variable current is updated even in
Packit 6c4009
   __resolv_context_get_override, to support case (b) above.  This does
Packit 6c4009
   not override the per-thread resolver state (as obtained by the
Packit 6c4009
   non-res_state function such as __resolv_context_get) in an
Packit 6c4009
   observable way because the wrapped context is only used to
Packit 6c4009
   implement the res_n* functions in the resolver, and those do not
Packit 6c4009
   call back into user code which could indirectly use the per-thread
Packit 6c4009
   resolver state.  */
Packit 6c4009
static __thread struct resolv_context *current attribute_tls_model_ie;
Packit 6c4009
Packit 6c4009
/* The resolv_conf handling will gives us a ctx->conf pointer even if
Packit 6c4009
   these fields do not match because a mis-match does not cause a loss
Packit 6c4009
   of state (_res objects can store the full information).  This
Packit 6c4009
   function checks to ensure that there is a full patch, to prevent
Packit 6c4009
   overwriting a patched configuration.  */
Packit 6c4009
static bool
Packit 6c4009
replicated_configuration_matches (const struct resolv_context *ctx)
Packit 6c4009
{
Packit 6c4009
  return ctx->resp->options == ctx->conf->options
Packit 6c4009
    && ctx->resp->retrans == ctx->conf->retrans
Packit 6c4009
    && ctx->resp->retry == ctx->conf->retry
Packit 6c4009
    && ctx->resp->ndots == ctx->conf->ndots;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Initialize *RESP if RES_INIT is not yet set in RESP->options, or if
Packit 6c4009
   res_init in some other thread requested re-initializing.  */
Packit 6c4009
static __attribute__ ((warn_unused_result)) bool
Packit 6c4009
maybe_init (struct resolv_context *ctx, bool preinit)
Packit 6c4009
{
Packit 6c4009
  struct __res_state *resp = ctx->resp;
Packit 6c4009
  if (resp->options & RES_INIT)
Packit 6c4009
    {
Packit 6c4009
      if (resp->options & RES_NORELOAD)
Packit 6c4009
        /* Configuration reloading was explicitly disabled.  */
Packit 6c4009
        return true;
Packit 6c4009
Packit 6c4009
      /* If there is no associated resolv_conf object despite the
Packit 6c4009
         initialization, something modified *ctx->resp.  Do not
Packit 6c4009
         override those changes.  */
Packit 6c4009
      if (ctx->conf != NULL && replicated_configuration_matches (ctx))
Packit 6c4009
        {
Packit 6c4009
          struct resolv_conf *current = __resolv_conf_get_current ();
Packit 6c4009
          if (current == NULL)
Packit 6c4009
            return false;
Packit 6c4009
Packit 6c4009
          /* Check if the configuration changed.  */
Packit 6c4009
          if (current != ctx->conf)
Packit 6c4009
            {
Packit 6c4009
              /* This call will detach the extended resolver state.  */
Packit 6c4009
              if (resp->nscount > 0)
Packit 6c4009
                __res_iclose (resp, true);
Packit 6c4009
              /* Reattach the current configuration.  */
Packit 6c4009
              if (__resolv_conf_attach (ctx->resp, current))
Packit 6c4009
                {
Packit 6c4009
                  __resolv_conf_put (ctx->conf);
Packit 6c4009
                  /* ctx takes ownership, so we do not release current.  */
Packit 6c4009
                  ctx->conf = current;
Packit 6c4009
                }
Packit 6c4009
            }
Packit 6c4009
          else
Packit 6c4009
            /* No change.  Drop the reference count for current.  */
Packit 6c4009
            __resolv_conf_put (current);
Packit 6c4009
        }
Packit 6c4009
      return true;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  assert (ctx->conf == NULL);
Packit 6c4009
  if (preinit)
Packit 6c4009
    {
Packit 6c4009
      if (!resp->retrans)
Packit 6c4009
        resp->retrans = RES_TIMEOUT;
Packit 6c4009
      if (!resp->retry)
Packit 6c4009
        resp->retry = RES_DFLRETRY;
Packit 6c4009
      resp->options = RES_DEFAULT;
Packit 6c4009
      if (!resp->id)
Packit 6c4009
        resp->id = res_randomid ();
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (__res_vinit (resp, preinit) < 0)
Packit 6c4009
    return false;
Packit 6c4009
  ctx->conf = __resolv_conf_get (ctx->resp);
Packit 6c4009
  return true;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Allocate a new context object and initialize it.  The object is put
Packit 6c4009
   on the current list.  */
Packit 6c4009
static struct resolv_context *
Packit 6c4009
context_alloc (struct __res_state *resp)
Packit 6c4009
{
Packit 6c4009
  struct resolv_context *ctx = malloc (sizeof (*ctx));
Packit 6c4009
  if (ctx == NULL)
Packit 6c4009
    return NULL;
Packit 6c4009
  ctx->resp = resp;
Packit 6c4009
  ctx->conf = __resolv_conf_get (resp);
Packit 6c4009
  ctx->__refcount = 1;
Packit 6c4009
  ctx->__from_res = true;
Packit 6c4009
  ctx->__next = current;
Packit 6c4009
  current = ctx;
Packit 6c4009
  return ctx;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Deallocate the context object and all the state within.   */
Packit 6c4009
static void
Packit 6c4009
context_free (struct resolv_context *ctx)
Packit 6c4009
{
Packit 6c4009
  int error_code = errno;
Packit 6c4009
  current = ctx->__next;
Packit 6c4009
  __resolv_conf_put (ctx->conf);
Packit 6c4009
  free (ctx);
Packit 6c4009
  __set_errno (error_code);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Reuse the current context object.  */
Packit 6c4009
static struct resolv_context *
Packit 6c4009
context_reuse (void)
Packit 6c4009
{
Packit 6c4009
  /* A context object created by __resolv_context_get_override cannot
Packit 6c4009
     be reused.  */
Packit 6c4009
  assert (current->__from_res);
Packit 6c4009
Packit 6c4009
  ++current->__refcount;
Packit 6c4009
Packit 6c4009
  /* Check for reference counter wraparound.  This can only happen if
Packit 6c4009
     the get/put functions are not properly paired.  */
Packit 6c4009
  assert (current->__refcount > 0);
Packit 6c4009
Packit 6c4009
  return current;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Backing function for the __resolv_context_get family of
Packit 6c4009
   functions.  */
Packit 6c4009
static struct resolv_context *
Packit 6c4009
context_get (bool preinit)
Packit 6c4009
{
Packit 6c4009
  if (current != NULL)
Packit 6c4009
    return context_reuse ();
Packit 6c4009
Packit 6c4009
  struct resolv_context *ctx = context_alloc (&_res);
Packit 6c4009
  if (ctx == NULL)
Packit 6c4009
    return NULL;
Packit 6c4009
  if (!maybe_init (ctx, preinit))
Packit 6c4009
    {
Packit 6c4009
      context_free (ctx);
Packit 6c4009
      return NULL;
Packit 6c4009
    }
Packit 6c4009
  return ctx;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
struct resolv_context *
Packit 6c4009
__resolv_context_get (void)
Packit 6c4009
{
Packit 6c4009
  return context_get (false);
Packit 6c4009
}
Packit 6c4009
libc_hidden_def (__resolv_context_get)
Packit 6c4009
Packit 6c4009
struct resolv_context *
Packit 6c4009
__resolv_context_get_preinit (void)
Packit 6c4009
{
Packit 6c4009
  return context_get (true);
Packit 6c4009
}
Packit 6c4009
libc_hidden_def (__resolv_context_get_preinit)
Packit 6c4009
Packit 6c4009
struct resolv_context *
Packit 6c4009
__resolv_context_get_override (struct __res_state *resp)
Packit 6c4009
{
Packit 6c4009
  /* NB: As explained asbove, context_alloc will put the context on
Packit 6c4009
     the current list.  */
Packit 6c4009
  struct resolv_context *ctx = context_alloc (resp);
Packit 6c4009
  if (ctx == NULL)
Packit 6c4009
    return NULL;
Packit 6c4009
Packit 6c4009
  ctx->__from_res = false;
Packit 6c4009
  return ctx;
Packit 6c4009
}
Packit 6c4009
libc_hidden_def (__resolv_context_get_override)
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
__resolv_context_put (struct resolv_context *ctx)
Packit 6c4009
{
Packit 6c4009
  if (ctx == NULL)
Packit 6c4009
    return;
Packit 6c4009
Packit 6c4009
  /* NB: Callers assume that this function preserves errno and
Packit 6c4009
     h_errno.  */
Packit 6c4009
Packit 6c4009
  assert (current == ctx);
Packit 6c4009
  assert (ctx->__refcount > 0);
Packit 6c4009
Packit 6c4009
  if (ctx->__from_res && --ctx->__refcount > 0)
Packit 6c4009
    /* Do not pop this context yet.  */
Packit 6c4009
    return;
Packit 6c4009
Packit 6c4009
  context_free (ctx);
Packit 6c4009
}
Packit 6c4009
libc_hidden_def (__resolv_context_put)
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
__resolv_context_freeres (void)
Packit 6c4009
{
Packit 6c4009
  /* Deallocate the entire chain of context objects.  */
Packit 6c4009
  struct resolv_context *ctx = current;
Packit 6c4009
  current = NULL;
Packit 6c4009
  while (ctx != NULL)
Packit 6c4009
    {
Packit 6c4009
      struct resolv_context *next = ctx->__next;
Packit 6c4009
      context_free (ctx);
Packit 6c4009
      ctx = next;
Packit 6c4009
    }
Packit 6c4009
}