about summary refs log tree commit diff
path: root/resolv/resolv_context.c
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2017-07-03 20:31:23 +0200
committerFlorian Weimer <fweimer@redhat.com>2017-07-03 20:57:28 +0200
commitf30a54b21b83f254533c59ca72ad17af5249c6be (patch)
tree174c6e8b77d8fc1514f4108e3290b91d19d838a6 /resolv/resolv_context.c
parent352f4ff9a268b81ef5d4b2413f582565806e4790 (diff)
downloadglibc-f30a54b21b83f254533c59ca72ad17af5249c6be.tar.gz
glibc-f30a54b21b83f254533c59ca72ad17af5249c6be.tar.xz
glibc-f30a54b21b83f254533c59ca72ad17af5249c6be.zip
resolv: Introduce struct resolv_conf with extended resolver state
This change provides additional resolver configuration state which
is not exposed through the _res ABI.  It reuses the existing
initstamp field in the supposedly-private part of _res.  Some effort
is undertaken to avoid memory safety issues introduced by applications
which directly patch the _res object.

With this commit, only the initstamp field is moved into struct
resolv_conf.  Additional members will be added later, eventually
migrating the entire resolver configuration.
Diffstat (limited to 'resolv/resolv_context.c')
-rw-r--r--resolv/resolv_context.c38
1 files changed, 33 insertions, 5 deletions
diff --git a/resolv/resolv_context.c b/resolv/resolv_context.c
index 5083a40419..0ee2184055 100644
--- a/resolv/resolv_context.c
+++ b/resolv/resolv_context.c
@@ -17,9 +17,11 @@
    <http://www.gnu.org/licenses/>.  */
 
 #include <resolv_context.h>
+#include <resolv_conf.h>
 #include <resolv-internal.h>
 
 #include <assert.h>
+#include <errno.h>
 #include <stdlib.h>
 #include <stdio.h>
 
@@ -52,19 +54,37 @@ 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)
+maybe_init (struct resolv_context *ctx, bool preinit)
 {
+  struct __res_state *resp = ctx->resp;
   if (resp->options & RES_INIT)
     {
-      if (__res_initstamp != resp->_u._ext.initstamp)
+      /* If there is no associated resolv_conf object despite the
+         initialization, something modified *ctx->resp.  Do not
+         override those changes.  */
+      if (ctx->conf != NULL && ctx->conf->initstamp != __res_initstamp)
         {
           if (resp->nscount > 0)
+            /* This call will detach the extended resolver state.  */
             __res_iclose (resp, true);
-          return __res_vinit (resp, 1) == 0;
+          /* And this call will attach it again.  */
+          if (__res_vinit (resp, 1) < 0)
+            {
+              /* The configuration no longer matches after failed
+                 initialization.  */
+              __resolv_conf_put (ctx->conf);
+              ctx->conf = NULL;
+              return false;
+            }
+          /* Delay the release of the old configuration until this
+             point, so that __res_vinit can reuse it if possible.  */
+          __resolv_conf_put (ctx->conf);
+          ctx->conf = __resolv_conf_get (ctx->resp);
         }
       return true;
     }
 
+  assert (ctx->conf == NULL);
   if (preinit)
     {
       if (!resp->retrans)
@@ -75,7 +95,11 @@ maybe_init (struct __res_state *resp, bool preinit)
       if (!resp->id)
         resp->id = res_randomid ();
     }
-  return __res_vinit (resp, preinit) == 0;
+
+  if (__res_vinit (resp, preinit) < 0)
+    return false;
+  ctx->conf = __resolv_conf_get (ctx->resp);
+  return true;
 }
 
 /* Allocate a new context object and initialize it.  The object is put
@@ -87,6 +111,7 @@ context_alloc (struct __res_state *resp)
   if (ctx == NULL)
     return NULL;
   ctx->resp = resp;
+  ctx->conf = __resolv_conf_get (resp);
   ctx->__refcount = 1;
   ctx->__from_res = true;
   ctx->__next = current;
@@ -98,8 +123,11 @@ context_alloc (struct __res_state *resp)
 static void
 context_free (struct resolv_context *ctx)
 {
+  int error_code = errno;
   current = ctx->__next;
+  __resolv_conf_put (ctx->conf);
   free (ctx);
+  __set_errno (error_code);
 }
 
 /* Reuse the current context object.  */
@@ -130,7 +158,7 @@ context_get (bool preinit)
   struct resolv_context *ctx = context_alloc (&_res);
   if (ctx == NULL)
     return NULL;
-  if (!maybe_init (ctx->resp, preinit))
+  if (!maybe_init (ctx, preinit))
     {
       context_free (ctx);
       return NULL;