mirror of
https://github.com/isc-projects/bind9.git
synced 2026-06-08 19:52:07 -04:00
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.
This commit is contained in:
parent
f203f6e77a
commit
8ae0828e15
4 changed files with 550 additions and 492 deletions
55
bin/named/include/named/nzd.h
Normal file
55
bin/named/include/named/nzd.h
Normal file
|
|
@ -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 <lmdb.h>
|
||||
|
||||
#include <isc/buffer.h>
|
||||
#include <isc/magic.h>
|
||||
#include <isc/result.h>
|
||||
|
||||
#include <dns/view.h>
|
||||
|
||||
#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);
|
||||
|
|
@ -32,6 +32,7 @@ named_srcset.add(
|
|||
'log.c',
|
||||
'logconf.c',
|
||||
'main.c',
|
||||
'nzd.c',
|
||||
'os.c',
|
||||
'server.c',
|
||||
'statschannel.c',
|
||||
|
|
|
|||
492
bin/named/nzd.c
Normal file
492
bin/named/nzd.c
Normal file
|
|
@ -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 <lmdb.h>
|
||||
|
||||
#include <isc/file.h>
|
||||
|
||||
#include <dns/fixedname.h>
|
||||
#include <dns/name.h>
|
||||
#include <dns/zone.h>
|
||||
|
||||
#include <isccfg/cfg.h>
|
||||
#include <isccfg/namedconf.h>
|
||||
|
||||
#include <named/nzd.h>
|
||||
#include <named/os.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -117,6 +117,7 @@
|
|||
|
||||
#include <named/config.h>
|
||||
#include <named/control.h>
|
||||
#include <named/nzd.h>
|
||||
#if defined(HAVE_GEOIP2)
|
||||
#include <named/geoip.h>
|
||||
#endif /* HAVE_GEOIP2 */
|
||||
|
|
@ -145,8 +146,6 @@
|
|||
#define JEMALLOC_API_SUPPORTED 1
|
||||
#endif
|
||||
|
||||
#include <lmdb.h>
|
||||
|
||||
#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,
|
||||
|
|
|
|||
Loading…
Reference in a new issue