about summary refs log tree commit diff
path: root/resolv/res_init.c
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2017-07-03 21:06:23 +0200
committerFlorian Weimer <fweimer@redhat.com>2017-07-03 21:06:23 +0200
commitaef16cc8a4c670036d45590877d411a97f01e0cd (patch)
tree4403f1962170ce92087a6e1af62dbb92e15d27f6 /resolv/res_init.c
parenta1c4eb8794e789b5055d7ceb13b2b3231abf5e26 (diff)
downloadglibc-aef16cc8a4c670036d45590877d411a97f01e0cd.tar.gz
glibc-aef16cc8a4c670036d45590877d411a97f01e0cd.tar.xz
glibc-aef16cc8a4c670036d45590877d411a97f01e0cd.zip
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.
Diffstat (limited to 'resolv/res_init.c')
-rw-r--r--resolv/res_init.c78
1 files changed, 48 insertions, 30 deletions
diff --git a/resolv/res_init.c b/resolv/res_init.c
index 80a21fb90d..fa46ce7813 100644
--- a/resolv/res_init.c
+++ b/resolv/res_init.c
@@ -106,8 +106,6 @@
 
 static uint32_t net_mask (struct in_addr);
 
-unsigned long long int __res_initstamp;
-
 int
 res_ninit (res_state statp)
 {
@@ -164,6 +162,16 @@ struct resolv_conf_parser
   struct resolv_conf template;
 };
 
+/* Return true if *PREINIT contains actual preinitialization.  */
+static bool
+has_preinit_values (const struct __res_state *preinit)
+{
+  return (preinit->retrans != 0 && preinit->retrans != RES_TIMEOUT)
+    || (preinit->retry != 0 && preinit->retry != RES_DFLRETRY)
+    || (preinit->options != 0
+        && (preinit->options & ~RES_INIT) != RES_DEFAULT);
+}
+
 static void
 resolv_conf_parser_init (struct resolv_conf_parser *parser,
                          const struct __res_state *preinit)
@@ -531,14 +539,8 @@ res_vinit_1 (FILE *fp, struct resolv_conf_parser *parser)
   return true;
 }
 
-/* Set up default settings.  If the /etc/resolv.conf configuration
-   file exist, the values there will have precedence.  Otherwise, the
-   server address is set to INADDR_LOOPBACK and the default domain
-   name comes from gethostname.  The RES_OPTIONS and LOCALDOMAIN
-   environment variables can be used to override some settings.
-   Return 0 if completes successfully, -1 on error.  */
-int
-__res_vinit (res_state statp, int preinit)
+struct resolv_conf *
+__resolv_conf_load (struct __res_state *preinit)
 {
   /* Ensure that /etc/hosts.conf has been loaded (once).  */
   _res_hconf_init ();
@@ -559,20 +561,14 @@ __res_vinit (res_state statp, int preinit)
       default:
         /* Other errors refer to resource allocation problems and
            need to be handled by the application.  */
-        return -1;
+        return NULL;
       }
 
   struct resolv_conf_parser parser;
-  if (preinit)
-    {
-      resolv_conf_parser_init (&parser, statp);
-      statp->id = res_randomid ();
-    }
-  else
-    resolv_conf_parser_init (&parser, NULL);
+  resolv_conf_parser_init (&parser, preinit);
 
-  bool ok = res_vinit_1 (fp, &parser);
-  if (ok)
+  struct resolv_conf *conf = NULL;
+  if (res_vinit_1 (fp, &parser))
     {
       parser.template.nameserver_list
         = nameserver_list_begin (&parser.nameserver_list);
@@ -583,21 +579,42 @@ __res_vinit (res_state statp, int preinit)
         = search_list_size (&parser.search_list);
       parser.template.sort_list = sort_list_begin (&parser.sort_list);
       parser.template.sort_list_size = sort_list_size (&parser.sort_list);
-      struct resolv_conf *conf = __resolv_conf_allocate (&parser.template);
-      if (conf == NULL)
-        ok = false;
-      else
-        {
-          ok = __resolv_conf_attach (statp, conf);
-          __resolv_conf_put (conf);
-        }
+      conf = __resolv_conf_allocate (&parser.template);
     }
   resolv_conf_parser_free (&parser);
 
-  if (!ok)
+  return conf;
+}
+
+/* Set up default settings.  If the /etc/resolv.conf configuration
+   file exist, the values there will have precedence.  Otherwise, the
+   server address is set to INADDR_LOOPBACK and the default domain
+   name comes from gethostname.  The RES_OPTIONS and LOCALDOMAIN
+   environment variables can be used to override some settings.
+   Return 0 if completes successfully, -1 on error.  */
+int
+__res_vinit (res_state statp, int preinit)
+{
+  struct resolv_conf *conf;
+  if (preinit && has_preinit_values (statp))
+    /* For the preinit case, we cannot use the cached configuration
+       because some settings could be different.  */
+    conf = __resolv_conf_load (statp);
+  else
+    conf = __resolv_conf_get_current ();
+  if (conf == NULL)
     return -1;
+
+  bool ok = __resolv_conf_attach (statp, conf);
+  __resolv_conf_put (conf);
+  if (ok)
+    {
+      if (preinit)
+        statp->id = res_randomid ();
+      return 0;
+    }
   else
-    return 0;
+    return -1;
 }
 
 static void
@@ -652,6 +669,7 @@ res_setoptions (struct resolv_conf_parser *parser, const char *options)
             { STRnLEN ("single-request"), 0, RES_SNGLKUP },
             { STRnLEN ("no_tld_query"), 0, RES_NOTLDQUERY },
             { STRnLEN ("no-tld-query"), 0, RES_NOTLDQUERY },
+            { STRnLEN ("no-reload"), 0, RES_NORELOAD },
             { STRnLEN ("use-vc"), 0, RES_USEVC }
           };
 #define noptions (sizeof (options) / sizeof (options[0]))