mirror of
https://sourceware.org/git/glibc.git
synced 2025-08-07 06:43:00 +03:00
resolv: Introduce struct resolv_context [BZ #21668]
struct resolv_context objects provide a temporary resolver context which does not change during a name lookup operation. Only when the outmost context is created, the stub resolver configuration is verified to be current (at present, only against previous res_init calls). Subsequent attempts to obtain the context will reuse the result of the initial verification operation. struct resolv_context can also be extended in the future to store data which needs to be deallocated during thread cancellation.
This commit is contained in:
201
resolv/resolv_context.c
Normal file
201
resolv/resolv_context.c
Normal file
@@ -0,0 +1,201 @@
|
||||
/* Temporary, thread-local resolver state.
|
||||
Copyright (C) 2017 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <resolv_context.h>
|
||||
#include <resolv-internal.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* Currently active struct resolv_context object. This pointer forms
|
||||
the start of a single-linked list, using the __next member of
|
||||
struct resolv_context. This list serves two purposes:
|
||||
|
||||
(a) A subsequent call to __resolv_context_get will only increment
|
||||
the reference counter and will not allocate a new object. The
|
||||
_res state freshness check is skipped in this case, too.
|
||||
|
||||
(b) The per-thread cleanup function defined by the resolver calls
|
||||
__resolv_context_freeres, which will deallocate all the context
|
||||
objects. This avoids the need for cancellation handlers and
|
||||
the complexity they bring, but it requires heap allocation of
|
||||
the context object because the per-thread cleanup functions run
|
||||
only after the stack has been fully unwound (and all on-stack
|
||||
objects have been deallocated at this point).
|
||||
|
||||
The TLS variable current is updated even in
|
||||
__resolv_context_get_override, to support case (b) above. This does
|
||||
not override the per-thread resolver state (as obtained by the
|
||||
non-res_state function such as __resolv_context_get) in an
|
||||
observable way because the wrapped context is only used to
|
||||
implement the res_n* functions in the resolver, and those do not
|
||||
call back into user code which could indirectly use the per-thread
|
||||
resolver state. */
|
||||
static __thread struct resolv_context *current attribute_tls_model_ie;
|
||||
|
||||
/* Initialize *RESP if RES_INIT is not yet set in RESP->options, or if
|
||||
res_init in some other thread requested re-initializing. */
|
||||
static __attribute__ ((warn_unused_result)) bool
|
||||
maybe_init (struct __res_state *resp, bool preinit)
|
||||
{
|
||||
if (resp->options & RES_INIT)
|
||||
{
|
||||
if (__res_initstamp != resp->_u._ext.initstamp)
|
||||
{
|
||||
if (resp->nscount > 0)
|
||||
__res_iclose (resp, true);
|
||||
return __res_vinit (resp, 1) == 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (preinit)
|
||||
{
|
||||
if (!resp->retrans)
|
||||
resp->retrans = RES_TIMEOUT;
|
||||
if (!resp->retry)
|
||||
resp->retry = RES_DFLRETRY;
|
||||
resp->options = RES_DEFAULT;
|
||||
if (!resp->id)
|
||||
resp->id = res_randomid ();
|
||||
}
|
||||
return __res_vinit (resp, preinit) == 0;
|
||||
}
|
||||
|
||||
/* Allocate a new context object and initialize it. The object is put
|
||||
on the current list. */
|
||||
static struct resolv_context *
|
||||
context_alloc (struct __res_state *resp)
|
||||
{
|
||||
struct resolv_context *ctx = malloc (sizeof (*ctx));
|
||||
if (ctx == NULL)
|
||||
return NULL;
|
||||
ctx->resp = resp;
|
||||
ctx->__refcount = 1;
|
||||
ctx->__from_res = true;
|
||||
ctx->__next = current;
|
||||
current = ctx;
|
||||
return ctx;
|
||||
}
|
||||
|
||||
/* Deallocate the context object and all the state within. */
|
||||
static void
|
||||
context_free (struct resolv_context *ctx)
|
||||
{
|
||||
current = ctx->__next;
|
||||
free (ctx);
|
||||
}
|
||||
|
||||
/* Reuse the current context object. */
|
||||
static struct resolv_context *
|
||||
context_reuse (void)
|
||||
{
|
||||
/* A context object created by __resolv_context_get_override cannot
|
||||
be reused. */
|
||||
assert (current->__from_res);
|
||||
|
||||
++current->__refcount;
|
||||
|
||||
/* Check for reference counter wraparound. This can only happen if
|
||||
the get/put functions are not properly paired. */
|
||||
assert (current->__refcount > 0);
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
/* Backing function for the __resolv_context_get family of
|
||||
functions. */
|
||||
static struct resolv_context *
|
||||
context_get (bool preinit)
|
||||
{
|
||||
if (current != NULL)
|
||||
return context_reuse ();
|
||||
|
||||
struct resolv_context *ctx = context_alloc (&_res);
|
||||
if (ctx == NULL)
|
||||
return NULL;
|
||||
if (!maybe_init (ctx->resp, preinit))
|
||||
{
|
||||
context_free (ctx);
|
||||
return NULL;
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
struct resolv_context *
|
||||
__resolv_context_get (void)
|
||||
{
|
||||
return context_get (false);
|
||||
}
|
||||
libc_hidden_def (__resolv_context_get)
|
||||
|
||||
struct resolv_context *
|
||||
__resolv_context_get_preinit (void)
|
||||
{
|
||||
return context_get (true);
|
||||
}
|
||||
libc_hidden_def (__resolv_context_get_preinit)
|
||||
|
||||
struct resolv_context *
|
||||
__resolv_context_get_override (struct __res_state *resp)
|
||||
{
|
||||
/* NB: As explained asbove, context_alloc will put the context on
|
||||
the current list. */
|
||||
struct resolv_context *ctx = context_alloc (resp);
|
||||
if (ctx == NULL)
|
||||
return NULL;
|
||||
|
||||
ctx->__from_res = false;
|
||||
return ctx;
|
||||
}
|
||||
libc_hidden_def (__resolv_context_get_override)
|
||||
|
||||
void
|
||||
__resolv_context_put (struct resolv_context *ctx)
|
||||
{
|
||||
if (ctx == NULL)
|
||||
return;
|
||||
|
||||
/* NB: Callers assume that this function preserves errno and
|
||||
h_errno. */
|
||||
|
||||
assert (current == ctx);
|
||||
assert (ctx->__refcount > 0);
|
||||
|
||||
if (ctx->__from_res && --ctx->__refcount > 0)
|
||||
/* Do not pop this context yet. */
|
||||
return;
|
||||
|
||||
context_free (ctx);
|
||||
}
|
||||
libc_hidden_def (__resolv_context_put)
|
||||
|
||||
void
|
||||
__resolv_context_freeres (void)
|
||||
{
|
||||
/* Deallocate the entire chain of context objects. */
|
||||
struct resolv_context *ctx = current;
|
||||
current = NULL;
|
||||
while (ctx != NULL)
|
||||
{
|
||||
struct resolv_context *next = ctx->__next;
|
||||
context_free (ctx);
|
||||
ctx = next;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user