]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
parse user configuration before exclusive mode
authorColin Vidal <colin@isc.org>
Tue, 22 Apr 2025 11:46:47 +0000 (13:46 +0200)
committerEvan Hunt <each@isc.org>
Mon, 23 Jun 2025 17:35:31 +0000 (10:35 -0700)
The configuration file was parsed when named was in exclusive
(i.e. single-threaded) mode and unable to answer queries. Because
the parsing is a self-contained operation, it is now done before
named enters exclusive mode.

This reduces the amount of time named can't answer queries when
reloading the configuration when the configuration file is large.
Note that exclusive mode is still used for applying the
configuration changes to the server.

Also, simplify the configuration logic by parsing the built-in
configuration only once at server start time.

bin/named/config.c
bin/named/include/named/config.h
bin/named/server.c

index 59ca2883cb197d6173f0af6123a143a6b32ed68b..de180cf14d36bbb2956f6172772aea034cb14b89 100644 (file)
@@ -18,6 +18,8 @@
 #include <stdlib.h>
 
 #include <isc/buffer.h>
+#include <isc/dir.h>
+#include <isc/file.h>
 #include <isc/log.h>
 #include <isc/mem.h>
 #include <isc/netmgr.h>
@@ -38,6 +40,7 @@
 
 #include <dst/dst.h>
 
+#include <isccfg/check.h>
 #include <isccfg/grammar.h>
 #include <isccfg/namedconf.h>
 
@@ -369,6 +372,98 @@ named_config_parsedefaults(cfg_parser_t *parser, cfg_obj_t **conf) {
                                conf);
 }
 
+/*
+ * This function is called as soon as the 'directory' statement has been
+ * parsed.  This can be extended to support other options if necessary.
+ */
+static isc_result_t
+directory_callback(const char *clausename, const cfg_obj_t *obj, void *arg) {
+       isc_result_t result;
+       const char *directory;
+
+       REQUIRE(strcasecmp("directory", clausename) == 0);
+
+       UNUSED(arg);
+       UNUSED(clausename);
+
+       /*
+        * Change directory.
+        */
+       directory = cfg_obj_asstring(obj);
+
+       if (!isc_file_ischdiridempotent(directory)) {
+               cfg_obj_log(obj, ISC_LOG_WARNING,
+                           "option 'directory' contains relative path '%s'",
+                           directory);
+       }
+
+       if (!isc_file_isdirwritable(directory)) {
+               isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER,
+                             ISC_LOG_ERROR, "directory '%s' is not writable",
+                             directory);
+               return ISC_R_NOPERM;
+       }
+
+       result = isc_dir_chdir(directory);
+       if (result != ISC_R_SUCCESS) {
+               cfg_obj_log(obj, ISC_LOG_ERROR,
+                           "change directory to '%s' failed: %s", directory,
+                           isc_result_totext(result));
+               return result;
+       }
+
+       char cwd[PATH_MAX];
+       if (getcwd(cwd, sizeof(cwd)) == cwd) {
+               isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER,
+                             ISC_LOG_INFO, "the working directory is now '%s'",
+                             cwd);
+       }
+
+       return ISC_R_SUCCESS;
+}
+
+isc_result_t
+named_config_parsefile(cfg_parser_t *parser, cfg_obj_t **conf) {
+       isc_result_t result;
+
+       REQUIRE(parser);
+       REQUIRE(conf && *conf == NULL);
+
+       isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER,
+                     ISC_LOG_INFO, "parsing user configuration from '%s'",
+                     named_g_conffile);
+
+       cfg_parser_setcallback(parser, directory_callback, NULL);
+       result = cfg_parse_file(parser, named_g_conffile, &cfg_type_namedconf,
+                               conf);
+       if (result != ISC_R_SUCCESS) {
+               goto cleanup;
+       }
+
+       /*
+        * Check the validity of the configuration.
+        *
+        * (Ignore plugin parameters for now; they will be
+        * checked later when the modules are actually loaded and
+        * registered.)
+        */
+       result = isccfg_check_namedconf(*conf, BIND_CHECK_ALGORITHMS,
+                                       named_g_mctx);
+       if (result != ISC_R_SUCCESS) {
+               goto cleanup;
+       }
+
+       goto out;
+
+cleanup:
+       if (*conf) {
+               cfg_obj_destroy(parser, conf);
+       }
+
+out:
+       return result;
+}
+
 const char *
 named_config_getdefault(void) {
        return defaultconf;
index 62f0e3c815d3bf7fd3e6a2fbfb0074c901a80cee..744f583423ae96539ecfbd5e05d285c4ab8fe1fd 100644 (file)
@@ -27,6 +27,9 @@
 isc_result_t
 named_config_parsedefaults(cfg_parser_t *parser, cfg_obj_t **conf);
 
+isc_result_t
+named_config_parsefile(cfg_parser_t *parser, cfg_obj_t **conf);
+
 const char *
 named_config_getdefault(void);
 
index 9831f24c68a430af0d0e1ca4b643f39ab9090967..d5be63179aa96facebf41f46bbe25ac6615822a1 100644 (file)
@@ -1142,7 +1142,8 @@ configure_view_dnsseckeys(dns_view_t *view, const cfg_obj_t *vconfig,
 
                /*
                 * If bind.keys exists and is populated, it overrides
-                * the trust-anchors clause hard-coded in named_g_defaultconfig.
+                * the trust-anchors clause hard-coded in
+                * named_g_defaultconfig.
                 */
                if (bindkeys != NULL) {
                        isc_log_write(DNS_LOGCATEGORY_SECURITY,
@@ -6732,56 +6733,6 @@ configure_server_quota(const cfg_obj_t **maps, const char *name,
        isc_quota_max(quota, cfg_obj_asuint32(obj));
 }
 
-/*
- * This function is called as soon as the 'directory' statement has been
- * parsed.  This can be extended to support other options if necessary.
- */
-static isc_result_t
-directory_callback(const char *clausename, const cfg_obj_t *obj, void *arg) {
-       isc_result_t result;
-       const char *directory;
-
-       REQUIRE(strcasecmp("directory", clausename) == 0);
-
-       UNUSED(arg);
-       UNUSED(clausename);
-
-       /*
-        * Change directory.
-        */
-       directory = cfg_obj_asstring(obj);
-
-       if (!isc_file_ischdiridempotent(directory)) {
-               cfg_obj_log(obj, ISC_LOG_WARNING,
-                           "option 'directory' contains relative path '%s'",
-                           directory);
-       }
-
-       if (!isc_file_isdirwritable(directory)) {
-               isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER,
-                             ISC_LOG_ERROR, "directory '%s' is not writable",
-                             directory);
-               return ISC_R_NOPERM;
-       }
-
-       result = isc_dir_chdir(directory);
-       if (result != ISC_R_SUCCESS) {
-               cfg_obj_log(obj, ISC_LOG_ERROR,
-                           "change directory to '%s' failed: %s", directory,
-                           isc_result_totext(result));
-               return result;
-       }
-
-       char cwd[PATH_MAX];
-       if (getcwd(cwd, sizeof(cwd)) == cwd) {
-               isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER,
-                             ISC_LOG_INFO, "the working directory is now '%s'",
-                             cwd);
-       }
-
-       return ISC_R_SUCCESS;
-}
-
 /*
  * This event callback is invoked to do periodic network interface
  * scanning.
@@ -7336,7 +7287,7 @@ cleanup:
 
 static isc_result_t
 setup_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
-              cfg_parser_t *conf_parser, cfg_aclconfctx_t *actx) {
+              cfg_parser_t *config_parser, cfg_aclconfctx_t *actx) {
        isc_result_t result = ISC_R_SUCCESS;
        bool allow = false;
        ns_cfgctx_t *nzcfg = NULL;
@@ -7439,12 +7390,11 @@ setup_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
        *nzcfg = (ns_cfgctx_t){ 0 };
 
        /*
-        * We attach the parser that was used for config as well
-        * as the one that will be used for added zones, to avoid
+        * We attach the parser that will be used for added zones to avoid
         * a shutdown race later.
         */
        isc_mem_attach(view->mctx, &nzcfg->mctx);
-       cfg_parser_attach(conf_parser, &nzcfg->conf_parser);
+       cfg_parser_attach(config_parser, &nzcfg->conf_parser);
        cfg_parser_attach(named_g_addparser, &nzcfg->add_parser);
        cfg_aclconfctx_attach(actx, &nzcfg->actx);
 
@@ -7452,8 +7402,8 @@ setup_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
                                      mapsize);
        if (result != ISC_R_SUCCESS) {
                cfg_aclconfctx_detach(&nzcfg->actx);
-               cfg_parser_destroy(&nzcfg->add_parser);
                cfg_parser_destroy(&nzcfg->conf_parser);
+               cfg_parser_destroy(&nzcfg->add_parser);
                isc_mem_putanddetach(&nzcfg->mctx, nzcfg, sizeof(*nzcfg));
                dns_view_setnewzones(view, false, NULL, NULL, 0ULL);
                return result;
@@ -7850,10 +7800,10 @@ cleanup:
 #endif /* HAVE_LMDB */
 
 static isc_result_t
-load_configuration(const char *filename, named_server_t *server,
-                  bool first_time) {
-       cfg_obj_t *config = NULL, *bindkeys = NULL;
-       cfg_parser_t *conf_parser = NULL, *bindkeys_parser = NULL;
+apply_configuration(cfg_parser_t *configparser, cfg_obj_t *config,
+                   named_server_t *server, bool first_time) {
+       cfg_obj_t *bindkeys = NULL;
+       cfg_parser_t *bindkeys_parser = NULL;
        const cfg_obj_t *builtin_views = NULL;
        const cfg_obj_t *maps[3];
        const cfg_obj_t *obj = NULL;
@@ -7891,6 +7841,9 @@ load_configuration(const char *filename, named_server_t *server,
        dns_aclenv_t *env =
                ns_interfacemgr_getaclenv(named_g_server->interfacemgr);
 
+       isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER,
+                     ISC_LOG_DEBUG(1), "apply_configuration");
+
        /*
         * Require the reconfiguration to happen always on the main loop
         */
@@ -7920,22 +7873,6 @@ load_configuration(const char *filename, named_server_t *server,
         */
        dns_dyndb_cleanup();
 
-       /*
-        * Parse the global default pseudo-config file.
-        */
-       if (first_time) {
-               result = named_config_parsedefaults(named_g_parser,
-                                                   &named_g_defaultconfig);
-               if (result != ISC_R_SUCCESS) {
-                       named_main_earlyfatal("unable to load "
-                                             "internal defaults: %s",
-                                             isc_result_totext(result));
-               }
-               RUNTIME_CHECK(cfg_map_get(named_g_defaultconfig, "options",
-                                         &named_g_defaultoptions) ==
-                             ISC_R_SUCCESS);
-       }
-
        /*
         * Log the current working directory.
         */
@@ -7949,38 +7886,6 @@ load_configuration(const char *filename, named_server_t *server,
                }
        }
 
-       /*
-        * Parse the configuration file using the new config code.
-        */
-       config = NULL;
-       isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER,
-                     ISC_LOG_INFO, "loading configuration from '%s'",
-                     filename);
-       result = cfg_parser_create(named_g_mctx, &conf_parser);
-       if (result != ISC_R_SUCCESS) {
-               goto cleanup_exclusive;
-       }
-
-       cfg_parser_setcallback(conf_parser, directory_callback, NULL);
-       result = cfg_parse_file(conf_parser, filename, &cfg_type_namedconf,
-                               &config);
-       if (result != ISC_R_SUCCESS) {
-               goto cleanup_conf_parser;
-       }
-
-       /*
-        * Check the validity of the configuration.
-        *
-        * (Ignore plugin parameters for now; they will be
-        * checked later when the modules are actually loaded and
-        * registered.)
-        */
-       result = isccfg_check_namedconf(config, BIND_CHECK_ALGORITHMS,
-                                       named_g_mctx);
-       if (result != ISC_R_SUCCESS) {
-               goto cleanup_config;
-       }
-
        /* Let's recreate the TLS context cache */
        if (server->tlsctx_server_cache != NULL) {
                isc_tlsctx_cache_detach(&server->tlsctx_server_cache);
@@ -8052,7 +7957,7 @@ load_configuration(const char *filename, named_server_t *server,
                        result = cfg_parser_create(named_g_mctx,
                                                   &bindkeys_parser);
                        if (result != ISC_R_SUCCESS) {
-                               goto cleanup_config;
+                               goto cleanup_bindkeys_parser;
                        }
 
                        result = cfg_parse_file(bindkeys_parser,
@@ -8689,7 +8594,7 @@ load_configuration(const char *filename, named_server_t *server,
                }
                INSIST(view != NULL);
 
-               result = setup_newzones(view, config, vconfig, conf_parser,
+               result = setup_newzones(view, config, vconfig, configparser,
                                        named_g_aclconfctx);
                dns_view_detach(&view);
 
@@ -8711,7 +8616,7 @@ load_configuration(const char *filename, named_server_t *server,
                }
                INSIST(view != NULL);
 
-               result = setup_newzones(view, config, NULL, conf_parser,
+               result = setup_newzones(view, config, NULL, configparser,
                                        named_g_aclconfctx);
 
                dns_view_detach(&view);
@@ -9284,7 +9189,6 @@ cleanup_portsets:
        isc_portset_destroy(named_g_mctx, &v4portset);
 
 cleanup_bindkeys_parser:
-
        if (bindkeys_parser != NULL) {
                if (bindkeys != NULL) {
                        cfg_obj_destroy(bindkeys_parser, &bindkeys);
@@ -9292,24 +9196,49 @@ cleanup_bindkeys_parser:
                cfg_parser_destroy(&bindkeys_parser);
        }
 
-cleanup_config:
-       cfg_obj_destroy(conf_parser, &config);
-
-cleanup_conf_parser:
-       cfg_parser_destroy(&conf_parser);
-
 cleanup_exclusive:
        if (exclusive) {
                isc_loopmgr_resume(named_g_loopmgr);
        }
 
        isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER,
-                     ISC_LOG_DEBUG(1), "load_configuration: %s",
+                     ISC_LOG_DEBUG(1), "apply_configuration: %s",
                      isc_result_totext(result));
 
        return result;
 }
 
+static isc_result_t
+load_configuration(named_server_t *server, bool first_time) {
+       isc_result_t result;
+       cfg_parser_t *parser = NULL;
+       cfg_obj_t *config = NULL;
+
+       isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER,
+                     ISC_LOG_DEBUG(1), "load_configuration");
+
+       result = cfg_parser_create(named_g_mctx, &parser);
+       if (result != ISC_R_SUCCESS) {
+               goto out;
+       }
+
+       result = named_config_parsefile(parser, &config);
+       if (result != ISC_R_SUCCESS) {
+               goto cleanup;
+       }
+
+       result = apply_configuration(parser, config, server, first_time);
+
+cleanup:
+       if (config) {
+               cfg_obj_destroy(parser, &config);
+       }
+       cfg_parser_destroy(&parser);
+
+out:
+       return result;
+}
+
 static isc_result_t
 view_loaded(void *arg) {
        isc_result_t result;
@@ -9485,11 +9414,18 @@ run_server(void *arg) {
        CHECKFATAL(cfg_parser_create(named_g_mctx, &named_g_parser),
                   "creating default configuration parser");
 
+       CHECKFATAL(named_config_parsedefaults(named_g_parser,
+                                             &named_g_defaultconfig),
+                  "unable to parse defaults config");
+
+       CHECKFATAL(cfg_map_get(named_g_defaultconfig, "options",
+                              &named_g_defaultoptions),
+                  "missing 'options' in default config");
+
        CHECKFATAL(cfg_parser_create(named_g_mctx, &named_g_addparser),
                   "creating additional configuration parser");
 
-       CHECKFATAL(load_configuration(named_g_conffile, server, true),
-                  "loading configuration");
+       CHECKFATAL(load_configuration(server, true), "loading configuration");
 
        CHECKFATAL(load_zones(server, false), "loading zones");
 #ifdef ENABLE_AFL
@@ -9969,7 +9905,7 @@ fatal(const char *msg, isc_result_t result) {
 static isc_result_t
 loadconfig(named_server_t *server) {
        isc_result_t result;
-       result = load_configuration(named_g_conffile, server, false);
+       result = load_configuration(server, false);
        if (result == ISC_R_SUCCESS) {
                isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER,
                              ISC_LOG_INFO,