1
0
mirror of https://sourceware.org/git/glibc.git synced 2025-08-08 17:42:12 +03:00

resolv: Automatically reload a changed /etc/resolv.conf file [BZ #984]

This commit enhances the stub resolver to reload the configuration
in the per-thread _res object if the /etc/resolv.conf file has
changed.  The resolver checks whether the application has modified
_res and will not overwrite the _res object in that case.

The struct resolv_context mechanism is used to check the
configuration file only once per name lookup.
This commit is contained in:
Florian Weimer
2017-07-03 21:06:23 +02:00
parent a1c4eb8794
commit aef16cc8a4
11 changed files with 273 additions and 95 deletions

View File

@@ -51,6 +51,20 @@
resolver state. */
static __thread struct resolv_context *current attribute_tls_model_ie;
/* The resolv_conf handling will gives us a ctx->conf pointer even if
these fields do not match because a mis-match does not cause a loss
of state (_res objects can store the full information). This
function checks to ensure that there is a full patch, to prevent
overwriting a patched configuration. */
static bool
replicated_configuration_matches (const struct resolv_context *ctx)
{
return ctx->resp->options == ctx->conf->options
&& ctx->resp->retrans == ctx->conf->retrans
&& ctx->resp->retry == ctx->conf->retry
&& ctx->resp->ndots == ctx->conf->ndots;
}
/* 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
@@ -59,27 +73,36 @@ maybe_init (struct resolv_context *ctx, bool preinit)
struct __res_state *resp = ctx->resp;
if (resp->options & RES_INIT)
{
if (resp->options & RES_NORELOAD)
/* Configuration reloading was explicitly disabled. */
return true;
/* 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 (ctx->conf != NULL && replicated_configuration_matches (ctx))
{
if (resp->nscount > 0)
/* This call will detach the extended resolver state. */
__res_iclose (resp, true);
/* And this call will attach it again. */
if (__res_vinit (resp, 1) < 0)
struct resolv_conf *current = __resolv_conf_get_current ();
if (current == NULL)
return false;
/* Check if the configuration changed. */
if (current != ctx->conf)
{
/* The configuration no longer matches after failed
initialization. */
__resolv_conf_put (ctx->conf);
ctx->conf = NULL;
return false;
/* This call will detach the extended resolver state. */
if (resp->nscount > 0)
__res_iclose (resp, true);
/* Reattach the current configuration. */
if (__resolv_conf_attach (ctx->resp, current))
{
__resolv_conf_put (ctx->conf);
/* ctx takes ownership, so we do not release current. */
ctx->conf = current;
}
}
/* 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);
else
/* No change. Drop the reference count for current. */
__resolv_conf_put (current);
}
return true;
}