Blame resolv/resolv_context.c

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