Commit eca74c52 authored by Evan Hunt's avatar Evan Hunt
Browse files

[master] store "addzone" zone config in a NZD database

4421.	[func]		When built with LMDB (Lightning Memory-mapped
			Database), named will now use a database to store
			the configuration for zones added by "rndc addzone"
			instead of using a flat NZF file. This improves
			performance of "rndc delzone" and "rndc modzone"
			significantly. Existing NZF files will
			automatically by converted to NZD databases.
			To view the contents of an NZD or to roll back to
			NZF format, use "named-nzd2nzf". To disable
                        this feature, use "configure --without-lmdb".
                        [RT #39837]
parent e7e7efe9
4421. [func] When built with LMDB (Lightning Memory-mapped
Database), named will now use a database to store
the configuration for zones added by "rndc addzone"
instead of using a flat NZF file. This improves
performance of "rndc delzone" and "rndc modzone"
significantly. Existing NZF files will
automatically by converted to NZD databases.
To view the contents of an NZD or to roll back to
NZF format, use "named-nzd2nzf". To disable
this feature, use "configure --without-lmdb".
[RT #39837]
4420. [func] nslookup now looks for AAAA as well as A by default.
[RT #40420]
......
......@@ -11,7 +11,7 @@ VPATH = @srcdir@
top_srcdir = @top_srcdir@
SUBDIRS = named rndc dig delv dnssec tools tests nsupdate \
check confgen @PYTHON_TOOLS@ @PKCS11_TOOLS@
check confgen @NZD_TOOLS@ @PYTHON_TOOLS@ @PKCS11_TOOLS@
TARGETS =
@BIND9_MAKE_RULES@
......@@ -27,6 +27,7 @@
#define NS_EVENTCLASS ISC_EVENTCLASS(0x4E43)
#define NS_EVENT_RELOAD (NS_EVENTCLASS + 0)
#define NS_EVENT_CLIENTCONTROL (NS_EVENTCLASS + 1)
#define NS_EVENT_DELZONE (NS_EVENTCLASS + 2)
/*%
* Name server state. Better here than in lots of separate global variables.
......
......@@ -64,6 +64,7 @@
#include <dns/dyndb.h>
#include <dns/events.h>
#include <dns/forward.h>
#include <dns/fixedname.h>
#include <dns/journal.h>
#include <dns/keytable.h>
#include <dns/keyvalues.h>
......@@ -99,7 +100,9 @@
#include <named/client.h>
#include <named/config.h>
#include <named/control.h>
#ifdef HAVE_GEOIP
#include <named/geoip.h>
#endif /* HAVE_GEOIP */
#include <named/interfacemgr.h>
#include <named/log.h>
#include <named/logconf.h>
......@@ -115,9 +118,17 @@
#include <named/ns_smf_globals.h>
#include <stdlib.h>
#endif
#ifdef HAVE_GEOIP
#include <named/geoip.h>
#endif /* HAVE_GEOIP */
#ifdef HAVE_LMDB
#include <lmdb.h>
#define count_newzones count_newzones_db
#define configure_newzones configure_newzones_db
#define dumpzone dumpzone_db
#else /* HAVE_LMDB */
#define count_newzones count_newzones_file
#define configure_newzones configure_newzones_file
#define dumpzone dumpzone_file
#endif /* HAVE_LMDB */
#ifndef PATH_MAX
#define PATH_MAX 1024
......@@ -253,10 +264,16 @@ typedef struct ns_cfgctx {
cfg_parser_t * add_parser;
cfg_obj_t * config;
cfg_obj_t * vconfig;
cfg_obj_t * nzconfig;
cfg_obj_t * nzf_config;
cfg_aclconfctx_t * actx;
} ns_cfgctx_t;
/*%
* A function to write out added-zone configuration to the new_zone_file
* specified in 'view'. Maybe called by delete_zoneconf().
*/
typedef isc_result_t (*nzfwriter_t)(const cfg_obj_t *config, dns_view_t *view);
/*%
* Holds state information for the initial zone loading process.
* Uses the isc_refcount structure to count the number of views
......@@ -429,6 +446,10 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
isc_boolean_t added, isc_boolean_t old_rpz_ok,
isc_boolean_t modify);
static isc_result_t
configure_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
isc_mem_t *mctx, cfg_aclconfctx_t *actx);
static isc_result_t
add_keydata_zone(dns_view_t *view, const char *directory, isc_mem_t *mctx);
......@@ -450,8 +471,25 @@ putuint8(isc_buffer_t **b, isc_uint8_t val);
static inline isc_result_t
putnull(isc_buffer_t **b);
static int
count_zones(const cfg_obj_t *conf);
#ifdef HAVE_LMDB
static isc_result_t
migrate_nzf(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_close(MDB_txn **txnp, isc_boolean_t commit);
static isc_result_t
nzd_count(dns_view_t *view, int *countp);
#else
static isc_result_t
add_comment(FILE *fp, const char *viewname);
nzf_append(dns_view_t *view, const cfg_obj_t *zconfig);
#endif
/*%
* Configure a single view ACL at '*aclp'. Get its configuration from
......@@ -2177,7 +2215,7 @@ catz_delzone_taskaction(isc_task_t *task, isc_event_t *event0) {
RUNTIME_CHECK(result == ISC_R_SUCCESS);
dns_name_format(dns_catz_entry_getname(ev->entry), cname,
DNS_NAME_FORMATSIZE);
DNS_NAME_FORMATSIZE);
result = dns_zt_find(ev->view->zonetable,
dns_catz_entry_getname(ev->entry), 0, NULL, &zone);
if (result != ISC_R_SUCCESS) {
......@@ -3095,7 +3133,6 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist,
isc_boolean_t zero_no_soattl;
dns_acl_t *clients = NULL, *mapped = NULL, *excluded = NULL;
unsigned int query_timeout, ndisp;
ns_cfgctx_t *nzctx;
isc_boolean_t old_rpz_ok = ISC_FALSE;
isc_dscp_t dscp4 = -1, dscp6 = -1;
dns_dyndbctx_t *dctx = NULL;
......@@ -3255,26 +3292,7 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist,
* from the newzone file for zones that were added during previous
* runs.
*/
nzctx = view->new_zone_config;
if (nzctx != NULL && nzctx->nzconfig != NULL) {
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_SERVER, ISC_LOG_INFO,
"loading additional zones for view '%s'",
view->name);
zonelist = NULL;
cfg_map_get(nzctx->nzconfig, "zone", &zonelist);
for (element = cfg_list_first(zonelist);
element != NULL;
element = cfg_list_next(element))
{
const cfg_obj_t *zconfig = cfg_listelt_value(element);
CHECK(configure_zone(config, zconfig, vconfig,
mctx, view, NULL, actx,
ISC_TRUE, ISC_FALSE, ISC_FALSE));
}
}
CHECK(configure_newzones(view, config, vconfig, mctx, actx));
/*
* Create Dynamically Loadable Zone driver.
......@@ -6197,9 +6215,91 @@ configure_session_key(const cfg_obj_t **maps, ns_server_t *server,
return (result);
}
#ifndef HAVE_LMDB
static isc_result_t
count_newzones(dns_view_t *view, ns_cfgctx_t *nzcfg, int *num_zonesp) {
isc_result_t result;
/*
* In the case of NZF files, we also parse the configuration in
* the file at this stage.
*
* This may be called in multiple views, so we reset
* the parser each time.
*/
cfg_parser_reset(ns_g_addparser);
result = cfg_parse_file(ns_g_addparser, view->new_zone_file,
&cfg_type_addzoneconf, &nzcfg->nzf_config);
if (result == ISC_R_SUCCESS) {
int num_zones;
num_zones = count_zones(nzcfg->nzf_config);
isc_log_write(ns_g_lctx,
NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
ISC_LOG_INFO,
"NZF file '%s' contains %d zones",
view->new_zone_file, num_zones);
if (num_zonesp != NULL)
*num_zonesp = num_zones;
} else if (result == ISC_R_FILENOTFOUND) {
/* The new zone file may not exist. That is OK. */
result = ISC_R_SUCCESS;
if (num_zonesp != NULL)
*num_zonesp = 0;
} else {
isc_log_write(ns_g_lctx,
NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
ISC_LOG_ERROR,
"Error parsing NZF file '%s': %s",
view->new_zone_file,
isc_result_totext(result));
}
return (result);
}
#else /* HAVE_LMDB */
static isc_result_t
count_newzones(dns_view_t *view, ns_cfgctx_t *nzcfg, int *num_zonesp) {
isc_result_t result;
int n;
UNUSED(nzcfg);
REQUIRE(num_zonesp != NULL);
CHECK(migrate_nzf(view));
isc_log_write(ns_g_lctx,
NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
ISC_LOG_INFO, "loading NZD zone count from '%s' "
"for view '%s'",
view->new_zone_db, view->name);
CHECK(nzd_count(view, &n));
*num_zonesp = n;
isc_log_write(ns_g_lctx,
NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
ISC_LOG_INFO,
"NZD database '%s' contains %d zones",
view->new_zone_db, n);
cleanup:
if (result != ISC_R_SUCCESS)
*num_zonesp = 0;
return (ISC_R_SUCCESS);
}
#endif /* HAVE_LMDB */
static isc_result_t
setup_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
cfg_parser_t *conf_parser, cfg_aclconfctx_t *actx)
cfg_parser_t *conf_parser, cfg_aclconfctx_t *actx,
int *num_zones)
{
isc_result_t result = ISC_R_SUCCESS;
isc_boolean_t allow = ISC_FALSE;
......@@ -6241,6 +6341,8 @@ setup_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
if (!allow) {
dns_view_setnewzones(view, ISC_FALSE, NULL, NULL);
if (num_zones != NULL)
*num_zones = 0;
return (ISC_R_SUCCESS);
}
......@@ -6252,7 +6354,12 @@ setup_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
memset(nzcfg, 0, sizeof(*nzcfg));
dns_view_setnewzones(view, allow, nzcfg, newzone_cfgctx_destroy);
result = dns_view_setnewzones(view, allow, nzcfg,
newzone_cfgctx_destroy);
if (result != ISC_R_SUCCESS) {
isc_mem_free(view->mctx, nzcfg);
return (result);
}
cfg_obj_attach(config, &nzcfg->config);
if (vconfig != NULL)
......@@ -6268,18 +6375,249 @@ setup_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
isc_mem_attach(view->mctx, &nzcfg->mctx);
cfg_aclconfctx_attach(actx, &nzcfg->actx);
/*
* This may be called in multiple views, so we reset
* the parser each time.
*/
result = count_newzones(view, nzcfg, num_zones);
return (result);
}
#ifndef HAVE_LMDB
static isc_result_t
configure_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
isc_mem_t *mctx, cfg_aclconfctx_t *actx)
{
isc_result_t result;
ns_cfgctx_t *nzctx;
const cfg_obj_t *zonelist;
const cfg_listelt_t *element;
nzctx = view->new_zone_config;
if (nzctx == NULL || nzctx->nzf_config == NULL)
return (ISC_R_SUCCESS);
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_SERVER, ISC_LOG_INFO,
"loading additional zones for view '%s'",
view->name);
zonelist = NULL;
cfg_map_get(nzctx->nzf_config, "zone", &zonelist);
for (element = cfg_list_first(zonelist);
element != NULL;
element = cfg_list_next(element))
{
const cfg_obj_t *zconfig = cfg_listelt_value(element);
CHECK(configure_zone(config, zconfig, vconfig,
mctx, view, NULL, actx,
ISC_TRUE, ISC_FALSE, ISC_FALSE));
}
result = ISC_R_SUCCESS;
cleanup:
return (result);
}
#else /* HAVE_LMDB */
static isc_result_t
data_to_cfg(dns_view_t *view, MDB_val *key, MDB_val *data,
isc_buffer_t **text, cfg_obj_t **zoneconfig)
{
isc_result_t result;
const char *zone_name;
size_t zone_name_len;
const char *zone_config;
size_t zone_config_len;
cfg_obj_t *zoneconf = NULL;
REQUIRE(view != NULL);
REQUIRE(key != NULL);
REQUIRE(data != NULL);
REQUIRE(text != NULL);
REQUIRE(zoneconfig != NULL && *zoneconfig == NULL);
if (*text == NULL) {
result = isc_buffer_allocate(view->mctx, text, 256);
if (result != ISC_R_SUCCESS)
goto cleanup;
} else {
isc_buffer_clear(*text);
}
zone_name = (const char *) key->mv_data;
zone_name_len = key->mv_size;
INSIST(zone_name != NULL && zone_name_len > 0);
zone_config = (const char *) data->mv_data;
zone_config_len = data->mv_size;
INSIST(zone_config != NULL && zone_config_len > 0);
/* zone zonename { config; }; */
result = isc_buffer_reserve(text, 5 + zone_name_len + 1 +
zone_config_len + 2);
if (result != ISC_R_SUCCESS)
goto cleanup;
putstr(text, "zone ");
putmem(text, (const void *) zone_name, zone_name_len);
putstr(text, " ");
putmem(text, (const void *) zone_config, zone_config_len);
putstr(text, ";\n");
cfg_parser_reset(ns_g_addparser);
result = cfg_parse_file(ns_g_addparser, view->new_zone_file,
&cfg_type_addzoneconf, &nzcfg->nzconfig);
if (result == ISC_R_FILENOTFOUND)
result = cfg_parse_buffer2(ns_g_addparser, *text, zone_name,
&cfg_type_addzoneconf, &zoneconf);
if (result != ISC_R_SUCCESS) {
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_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);
goto cleanup;
}
*zoneconfig = zoneconf;
zoneconf = NULL;
result = ISC_R_SUCCESS;
cleanup:
if (zoneconf != NULL)
cfg_obj_destroy(ns_g_addparser, &zoneconf);
return (result);
}
static isc_result_t
configure_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
isc_mem_t *mctx, cfg_aclconfctx_t *actx)
{
isc_result_t result = ISC_R_SUCCESS;
int status;
isc_buffer_t *text = NULL;
cfg_obj_t *zoneconf = NULL;
MDB_cursor *cursor = NULL;
MDB_txn *txn = NULL;
MDB_dbi dbi;
MDB_val key, data;
if (view->new_zone_config == NULL)
return (ISC_R_SUCCESS);
result = nzd_open(view, MDB_RDONLY, &txn, &dbi);
if (result != ISC_R_SUCCESS) {
result = ISC_R_SUCCESS;
goto cleanup;
}
isc_log_write(ns_g_lctx,
NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
ISC_LOG_INFO, "loading NZD configs from '%s' "
"for view '%s'",
view->new_zone_db, view->name);
status = mdb_cursor_open(txn, dbi, &cursor);
if (status != 0) {
result = ISC_R_FAILURE;
goto cleanup;
}
while (mdb_cursor_get(cursor, &key, &data, MDB_NEXT) == 0) {
const cfg_obj_t *zlist = NULL;
const cfg_obj_t *zoneobj = NULL;
result = data_to_cfg(view, &key, &data, &text, &zoneconf);
if (result != ISC_R_SUCCESS)
goto cleanup;
CHECK(cfg_map_get(zoneconf, "zone", &zlist));
if (!cfg_obj_islist(zlist))
CHECK(ISC_R_FAILURE);
zoneobj = cfg_listelt_value(cfg_list_first(zlist));
CHECK(configure_zone(config, zoneobj, vconfig,
mctx, view, NULL, actx,
ISC_TRUE, ISC_FALSE, ISC_FALSE));
cfg_obj_destroy(ns_g_addparser, &zoneconf);
}
result = ISC_R_SUCCESS;
cleanup:
if (cursor != NULL)
mdb_cursor_close(cursor);
(void) nzd_close(&txn, ISC_FALSE);
if (zoneconf != NULL)
cfg_obj_destroy(ns_g_addparser, &zoneconf);
if (text != NULL)
isc_buffer_free(&text);
return (result);
}
static isc_result_t
get_newzone_config(dns_view_t *view, const char *zonename,
cfg_obj_t **zoneconfig)
{
isc_result_t result;
int status;
cfg_obj_t *zoneconf = NULL;
isc_buffer_t *text = NULL;
MDB_txn *txn = NULL;
MDB_dbi dbi;
MDB_val key, data;
char zname[DNS_NAME_FORMATSIZE];
dns_fixedname_t fname;
dns_name_t *name;
isc_buffer_t b;
INSIST(zoneconfig != NULL && *zoneconfig == NULL);
CHECK(nzd_open(view, MDB_RDONLY, &txn, &dbi));
isc_log_write(ns_g_lctx,
NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
ISC_LOG_INFO, "loading NZD config from '%s' "
"for zone '%s'",
view->new_zone_db, zonename);
/* Normalize zone name */
isc_buffer_constinit(&b, zonename, strlen(zonename));
isc_buffer_add(&b, strlen(zonename));
dns_fixedname_init(&fname);
name = dns_fixedname_name(&fname);
CHECK(dns_name_fromtext(name, &b, dns_rootname,
DNS_NAME_DOWNCASE, NULL));
dns_name_format(name, zname, sizeof(zname));
key.mv_data = zname;
key.mv_size = strlen(zname);
status = mdb_get(txn, dbi, &key, &data);
if (status != 0)
CHECK(ISC_R_FAILURE);
CHECK(data_to_cfg(view, &key, &data, &text, &zoneconf));
*zoneconfig = zoneconf;
zoneconf = NULL;
result = ISC_R_SUCCESS;
cleanup:
(void) nzd_close(&txn, ISC_FALSE);
if (zoneconf != NULL)
cfg_obj_destroy(ns_g_addparser, &zoneconf);
if (text != NULL)
isc_buffer_free(&text);
return (result);
}
#endif /* HAVE_LMDB */
static int
count_zones(const cfg_obj_t *conf) {
const cfg_obj_t *zonelist = NULL;
......@@ -6410,7 +6748,6 @@ load_configuration(const char *filename, ns_server_t *server,
isc_uint32_t transfer_message_size;
ns_cache_t *nsc;
ns_cachelist_t cachelist, tmpcachelist;
ns_cfgctx_t *nzctx;
unsigned int maxsocks;
isc_uint32_t softquota = 0;
......@@ -6966,6 +7303,8 @@ load_configuration(const char *filename, ns_server_t *server,
{
cfg_obj_t *vconfig = cfg_listelt_value(element);
const cfg_obj_t *voptions = cfg_tuple_get(vconfig, "options");
int nzf_num_zones;
view = NULL;
CHECK(create_view(vconfig, &viewlist, &view));
......@@ -6974,10 +7313,8 @@ load_configuration(const char *filename, ns_server_t *server,
num_zones += count_zones(voptions);
CHECK(setup_newzones(view, config, vconfig, conf_parser,
ns_g_aclconfctx));
nzctx = view->new_zone_config;
if (nzctx != NULL && nzctx->nzconfig != NULL)
num_zones += count_zones(nzctx->nzconfig);
ns_g_aclconfctx, &nzf_num_zones));
num_zones += nzf_num_zones;
dns_view_detach(&view);
}
......@@ -6987,17 +7324,16 @@ load_configuration(const char *filename, ns_server_t *server,
* view here.
*/
if (views == NULL) {
int nzf_num_zones;
CHECK(create_view(NULL, &viewlist, &view));
INSIST(view != NULL);
num_zones = count_zones(config);
CHECK(setup_newzones(view, config, NULL, conf_parser,
ns_g_aclconfctx));
nzctx = view->new_zone_config;
if (nzctx != NULL && nzctx->nzconfig != NULL)
num_zones += count_zones(nzctx->nzconfig);
ns_g_aclconfctx, &nzf_num_zones));
num_zones += nzf_num_zones;
dns_view_detach(&view);
}
......@@ -10171,6 +10507,8 @@ ns_smf_add_message(isc_buffer_t **text) {
}
#endif /* HAVE_LIBSCF */
#ifndef HAVE_LMDB
/*
* Emit a comment at the top of the nzf file containing the viewname
* Expects the fp to already be open for writing
......@@ -10188,164 +10526,420 @@ add_comment(FILE *fp, const char *viewname) {
return (result);
}
#if 0
/*
* XXXMPA When we move to using a database instead of a flat file
* we will use nzf_remove. Remember that we need to use a cannonical
* form of the zone name when we do.
*/
static void
dumpzone(void *arg, const char *buf, int len) {
FILE *fp = arg;
(void) isc_stdio_write(buf, len, 1, fp, NULL);
}
static isc_result_t
nzf_remove(const char *nzfile, const char *viewname, const char *zonename) {
nzf_append(dns_view_t *view, const cfg_obj_t *zconfig) {
isc_result_t result;
FILE *ifp = NULL, *ofp = NULL;
size_t znamelen = 0;
char tmp[1024], buf[1024];
isc_boolean_t inheader = ISC_TRUE;
znamelen = strlen(zonename);
/* Rewrite zone list */
result = isc_stdio_open(nzfile, "r", &ifp);
if (ifp != NULL && result == ISC_R_SUCCESS) {
char *found = NULL, *p = NULL;
size_t n;
/* Create a temporary file */
CHECK(isc_file_template("", "nzf-XXXXXXXX", tmp, sizeof(tmp)));
CHECK(isc_file_openunique(tmp, &ofp));
CHECK(add_comment(ofp, viewname));
/* Look for the entry for that zone */
while (fgets(buf, 1024, ifp)) {
/* Skip initial comment, if any */
if (inheader && *buf == '#')
continue;
if (*buf != '#')
inheader = ISC_FALSE;
off_t offset;
FILE *fp = NULL;
/*
* Any other lines not starting with zone, copy
</