aboutsummaryrefslogtreecommitdiff
path: root/resolv/resolv_context.c
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2017-06-30 21:10:23 +0200
committerFlorian Weimer <fweimer@redhat.com>2017-07-03 20:52:59 +0200
commit352f4ff9a268b81ef5d4b2413f582565806e4790 (patch)
treefb27056dfdeafe43c021f6127c9544c016e78019 /resolv/resolv_context.c
parent4e45d83c92dbb5b8dc20654f32395108d18cf739 (diff)
downloadglibc-352f4ff9a268b81ef5d4b2413f582565806e4790.tar
glibc-352f4ff9a268b81ef5d4b2413f582565806e4790.tar.gz
glibc-352f4ff9a268b81ef5d4b2413f582565806e4790.tar.bz2
glibc-352f4ff9a268b81ef5d4b2413f582565806e4790.zip
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.
Diffstat (limited to 'resolv/resolv_context.c')
-rw-r--r--resolv/resolv_context.c201
1 files changed, 201 insertions, 0 deletions
diff --git a/resolv/resolv_context.c b/resolv/resolv_context.c
new file mode 100644
index 0000000000..5083a40419
--- /dev/null
+++ b/resolv/resolv_context.c
@@ -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;
+ }
+}