From: Ondřej Surý Date: Sun, 15 Mar 2026 04:05:09 +0000 (+0100) Subject: Split NZD functions into a separate compilation unit X-Git-Tag: v9.21.21~31^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8ae0828e15993fb3d813ba955c7b800d93bd8416;p=thirdparty%2Fbind9.git Split NZD functions into a separate compilation unit Move all LMDB-based new zone database functions from server.c into nzd.c to reduce the size of server.c and isolate the NZD/LMDB interface. Rename load_nzf() to nzd_load_nzf() to match the nzd_ namespace. --- diff --git a/bin/named/include/named/nzd.h b/bin/named/include/named/nzd.h new file mode 100644 index 00000000000..a79636935a7 --- /dev/null +++ b/bin/named/include/named/nzd.h @@ -0,0 +1,55 @@ +/* + * 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. + */ + +#pragma once + +#include + +#include +#include +#include + +#include + +#define DZARG_MAGIC ISC_MAGIC('D', 'z', 'a', 'r') + +typedef struct { + unsigned int magic; + isc_buffer_t *text; + isc_result_t result; +} ns_dzarg_t; + +isc_result_t +nzd_writable(dns_view_t *view); + +isc_result_t +nzd_open(dns_view_t *view, unsigned int flags, MDB_txn **txnp, MDB_dbi *dbi); + +isc_result_t +nzd_env_reopen(dns_view_t *view); + +void +nzd_env_close(dns_view_t *view); + +isc_result_t +nzd_close(MDB_txn **txnp, bool commit); + +isc_result_t +nzd_save(MDB_txn **txnp, MDB_dbi dbi, dns_zone_t *zone, + const cfg_obj_t *zconfig); + +isc_result_t +nzd_load_nzf(dns_view_t *view); + +void +nzd_setkey(MDB_val *key, dns_name_t *name, char *namebuf, size_t buflen); diff --git a/bin/named/meson.build b/bin/named/meson.build index bf9e8f9dbf3..18b221ab620 100644 --- a/bin/named/meson.build +++ b/bin/named/meson.build @@ -32,6 +32,7 @@ named_srcset.add( 'log.c', 'logconf.c', 'main.c', + 'nzd.c', 'os.c', 'server.c', 'statschannel.c', diff --git a/bin/named/nzd.c b/bin/named/nzd.c new file mode 100644 index 00000000000..f3707e497ea --- /dev/null +++ b/bin/named/nzd.c @@ -0,0 +1,492 @@ +/* + * 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. + */ + +#include + +#include + +#include +#include +#include + +#include +#include + +#include +#include + +void +nzd_setkey(MDB_val *key, dns_name_t *name, char *namebuf, size_t buflen) { + dns_fixedname_t fixed; + + dns_fixedname_init(&fixed); + dns_name_downcase(name, dns_fixedname_name(&fixed)); + dns_name_format(dns_fixedname_name(&fixed), namebuf, buflen); + + key->mv_data = namebuf; + key->mv_size = strlen(namebuf); +} + +static void +dumpzone(void *arg, const char *buf, int len) { + ns_dzarg_t *dzarg = arg; + isc_result_t result; + + REQUIRE(dzarg != NULL && ISC_MAGIC_VALID(dzarg, DZARG_MAGIC)); + + result = isc_buffer_reserve(dzarg->text, (unsigned int)len); + if (result == ISC_R_SUCCESS) { + isc_buffer_putmem(dzarg->text, (const unsigned char *)buf, len); + } else if (dzarg->result == ISC_R_SUCCESS) { + dzarg->result = result; + } +} + +isc_result_t +nzd_save(MDB_txn **txnp, MDB_dbi dbi, dns_zone_t *zone, + const cfg_obj_t *zconfig) { + isc_result_t result; + int status; + dns_view_t *view; + bool commit = false; + isc_buffer_t *text = NULL; + char namebuf[1024]; + MDB_val key, data; + ns_dzarg_t dzarg; + + view = dns_zone_getview(zone); + + nzd_setkey(&key, dns_zone_getorigin(zone), namebuf, sizeof(namebuf)); + + if (zconfig == NULL) { + /* We're deleting the zone from the database */ + status = mdb_del(*txnp, dbi, &key, NULL); + if (status != MDB_SUCCESS && status != MDB_NOTFOUND) { + isc_log_write(NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "Error deleting zone %s " + "from NZD database: %s", + namebuf, mdb_strerror(status)); + CLEANUP(ISC_R_FAILURE); + } else if (status != MDB_NOTFOUND) { + commit = true; + } + } else { + /* We're creating or overwriting the zone */ + const cfg_obj_t *zoptions = cfg_tuple_get(zconfig, "options"); + + isc_buffer_allocate(view->mctx, &text, 256); + if (zoptions == NULL) { + isc_log_write(NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "Unable to get options from config in " + "nzd_save()"); + CLEANUP(ISC_R_FAILURE); + } + + dzarg.magic = DZARG_MAGIC; + dzarg.text = text; + dzarg.result = ISC_R_SUCCESS; + cfg_printx(zoptions, CFG_PRINTER_ONELINE, dumpzone, &dzarg); + if (dzarg.result != ISC_R_SUCCESS) { + isc_log_write(NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "Error writing zone config to " + "buffer in nzd_save(): %s", + isc_result_totext(dzarg.result)); + CHECK(dzarg.result); + } + + data.mv_data = isc_buffer_base(text); + data.mv_size = isc_buffer_usedlength(text); + + status = mdb_put(*txnp, dbi, &key, &data, 0); + if (status != MDB_SUCCESS) { + isc_log_write(NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "Error inserting zone in " + "NZD database: %s", + mdb_strerror(status)); + CLEANUP(ISC_R_FAILURE); + } + + commit = true; + } + + result = ISC_R_SUCCESS; + +cleanup: + if (!commit || result != ISC_R_SUCCESS) { + (void)mdb_txn_abort(*txnp); + } else { + status = mdb_txn_commit(*txnp); + if (status != MDB_SUCCESS) { + isc_log_write(NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "Error committing " + "NZD database: %s", + mdb_strerror(status)); + result = ISC_R_FAILURE; + } + } + *txnp = NULL; + + if (text != NULL) { + isc_buffer_free(&text); + } + + return result; +} + +/* + * Check whether the new zone database for 'view' can be opened for writing. + * + * Caller must hold 'view->newzone.lock'. + */ +isc_result_t +nzd_writable(dns_view_t *view) { + isc_result_t result = ISC_R_SUCCESS; + int status; + MDB_dbi dbi; + MDB_txn *txn = NULL; + + REQUIRE(view != NULL); + + 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", + mdb_strerror(status)); + return ISC_R_FAILURE; + } + + status = mdb_dbi_open(txn, NULL, 0, &dbi); + if (status != MDB_SUCCESS) { + isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER, + ISC_LOG_WARNING, "mdb_dbi_open: %s", + mdb_strerror(status)); + result = ISC_R_FAILURE; + } + + mdb_txn_abort(txn); + return result; +} + +/* + * Open the new zone database for 'view' and start a transaction for it. + * + * Caller must hold 'view->newzone.lock'. + */ +isc_result_t +nzd_open(dns_view_t *view, unsigned int flags, MDB_txn **txnp, MDB_dbi *dbi) { + int status; + MDB_txn *txn = NULL; + + REQUIRE(view != NULL); + REQUIRE(txnp != NULL && *txnp == NULL); + REQUIRE(dbi != NULL); + + 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", + mdb_strerror(status)); + goto cleanup; + } + + status = mdb_dbi_open(txn, NULL, 0, dbi); + if (status != MDB_SUCCESS) { + isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER, + ISC_LOG_WARNING, "mdb_dbi_open: %s", + mdb_strerror(status)); + goto cleanup; + } + + *txnp = txn; + +cleanup: + if (status != MDB_SUCCESS) { + if (txn != NULL) { + mdb_txn_abort(txn); + } + return ISC_R_FAILURE; + } + + return ISC_R_SUCCESS; +} + +/* + * 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. + */ +void +nzd_env_close(dns_view_t *view) { + const char *dbpath = NULL; + char dbpath_copy[PATH_MAX]; + char lockpath[PATH_MAX]; + int status, ret; + + if (view->newzone.dbenv == NULL) { + return; + } + + 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(view->newzone.dbenv); + + /* + * Database files must be owned by the eventual user, not by root. + */ + ret = chown(dbpath_copy, named_os_uid(), -1); + UNUSED(ret); + + /* + * Some platforms need the lockfile not to exist when we reopen the + * environment. + */ + (void)isc_file_remove(lockpath); + + view->newzone.dbenv = NULL; +} + +isc_result_t +nzd_env_reopen(dns_view_t *view) { + isc_result_t result; + MDB_env *env = NULL; + int status; + + if (view->newzone.db == NULL) { + return ISC_R_SUCCESS; + } + + nzd_env_close(view); + + 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)); + CLEANUP(ISC_R_FAILURE); + } + + 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, + "mdb_env_set_mapsize failed: %s", + mdb_strerror(status)); + CLEANUP(ISC_R_FAILURE); + } + } + + 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)); + CLEANUP(ISC_R_FAILURE); + } + + view->newzone.dbenv = env; + env = NULL; + result = ISC_R_SUCCESS; + +cleanup: + if (env != NULL) { + mdb_env_close(env); + } + return result; +} + +/* + * If 'commit' is true, commit the new zone database transaction pointed to by + * 'txnp'; otherwise, abort that transaction. + * + * Caller must hold 'view->newzone.lock' for the view that the transaction + * pointed to by 'txnp' was started for. + */ +isc_result_t +nzd_close(MDB_txn **txnp, bool commit) { + isc_result_t result = ISC_R_SUCCESS; + int status; + + REQUIRE(txnp != NULL); + + if (*txnp != NULL) { + if (commit) { + status = mdb_txn_commit(*txnp); + if (status != MDB_SUCCESS) { + result = ISC_R_FAILURE; + } + } else { + mdb_txn_abort(*txnp); + } + *txnp = NULL; + } + + return result; +} + +/* + * If there's an existing NZF file, load it and migrate its data + * to the NZD. + * + * Caller must hold view->newzone.lock. + */ +isc_result_t +nzd_load_nzf(dns_view_t *view) { + isc_result_t result; + cfg_obj_t *nzf_config = NULL; + int status; + isc_buffer_t *text = NULL; + bool commit = false; + const cfg_obj_t *zonelist = NULL; + char tempname[PATH_MAX]; + MDB_txn *txn = NULL; + MDB_dbi dbi; + MDB_val key, data; + ns_dzarg_t dzarg; + + /* + * If NZF file doesn't exist, or NZD DB exists and already + * has data, return without attempting migration. + */ + if (!isc_file_exists(view->newzone.file)) { + result = ISC_R_SUCCESS; + goto cleanup; + } + + isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER, + ISC_LOG_INFO, + "Migrating zones from NZF file '%s' to " + "NZD database '%s'", + 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(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->newzone.file, isc_result_totext(result)); + goto cleanup; + } + + zonelist = NULL; + CHECK(cfg_map_get(nzf_config, "zone", &zonelist)); + if (!cfg_obj_islist(zonelist)) { + CLEANUP(ISC_R_FAILURE); + } + + CHECK(nzd_open(view, 0, &txn, &dbi)); + + isc_buffer_allocate(view->mctx, &text, 256); + + CFG_LIST_FOREACH(zonelist, element) { + const cfg_obj_t *zconfig = cfg_listelt_value(element); + const cfg_obj_t *zoptions; + char zname[DNS_NAME_FORMATSIZE]; + dns_fixedname_t fname; + dns_name_t *name = NULL; + const char *origin = NULL; + isc_buffer_t b; + + origin = cfg_obj_asstring(cfg_tuple_get(zconfig, "name")); + if (origin == NULL) { + CLEANUP(ISC_R_FAILURE); + } + + /* Normalize zone name */ + isc_buffer_constinit(&b, origin, strlen(origin)); + isc_buffer_add(&b, strlen(origin)); + name = dns_fixedname_initname(&fname); + CHECK(dns_name_fromtext(name, &b, dns_rootname, + DNS_NAME_DOWNCASE)); + dns_name_format(name, zname, sizeof(zname)); + + key.mv_data = zname; + key.mv_size = strlen(zname); + + zoptions = cfg_tuple_get(zconfig, "options"); + if (zoptions == NULL) { + CLEANUP(ISC_R_FAILURE); + } + + isc_buffer_clear(text); + dzarg.magic = DZARG_MAGIC; + dzarg.text = text; + dzarg.result = ISC_R_SUCCESS; + cfg_printx(zoptions, CFG_PRINTER_ONELINE, dumpzone, &dzarg); + if (dzarg.result != ISC_R_SUCCESS) { + isc_log_write(NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "Error writing zone config to " + "buffer in load_nzf(): %s", + isc_result_totext(result)); + CHECK(dzarg.result); + } + + data.mv_data = isc_buffer_base(text); + data.mv_size = isc_buffer_usedlength(text); + + status = mdb_put(txn, dbi, &key, &data, MDB_NOOVERWRITE); + if (status != MDB_SUCCESS) { + isc_log_write(NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "Error inserting zone in " + "NZD database: %s", + mdb_strerror(status)); + CLEANUP(ISC_R_FAILURE); + } + + commit = true; + } + + result = ISC_R_SUCCESS; + + /* + * Leaving the NZF file in place is harmless as we won't use it + * if an NZD database is found for the view. But we rename NZF file + * to a backup name here. + */ + strlcpy(tempname, view->newzone.file, sizeof(tempname)); + if (strlen(tempname) < sizeof(tempname) - 1) { + strlcat(tempname, "~", sizeof(tempname)); + isc_file_rename(view->newzone.file, tempname); + } + +cleanup: + if (result != ISC_R_SUCCESS) { + (void)nzd_close(&txn, false); + } else { + result = nzd_close(&txn, commit); + } + + if (text != NULL) { + isc_buffer_free(&text); + } + + if (nzf_config != NULL) { + cfg_obj_detach(&nzf_config); + } + + return result; +} diff --git a/bin/named/server.c b/bin/named/server.c index 24432cdfac2..14c8d48bc39 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -117,6 +117,7 @@ #include #include +#include #if defined(HAVE_GEOIP2) #include #endif /* HAVE_GEOIP2 */ @@ -145,8 +146,6 @@ #define JEMALLOC_API_SUPPORTED 1 #endif -#include - #ifndef SIZE_MAX #define SIZE_MAX ((size_t)(-1)) #endif /* ifndef SIZE_MAX */ @@ -326,13 +325,6 @@ typedef enum { CATZ_DELZONE, } catz_type_t; -typedef struct { - unsigned int magic; -#define DZARG_MAGIC ISC_MAGIC('D', 'z', 'a', 'r') - isc_buffer_t *text; - isc_result_t result; -} ns_dzarg_t; - typedef enum { MEMPROF_UNSUPPORTED = 0x00, MEMPROF_INACTIVE = 0x01, @@ -492,24 +484,6 @@ putuint8(isc_buffer_t *b, uint8_t val); static isc_result_t putnull(isc_buffer_t *b); -static isc_result_t -nzd_writable(dns_view_t *view); - -static isc_result_t -nzd_open(dns_view_t *view, unsigned int flags, MDB_txn **txnp, MDB_dbi *dbi); - -static isc_result_t -nzd_env_reopen(dns_view_t *view); - -static void -nzd_env_close(dns_view_t *view); - -static isc_result_t -nzd_close(MDB_txn **txnp, bool commit); - -static isc_result_t -load_nzf(dns_view_t *view); - /*% * Configure a single view ACL at '*aclp'. Get its configuration from * 'vconfig' (for per-view configuration) and maybe from 'config' @@ -7155,7 +7129,7 @@ setup_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig) { } if (view->newzone.allowed) { - result = load_nzf(view); + result = nzd_load_nzf(view); } return result; @@ -11915,470 +11889,6 @@ named_smf_add_message(isc_buffer_t *text) { } #endif /* HAVE_LIBSCF */ -static void -nzd_setkey(MDB_val *key, dns_name_t *name, char *namebuf, size_t buflen) { - dns_fixedname_t fixed; - - dns_fixedname_init(&fixed); - dns_name_downcase(name, dns_fixedname_name(&fixed)); - dns_name_format(dns_fixedname_name(&fixed), namebuf, buflen); - - key->mv_data = namebuf; - key->mv_size = strlen(namebuf); -} - -static void -dumpzone(void *arg, const char *buf, int len) { - ns_dzarg_t *dzarg = arg; - isc_result_t result; - - REQUIRE(dzarg != NULL && ISC_MAGIC_VALID(dzarg, DZARG_MAGIC)); - - result = putmem(dzarg->text, buf, len); - if (result != ISC_R_SUCCESS && dzarg->result == ISC_R_SUCCESS) { - dzarg->result = result; - } -} - -static isc_result_t -nzd_save(MDB_txn **txnp, MDB_dbi dbi, dns_zone_t *zone, - const cfg_obj_t *zconfig) { - isc_result_t result; - int status; - dns_view_t *view; - bool commit = false; - isc_buffer_t *text = NULL; - char namebuf[1024]; - MDB_val key, data; - ns_dzarg_t dzarg; - - view = dns_zone_getview(zone); - - nzd_setkey(&key, dns_zone_getorigin(zone), namebuf, sizeof(namebuf)); - - if (zconfig == NULL) { - /* We're deleting the zone from the database */ - status = mdb_del(*txnp, dbi, &key, NULL); - if (status != MDB_SUCCESS && status != MDB_NOTFOUND) { - isc_log_write(NAMED_LOGCATEGORY_GENERAL, - NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, - "Error deleting zone %s " - "from NZD database: %s", - namebuf, mdb_strerror(status)); - CLEANUP(ISC_R_FAILURE); - } else if (status != MDB_NOTFOUND) { - commit = true; - } - } else { - /* We're creating or overwriting the zone */ - const cfg_obj_t *zoptions = cfg_tuple_get(zconfig, "options"); - - isc_buffer_allocate(view->mctx, &text, 256); - if (zoptions == NULL) { - isc_log_write(NAMED_LOGCATEGORY_GENERAL, - NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, - "Unable to get options from config in " - "nzd_save()"); - CLEANUP(ISC_R_FAILURE); - } - - dzarg.magic = DZARG_MAGIC; - dzarg.text = text; - dzarg.result = ISC_R_SUCCESS; - cfg_printx(zoptions, CFG_PRINTER_ONELINE, dumpzone, &dzarg); - if (dzarg.result != ISC_R_SUCCESS) { - isc_log_write(NAMED_LOGCATEGORY_GENERAL, - NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, - "Error writing zone config to " - "buffer in nzd_save(): %s", - isc_result_totext(dzarg.result)); - CHECK(dzarg.result); - } - - data.mv_data = isc_buffer_base(text); - data.mv_size = isc_buffer_usedlength(text); - - status = mdb_put(*txnp, dbi, &key, &data, 0); - if (status != MDB_SUCCESS) { - isc_log_write(NAMED_LOGCATEGORY_GENERAL, - NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, - "Error inserting zone in " - "NZD database: %s", - mdb_strerror(status)); - CLEANUP(ISC_R_FAILURE); - } - - commit = true; - } - - result = ISC_R_SUCCESS; - -cleanup: - if (!commit || result != ISC_R_SUCCESS) { - (void)mdb_txn_abort(*txnp); - } else { - status = mdb_txn_commit(*txnp); - if (status != MDB_SUCCESS) { - isc_log_write(NAMED_LOGCATEGORY_GENERAL, - NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, - "Error committing " - "NZD database: %s", - mdb_strerror(status)); - result = ISC_R_FAILURE; - } - } - *txnp = NULL; - - if (text != NULL) { - isc_buffer_free(&text); - } - - return result; -} - -/* - * Check whether the new zone database for 'view' can be opened for writing. - * - * Caller must hold 'view->newzone.lock'. - */ -static isc_result_t -nzd_writable(dns_view_t *view) { - isc_result_t result = ISC_R_SUCCESS; - int status; - MDB_dbi dbi; - MDB_txn *txn = NULL; - - REQUIRE(view != NULL); - - 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", - mdb_strerror(status)); - return ISC_R_FAILURE; - } - - status = mdb_dbi_open(txn, NULL, 0, &dbi); - if (status != MDB_SUCCESS) { - isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER, - ISC_LOG_WARNING, "mdb_dbi_open: %s", - mdb_strerror(status)); - result = ISC_R_FAILURE; - } - - mdb_txn_abort(txn); - return result; -} - -/* - * Open the new zone database for 'view' and start a transaction for it. - * - * 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) { - int status; - MDB_txn *txn = NULL; - - REQUIRE(view != NULL); - REQUIRE(txnp != NULL && *txnp == NULL); - REQUIRE(dbi != NULL); - - 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", - mdb_strerror(status)); - goto cleanup; - } - - status = mdb_dbi_open(txn, NULL, 0, dbi); - if (status != MDB_SUCCESS) { - isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER, - ISC_LOG_WARNING, "mdb_dbi_open: %s", - mdb_strerror(status)); - goto cleanup; - } - - *txnp = txn; - -cleanup: - if (status != MDB_SUCCESS) { - if (txn != NULL) { - mdb_txn_abort(txn); - } - return ISC_R_FAILURE; - } - - return ISC_R_SUCCESS; -} - -/* - * 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. - */ -static void -nzd_env_close(dns_view_t *view) { - const char *dbpath = NULL; - char dbpath_copy[PATH_MAX]; - char lockpath[PATH_MAX]; - int status, ret; - - if (view->newzone.dbenv == NULL) { - return; - } - - 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(view->newzone.dbenv); - - /* - * Database files must be owned by the eventual user, not by root. - */ - ret = chown(dbpath_copy, named_os_uid(), -1); - UNUSED(ret); - - /* - * Some platforms need the lockfile not to exist when we reopen the - * environment. - */ - (void)isc_file_remove(lockpath); - - view->newzone.dbenv = NULL; -} - -static isc_result_t -nzd_env_reopen(dns_view_t *view) { - isc_result_t result; - MDB_env *env = NULL; - int status; - - if (view->newzone.db == NULL) { - return ISC_R_SUCCESS; - } - - nzd_env_close(view); - - 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)); - CLEANUP(ISC_R_FAILURE); - } - - 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, - "mdb_env_set_mapsize failed: %s", - mdb_strerror(status)); - CLEANUP(ISC_R_FAILURE); - } - } - - 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)); - CLEANUP(ISC_R_FAILURE); - } - - view->newzone.dbenv = env; - env = NULL; - result = ISC_R_SUCCESS; - -cleanup: - if (env != NULL) { - mdb_env_close(env); - } - return result; -} - -/* - * If 'commit' is true, commit the new zone database transaction pointed to by - * 'txnp'; otherwise, abort that transaction. - * - * Caller must hold 'view->newzone.lock' for the view that the transaction - * pointed to by 'txnp' was started for. - */ -static isc_result_t -nzd_close(MDB_txn **txnp, bool commit) { - isc_result_t result = ISC_R_SUCCESS; - int status; - - REQUIRE(txnp != NULL); - - if (*txnp != NULL) { - if (commit) { - status = mdb_txn_commit(*txnp); - if (status != MDB_SUCCESS) { - result = ISC_R_FAILURE; - } - } else { - mdb_txn_abort(*txnp); - } - *txnp = NULL; - } - - return result; -} - -/* - * If there's an existing NZF file, load it and migrate its data - * to the NZD. - * - * Caller must hold view->newzone.lock. - */ -static isc_result_t -load_nzf(dns_view_t *view) { - isc_result_t result; - cfg_obj_t *nzf_config = NULL; - int status; - isc_buffer_t *text = NULL; - bool commit = false; - const cfg_obj_t *zonelist = NULL; - char tempname[PATH_MAX]; - MDB_txn *txn = NULL; - MDB_dbi dbi; - MDB_val key, data; - ns_dzarg_t dzarg; - - /* - * If NZF file doesn't exist, or NZD DB exists and already - * has data, return without attempting migration. - */ - if (!isc_file_exists(view->newzone.file)) { - result = ISC_R_SUCCESS; - goto cleanup; - } - - isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER, - ISC_LOG_INFO, - "Migrating zones from NZF file '%s' to " - "NZD database '%s'", - 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(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->newzone.file, isc_result_totext(result)); - goto cleanup; - } - - zonelist = NULL; - CHECK(cfg_map_get(nzf_config, "zone", &zonelist)); - if (!cfg_obj_islist(zonelist)) { - CLEANUP(ISC_R_FAILURE); - } - - CHECK(nzd_open(view, 0, &txn, &dbi)); - - isc_buffer_allocate(view->mctx, &text, 256); - - CFG_LIST_FOREACH(zonelist, element) { - const cfg_obj_t *zconfig = cfg_listelt_value(element); - const cfg_obj_t *zoptions; - char zname[DNS_NAME_FORMATSIZE]; - dns_fixedname_t fname; - dns_name_t *name = NULL; - const char *origin = NULL; - isc_buffer_t b; - - origin = cfg_obj_asstring(cfg_tuple_get(zconfig, "name")); - if (origin == NULL) { - CLEANUP(ISC_R_FAILURE); - } - - /* Normalize zone name */ - isc_buffer_constinit(&b, origin, strlen(origin)); - isc_buffer_add(&b, strlen(origin)); - name = dns_fixedname_initname(&fname); - CHECK(dns_name_fromtext(name, &b, dns_rootname, - DNS_NAME_DOWNCASE)); - dns_name_format(name, zname, sizeof(zname)); - - key.mv_data = zname; - key.mv_size = strlen(zname); - - zoptions = cfg_tuple_get(zconfig, "options"); - if (zoptions == NULL) { - CLEANUP(ISC_R_FAILURE); - } - - isc_buffer_clear(text); - dzarg.magic = DZARG_MAGIC; - dzarg.text = text; - dzarg.result = ISC_R_SUCCESS; - cfg_printx(zoptions, CFG_PRINTER_ONELINE, dumpzone, &dzarg); - if (dzarg.result != ISC_R_SUCCESS) { - isc_log_write(NAMED_LOGCATEGORY_GENERAL, - NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, - "Error writing zone config to " - "buffer in load_nzf(): %s", - isc_result_totext(result)); - CHECK(dzarg.result); - } - - data.mv_data = isc_buffer_base(text); - data.mv_size = isc_buffer_usedlength(text); - - status = mdb_put(txn, dbi, &key, &data, MDB_NOOVERWRITE); - if (status != MDB_SUCCESS) { - isc_log_write(NAMED_LOGCATEGORY_GENERAL, - NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, - "Error inserting zone in " - "NZD database: %s", - mdb_strerror(status)); - CLEANUP(ISC_R_FAILURE); - } - - commit = true; - } - - result = ISC_R_SUCCESS; - - /* - * Leaving the NZF file in place is harmless as we won't use it - * if an NZD database is found for the view. But we rename NZF file - * to a backup name here. - */ - strlcpy(tempname, view->newzone.file, sizeof(tempname)); - if (strlen(tempname) < sizeof(tempname) - 1) { - strlcat(tempname, "~", sizeof(tempname)); - isc_file_rename(view->newzone.file, tempname); - } - -cleanup: - if (result != ISC_R_SUCCESS) { - (void)nzd_close(&txn, false); - } else { - result = nzd_close(&txn, commit); - } - - if (text != NULL) { - isc_buffer_free(&text); - } - - if (nzf_config != NULL) { - cfg_obj_detach(&nzf_config); - } - - return result; -} - static isc_result_t newzone_parse(named_server_t *server, char *command, dns_view_t **viewp, cfg_obj_t **zoneconfp, const cfg_obj_t **zoneobjp,