From 3d618684de66c6012b630a976c91669b0fa149d3 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Thu, 23 Oct 2025 13:44:56 -0700 Subject: [PATCH 1/3] refactor newzones configuration instead of using an opaque ns_cfgctx pointer to store the configuration data to be used by addzone and modzone, there are now fields in the dns_view object to store the view configuration and LMDB database environment. the global configuration is now stored in the named_server object, along with the ACL context. --- bin/named/include/named/server.h | 1 + bin/named/server.c | 574 +++++++++++++++------------- lib/dns/include/dns/view.h | 79 ++-- lib/dns/view.c | 201 +--------- lib/isccfg/aclconf.c | 33 +- lib/isccfg/include/isccfg/aclconf.h | 15 +- 6 files changed, 346 insertions(+), 557 deletions(-) diff --git a/bin/named/include/named/server.h b/bin/named/include/named/server.h index da28b0cf5b..d22c943d1a 100644 --- a/bin/named/include/named/server.h +++ b/bin/named/include/named/server.h @@ -110,6 +110,7 @@ struct named_server { isc_signal_t *sigusr1; cfg_aclconfctx_t *aclctx; + cfg_obj_t *config; }; #define NAMED_SERVER_MAGIC ISC_MAGIC('S', 'V', 'E', 'R') diff --git a/bin/named/server.c b/bin/named/server.c index 61cc44cca3..1b3767467b 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -301,18 +301,6 @@ typedef struct matching_view_ctx { dns_view_t *view; } matching_view_ctx_t; -/*% - * Configuration context to retain for each view that allows - * new zones to be added at runtime. - */ -typedef struct ns_cfgctx { - isc_mem_t *mctx; - cfg_obj_t *config; - cfg_obj_t *vconfig; - cfg_obj_t *nzf_config; - cfg_aclconfctx_t *aclctx; -} ns_cfgctx_t; - /*% * A function to write out added-zone configuration to the new_zone_file * specified in 'view'. Maybe called by delete_zoneconf(). @@ -509,9 +497,6 @@ find_maplist(const cfg_obj_t *config, const char *listname, const char *name); static isc_result_t add_keydata_zone(dns_view_t *view, const char *directory, isc_mem_t *mctx); -static void -newzone_cfgctx_destroy(void **cfgp); - static isc_result_t putstr(isc_buffer_t **b, const char *str); @@ -545,7 +530,7 @@ nzf_append(dns_view_t *view, const cfg_obj_t *zconfig); #endif /* ifdef HAVE_LMDB */ static isc_result_t -load_nzf(dns_view_t *view, ns_cfgctx_t *nzcfg); +load_nzf(dns_view_t *view); /*% * Configure a single view ACL at '*aclp'. Get its configuration from @@ -2339,29 +2324,20 @@ catz_addmodzone_cb(void *arg) { const cfg_obj_t *zlist = NULL; cfg_obj_t *zoneconf = NULL; cfg_obj_t *zoneobj = NULL; - ns_cfgctx_t *cfg = NULL; dns_zone_t *zone = NULL; + dns_view_t *view = cz->view; if (isc_loop_shuttingdown(isc_loop_get(isc_tid()))) { goto cleanup; } - /* - * A non-empty 'catalog-zones' statement implies that 'allow-new-zones' - * is true, so this is expected to be non-NULL. - */ - cfg = (ns_cfgctx_t *)cz->view->new_zone_config; - if (cfg == NULL) { - CHECK(ISC_R_FAILURE); - } - name = dns_catz_entry_getname(cz->entry); isc_buffer_init(&namebuf, nameb, DNS_NAME_FORMATSIZE); dns_name_totext(name, DNS_NAME_OMITFINALDOT, &namebuf); isc_buffer_putuint8(&namebuf, 0); - result = dns_fwdtable_find(cz->view->fwdtable, name, &dnsforwarders); + result = dns_fwdtable_find(view->fwdtable, name, &dnsforwarders); if (result == ISC_R_SUCCESS && dnsforwarders->fwdpolicy == dns_fwdpolicy_only) { @@ -2374,7 +2350,7 @@ catz_addmodzone_cb(void *arg) { goto cleanup; } - result = dns_view_findzone(cz->view, name, DNS_ZTFIND_EXACT, &zone); + result = dns_view_findzone(view, name, DNS_ZTFIND_EXACT, &zone); if (cz->mod) { dns_catz_zone_t *parentcatz; @@ -2455,7 +2431,7 @@ catz_addmodzone_cb(void *arg) { confbuf = NULL; result = dns_catz_generate_zonecfg(cz->origin, cz->entry, &confbuf); if (result == ISC_R_SUCCESS) { - result = cfg_parse_buffer(cfg->mctx, confbuf, "catz", 0, + result = cfg_parse_buffer(isc_g_mctx, confbuf, "catz", 0, &cfg_type_addzoneconf, 0, &zoneconf); isc_buffer_free(&confbuf); } @@ -2481,12 +2457,12 @@ catz_addmodzone_cb(void *arg) { /* Mark view unfrozen so that zone can be added */ isc_loopmgr_pause(); - dns_view_thaw(cz->view); - result = configure_zone(cfg->config, zoneobj, cfg->vconfig, cz->view, - &cz->cbd->server->viewlist, - &cz->cbd->server->kasplist, cfg->aclctx, true, - false, true, cz->mod); - dns_view_freeze(cz->view); + dns_view_thaw(view); + result = configure_zone( + cz->cbd->server->config, zoneobj, view->newzone.vconfig, view, + &cz->cbd->server->viewlist, &cz->cbd->server->kasplist, + cz->cbd->server->aclctx, true, false, true, cz->mod); + dns_view_freeze(view); isc_loopmgr_resume(); if (result != ISC_R_SUCCESS) { @@ -2498,7 +2474,7 @@ catz_addmodzone_cb(void *arg) { } /* Is it there yet? */ - CHECK(dns_view_findzone(cz->view, name, DNS_ZTFIND_EXACT, &zone)); + CHECK(dns_view_findzone(view, name, DNS_ZTFIND_EXACT, &zone)); /* * Load the zone from the master file. If this fails, we'll @@ -2520,7 +2496,7 @@ catz_addmodzone_cb(void *arg) { } /* Remove the zone from the zone table */ - dns_view_delzone(cz->view, zone); + dns_view_delzone(view, zone); goto cleanup; } @@ -2540,7 +2516,7 @@ cleanup: } dns_catz_entry_detach(cz->origin, &cz->entry); dns_catz_zone_detach(&cz->origin); - dns_view_weakdetach(&cz->view); + dns_view_weakdetach(&view); isc_mem_putanddetach(&cz->mctx, cz, sizeof(*cz)); } @@ -2707,7 +2683,6 @@ catz_reconfigure(dns_catz_entry_t *entry, void *arg1, void *arg2) { char nameb[DNS_NAME_FORMATSIZE]; cfg_obj_t *zoneconf = NULL; cfg_obj_t *zoneobj = NULL; - ns_cfgctx_t *cfg = NULL; dns_zone_t *zone = NULL; isc_result_t result; @@ -2722,18 +2697,9 @@ catz_reconfigure(dns_catz_entry_t *entry, void *arg1, void *arg2) { return; } - /* - * A non-empty 'catalog-zones' statement implies that 'allow-new-zones' - * is true, so this is expected to be non-NULL. - */ - cfg = (ns_cfgctx_t *)view->new_zone_config; - if (cfg == NULL) { - CHECK(ISC_R_FAILURE); - } - result = dns_catz_generate_zonecfg(data->catz, entry, &confbuf); if (result == ISC_R_SUCCESS) { - result = cfg_parse_buffer(cfg->mctx, confbuf, "catz", 0, + result = cfg_parse_buffer(isc_g_mctx, confbuf, "catz", 0, &cfg_type_addzoneconf, 0, &zoneconf); isc_buffer_free(&confbuf); } @@ -2756,10 +2722,10 @@ catz_reconfigure(dns_catz_entry_t *entry, void *arg1, void *arg2) { } zoneobj = cfg_listelt_value(cfg_list_first(zlist)); - result = configure_zone(data->config, zoneobj, cfg->vconfig, view, - &data->cbd->server->viewlist, - &data->cbd->server->kasplist, cfg->aclctx, true, - false, true, true); + result = configure_zone( + data->config, zoneobj, view->newzone.vconfig, view, + &data->cbd->server->viewlist, &data->cbd->server->kasplist, + data->cbd->server->aclctx, true, false, true, true); if (result != ISC_R_SUCCESS) { isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, @@ -7156,23 +7122,83 @@ cleanup: return result; } +static void +newzone_cleanup(dns_view_t *view) { + if (view->newzone.vconfig != NULL) { + cfg_obj_detach(&view->newzone.vconfig); + } + if (view->newzone.nzconfig != NULL) { + cfg_obj_detach(&view->newzone.nzconfig); + } + +#ifdef HAVE_LMDB + if (view->newzone.dbenv != NULL) { + mdb_env_close(view->newzone.dbenv); + view->newzone.dbenv = NULL; + } +#endif /* ifdef HAVE_LMDB */ + + if (view->newzone.file != NULL) { + isc_mem_free(view->mctx, view->newzone.file); + } + if (view->newzone.dir != NULL) { + isc_mem_free(view->mctx, view->newzone.dir); + } + if (view->newzone.db != NULL) { + isc_mem_free(view->mctx, view->newzone.db); + } + + view->newzone.allowed = false; +} + static isc_result_t -setup_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig, - cfg_aclconfctx_t *aclctx) { +nz_filename(dns_view_t *view, const char *suffix, char *buffer, size_t buflen) { + isc_result_t result; + char newbuf[PATH_MAX]; + + result = isc_file_sanitize(view->newzone.dir, view->name, suffix, + buffer, buflen); + if (result != ISC_R_SUCCESS) { + return result; + } else if (view->newzone.dir == NULL || isc_file_exists(buffer)) { + return ISC_R_SUCCESS; + } else { + /* Save buffer */ + strlcpy(newbuf, buffer, sizeof(newbuf)); + } + + /* + * It isn't in the specified directory; check CWD. + */ + result = isc_file_sanitize(NULL, view->name, suffix, buffer, buflen); + if (result != ISC_R_SUCCESS || isc_file_exists(buffer)) { + return result; + } + + /* + * File does not exist in either 'directory' or CWD, + * so use the path in 'directory'. + */ + strlcpy(buffer, newbuf, buflen); + return ISC_R_SUCCESS; +} + +static isc_result_t +setup_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig) { isc_result_t result = ISC_R_SUCCESS; - bool allow = false; - ns_cfgctx_t *nzcfg = NULL; const cfg_obj_t *maps[4]; const cfg_obj_t *options = NULL, *voptions = NULL; const cfg_obj_t *nz = NULL; - const cfg_obj_t *nzdir = NULL; const char *dir = NULL; const cfg_obj_t *obj = NULL; + char buffer[1024]; int i = 0; - uint64_t mapsize = 0ULL; REQUIRE(config != NULL); + /* Unnecessary but harmless */ + newzone_cleanup(view); + if (vconfig != NULL) { voptions = cfg_tuple_get(vconfig, "options"); } @@ -7187,11 +7213,26 @@ setup_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig, result = named_config_get(maps, "allow-new-zones", &nz); if (result == ISC_R_SUCCESS) { - allow = cfg_obj_asboolean(nz); + view->newzone.allowed = cfg_obj_asboolean(nz); } - result = named_config_get(maps, "new-zones-directory", &nzdir); + if (!view->newzone.allowed) { + /* + * A non-empty catalog-zones statement implies allow-new-zones + */ + const cfg_obj_t *cz = NULL; + result = named_config_get(maps, "catalog-zones", &cz); + if (result == ISC_R_SUCCESS) { + const cfg_listelt_t *e = + cfg_list_first(cfg_tuple_get(cz, "zone list")); + if (e != NULL) { + view->newzone.allowed = true; + } + } + } + + result = named_config_get(maps, "new-zones-directory", &obj); if (result == ISC_R_SUCCESS) { - dir = cfg_obj_asstring(nzdir); + dir = cfg_obj_asstring(obj); result = isc_file_isdirectory(dir); if (result != ISC_R_SUCCESS) { isc_log_write(DNS_LOGCATEGORY_SECURITY, @@ -7209,78 +7250,86 @@ setup_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig, return ISC_R_NOPERM; } - dns_view_setnewzonedir(view, dir); + view->newzone.dir = isc_mem_strdup(view->mctx, dir); } + CHECK(nz_filename(view, "nzf", buffer, sizeof(buffer))); + view->newzone.file = isc_mem_strdup(view->mctx, buffer); + #ifdef HAVE_LMDB + uint64_t mapsize = 0ULL; + + obj = NULL; result = named_config_get(maps, "lmdb-mapsize", &obj); if (result == ISC_R_SUCCESS && obj != NULL) { mapsize = cfg_obj_asuint64(obj); if (mapsize < (1ULL << 20)) { /* 1 megabyte */ cfg_obj_log(obj, ISC_LOG_ERROR, - "'lmdb-mapsize " - "%" PRId64 "' " - "is too small", + "'lmdb-mapsize %" PRId64 "' is too small", mapsize); return ISC_R_FAILURE; } else if (mapsize > (1ULL << 40)) { /* 1 terabyte */ cfg_obj_log(obj, ISC_LOG_ERROR, - "'lmdb-mapsize " - "%" PRId64 "' " - "is too large", + "'lmdb-mapsize %" PRId64 "' is too large", mapsize); return ISC_R_FAILURE; } } -#else /* ifdef HAVE_LMDB */ - UNUSED(obj); + + if (view->newzone.allowed) { + CHECK(nz_filename(view, "nzd", buffer, sizeof(buffer))); + view->newzone.db = isc_mem_strdup(view->mctx, buffer); + + MDB_env *env = NULL; + int status = mdb_env_create(&env); + if (status != MDB_SUCCESS) { + isc_log_write(DNS_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_OTHER, ISC_LOG_ERROR, + "mdb_env_create failed: %s", + mdb_strerror(status)); + CHECK(ISC_R_FAILURE); + } + + if (mapsize != 0ULL) { + status = mdb_env_set_mapsize(env, mapsize); + if (status != MDB_SUCCESS) { + isc_log_write(DNS_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_OTHER, + ISC_LOG_ERROR, + "mdb_env_set_mapsize failed: %s", + mdb_strerror(status)); + CHECK(ISC_R_FAILURE); + } + view->newzone.mapsize = mapsize; + } + + status = mdb_env_open(env, view->newzone.db, DNS_LMDB_FLAGS, + 0600); + if (status != MDB_SUCCESS) { + isc_log_write(DNS_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_OTHER, ISC_LOG_ERROR, + "mdb_env_open of '%s' failed: %s", + view->newzone.db, mdb_strerror(status)); + CHECK(ISC_R_FAILURE); + } + + view->newzone.dbenv = env; + env = NULL; + } #endif /* HAVE_LMDB */ - /* - * A non-empty catalog-zones statement implies allow-new-zones - */ - if (!allow) { - const cfg_obj_t *cz = NULL; - result = named_config_get(maps, "catalog-zones", &cz); - if (result == ISC_R_SUCCESS) { - const cfg_listelt_t *e = - cfg_list_first(cfg_tuple_get(cz, "zone list")); - if (e != NULL) { - allow = true; - } - } - } - - if (!allow) { - dns_view_setnewzones(view, false, NULL, NULL, 0ULL); - return ISC_R_SUCCESS; - } - - nzcfg = isc_mem_get(view->mctx, sizeof(*nzcfg)); - *nzcfg = (ns_cfgctx_t){ 0 }; - - /* - * 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_aclconfctx_attach(aclctx, &nzcfg->aclctx); - - result = dns_view_setnewzones(view, true, nzcfg, newzone_cfgctx_destroy, - mapsize); - if (result != ISC_R_SUCCESS) { - cfg_aclconfctx_detach(&nzcfg->aclctx); - isc_mem_putanddetach(&nzcfg->mctx, nzcfg, sizeof(*nzcfg)); - dns_view_setnewzones(view, false, NULL, NULL, 0ULL); - return result; - } - - cfg_obj_attach(config, &nzcfg->config); + view->newzone.cleanup = newzone_cleanup; if (vconfig != NULL) { - cfg_obj_attach(vconfig, &nzcfg->vconfig); + cfg_obj_attach(vconfig, &view->newzone.vconfig); } - result = load_nzf(view, nzcfg); + if (view->newzone.allowed) { + result = load_nzf(view); + } + return result; + +cleanup: + newzone_cleanup(view); return result; } @@ -7330,11 +7379,10 @@ static isc_result_t configure_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig, cfg_aclconfctx_t *aclctx, dns_kasplist_t *kasplist) { isc_result_t result; - ns_cfgctx_t *nzctx = NULL; const cfg_obj_t *zonelist = NULL; - nzctx = view->new_zone_config; - if (nzctx == NULL || nzctx->nzf_config == NULL) { + if (!view->newzone.allowed || view->newzone.nzconfig == NULL) { + /* New zones are disallowed, or the NZF file is empty */ return ISC_R_SUCCESS; } @@ -7343,7 +7391,7 @@ configure_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig, view->name); zonelist = NULL; - cfg_map_get(nzctx->nzf_config, "zone", &zonelist); + cfg_map_get(view->newzone.nzconfig, "zone", &zonelist); CFG_LIST_FOREACH(zonelist, element) { const cfg_obj_t *zconfig = cfg_listelt_value(element); @@ -7412,14 +7460,14 @@ data_to_cfg(dns_view_t *view, MDB_val *key, MDB_val *data, isc_buffer_t **text, snprintf(bufname, sizeof(bufname), "%.*s", (int)zone_name_len, zone_name); - result = cfg_parse_buffer(view->mctx, *text, bufname, 0, + result = cfg_parse_buffer(isc_g_mctx, *text, bufname, 0, &cfg_type_addzoneconf, 0, &zoneconf); if (result != ISC_R_SUCCESS) { isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, "parsing config for zone '%.*s' in " "NZD database '%s' failed", - (int)zone_name_len, zone_name, view->new_zone_db); + (int)zone_name_len, zone_name, view->newzone.db); goto cleanup; } @@ -7453,7 +7501,7 @@ typedef isc_result_t (*newzone_cfg_cb_t)(const cfg_obj_t *zconfig, * transforming NZD data into a zone configuration object or if "callback" * returns an error. * - * Caller must hold 'view->new_zone_lock'. + * Caller must hold 'view->newzone.lock'. */ static isc_result_t for_all_newzone_cfgs(newzone_cfg_cb_t callback, cfg_obj_t *config, @@ -7560,23 +7608,22 @@ configure_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig, MDB_txn *txn = NULL; MDB_dbi dbi; - if (view->new_zone_config == NULL) { + if (!view->newzone.allowed) { return ISC_R_SUCCESS; } - LOCK(&view->new_zone_lock); + LOCK(&view->newzone.lock); result = nzd_open(view, MDB_RDONLY, &txn, &dbi); if (result != ISC_R_SUCCESS) { - UNLOCK(&view->new_zone_lock); + UNLOCK(&view->newzone.lock); return ISC_R_SUCCESS; } isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, - "loading NZD configs from '%s' " - "for view '%s'", - view->new_zone_db, view->name); + "loading NZD configs from '%s' for view '%s'", + view->newzone.db, view->name); result = for_all_newzone_cfgs(configure_newzone, config, vconfig, view, aclctx, kasplist, txn, dbi); @@ -7596,7 +7643,7 @@ configure_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig, (void)nzd_close(&txn, false); - UNLOCK(&view->new_zone_lock); + UNLOCK(&view->newzone.lock); return result; } @@ -7607,8 +7654,7 @@ configure_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig, ISC_LOG_DEBUG(1), "apply_configuration: %s", __func__); static isc_result_t -create_views(cfg_obj_t *config, cfg_aclconfctx_t *aclctx, - dns_viewlist_t *viewlist) { +create_views(cfg_obj_t *config, dns_viewlist_t *viewlist) { isc_result_t result = ISC_R_SUCCESS; const cfg_obj_t *bindview = NULL; const cfg_obj_t *views = NULL; @@ -7640,9 +7686,8 @@ create_views(cfg_obj_t *config, cfg_aclconfctx_t *aclctx, CHECK(create_view(vconfig, viewlist, &view)); INSIST(view != NULL); - result = setup_newzones(view, config, vconfig, aclctx); + CHECK(setup_newzones(view, config, vconfig)); explicitviews = true; - dns_view_detach(&view); } @@ -7654,7 +7699,7 @@ create_views(cfg_obj_t *config, cfg_aclconfctx_t *aclctx, CHECK(create_view(NULL, viewlist, &view)); INSIST(view != NULL); - CHECK(setup_newzones(view, config, NULL, aclctx)); + CHECK(setup_newzones(view, config, NULL)); dns_view_detach(&view); } @@ -7919,7 +7964,7 @@ apply_configuration(cfg_obj_t *config, cfg_obj_t *bindkeys, goto cleanup_kasplist; } - result = create_views(config, aclctx, &viewlist); + result = create_views(config, &viewlist); if (result != ISC_R_SUCCESS) { goto cleanup_viewlist; } @@ -8903,6 +8948,11 @@ apply_configuration(cfg_obj_t *config, cfg_obj_t *bindkeys, */ named_g_defaultconfigtime = isc_time_now(); + if (server->config != NULL) { + cfg_obj_detach(&server->config); + } + cfg_obj_attach(config, &server->config); + isc_loopmgr_resume(); exclusive = false; @@ -9701,6 +9751,10 @@ named_server_destroy(named_server_t **serverp) { isc_tlsctx_cache_detach(&server->tlsctx_client_cache); } + if (server->config != NULL) { + cfg_obj_detach(&server->config); + } + server->magic = 0; isc_mem_put(server->mctx, server, sizeof(*server)); *serverp = NULL; @@ -12147,9 +12201,9 @@ nzf_append(dns_view_t *view, const cfg_obj_t *zconfig) { FILE *fp = NULL; bool offsetok = false; - LOCK(&view->new_zone_lock); + LOCK(&view->newzone.lock); - CHECK(isc_stdio_open(view->new_zone_file, "a", &fp)); + CHECK(isc_stdio_open(view->newzone.file, "a", &fp)); CHECK(isc_stdio_seek(fp, 0, SEEK_END)); CHECK(isc_stdio_tell(fp, &offset)); @@ -12171,8 +12225,7 @@ cleanup: if (offsetok) { isc_result_t result2; - result2 = isc_file_truncate(view->new_zone_file, - offset); + result2 = isc_file_truncate(view->newzone.file, offset); if (result2 != ISC_R_SUCCESS) { isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER, @@ -12180,12 +12233,12 @@ cleanup: "Error truncating NZF file '%s' " "during rollback from append: " "%s", - view->new_zone_file, + view->newzone.file, isc_result_totext(result2)); } } } - UNLOCK(&view->new_zone_lock); + UNLOCK(&view->newzone.lock); return result; } @@ -12197,7 +12250,7 @@ nzf_writeconf(const cfg_obj_t *config, dns_view_t *view) { char tmp[1024]; isc_result_t result; - result = isc_file_template(view->new_zone_file, "nzf-XXXXXXXX", tmp, + result = isc_file_template(view->newzone.file, "nzf-XXXXXXXX", tmp, sizeof(tmp)); if (result == ISC_R_SUCCESS) { result = isc_file_openunique(tmp, &fp); @@ -12229,7 +12282,7 @@ nzf_writeconf(const cfg_obj_t *config, dns_view_t *view) { if (result != ISC_R_SUCCESS) { goto cleanup; } - CHECK(isc_file_rename(tmp, view->new_zone_file)); + CHECK(isc_file_rename(tmp, view->newzone.file)); return result; cleanup: @@ -12241,24 +12294,24 @@ cleanup: } static isc_result_t -load_nzf(dns_view_t *view, ns_cfgctx_t *nzcfg) { +load_nzf(dns_view_t *view) { isc_result_t result; /* The new zone file may not exist. That is OK. */ - if (!isc_file_exists(view->new_zone_file)) { + if (!isc_file_exists(view->newzone.file)) { return ISC_R_SUCCESS; } /* - * Parse the configuration in the NZF file. This may be called in - * multiple views, so we reset the parser each time. + * Parse the configuration in the NZF file. */ - result = cfg_parse_file(nzcfg->mctx, view->new_zone_file, - &cfg_type_addzoneconf, 0, &nzcfg->nzf_config); + result = cfg_parse_file(view->mctx, view->newzone.file, + &cfg_type_addzoneconf, 0, + &view->newzone.nzconfig); if (result != ISC_R_SUCCESS) { isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, "Error parsing NZF file '%s': %s", - view->new_zone_file, isc_result_totext(result)); + view->newzone.file, isc_result_totext(result)); } return result; @@ -12393,7 +12446,7 @@ cleanup: /* * Check whether the new zone database for 'view' can be opened for writing. * - * Caller must hold 'view->new_zone_lock'. + * Caller must hold 'view->newzone.lock'. */ static isc_result_t nzd_writable(dns_view_t *view) { @@ -12404,7 +12457,7 @@ nzd_writable(dns_view_t *view) { REQUIRE(view != NULL); - status = mdb_txn_begin((MDB_env *)view->new_zone_dbenv, 0, 0, &txn); + status = mdb_txn_begin(view->newzone.dbenv, 0, 0, &txn); if (status != MDB_SUCCESS) { isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, "mdb_txn_begin: %s", @@ -12427,7 +12480,7 @@ nzd_writable(dns_view_t *view) { /* * Open the new zone database for 'view' and start a transaction for it. * - * Caller must hold 'view->new_zone_lock'. + * Caller must hold 'view->newzone.lock'. */ static isc_result_t nzd_open(dns_view_t *view, unsigned int flags, MDB_txn **txnp, MDB_dbi *dbi) { @@ -12438,7 +12491,7 @@ nzd_open(dns_view_t *view, unsigned int flags, MDB_txn **txnp, MDB_dbi *dbi) { REQUIRE(txnp != NULL && *txnp == NULL); REQUIRE(dbi != NULL); - status = mdb_txn_begin((MDB_env *)view->new_zone_dbenv, 0, flags, &txn); + status = mdb_txn_begin(view->newzone.dbenv, 0, flags, &txn); if (status != MDB_SUCCESS) { isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, "mdb_txn_begin: %s", @@ -12468,7 +12521,7 @@ cleanup: } /* - * nzd_env_close() and nzd_env_reopen are a kluge to address the + * nzd_env_close() and nzd_env_reopen() are a kluge to address the * problem of an NZD file possibly being created before we drop * root privileges. */ @@ -12479,15 +12532,15 @@ nzd_env_close(dns_view_t *view) { char lockpath[PATH_MAX]; int status, ret; - if (view->new_zone_dbenv == NULL) { + if (view->newzone.dbenv == NULL) { return; } - status = mdb_env_get_path(view->new_zone_dbenv, &dbpath); + status = mdb_env_get_path(view->newzone.dbenv, &dbpath); INSIST(status == MDB_SUCCESS); snprintf(lockpath, sizeof(lockpath), "%s-lock", dbpath); strlcpy(dbpath_copy, dbpath, sizeof(dbpath_copy)); - mdb_env_close((MDB_env *)view->new_zone_dbenv); + mdb_env_close(view->newzone.dbenv); /* * Database files must be owned by the eventual user, not by root. @@ -12501,7 +12554,7 @@ nzd_env_close(dns_view_t *view) { */ (void)isc_file_remove(lockpath); - view->new_zone_dbenv = NULL; + view->newzone.dbenv = NULL; } static isc_result_t @@ -12510,7 +12563,7 @@ nzd_env_reopen(dns_view_t *view) { MDB_env *env = NULL; int status; - if (view->new_zone_db == NULL) { + if (view->newzone.db == NULL) { return ISC_R_SUCCESS; } @@ -12524,8 +12577,8 @@ nzd_env_reopen(dns_view_t *view) { CHECK(ISC_R_FAILURE); } - if (view->new_zone_mapsize != 0ULL) { - status = mdb_env_set_mapsize(env, view->new_zone_mapsize); + if (view->newzone.mapsize != 0ULL) { + status = mdb_env_set_mapsize(env, view->newzone.mapsize); if (status != MDB_SUCCESS) { isc_log_write(DNS_LOGCATEGORY_GENERAL, ISC_LOGMODULE_OTHER, ISC_LOG_ERROR, @@ -12535,15 +12588,15 @@ nzd_env_reopen(dns_view_t *view) { } } - status = mdb_env_open(env, view->new_zone_db, DNS_LMDB_FLAGS, 0600); + status = mdb_env_open(env, view->newzone.db, DNS_LMDB_FLAGS, 0600); if (status != MDB_SUCCESS) { isc_log_write(DNS_LOGCATEGORY_GENERAL, ISC_LOGMODULE_OTHER, ISC_LOG_ERROR, "mdb_env_open of '%s' failed: %s", - view->new_zone_db, mdb_strerror(status)); + view->newzone.db, mdb_strerror(status)); CHECK(ISC_R_FAILURE); } - view->new_zone_dbenv = env; + view->newzone.dbenv = env; env = NULL; result = ISC_R_SUCCESS; @@ -12558,7 +12611,7 @@ cleanup: * If 'commit' is true, commit the new zone database transaction pointed to by * 'txnp'; otherwise, abort that transaction. * - * Caller must hold 'view->new_zone_lock' for the view that the transaction + * Caller must hold 'view->newzone.lock' for the view that the transaction * pointed to by 'txnp' was started for. */ static isc_result_t @@ -12587,10 +12640,10 @@ nzd_close(MDB_txn **txnp, bool commit) { * If there's an existing NZF file, load it and migrate its data * to the NZD. * - * Caller must hold view->new_zone_lock. + * Caller must hold view->newzone.lock. */ static isc_result_t -load_nzf(dns_view_t *view, ns_cfgctx_t *nzcfg) { +load_nzf(dns_view_t *view) { isc_result_t result; cfg_obj_t *nzf_config = NULL; int status; @@ -12603,13 +12656,11 @@ load_nzf(dns_view_t *view, ns_cfgctx_t *nzcfg) { MDB_val key, data; ns_dzarg_t dzarg; - UNUSED(nzcfg); - /* * If NZF file doesn't exist, or NZD DB exists and already * has data, return without attempting migration. */ - if (!isc_file_exists(view->new_zone_file)) { + if (!isc_file_exists(view->newzone.file)) { result = ISC_R_SUCCESS; goto cleanup; } @@ -12618,19 +12669,19 @@ load_nzf(dns_view_t *view, ns_cfgctx_t *nzcfg) { ISC_LOG_INFO, "Migrating zones from NZF file '%s' to " "NZD database '%s'", - view->new_zone_file, view->new_zone_db); + view->newzone.file, view->newzone.db); /* * Instead of blindly copying lines, we parse the NZF file using * the configuration parser, because it validates it against the * config type, giving us a guarantee that valid configuration * will be written to DB. */ - result = cfg_parse_file(nzcfg->mctx, view->new_zone_file, + result = cfg_parse_file(isc_g_mctx, view->newzone.file, &cfg_type_addzoneconf, 0, &nzf_config); if (result != ISC_R_SUCCESS) { isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, "Error parsing NZF file '%s': %s", - view->new_zone_file, isc_result_totext(result)); + view->newzone.file, isc_result_totext(result)); goto cleanup; } @@ -12715,10 +12766,10 @@ load_nzf(dns_view_t *view, ns_cfgctx_t *nzcfg) { * if an NZD database is found for the view. But we rename NZF file * to a backup name here. */ - strlcpy(tempname, view->new_zone_file, sizeof(tempname)); + strlcpy(tempname, view->newzone.file, sizeof(tempname)); if (strlen(tempname) < sizeof(tempname) - 1) { strlcat(tempname, "~", sizeof(tempname)); - isc_file_rename(view->new_zone_file, tempname); + isc_file_rename(view->newzone.file, tempname); } cleanup: @@ -12876,7 +12927,7 @@ delete_zoneconf(dns_view_t *view, const cfg_obj_t *config, REQUIRE(config != NULL); REQUIRE(zname != NULL); - LOCK(&view->new_zone_lock); + LOCK(&view->newzone.lock); cfg_map_get(config, "zone", &zl); @@ -12906,19 +12957,19 @@ delete_zoneconf(dns_view_t *view, const cfg_obj_t *config, /* * Write config to NZF file if appropriate */ - if (nzfwriter != NULL && view->new_zone_file != NULL) { + if (nzfwriter != NULL && view->newzone.file != NULL) { result = nzfwriter(config, view); } cleanup: - UNLOCK(&view->new_zone_lock); + UNLOCK(&view->newzone.lock); return result; } static isc_result_t -do_addzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view, - dns_name_t *name, cfg_obj_t *zoneconf, const cfg_obj_t *zoneobj, - bool redirect, isc_buffer_t **text) { +do_addzone(named_server_t *server, dns_view_t *view, dns_name_t *name, + cfg_obj_t *zoneconf, const cfg_obj_t *zoneobj, bool redirect, + isc_buffer_t **text) { isc_result_t result, tresult; dns_zone_t *zone = NULL; const cfg_obj_t *voptions = NULL; @@ -12933,6 +12984,12 @@ do_addzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view, UNUSED(zoneconf); #endif + if (!view->newzone.allowed) { + result = ISC_R_NOPERM; + TCHECK(putstr(text, "new zone configuration is not allowed")); + goto cleanup; + } + /* Zone shouldn't already exist */ if (redirect) { result = (view->redirect == NULL) ? ISC_R_NOTFOUND @@ -12953,11 +13010,11 @@ do_addzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view, /* * Make sure we can open the configuration save file */ - result = isc_stdio_open(view->new_zone_file, "a", &fp); + result = isc_stdio_open(view->newzone.file, "a", &fp); if (result != ISC_R_SUCCESS) { isc_loopmgr_resume(); TCHECK(putstr(text, "unable to create '")); - TCHECK(putstr(text, view->new_zone_file)); + TCHECK(putstr(text, view->newzone.file)); TCHECK(putstr(text, "': ")); TCHECK(putstr(text, isc_result_totext(result))); goto cleanup; @@ -12966,14 +13023,14 @@ do_addzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view, (void)isc_stdio_close(fp); fp = NULL; #else /* HAVE_LMDB */ - LOCK(&view->new_zone_lock); + LOCK(&view->newzone.lock); locked = true; /* Make sure we can open the NZD database */ result = nzd_writable(view); if (result != ISC_R_SUCCESS) { isc_loopmgr_resume(); TCHECK(putstr(text, "unable to open NZD database for '")); - TCHECK(putstr(text, view->new_zone_db)); + TCHECK(putstr(text, view->newzone.db)); TCHECK(putstr(text, "'")); result = ISC_R_FAILURE; goto cleanup; @@ -12981,13 +13038,13 @@ do_addzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view, #endif /* HAVE_LMDB */ /* Check zone syntax */ - if (cfg->vconfig != NULL) { - voptions = cfg_tuple_get(cfg->vconfig, "options"); + if (view->newzone.vconfig != NULL) { + voptions = cfg_tuple_get(view->newzone.vconfig, "options"); } - result = isccfg_check_zoneconf(zoneobj, voptions, cfg->config, NULL, + result = isccfg_check_zoneconf(zoneobj, voptions, server->config, NULL, NULL, NULL, NULL, view->name, view->rdclass, BIND_CHECK_PLUGINS, - cfg->aclctx, cfg->mctx); + server->aclctx, isc_g_mctx); if (result != ISC_R_SUCCESS) { isc_loopmgr_resume(); goto cleanup; @@ -12995,9 +13052,9 @@ do_addzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view, /* Mark view unfrozen and configure zone */ dns_view_thaw(view); - result = configure_zone(cfg->config, zoneobj, cfg->vconfig, view, - &server->viewlist, &server->kasplist, - cfg->aclctx, true, false, false, false); + result = configure_zone(server->config, zoneobj, view->newzone.vconfig, + view, &server->viewlist, &server->kasplist, + server->aclctx, true, false, false, false); dns_view_freeze(view); isc_loopmgr_resume(); @@ -13031,11 +13088,11 @@ do_addzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view, * we've created. If there was a previous one, merge the new * zone into it. */ - if (cfg->nzf_config == NULL) { - cfg_obj_attach(zoneconf, &cfg->nzf_config); + if (view->newzone.nzconfig == NULL) { + cfg_obj_attach(zoneconf, &view->newzone.nzconfig); } else { cfg_obj_t *z = UNCONST(zoneobj); - CHECK(cfg_map_add(cfg->nzf_config, z, "zone")); + CHECK(cfg_map_add(view->newzone.nzconfig, z, "zone")); } cleanup_config = true; #endif /* HAVE_LMDB */ @@ -13084,7 +13141,8 @@ cleanup: (void)isc_stdio_close(fp); } if (result != ISC_R_SUCCESS && cleanup_config) { - tresult = delete_zoneconf(view, cfg->nzf_config, name, NULL); + tresult = delete_zoneconf(view, view->newzone.nzconfig, name, + NULL); RUNTIME_CHECK(tresult == ISC_R_SUCCESS); } #else /* HAVE_LMDB */ @@ -13092,7 +13150,7 @@ cleanup: (void)nzd_close(&txn, false); } if (locked) { - UNLOCK(&view->new_zone_lock); + UNLOCK(&view->newzone.lock); } #endif /* HAVE_LMDB */ @@ -13104,9 +13162,9 @@ cleanup: } static isc_result_t -do_modzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view, - dns_name_t *name, const char *zname, const cfg_obj_t *zoneobj, - bool redirect, isc_buffer_t **text) { +do_modzone(named_server_t *server, dns_view_t *view, dns_name_t *name, + const char *zname, const cfg_obj_t *zoneobj, bool redirect, + isc_buffer_t **text) { isc_result_t result, tresult; dns_zone_t *zone = NULL; const cfg_obj_t *voptions = NULL; @@ -13120,6 +13178,12 @@ do_modzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view, bool locked = false; #endif /* HAVE_LMDB */ + if (!view->newzone.allowed) { + result = ISC_R_NOPERM; + TCHECK(putstr(text, "new zone configuration is not allowed")); + goto cleanup; + } + /* Zone must already exist */ if (redirect) { if (view->redirect != NULL) { @@ -13138,22 +13202,14 @@ do_modzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view, added = dns_zone_getadded(zone); dns_zone_detach(&zone); -#ifndef HAVE_LMDB - cfg = (ns_cfgctx_t *)view->new_zone_config; - if (cfg == NULL) { - TCHECK(putstr(text, "new zone config is not set")); - CHECK(ISC_R_FAILURE); - } -#endif /* ifndef HAVE_LMDB */ - isc_loopmgr_pause(); #ifndef HAVE_LMDB /* Make sure we can open the configuration save file */ - result = isc_stdio_open(view->new_zone_file, "a", &fp); + result = isc_stdio_open(view->newzone.file, "a", &fp); if (result != ISC_R_SUCCESS) { TCHECK(putstr(text, "unable to open '")); - TCHECK(putstr(text, view->new_zone_file)); + TCHECK(putstr(text, view->newzone.file)); TCHECK(putstr(text, "': ")); TCHECK(putstr(text, isc_result_totext(result))); isc_loopmgr_resume(); @@ -13162,13 +13218,13 @@ do_modzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view, (void)isc_stdio_close(fp); fp = NULL; #else /* HAVE_LMDB */ - LOCK(&view->new_zone_lock); + LOCK(&view->newzone.lock); locked = true; /* Make sure we can open the NZD database */ result = nzd_writable(view); if (result != ISC_R_SUCCESS) { TCHECK(putstr(text, "unable to open NZD database for '")); - TCHECK(putstr(text, view->new_zone_db)); + TCHECK(putstr(text, view->newzone.db)); TCHECK(putstr(text, "'")); result = ISC_R_FAILURE; isc_loopmgr_resume(); @@ -13177,13 +13233,13 @@ do_modzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view, #endif /* HAVE_LMDB */ /* Check zone syntax */ - if (cfg->vconfig != NULL) { - voptions = cfg_tuple_get(cfg->vconfig, "options"); + if (view->newzone.vconfig != NULL) { + voptions = cfg_tuple_get(view->newzone.vconfig, "options"); } - result = isccfg_check_zoneconf(zoneobj, voptions, cfg->config, NULL, + result = isccfg_check_zoneconf(zoneobj, voptions, server->config, NULL, NULL, NULL, NULL, view->name, view->rdclass, BIND_CHECK_PLUGINS, - cfg->aclctx, cfg->mctx); + server->aclctx, isc_g_mctx); if (result != ISC_R_SUCCESS) { isc_loopmgr_resume(); goto cleanup; @@ -13191,9 +13247,9 @@ do_modzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view, /* Reconfigure the zone */ dns_view_thaw(view); - result = configure_zone(cfg->config, zoneobj, cfg->vconfig, view, - &server->viewlist, &server->kasplist, - cfg->aclctx, true, false, false, true); + result = configure_zone(server->config, zoneobj, view->newzone.vconfig, + view, &server->viewlist, &server->kasplist, + server->aclctx, true, false, false, true); dns_view_freeze(view); isc_loopmgr_resume(); @@ -13217,7 +13273,7 @@ do_modzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view, #ifndef HAVE_LMDB /* Remove old zone from configuration (and NZF file if applicable) */ if (added) { - result = delete_zoneconf(view, cfg->nzf_config, + result = delete_zoneconf(view, view->newzone.nzconfig, dns_zone_getorigin(zone), nzf_writeconf); if (result != ISC_R_SUCCESS) { @@ -13230,12 +13286,12 @@ do_modzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view, #endif /* HAVE_LMDB */ if (!added) { - if (cfg->vconfig == NULL) { - result = delete_zoneconf(view, cfg->config, + if (view->newzone.vconfig == NULL) { + result = delete_zoneconf(view, server->config, dns_zone_getorigin(zone), NULL); } else { - voptions = cfg_tuple_get(cfg->vconfig, "options"); + voptions = cfg_tuple_get(server->config, "options"); result = delete_zoneconf( view, voptions, dns_zone_getorigin(zone), NULL); } @@ -13286,7 +13342,7 @@ do_modzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view, #ifndef HAVE_LMDB /* Store the new zone configuration; also in NZF if applicable */ z = UNCONST(zoneobj); - CHECK(cfg_map_add(cfg->nzf_config, z, "zone")); + CHECK(cfg_map_add(view->newzone.nzconfig, z, "zone")); #endif /* HAVE_LMDB */ if (added) { @@ -13322,7 +13378,7 @@ cleanup: (void)nzd_close(&txn, false); } if (locked) { - UNLOCK(&view->new_zone_lock); + UNLOCK(&view->newzone.lock); } #endif /* HAVE_LMDB */ @@ -13342,7 +13398,6 @@ named_server_changezone(named_server_t *server, char *command, isc_result_t result; bool addzone; bool redirect = false; - ns_cfgctx_t *cfg = NULL; cfg_obj_t *zoneconf = NULL; const cfg_obj_t *zoneobj = NULL; const char *zonename; @@ -13365,9 +13420,9 @@ named_server_changezone(named_server_t *server, char *command, /* Are we accepting new zones in this view? */ #ifdef HAVE_LMDB - if (view->new_zone_db == NULL) + if (view->newzone.db == NULL) #else /* ifdef HAVE_LMDB */ - if (view->new_zone_file == NULL) + if (view->newzone.file == NULL) #endif /* HAVE_LMDB */ { (void)putstr(text, "Not allowing new zones in view '"); @@ -13376,11 +13431,6 @@ named_server_changezone(named_server_t *server, char *command, CHECK(ISC_R_NOPERM); } - cfg = (ns_cfgctx_t *)view->new_zone_config; - if (cfg == NULL) { - CHECK(ISC_R_FAILURE); - } - zonename = cfg_obj_asstring(cfg_tuple_get(zoneobj, "name")); isc_buffer_constinit(&buf, zonename, strlen(zonename)); isc_buffer_add(&buf, strlen(zonename)); @@ -13397,10 +13447,10 @@ named_server_changezone(named_server_t *server, char *command, } if (addzone) { - CHECK(do_addzone(server, cfg, view, dnsname, zoneconf, zoneobj, + CHECK(do_addzone(server, view, dnsname, zoneconf, zoneobj, redirect, text)); } else { - CHECK(do_modzone(server, cfg, view, dnsname, zonename, zoneobj, + CHECK(do_modzone(server, view, dnsname, zonename, zoneobj, redirect, text)); } @@ -13444,6 +13494,7 @@ inuse(const char *file, bool first, isc_buffer_t **text) { typedef struct { dns_zone_t *zone; + named_server_t *server; bool cleanup; } ns_dzctx_t; @@ -13457,7 +13508,6 @@ rmzone(void *arg) { dns_catz_zone_t *catz = NULL; char zonename[DNS_NAME_FORMATSIZE]; dns_view_t *view = NULL; - ns_cfgctx_t *cfg = NULL; dns_db_t *dbp = NULL; bool added; isc_result_t result; @@ -13471,7 +13521,6 @@ rmzone(void *arg) { /* Dig out configuration for this zone */ zone = dz->zone; view = dns_zone_getview(zone); - cfg = (ns_cfgctx_t *)view->new_zone_config; dns_name_format(dns_zone_getorigin(zone), zonename, sizeof(zonename)); isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER, @@ -13485,16 +13534,16 @@ rmzone(void *arg) { added = dns_zone_getadded(zone); catz = dns_zone_get_parentcatz(zone); - if (added && catz == NULL && cfg != NULL) { + if (added && catz == NULL) { #ifdef HAVE_LMDB /* Make sure we can open the NZD database */ - LOCK(&view->new_zone_lock); + LOCK(&view->newzone.lock); result = nzd_open(view, 0, &txn, &dbi); if (result != ISC_R_SUCCESS) { isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, "unable to open NZD database for '%s'", - view->new_zone_db); + view->newzone.db); } else { result = nzd_save(&txn, dbi, zone, NULL); } @@ -13509,9 +13558,9 @@ rmzone(void *arg) { if (txn != NULL) { (void)nzd_close(&txn, false); } - UNLOCK(&view->new_zone_lock); + UNLOCK(&view->newzone.lock); #else /* ifdef HAVE_LMDB */ - result = delete_zoneconf(view, cfg->nzf_config, + result = delete_zoneconf(view, view->newzone.nzconfig, dns_zone_getorigin(zone), nzf_writeconf); if (result != ISC_R_SUCCESS) { @@ -13523,14 +13572,14 @@ rmzone(void *arg) { #endif /* HAVE_LMDB */ } - if (!added && cfg != NULL) { - if (cfg->vconfig != NULL) { - const cfg_obj_t *voptions = cfg_tuple_get(cfg->vconfig, - "options"); + if (!added) { + if (view->newzone.vconfig != NULL) { + const cfg_obj_t *voptions = + cfg_tuple_get(view->newzone.vconfig, "options"); result = delete_zoneconf( view, voptions, dns_zone_getorigin(zone), NULL); } else { - result = delete_zoneconf(view, cfg->config, + result = delete_zoneconf(view, dz->server->config, dns_zone_getorigin(zone), NULL); } @@ -13659,6 +13708,12 @@ named_server_delzone(named_server_t *server, isc_lex_t *lex, } view = dns_zone_getview(zone); + + if (!view->newzone.allowed) { + TCHECK(putstr(text, "new zone configuration is not allowed")); + CHECK(ISC_R_NOPERM); + } + if (dns_zone_gettype(zone) == dns_zone_redirect) { dns_zone_detach(&view->redirect); } else { @@ -13669,6 +13724,7 @@ named_server_delzone(named_server_t *server, isc_lex_t *lex, dz = isc_mem_get(isc_g_mctx, sizeof(*dz)); *dz = (ns_dzctx_t){ .cleanup = cleanup, + .server = server, }; dns_zone_attach(zone, &dz->zone); isc_async_run(dns_zone_getloop(zone), rmzone, dz); @@ -13798,32 +13854,6 @@ cleanup: return result; } -static void -newzone_cfgctx_destroy(void **cfgp) { - ns_cfgctx_t *cfg; - - REQUIRE(cfgp != NULL && *cfgp != NULL); - - cfg = *cfgp; - - if (cfg->config != NULL) { - cfg_obj_detach(&cfg->config); - } - if (cfg->vconfig != NULL) { - cfg_obj_detach(&cfg->vconfig); - } - if (cfg->nzf_config != NULL) { - cfg_obj_detach(&cfg->nzf_config); - } - - if (cfg->aclctx != NULL) { - cfg_aclconfctx_detach(&cfg->aclctx); - } - - isc_mem_putanddetach(&cfg->mctx, cfg, sizeof(*cfg)); - *cfgp = NULL; -} - isc_result_t named_server_signing(named_server_t *server, isc_lex_t *lex, isc_buffer_t **text) { diff --git a/lib/dns/include/dns/view.h b/lib/dns/include/dns/view.h index d536cccbd3..1c3bbdb06e 100644 --- a/lib/dns/include/dns/view.h +++ b/lib/dns/include/dns/view.h @@ -77,6 +77,13 @@ #include #include +/* + * These are opaque types that will be used by named to save + * newzones configuration in the view. + */ +typedef struct cfg_obj cfg_obj_t; +typedef struct MDB_env MDB_env; + struct dns_view { /* Unlocked. */ unsigned int magic; @@ -203,28 +210,26 @@ struct dns_view { ISC_LINK(struct dns_view) link; dns_viewlist_t *viewlist; - dns_zone_t *managed_keys; - dns_zone_t *redirect; - dns_name_t *redirectzone; /* points to - * redirectfixed - * when valid */ + dns_zone_t *managed_keys; + dns_zone_t *redirect; + dns_name_t *redirectzone; /* points to redirectfixed when valid */ dns_fixedname_t redirectfixed; /* - * File and configuration data for zones added at runtime - * (only used in BIND9). - * - * XXX: This should be a pointer to an opaque type that - * named implements. + * File and configuration data for zones added at runtime. */ - char *new_zone_dir; - char *new_zone_file; - char *new_zone_db; - void *new_zone_dbenv; - uint64_t new_zone_mapsize; - void *new_zone_config; - void (*cfg_destroy)(void **); - isc_mutex_t new_zone_lock; + struct { + bool allowed; + char *dir; + char *file; + char *db; + MDB_env *dbenv; + uint64_t mapsize; + cfg_obj_t *vconfig; + cfg_obj_t *nzconfig; + void (*cleanup)(dns_view_t *); + isc_mutex_t lock; + } newzone; unsigned char secret[32]; /* Client secret */ unsigned int v6bias; @@ -1047,44 +1052,6 @@ dns_view_istrusted(dns_view_t *view, const dns_name_t *keyname, * \li 'dnskey' is valid. */ -isc_result_t -dns_view_setnewzones(dns_view_t *view, bool allow, void *cfgctx, - void (*cfg_destroy)(void **), uint64_t mapsize); -/*%< - * Set whether or not to allow zones to be created or deleted at runtime. - * - * If 'allow' is true, determines the filename into which new zone - * configuration will be written. Preserves the configuration context - * (a pointer to which is passed in 'cfgctx') for use when parsing new - * zone configuration. 'cfg_destroy' points to a callback routine to - * destroy the configuration context when the view is destroyed. (This - * roundabout method is used in order to avoid libdns having a dependency - * on libisccfg and libbind9.) - * - * If 'allow' is false, removes any existing references to - * configuration context and frees any memory. - * - * Requires: - * \li 'view' is valid. - * - * Returns: - * \li ISC_R_SUCCESS - * \li ISC_R_NOSPACE - */ - -void -dns_view_setnewzonedir(dns_view_t *view, const char *dir); -const char * -dns_view_getnewzonedir(dns_view_t *view); -/*%< - * Set/get the path to the directory in which NZF or NZD files should - * be stored. If the path was previously set to a non-NULL value, - * the previous value is freed. - * - * Requires: - * \li 'view' is valid. - */ - void dns_view_restorekeyring(dns_view_t *view); diff --git a/lib/dns/view.c b/lib/dns/view.c index 164b54becb..7efc9234a2 100644 --- a/lib/dns/view.c +++ b/lib/dns/view.c @@ -160,7 +160,7 @@ dns_view_create(isc_mem_t *mctx, dns_dispatchmgr_t *dispatchmgr, view->mctx, UNREACH_HOLD_TIME_INITIAL_SEC, UNREACH_HOLD_TIME_MAX_SEC, UNREACH_BACKOFF_ELIGIBLE_SEC); - isc_mutex_init(&view->new_zone_lock); + isc_mutex_init(&view->newzone.lock); dns_order_create(view->mctx, &view->order); @@ -345,20 +345,16 @@ destroy(dns_view_t *view) { dns_dt_detach(&view->dtenv); } #endif /* HAVE_DNSTAP */ - dns_view_setnewzones(view, false, NULL, NULL, 0ULL); - if (view->new_zone_file != NULL) { - isc_mem_free(view->mctx, view->new_zone_file); - } - if (view->new_zone_dir != NULL) { - isc_mem_free(view->mctx, view->new_zone_dir); + if (view->newzone.cleanup != NULL) { + view->newzone.cleanup(view); } #ifdef HAVE_LMDB - if (view->new_zone_dbenv != NULL) { - mdb_env_close((MDB_env *)view->new_zone_dbenv); - view->new_zone_dbenv = NULL; + if (view->newzone.dbenv != NULL) { + mdb_env_close((MDB_env *)view->newzone.dbenv); + view->newzone.dbenv = NULL; } - if (view->new_zone_db != NULL) { - isc_mem_free(view->mctx, view->new_zone_db); + if (view->newzone.db != NULL) { + isc_mem_free(view->mctx, view->newzone.db); } #endif /* HAVE_LMDB */ dns_fwdtable_destroy(&view->fwdtable); @@ -369,7 +365,7 @@ destroy(dns_view_t *view) { if (view->unreachcache != NULL) { dns_unreachcache_destroy(&view->unreachcache); } - isc_mutex_destroy(&view->new_zone_lock); + isc_mutex_destroy(&view->newzone.lock); isc_mutex_destroy(&view->lock); isc_refcount_destroy(&view->references); isc_refcount_destroy(&view->weakrefs); @@ -1680,185 +1676,6 @@ finish: return answer; } -/* - * Create path to a directory and a filename constructed from viewname. - * This is a front-end to isc_file_sanitize(), allowing backward - * compatibility to older versions when a file couldn't be expected - * to be in the specified directory but might be in the current working - * directory instead. - * - * It first tests for the existence of a file . in - * 'directory'. If the file does not exist, it checks again in the - * current working directory. If it does not exist there either, - * return the path inside the directory. - * - * Returns ISC_R_SUCCESS if a path to an existing file is found or - * a new path is created; returns ISC_R_NOSPACE if the path won't - * fit in 'buflen'. - */ - -static isc_result_t -nz_legacy(const char *directory, const char *viewname, const char *suffix, - char *buffer, size_t buflen) { - isc_result_t result; - char newbuf[PATH_MAX]; - - result = isc_file_sanitize(directory, viewname, suffix, buffer, buflen); - if (result != ISC_R_SUCCESS) { - return result; - } else if (directory == NULL || isc_file_exists(buffer)) { - return ISC_R_SUCCESS; - } else { - /* Save buffer */ - strlcpy(newbuf, buffer, sizeof(newbuf)); - } - - /* - * It isn't in the specified directory; check CWD. - */ - result = isc_file_sanitize(NULL, viewname, suffix, buffer, buflen); - if (result != ISC_R_SUCCESS || isc_file_exists(buffer)) { - return result; - } - - /* - * File does not exist in either 'directory' or CWD, - * so use the path in 'directory'. - */ - strlcpy(buffer, newbuf, buflen); - return ISC_R_SUCCESS; -} - -isc_result_t -dns_view_setnewzones(dns_view_t *view, bool allow, void *cfgctx, - void (*cfg_destroy)(void **), uint64_t mapsize) { - isc_result_t result = ISC_R_SUCCESS; - char buffer[1024]; -#ifdef HAVE_LMDB - MDB_env *env = NULL; - int status; -#endif /* ifdef HAVE_LMDB */ - -#ifndef HAVE_LMDB - UNUSED(mapsize); -#endif /* ifndef HAVE_LMDB */ - - REQUIRE(DNS_VIEW_VALID(view)); - REQUIRE((cfgctx != NULL && cfg_destroy != NULL) || !allow); - - if (view->new_zone_file != NULL) { - isc_mem_free(view->mctx, view->new_zone_file); - } - -#ifdef HAVE_LMDB - if (view->new_zone_dbenv != NULL) { - mdb_env_close((MDB_env *)view->new_zone_dbenv); - view->new_zone_dbenv = NULL; - } - - if (view->new_zone_db != NULL) { - isc_mem_free(view->mctx, view->new_zone_db); - } -#endif /* HAVE_LMDB */ - - if (view->new_zone_config != NULL) { - view->cfg_destroy(&view->new_zone_config); - view->cfg_destroy = NULL; - } - - if (!allow) { - return ISC_R_SUCCESS; - } - - CHECK(nz_legacy(view->new_zone_dir, view->name, "nzf", buffer, - sizeof(buffer))); - - view->new_zone_file = isc_mem_strdup(view->mctx, buffer); - -#ifdef HAVE_LMDB - CHECK(nz_legacy(view->new_zone_dir, view->name, "nzd", buffer, - sizeof(buffer))); - - view->new_zone_db = isc_mem_strdup(view->mctx, buffer); - - status = mdb_env_create(&env); - if (status != MDB_SUCCESS) { - isc_log_write(DNS_LOGCATEGORY_GENERAL, ISC_LOGMODULE_OTHER, - ISC_LOG_ERROR, "mdb_env_create failed: %s", - mdb_strerror(status)); - CHECK(ISC_R_FAILURE); - } - - if (mapsize != 0ULL) { - status = mdb_env_set_mapsize(env, mapsize); - if (status != MDB_SUCCESS) { - isc_log_write(DNS_LOGCATEGORY_GENERAL, - ISC_LOGMODULE_OTHER, ISC_LOG_ERROR, - "mdb_env_set_mapsize failed: %s", - mdb_strerror(status)); - CHECK(ISC_R_FAILURE); - } - view->new_zone_mapsize = mapsize; - } - - status = mdb_env_open(env, view->new_zone_db, DNS_LMDB_FLAGS, 0600); - if (status != MDB_SUCCESS) { - isc_log_write(DNS_LOGCATEGORY_GENERAL, ISC_LOGMODULE_OTHER, - ISC_LOG_ERROR, "mdb_env_open of '%s' failed: %s", - view->new_zone_db, mdb_strerror(status)); - CHECK(ISC_R_FAILURE); - } - - view->new_zone_dbenv = env; - env = NULL; -#endif /* HAVE_LMDB */ - - view->new_zone_config = cfgctx; - view->cfg_destroy = cfg_destroy; - -cleanup: - if (result != ISC_R_SUCCESS) { - if (view->new_zone_file != NULL) { - isc_mem_free(view->mctx, view->new_zone_file); - } - -#ifdef HAVE_LMDB - if (view->new_zone_db != NULL) { - isc_mem_free(view->mctx, view->new_zone_db); - } - if (env != NULL) { - mdb_env_close(env); - } -#endif /* HAVE_LMDB */ - view->new_zone_config = NULL; - view->cfg_destroy = NULL; - } - - return result; -} - -void -dns_view_setnewzonedir(dns_view_t *view, const char *dir) { - REQUIRE(DNS_VIEW_VALID(view)); - - if (view->new_zone_dir != NULL) { - isc_mem_free(view->mctx, view->new_zone_dir); - } - - if (dir == NULL) { - return; - } - - view->new_zone_dir = isc_mem_strdup(view->mctx, dir); -} - -const char * -dns_view_getnewzonedir(dns_view_t *view) { - REQUIRE(DNS_VIEW_VALID(view)); - - return view->new_zone_dir; -} - isc_result_t dns_view_searchdlz(dns_view_t *view, const dns_name_t *name, unsigned int minlabels, dns_clientinfomethods_t *methods, diff --git a/lib/isccfg/aclconf.c b/lib/isccfg/aclconf.c index 2e386a1ffd..8076c82971 100644 --- a/lib/isccfg/aclconf.c +++ b/lib/isccfg/aclconf.c @@ -58,33 +58,18 @@ cfg_aclconfctx_create(isc_mem_t *mctx, cfg_aclconfctx_t **aclctxp) { return ISC_R_SUCCESS; } -void -cfg_aclconfctx_attach(cfg_aclconfctx_t *src, cfg_aclconfctx_t **dest) { - REQUIRE(src != NULL); - REQUIRE(dest != NULL && *dest == NULL); - - isc_refcount_increment(&src->references); - *dest = src; -} - -void -cfg_aclconfctx_detach(cfg_aclconfctx_t **aclctxp) { - REQUIRE(aclctxp != NULL && *aclctxp != NULL); - - cfg_aclconfctx_t *aclctx = *aclctxp; - *aclctxp = NULL; - - if (isc_refcount_decrement(&aclctx->references) == 1) { - isc_refcount_destroy(&aclctx->references); - ISC_LIST_FOREACH(aclctx->named_acl_cache, dacl, nextincache) { - ISC_LIST_UNLINK(aclctx->named_acl_cache, dacl, - nextincache); - dns_acl_detach(&dacl); - } - isc_mem_putanddetach(&aclctx->mctx, aclctx, sizeof(*aclctx)); +static void +destroy_aclctx(cfg_aclconfctx_t *aclctx) { + isc_refcount_destroy(&aclctx->references); + ISC_LIST_FOREACH(aclctx->named_acl_cache, dacl, nextincache) { + ISC_LIST_UNLINK(aclctx->named_acl_cache, dacl, nextincache); + dns_acl_detach(&dacl); } + isc_mem_putanddetach(&aclctx->mctx, aclctx, sizeof(*aclctx)); } +ISC_REFCOUNT_IMPL(cfg_aclconfctx, destroy_aclctx); + /* * Find the definition of the named acl whose name is "name". */ diff --git a/lib/isccfg/include/isccfg/aclconf.h b/lib/isccfg/include/isccfg/aclconf.h index 9ad8253bd1..6ce2a5b761 100644 --- a/lib/isccfg/include/isccfg/aclconf.h +++ b/lib/isccfg/include/isccfg/aclconf.h @@ -39,19 +39,6 @@ cfg_aclconfctx_create(isc_mem_t *mctx, cfg_aclconfctx_t **aclctxp); * Creates and initializes an ACL configuration context. */ -void -cfg_aclconfctx_detach(cfg_aclconfctx_t **aclctxp); -/* - * Removes a reference to an ACL configuration context; when references - * reaches zero, clears the contents and deallocate the structure. - */ - -void -cfg_aclconfctx_attach(cfg_aclconfctx_t *src, cfg_aclconfctx_t **dest); -/* - * Attaches a pointer to an existing ACL configuration context. - */ - isc_result_t cfg_acl_fromconfig(const cfg_obj_t *caml, const cfg_obj_t *cctx, cfg_aclconfctx_t *ctx, isc_mem_t *mctx, @@ -72,3 +59,5 @@ cfg_acl_fromconfig(const cfg_obj_t *caml, const cfg_obj_t *cctx, * 'ctx' to be non NULL. * '*target' to be NULL or a valid dns_acl_t. */ + +ISC_REFCOUNT_DECL(cfg_aclconfctx); From 20caa5be69bea8c614800da0cc8d035891f7b0ca Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Fri, 24 Oct 2025 00:42:33 -0700 Subject: [PATCH 2/3] implement "rndc showconf" add a new rndc command to dump server configuration info: - "rndc showconf -user" dumps the contents of named.conf - "rndc showconf -builtin" dumps named_g_defaltconfig - "rndc showconf -effective" dumps the effective configuration, i.e., the merger of the builtin and the user configurations. --- bin/named/control.c | 2 + bin/named/include/named/control.h | 1 + bin/named/include/named/server.h | 10 +- bin/named/server.c | 176 +++++++++++++++++++++--------- bin/rndc/rndc.rst | 22 ++++ 5 files changed, 157 insertions(+), 54 deletions(-) diff --git a/bin/named/control.c b/bin/named/control.c index aa2b9d3930..ba6001bac8 100644 --- a/bin/named/control.c +++ b/bin/named/control.c @@ -276,6 +276,8 @@ named_control_docommand(isccc_sexpr_t *message, bool readonly, result = named_server_dumpsecroots(named_g_server, lex, text); } else if (command_compare(command, NAMED_COMMAND_SERVESTALE)) { result = named_server_servestale(named_g_server, lex, text); + } else if (command_compare(command, NAMED_COMMAND_SHOWCONF)) { + result = named_server_showconf(named_g_server, lex, text); } else if (command_compare(command, NAMED_COMMAND_SHOWZONE)) { result = named_server_showzone(named_g_server, lex, text); } else if (command_compare(command, NAMED_COMMAND_SIGNING)) { diff --git a/bin/named/include/named/control.h b/bin/named/include/named/control.h index 343d41a272..82e9d4621e 100644 --- a/bin/named/include/named/control.h +++ b/bin/named/include/named/control.h @@ -61,6 +61,7 @@ #define NAMED_COMMAND_SCAN "scan" #define NAMED_COMMAND_SECROOTS "secroots" #define NAMED_COMMAND_SERVESTALE "serve-stale" +#define NAMED_COMMAND_SHOWCONF "showconf" #define NAMED_COMMAND_SHOWZONE "showzone" #define NAMED_COMMAND_SIGN "sign" #define NAMED_COMMAND_SIGNING "signing" diff --git a/bin/named/include/named/server.h b/bin/named/include/named/server.h index d22c943d1a..8da065e4b9 100644 --- a/bin/named/include/named/server.h +++ b/bin/named/include/named/server.h @@ -110,7 +110,8 @@ struct named_server { isc_signal_t *sigusr1; cfg_aclconfctx_t *aclctx; - cfg_obj_t *config; + cfg_obj_t *userconfig; + cfg_obj_t *effectiveconfig; }; #define NAMED_SERVER_MAGIC ISC_MAGIC('S', 'V', 'E', 'R') @@ -325,6 +326,13 @@ isc_result_t named_server_showzone(named_server_t *server, isc_lex_t *lex, isc_buffer_t **text); +/*% + * Show the full current user configuration. + */ +isc_result_t +named_server_showconf(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text); + /*% * Lists the status of the signing records for a given zone. */ diff --git a/bin/named/server.c b/bin/named/server.c index 1b3767467b..2be9f4f999 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -2459,9 +2459,10 @@ catz_addmodzone_cb(void *arg) { isc_loopmgr_pause(); dns_view_thaw(view); result = configure_zone( - cz->cbd->server->config, zoneobj, view->newzone.vconfig, view, - &cz->cbd->server->viewlist, &cz->cbd->server->kasplist, - cz->cbd->server->aclctx, true, false, true, cz->mod); + cz->cbd->server->effectiveconfig, zoneobj, + view->newzone.vconfig, view, &cz->cbd->server->viewlist, + &cz->cbd->server->kasplist, cz->cbd->server->aclctx, true, + false, true, cz->mod); dns_view_freeze(view); isc_loopmgr_resume(); @@ -7888,8 +7889,9 @@ configure_kasplist(const cfg_obj_t *config, dns_kasplist_t *kasplist, } static isc_result_t -apply_configuration(cfg_obj_t *config, cfg_obj_t *bindkeys, - named_server_t *server, bool first_time) { +apply_configuration(cfg_obj_t *effectiveconfig, cfg_obj_t *userconfig, + cfg_obj_t *bindkeys, named_server_t *server, + bool first_time) { const cfg_obj_t *maps[3]; const cfg_obj_t *obj = NULL; const cfg_obj_t *options = NULL; @@ -7942,7 +7944,7 @@ apply_configuration(cfg_obj_t *config, cfg_obj_t *bindkeys, */ i = 0; options = NULL; - result = cfg_map_get(config, "options", &options); + result = cfg_map_get(effectiveconfig, "options", &options); if (result == ISC_R_SUCCESS) { maps[i++] = options; } @@ -7954,17 +7956,17 @@ apply_configuration(cfg_obj_t *config, cfg_obj_t *bindkeys, goto cleanup_aclctx; } - result = configure_keystores(config, &keystorelist); + result = configure_keystores(effectiveconfig, &keystorelist); if (result != ISC_R_SUCCESS) { goto cleanup_keystorelist; } - result = configure_kasplist(config, &kasplist, &keystorelist); + result = configure_kasplist(effectiveconfig, &kasplist, &keystorelist); if (result != ISC_R_SUCCESS) { goto cleanup_kasplist; } - result = create_views(config, &viewlist); + result = create_views(effectiveconfig, &viewlist); if (result != ISC_R_SUCCESS) { goto cleanup_viewlist; } @@ -8079,7 +8081,7 @@ apply_configuration(cfg_obj_t *config, cfg_obj_t *bindkeys, result = named_config_get(maps, "sig0checks-quota-exempt", &obj); if (result == ISC_R_SUCCESS) { result = cfg_acl_fromconfig( - obj, config, aclctx, isc_g_mctx, 0, + obj, effectiveconfig, aclctx, isc_g_mctx, 0, &server->sctx->sig0checksquota_exempt); INSIST(result == ISC_R_SUCCESS); } @@ -8088,8 +8090,9 @@ apply_configuration(cfg_obj_t *config, cfg_obj_t *bindkeys, * Set "blackhole". Only legal at options level; there is * no default. */ - result = configure_view_acl(NULL, config, "blackhole", NULL, aclctx, - isc_g_mctx, &server->sctx->blackholeacl); + result = configure_view_acl(NULL, effectiveconfig, "blackhole", NULL, + aclctx, isc_g_mctx, + &server->sctx->blackholeacl); if (result != ISC_R_SUCCESS) { goto cleanup_tls; } @@ -8341,7 +8344,8 @@ apply_configuration(cfg_obj_t *config, cfg_obj_t *bindkeys, if (named_g_port != 0) { listen_port = named_g_port; } else { - result = named_config_getport(config, "port", &listen_port); + result = named_config_getport(effectiveconfig, "port", + &listen_port); if (result != ISC_R_SUCCESS) { goto cleanup_portsets; } @@ -8390,7 +8394,7 @@ apply_configuration(cfg_obj_t *config, cfg_obj_t *bindkeys, goto cleanup_portsets; } result = listenlist_fromconfig( - clistenon, config, aclctx, isc_g_mctx, AF_INET, + clistenon, effectiveconfig, aclctx, isc_g_mctx, AF_INET, server->tlsctx_server_cache, &listenon); if (result != ISC_R_SUCCESS) { goto cleanup_portsets; @@ -8414,8 +8418,8 @@ apply_configuration(cfg_obj_t *config, cfg_obj_t *bindkeys, goto cleanup_portsets; } result = listenlist_fromconfig( - clistenon, config, aclctx, isc_g_mctx, AF_INET6, - server->tlsctx_server_cache, &listenon); + clistenon, effectiveconfig, aclctx, isc_g_mctx, + AF_INET6, server->tlsctx_server_cache, &listenon); if (result != ISC_R_SUCCESS) { goto cleanup_portsets; } @@ -8536,9 +8540,9 @@ apply_configuration(cfg_obj_t *config, cfg_obj_t *bindkeys, */ (void)configure_session_key(maps, server, isc_g_mctx, first_time); - result = configure_views(config, bindkeys, aclctx, tlsctx_client_cache, - &viewlist, &cachelist, &kasplist, server, - first_time); + result = configure_views(effectiveconfig, bindkeys, aclctx, + tlsctx_client_cache, &viewlist, &cachelist, + &kasplist, server, first_time); if (result != ISC_R_SUCCESS) { goto cleanup_cachelist; } @@ -8620,7 +8624,7 @@ apply_configuration(cfg_obj_t *config, cfg_obj_t *bindkeys, "statement for logging due to " "-g option"); - (void)cfg_map_get(config, "logging", &logobj); + (void)cfg_map_get(effectiveconfig, "logging", &logobj); if (logobj != NULL) { result = named_logconfig(NULL, logobj); if (result != ISC_R_SUCCESS) { @@ -8639,7 +8643,7 @@ apply_configuration(cfg_obj_t *config, cfg_obj_t *bindkeys, isc_logconfig_create(&logc); logobj = NULL; - (void)cfg_map_get(config, "logging", &logobj); + (void)cfg_map_get(effectiveconfig, "logging", &logobj); if (logobj != NULL) { result = named_logconfig(logc, logobj); if (result != ISC_R_SUCCESS) { @@ -8699,7 +8703,7 @@ apply_configuration(cfg_obj_t *config, cfg_obj_t *bindkeys, ns_server_setoption(server->sctx, NS_SERVER_LOGQUERIES, cfg_obj_asboolean(obj)); } else { - (void)cfg_map_get(config, "logging", &logobj); + (void)cfg_map_get(effectiveconfig, "logging", &logobj); if (logobj != NULL) { (void)cfg_map_get(logobj, "category", &categories); @@ -8948,10 +8952,18 @@ apply_configuration(cfg_obj_t *config, cfg_obj_t *bindkeys, */ named_g_defaultconfigtime = isc_time_now(); - if (server->config != NULL) { - cfg_obj_detach(&server->config); + /* + * Set the current effective and user configuration + */ + if (server->effectiveconfig != NULL) { + cfg_obj_detach(&server->effectiveconfig); } - cfg_obj_attach(config, &server->config); + cfg_obj_attach(effectiveconfig, &server->effectiveconfig); + + if (server->userconfig != NULL) { + cfg_obj_detach(&server->userconfig); + } + cfg_obj_attach(userconfig, &server->userconfig); isc_loopmgr_resume(); exclusive = false; @@ -8962,8 +8974,8 @@ apply_configuration(cfg_obj_t *config, cfg_obj_t *bindkeys, } /* Configure the statistics channel(s) */ - result = named_statschannels_configure(named_g_server, config, - server->aclctx); + result = named_statschannels_configure( + named_g_server, server->effectiveconfig, server->aclctx); if (result != ISC_R_SUCCESS) { isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, @@ -8975,7 +8987,8 @@ apply_configuration(cfg_obj_t *config, cfg_obj_t *bindkeys, /* * Bind the control port(s). */ - result = named_controls_configure(named_g_server->controls, config, + result = named_controls_configure(named_g_server->controls, + server->effectiveconfig, server->aclctx); if (result != ISC_R_SUCCESS) { isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER, @@ -9066,7 +9079,7 @@ cleanup_aclctx: ISC_LOG_DEBUG(1), "apply_configuration: %s", isc_result_totext(result)); - cfg_obj_detach(&config); + cfg_obj_detach(&effectiveconfig); return result; } @@ -9110,8 +9123,10 @@ load_configuration(named_server_t *server, bool first_time) { } } + /* Merge and apply */ effective = cfg_effective_config(config, named_g_defaultconfig); - result = apply_configuration(effective, bindkeys, server, first_time); + result = apply_configuration(effective, config, bindkeys, server, + first_time); cleanup: if (bindkeys != NULL) { @@ -9751,8 +9766,12 @@ named_server_destroy(named_server_t **serverp) { isc_tlsctx_cache_detach(&server->tlsctx_client_cache); } - if (server->config != NULL) { - cfg_obj_detach(&server->config); + if (server->userconfig != NULL) { + cfg_obj_detach(&server->userconfig); + } + + if (server->effectiveconfig != NULL) { + cfg_obj_detach(&server->effectiveconfig); } server->magic = 0; @@ -13041,10 +13060,10 @@ do_addzone(named_server_t *server, dns_view_t *view, dns_name_t *name, if (view->newzone.vconfig != NULL) { voptions = cfg_tuple_get(view->newzone.vconfig, "options"); } - result = isccfg_check_zoneconf(zoneobj, voptions, server->config, NULL, - NULL, NULL, NULL, view->name, - view->rdclass, BIND_CHECK_PLUGINS, - server->aclctx, isc_g_mctx); + result = isccfg_check_zoneconf( + zoneobj, voptions, server->effectiveconfig, NULL, NULL, NULL, + NULL, view->name, view->rdclass, BIND_CHECK_PLUGINS, + server->aclctx, isc_g_mctx); if (result != ISC_R_SUCCESS) { isc_loopmgr_resume(); goto cleanup; @@ -13052,9 +13071,10 @@ do_addzone(named_server_t *server, dns_view_t *view, dns_name_t *name, /* Mark view unfrozen and configure zone */ dns_view_thaw(view); - result = configure_zone(server->config, zoneobj, view->newzone.vconfig, - view, &server->viewlist, &server->kasplist, - server->aclctx, true, false, false, false); + result = configure_zone(server->effectiveconfig, zoneobj, + view->newzone.vconfig, view, &server->viewlist, + &server->kasplist, server->aclctx, true, false, + false, false); dns_view_freeze(view); isc_loopmgr_resume(); @@ -13236,10 +13256,10 @@ do_modzone(named_server_t *server, dns_view_t *view, dns_name_t *name, if (view->newzone.vconfig != NULL) { voptions = cfg_tuple_get(view->newzone.vconfig, "options"); } - result = isccfg_check_zoneconf(zoneobj, voptions, server->config, NULL, - NULL, NULL, NULL, view->name, - view->rdclass, BIND_CHECK_PLUGINS, - server->aclctx, isc_g_mctx); + result = isccfg_check_zoneconf( + zoneobj, voptions, server->effectiveconfig, NULL, NULL, NULL, + NULL, view->name, view->rdclass, BIND_CHECK_PLUGINS, + server->aclctx, isc_g_mctx); if (result != ISC_R_SUCCESS) { isc_loopmgr_resume(); goto cleanup; @@ -13247,9 +13267,10 @@ do_modzone(named_server_t *server, dns_view_t *view, dns_name_t *name, /* Reconfigure the zone */ dns_view_thaw(view); - result = configure_zone(server->config, zoneobj, view->newzone.vconfig, - view, &server->viewlist, &server->kasplist, - server->aclctx, true, false, false, true); + result = configure_zone(server->effectiveconfig, zoneobj, + view->newzone.vconfig, view, &server->viewlist, + &server->kasplist, server->aclctx, true, false, + false, true); dns_view_freeze(view); isc_loopmgr_resume(); @@ -13287,11 +13308,12 @@ do_modzone(named_server_t *server, dns_view_t *view, dns_name_t *name, if (!added) { if (view->newzone.vconfig == NULL) { - result = delete_zoneconf(view, server->config, + result = delete_zoneconf(view, server->effectiveconfig, dns_zone_getorigin(zone), NULL); } else { - voptions = cfg_tuple_get(server->config, "options"); + voptions = cfg_tuple_get(server->effectiveconfig, + "options"); result = delete_zoneconf( view, voptions, dns_zone_getorigin(zone), NULL); } @@ -13579,9 +13601,9 @@ rmzone(void *arg) { result = delete_zoneconf( view, voptions, dns_zone_getorigin(zone), NULL); } else { - result = delete_zoneconf(view, dz->server->config, - dns_zone_getorigin(zone), - NULL); + result = delete_zoneconf( + view, dz->server->effectiveconfig, + dns_zone_getorigin(zone), NULL); } if (result != ISC_R_SUCCESS) { @@ -13796,7 +13818,7 @@ cleanup: } static void -emitzone(void *arg, const char *buf, int len) { +emit_text(void *arg, const char *buf, int len) { ns_dzarg_t *dzarg = arg; isc_result_t result; @@ -13839,7 +13861,7 @@ named_server_showzone(named_server_t *server, isc_lex_t *lex, dzarg.magic = DZARG_MAGIC; dzarg.text = text; dzarg.result = ISC_R_SUCCESS; - cfg_printx(zconfig, CFG_PRINTER_ONELINE, emitzone, &dzarg); + cfg_printx(zconfig, CFG_PRINTER_ONELINE, emit_text, &dzarg); CHECK(dzarg.result); CHECK(putstr(text, ";")); @@ -13854,6 +13876,54 @@ cleanup: return result; } +isc_result_t +named_server_showconf(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text) { + isc_result_t result, tresult; + const char *arg = NULL; + const cfg_obj_t *config = NULL; + ns_dzarg_t dzarg = { + .magic = DZARG_MAGIC, + .result = ISC_R_SUCCESS, + .text = text, + }; + + REQUIRE(text != NULL && *text != NULL); + + /* Skip the command name */ + (void)next_token(lex, text); + + arg = next_token(lex, text); + if (arg == NULL) { + return ISC_R_UNEXPECTEDEND; + } + if (strcasecmp(arg, "-user") == 0) { + config = server->userconfig; + } else if (strcasecmp(arg, "-builtin") == 0) { + config = named_g_defaultconfig; + } else if (strcasecmp(arg, "-effective") == 0) { + config = server->effectiveconfig; + } else { + CHECK(DNS_R_SYNTAX); + } + + if (config == NULL) { + result = ISC_R_NOTFOUND; + TCHECK(putstr(text, "configuration data not found.\n")); + goto cleanup; + } + + cfg_printx(config, 0, emit_text, &dzarg); + result = dzarg.result; + +cleanup: + if (isc_buffer_usedlength(*text) > 0) { + (void)putnull(text); + } + + return result; +} + isc_result_t named_server_signing(named_server_t *server, isc_lex_t *lex, isc_buffer_t **text) { diff --git a/bin/rndc/rndc.rst b/bin/rndc/rndc.rst index aaaf33e426..53804fef57 100644 --- a/bin/rndc/rndc.rst +++ b/bin/rndc/rndc.rst @@ -545,6 +545,28 @@ Currently supported commands are: answers is currently enabled or disabled. It also reports the values of ``stale-answer-ttl`` and ``max-stale-ttl``. +.. option:: showconf ( -user | -builtin | -effective ) + + This command prints the current running configuration for the server. + There are three modes: + + - ``rndc showconf -user`` prints the contents of :iscman:`named.conf`, + as of the time the server was most recently configured. + - ``rndc showconf -builtin`` prints the built-in default configuration + settings (which may be overridden by :iscman:`named.conf`). + - ``rndc showconf -effective`` prints the effective configuration + of the server. This is the merged combination of the ``-user`` and + ``-builtin`` configurations. + + Note that this information is only set or updated when the server is + loaded or reconfigured. Properties that can be dynamically changed + during runtime, such as the active root trust anchors, the debugging + level (via ``rndc trace``), or DNSSEC validation (via ``rndc + valiation``), are shown as configured, not as currently set, and zones that + are dynamically added using ``rndc addzone`` are not visible. + + See also :option:`rndc showzone`. + .. option:: showzone zone [class [view]] This command prints the configuration of a running zone. From eb064875688631b5751a0f7cfc178eac12fc2a74 Mon Sep 17 00:00:00 2001 From: Colin Vidal Date: Tue, 21 Oct 2025 12:10:34 +0200 Subject: [PATCH 3/3] add system test for rndc showconf Add system tests covering the rndc showconf command. It doesn't attempt to check the whole effective configuration (as any change to the builtin configuration would break it) but instead ensures that some parts of the user config are present, as well as some parts of the builtin config as well. It also checks that the effective config (in this context of running named instance) is about static configuration: a newly added zone is not visible in the effective configuration. --- bin/tests/system/showconf/ns1/example.db | 21 +++++++ bin/tests/system/showconf/ns1/named.conf.j2 | 39 +++++++++++++ bin/tests/system/showconf/tests_showconf.py | 62 +++++++++++++++++++++ 3 files changed, 122 insertions(+) create mode 100644 bin/tests/system/showconf/ns1/example.db create mode 100644 bin/tests/system/showconf/ns1/named.conf.j2 create mode 100644 bin/tests/system/showconf/tests_showconf.py diff --git a/bin/tests/system/showconf/ns1/example.db b/bin/tests/system/showconf/ns1/example.db new file mode 100644 index 0000000000..1d7173fea3 --- /dev/null +++ b/bin/tests/system/showconf/ns1/example.db @@ -0,0 +1,21 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; SPDX-License-Identifier: MPL-2.0 +; +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, you can obtain one at https://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$TTL 120 +@ SOA ns.unsigned. hostmaster.ns.unsigned. ( 1 3600 1200 604800 60 ) +@ NS ns +@ MX 10 mx + +ns A 10.53.0.1 + AAAA fd92:7065:b8e:ffff::1 + +a A 1.1.1.1 +mx A 2.2.2.2 diff --git a/bin/tests/system/showconf/ns1/named.conf.j2 b/bin/tests/system/showconf/ns1/named.conf.j2 new file mode 100644 index 0000000000..ad42e4bc25 --- /dev/null +++ b/bin/tests/system/showconf/ns1/named.conf.j2 @@ -0,0 +1,39 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ +options { + query-source address 10.53.0.1; + notify-source 10.53.0.1; + transfer-source 10.53.0.1; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.1; }; + recursion no; + dnssec-validation no; + notify yes; + minimal-responses no; + allow-new-zones yes; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm @DEFAULT_HMAC@; +}; + +controls { + inet 10.53.0.1 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +zone "example.com" { + type primary; + file "example.db"; +}; diff --git a/bin/tests/system/showconf/tests_showconf.py b/bin/tests/system/showconf/tests_showconf.py new file mode 100644 index 0000000000..ae7e3833cc --- /dev/null +++ b/bin/tests/system/showconf/tests_showconf.py @@ -0,0 +1,62 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +import dns +import isctest + + +def test_showconf(ns1): + # Basic testing of rndc showconf + msg = isctest.query.create("a.example.com", "A") + res = isctest.query.udp(msg, "10.53.0.1") + isctest.check.rcode(res, dns.rcode.NOERROR) + + effectiveconfig = ns1.rndc("showconf -effective", log=False) + assert 'zone "example.com"' in effectiveconfig + assert 'view "_bind" chaos {' in effectiveconfig + + # builtin-trust-anchors is non documented and internal clause only, it must + # not be visible. + assert "builtin-trust-anchors" not in effectiveconfig + + # Dynamically added zones are not visible from the effectiveconfig + zonedata = '"added.example" { type primary; file "example.db"; };' + ns1.rndc(f"addzone {zonedata}", log=False) + + msg = isctest.query.create("a.added.example", "A") + res = isctest.query.udp(msg, "10.53.0.1") + isctest.check.rcode(res, dns.rcode.NOERROR) + + effectiveconfig = ns1.rndc("showconf -effective", log=False) + assert 'zone "added.example"' not in effectiveconfig + + userconfig = ns1.rndc("showconf -user", log=False) + assert 'zone "example.com"' in userconfig + assert 'view "_bind" chaos {' not in userconfig + + builtinconfig = ns1.rndc("showconf -builtin", log=False) + assert len(userconfig.split()) < len(builtinconfig.split()) + assert len(builtinconfig.split()) < len(effectiveconfig.split()) + + # Errors handling + error_msg = "" + + try: + ns1.rndc("showconf -idontexist", log=False) + except isctest.rndc.RNDCException as e: + error_msg = str(e) + assert error_msg == "rndc: 'showconf' failed: syntax error\n" + + try: + ns1.rndc("showconf", log=False) + except isctest.rndc.RNDCException as e: + error_msg = str(e) + assert error_msg == "rndc: 'showconf' failed: unexpected end of input\n"