Compare commits
18
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
31be352cb2 | ||
|
|
27fab42e6f | ||
|
|
a6daf19d0b | ||
|
|
b50bf757f2 | ||
|
|
b3fba18b3c | ||
|
|
a4f5395184 | ||
|
|
e512cd66f7 | ||
|
|
e9352e195d | ||
|
|
6fc47b3724 | ||
|
|
828c7ad87c | ||
|
|
a125158762 | ||
|
|
5ec5650763 | ||
|
|
66ac1b2845 | ||
|
|
f8ab917471 | ||
|
|
e729f5bf50 | ||
|
|
a243999f4c | ||
|
|
62da04fd76 | ||
|
|
03d2b4a670 |
+2
-4
@@ -1029,13 +1029,12 @@ unit:gcc:ossl3:amd64:
|
||||
artifacts: true
|
||||
|
||||
# Jobs for regular GCC builds on Debian "sid" (amd64)
|
||||
# Also tests configration option: --without-lmdb.
|
||||
|
||||
gcc:sid:amd64:
|
||||
variables:
|
||||
CC: gcc
|
||||
CFLAGS: "${CFLAGS_COMMON} -O3"
|
||||
EXTRA_CONFIGURE: "--with-libidn2 --without-lmdb ${WITH_READLINE}"
|
||||
EXTRA_CONFIGURE: "--with-libidn2 ${WITH_READLINE}"
|
||||
RUN_MAKE_INSTALL: 1
|
||||
<<: *debian_sid_amd64_image
|
||||
<<: *build_job
|
||||
@@ -1056,14 +1055,13 @@ unit:gcc:sid:amd64:
|
||||
artifacts: true
|
||||
|
||||
# Job for out-of-tree GCC build on Debian 12 "bookworm" (amd64)
|
||||
# Also tests configration option: --with-lmdb.
|
||||
|
||||
gcc:out-of-tree:
|
||||
variables:
|
||||
CC: gcc
|
||||
CFLAGS: "${CFLAGS_COMMON} -Og"
|
||||
CONFIGURE: "${CI_PROJECT_DIR}/configure"
|
||||
EXTRA_CONFIGURE: "--with-libidn2 --with-lmdb"
|
||||
EXTRA_CONFIGURE: "--with-libidn2"
|
||||
RUN_MAKE_INSTALL: 1
|
||||
OUT_OF_TREE_WORKSPACE: workspace
|
||||
<<: *base_image
|
||||
|
||||
+3
-5
@@ -159,11 +159,9 @@ options {\n\
|
||||
fetch-quota-params 100 0.1 0.3 0.7;\n\
|
||||
fetches-per-server 0;\n\
|
||||
fetches-per-zone 0;\n\
|
||||
lame-ttl 0;\n"
|
||||
#ifdef HAVE_LMDB
|
||||
" lmdb-mapsize 32M;\n"
|
||||
#endif /* ifdef HAVE_LMDB */
|
||||
" max-cache-size 90%;\n\
|
||||
lame-ttl 0;\n\
|
||||
lmdb-mapsize 32M;\n\
|
||||
max-cache-size 90%;\n\
|
||||
max-cache-ttl 604800; /* 1 week */\n\
|
||||
max-clients-per-query 100;\n\
|
||||
max-ncache-ttl 10800; /* 3 hours */\n\
|
||||
|
||||
+11
-392
@@ -45,6 +45,7 @@
|
||||
#include <isc/httpd.h>
|
||||
#include <isc/job.h>
|
||||
#include <isc/lex.h>
|
||||
#include <isc/lmdb.h>
|
||||
#include <isc/loop.h>
|
||||
#include <isc/meminfo.h>
|
||||
#include <isc/netmgr.h>
|
||||
@@ -140,15 +141,6 @@
|
||||
#include <named/smf_globals.h>
|
||||
#endif /* ifdef HAVE_LIBSCF */
|
||||
|
||||
#ifdef HAVE_LMDB
|
||||
#include <lmdb.h>
|
||||
#define configure_newzones configure_newzones_db
|
||||
#define dumpzone dumpzone_db
|
||||
#else /* HAVE_LMDB */
|
||||
#define configure_newzones configure_newzones_file
|
||||
#define dumpzone dumpzone_file
|
||||
#endif /* HAVE_LMDB */
|
||||
|
||||
#ifndef SIZE_MAX
|
||||
#define SIZE_MAX ((size_t)-1)
|
||||
#endif /* ifndef SIZE_MAX */
|
||||
@@ -503,7 +495,6 @@ putuint8(isc_buffer_t **b, uint8_t val);
|
||||
static isc_result_t
|
||||
putnull(isc_buffer_t **b);
|
||||
|
||||
#ifdef HAVE_LMDB
|
||||
static isc_result_t
|
||||
nzd_writable(dns_view_t *view);
|
||||
|
||||
@@ -518,10 +509,6 @@ nzd_env_close(dns_view_t *view);
|
||||
|
||||
static isc_result_t
|
||||
nzd_close(MDB_txn **txnp, bool commit);
|
||||
#else /* ifdef HAVE_LMDB */
|
||||
static isc_result_t
|
||||
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);
|
||||
@@ -7361,7 +7348,6 @@ setup_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
|
||||
dns_view_setnewzonedir(view, dir);
|
||||
}
|
||||
|
||||
#ifdef HAVE_LMDB
|
||||
result = named_config_get(maps, "lmdb-mapsize", &obj);
|
||||
if (result == ISC_R_SUCCESS && obj != NULL) {
|
||||
mapsize = cfg_obj_asuint64(obj);
|
||||
@@ -7381,9 +7367,6 @@ setup_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
|
||||
return ISC_R_FAILURE;
|
||||
}
|
||||
}
|
||||
#else /* ifdef HAVE_LMDB */
|
||||
UNUSED(obj);
|
||||
#endif /* HAVE_LMDB */
|
||||
|
||||
/*
|
||||
* A non-empty catalog-zones statement implies allow-new-zones
|
||||
@@ -7478,54 +7461,6 @@ configure_zone_setviewcommit(isc_result_t result, const cfg_obj_t *zconfig,
|
||||
dns_view_detach(&pview);
|
||||
}
|
||||
|
||||
#ifndef HAVE_LMDB
|
||||
|
||||
static isc_result_t
|
||||
configure_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
|
||||
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(NAMED_LOGCATEGORY_GENERAL, NAMED_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, view,
|
||||
&named_g_server->viewlist,
|
||||
&named_g_server->kasplist,
|
||||
&named_g_server->keystorelist, actx, true,
|
||||
false, false, false));
|
||||
}
|
||||
|
||||
result = ISC_R_SUCCESS;
|
||||
|
||||
cleanup:
|
||||
for (element = cfg_list_first(zonelist); element != NULL;
|
||||
element = cfg_list_next(element))
|
||||
{
|
||||
const cfg_obj_t *zconfig = cfg_listelt_value(element);
|
||||
configure_zone_setviewcommit(result, zconfig, view);
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -7823,8 +7758,6 @@ cleanup:
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif /* HAVE_LMDB */
|
||||
|
||||
static isc_result_t
|
||||
load_configuration(const char *filename, named_server_t *server,
|
||||
bool first_time) {
|
||||
@@ -8832,12 +8765,11 @@ load_configuration(const char *filename, named_server_t *server,
|
||||
server->sctx->tkeyctx = tkeyctx;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LMDB
|
||||
/*
|
||||
* If we're using LMDB, we may have created newzones databases
|
||||
* as root, making it impossible to reopen them later after
|
||||
* switching to a new userid. We close them now, and reopen
|
||||
* after relinquishing privileges them.
|
||||
* We may have created newzones LMDB databases as root, making
|
||||
* it impossible to reopen them later after switching to a new
|
||||
* userid. We close them now, and reopen after relinquishing
|
||||
* privileges them.
|
||||
*/
|
||||
if (first_time) {
|
||||
for (dns_view_t *view = ISC_LIST_HEAD(server->viewlist);
|
||||
@@ -8846,7 +8778,6 @@ load_configuration(const char *filename, named_server_t *server,
|
||||
nzd_env_close(view);
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_LMDB */
|
||||
|
||||
/*
|
||||
* Switch to the effective UID for setting up files.
|
||||
@@ -8868,7 +8799,6 @@ load_configuration(const char *filename, named_server_t *server,
|
||||
goto cleanup_cachelist;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LMDB
|
||||
/*
|
||||
* Reopen NZD databases.
|
||||
*/
|
||||
@@ -8879,7 +8809,6 @@ load_configuration(const char *filename, named_server_t *server,
|
||||
nzd_env_reopen(view);
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_LMDB */
|
||||
|
||||
/*
|
||||
* Configure the logging system.
|
||||
@@ -12345,163 +12274,6 @@ named_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
|
||||
*/
|
||||
#define HEADER1 "# New zone file for view: "
|
||||
#define HEADER2 \
|
||||
"\n# This file contains configuration for zones added by\n" \
|
||||
"# the 'rndc addzone' command. DO NOT EDIT BY HAND.\n"
|
||||
static isc_result_t
|
||||
add_comment(FILE *fp, const char *viewname) {
|
||||
isc_result_t result;
|
||||
CHECK(isc_stdio_write(HEADER1, sizeof(HEADER1) - 1, 1, fp, NULL));
|
||||
CHECK(isc_stdio_write(viewname, strlen(viewname), 1, fp, NULL));
|
||||
CHECK(isc_stdio_write(HEADER2, sizeof(HEADER2) - 1, 1, fp, NULL));
|
||||
cleanup:
|
||||
return result;
|
||||
}
|
||||
|
||||
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_append(dns_view_t *view, const cfg_obj_t *zconfig) {
|
||||
isc_result_t result;
|
||||
off_t offset;
|
||||
FILE *fp = NULL;
|
||||
bool offsetok = false;
|
||||
|
||||
LOCK(&view->new_zone_lock);
|
||||
|
||||
CHECK(isc_stdio_open(view->new_zone_file, "a", &fp));
|
||||
CHECK(isc_stdio_seek(fp, 0, SEEK_END));
|
||||
|
||||
CHECK(isc_stdio_tell(fp, &offset));
|
||||
offsetok = true;
|
||||
if (offset == 0) {
|
||||
CHECK(add_comment(fp, view->name));
|
||||
}
|
||||
|
||||
CHECK(isc_stdio_write("zone ", 5, 1, fp, NULL));
|
||||
cfg_printx(zconfig, CFG_PRINTER_ONELINE, dumpzone, fp);
|
||||
CHECK(isc_stdio_write(";\n", 2, 1, fp, NULL));
|
||||
CHECK(isc_stdio_flush(fp));
|
||||
result = isc_stdio_close(fp);
|
||||
fp = NULL;
|
||||
|
||||
cleanup:
|
||||
if (fp != NULL) {
|
||||
(void)isc_stdio_close(fp);
|
||||
if (offsetok) {
|
||||
isc_result_t result2;
|
||||
|
||||
result2 = isc_file_truncate(view->new_zone_file,
|
||||
offset);
|
||||
if (result2 != ISC_R_SUCCESS) {
|
||||
isc_log_write(NAMED_LOGCATEGORY_GENERAL,
|
||||
NAMED_LOGMODULE_SERVER,
|
||||
ISC_LOG_ERROR,
|
||||
"Error truncating NZF file '%s' "
|
||||
"during rollback from append: "
|
||||
"%s",
|
||||
view->new_zone_file,
|
||||
isc_result_totext(result2));
|
||||
}
|
||||
}
|
||||
}
|
||||
UNLOCK(&view->new_zone_lock);
|
||||
return result;
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
nzf_writeconf(const cfg_obj_t *config, dns_view_t *view) {
|
||||
const cfg_obj_t *zl = NULL;
|
||||
cfg_list_t *list;
|
||||
const cfg_listelt_t *elt;
|
||||
|
||||
FILE *fp = NULL;
|
||||
char tmp[1024];
|
||||
isc_result_t result;
|
||||
|
||||
result = isc_file_template(view->new_zone_file, "nzf-XXXXXXXX", tmp,
|
||||
sizeof(tmp));
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
result = isc_file_openunique(tmp, &fp);
|
||||
}
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
cfg_map_get(config, "zone", &zl);
|
||||
if (!cfg_obj_islist(zl)) {
|
||||
CHECK(ISC_R_FAILURE);
|
||||
}
|
||||
|
||||
list = UNCONST(&zl->value.list);
|
||||
|
||||
CHECK(add_comment(fp, view->name)); /* force a comment */
|
||||
|
||||
for (elt = ISC_LIST_HEAD(*list); elt != NULL;
|
||||
elt = ISC_LIST_NEXT(elt, link))
|
||||
{
|
||||
const cfg_obj_t *zconfig = cfg_listelt_value(elt);
|
||||
|
||||
CHECK(isc_stdio_write("zone ", 5, 1, fp, NULL));
|
||||
cfg_printx(zconfig, CFG_PRINTER_ONELINE, dumpzone, fp);
|
||||
CHECK(isc_stdio_write(";\n", 2, 1, fp, NULL));
|
||||
}
|
||||
|
||||
CHECK(isc_stdio_flush(fp));
|
||||
result = isc_stdio_close(fp);
|
||||
fp = NULL;
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
goto cleanup;
|
||||
}
|
||||
CHECK(isc_file_rename(tmp, view->new_zone_file));
|
||||
return result;
|
||||
|
||||
cleanup:
|
||||
if (fp != NULL) {
|
||||
(void)isc_stdio_close(fp);
|
||||
}
|
||||
(void)isc_file_remove(tmp);
|
||||
return result;
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
load_nzf(dns_view_t *view, ns_cfgctx_t *nzcfg) {
|
||||
isc_result_t result;
|
||||
|
||||
/* The new zone file may not exist. That is OK. */
|
||||
if (!isc_file_exists(view->new_zone_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.
|
||||
*/
|
||||
cfg_parser_reset(named_g_addparser);
|
||||
result = cfg_parse_file(named_g_addparser, view->new_zone_file,
|
||||
&cfg_type_addzoneconf, &nzcfg->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));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
#else /* HAVE_LMDB */
|
||||
|
||||
static void
|
||||
nzd_setkey(MDB_val *key, dns_name_t *name, char *namebuf, size_t buflen) {
|
||||
dns_fixedname_t fixed;
|
||||
@@ -12983,7 +12755,6 @@ cleanup:
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif /* HAVE_LMDB */
|
||||
|
||||
static isc_result_t
|
||||
newzone_parse(named_server_t *server, char *command, dns_view_t **viewp,
|
||||
@@ -13168,21 +12939,14 @@ cleanup:
|
||||
|
||||
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) {
|
||||
dns_name_t *name, const cfg_obj_t *zoneobj, bool redirect,
|
||||
isc_buffer_t **text) {
|
||||
isc_result_t result, tresult;
|
||||
dns_zone_t *zone = NULL;
|
||||
#ifndef HAVE_LMDB
|
||||
FILE *fp = NULL;
|
||||
bool cleanup_config = false;
|
||||
#else /* HAVE_LMDB */
|
||||
MDB_txn *txn = NULL;
|
||||
MDB_dbi dbi;
|
||||
bool locked = false;
|
||||
|
||||
UNUSED(zoneconf);
|
||||
#endif
|
||||
|
||||
/* Zone shouldn't already exist */
|
||||
if (redirect) {
|
||||
result = (view->redirect == NULL) ? ISC_R_NOTFOUND
|
||||
@@ -13198,24 +12962,6 @@ do_addzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view,
|
||||
}
|
||||
|
||||
isc_loopmgr_pause(named_g_loopmgr);
|
||||
|
||||
#ifndef HAVE_LMDB
|
||||
/*
|
||||
* Make sure we can open the configuration save file
|
||||
*/
|
||||
result = isc_stdio_open(view->new_zone_file, "a", &fp);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
isc_loopmgr_resume(named_g_loopmgr);
|
||||
TCHECK(putstr(text, "unable to create '"));
|
||||
TCHECK(putstr(text, view->new_zone_file));
|
||||
TCHECK(putstr(text, "': "));
|
||||
TCHECK(putstr(text, isc_result_totext(result)));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
(void)isc_stdio_close(fp);
|
||||
fp = NULL;
|
||||
#else /* HAVE_LMDB */
|
||||
LOCK(&view->new_zone_lock);
|
||||
locked = true;
|
||||
/* Make sure we can open the NZD database */
|
||||
@@ -13228,7 +12974,6 @@ do_addzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view,
|
||||
result = ISC_R_FAILURE;
|
||||
goto cleanup;
|
||||
}
|
||||
#endif /* HAVE_LMDB */
|
||||
|
||||
/* Mark view unfrozen and configure zone */
|
||||
dns_view_thaw(view);
|
||||
@@ -13263,22 +13008,6 @@ do_addzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view,
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef HAVE_LMDB
|
||||
/*
|
||||
* If there wasn't a previous newzone config, just save the one
|
||||
* 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);
|
||||
} else {
|
||||
cfg_obj_t *z = UNCONST(zoneobj);
|
||||
CHECK(cfg_parser_mapadd(cfg->add_parser, cfg->nzf_config, z,
|
||||
"zone"));
|
||||
}
|
||||
cleanup_config = true;
|
||||
#endif /* HAVE_LMDB */
|
||||
|
||||
/*
|
||||
* Load the zone from the master file. If this fails, we'll
|
||||
* need to undo the configuration we've done already.
|
||||
@@ -13307,34 +13036,17 @@ do_addzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view,
|
||||
/* Flag the zone as having been added at runtime */
|
||||
dns_zone_setadded(zone, true);
|
||||
|
||||
#ifdef HAVE_LMDB
|
||||
/* Save the new zone configuration into the NZD */
|
||||
CHECK(nzd_open(view, 0, &txn, &dbi));
|
||||
CHECK(nzd_save(&txn, dbi, zone, zoneobj));
|
||||
#else /* ifdef HAVE_LMDB */
|
||||
/* Append the zone configuration to the NZF */
|
||||
result = nzf_append(view, zoneobj);
|
||||
#endif /* HAVE_LMDB */
|
||||
|
||||
cleanup:
|
||||
|
||||
#ifndef HAVE_LMDB
|
||||
if (fp != NULL) {
|
||||
(void)isc_stdio_close(fp);
|
||||
}
|
||||
if (result != ISC_R_SUCCESS && cleanup_config) {
|
||||
tresult = delete_zoneconf(view, cfg->add_parser,
|
||||
cfg->nzf_config, name, NULL);
|
||||
RUNTIME_CHECK(tresult == ISC_R_SUCCESS);
|
||||
}
|
||||
#else /* HAVE_LMDB */
|
||||
if (txn != NULL) {
|
||||
(void)nzd_close(&txn, false);
|
||||
}
|
||||
if (locked) {
|
||||
UNLOCK(&view->new_zone_lock);
|
||||
}
|
||||
#endif /* HAVE_LMDB */
|
||||
|
||||
if (zone != NULL) {
|
||||
dns_zone_detach(&zone);
|
||||
@@ -13350,14 +13062,9 @@ do_modzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view,
|
||||
isc_result_t result, tresult;
|
||||
dns_zone_t *zone = NULL;
|
||||
bool added;
|
||||
#ifndef HAVE_LMDB
|
||||
FILE *fp = NULL;
|
||||
cfg_obj_t *z;
|
||||
#else /* HAVE_LMDB */
|
||||
MDB_txn *txn = NULL;
|
||||
MDB_dbi dbi;
|
||||
bool locked = false;
|
||||
#endif /* HAVE_LMDB */
|
||||
|
||||
/* Zone must already exist */
|
||||
if (redirect) {
|
||||
@@ -13377,30 +13084,8 @@ 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(named_g_loopmgr);
|
||||
|
||||
#ifndef HAVE_LMDB
|
||||
/* Make sure we can open the configuration save file */
|
||||
result = isc_stdio_open(view->new_zone_file, "a", &fp);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
TCHECK(putstr(text, "unable to open '"));
|
||||
TCHECK(putstr(text, view->new_zone_file));
|
||||
TCHECK(putstr(text, "': "));
|
||||
TCHECK(putstr(text, isc_result_totext(result)));
|
||||
isc_loopmgr_resume(named_g_loopmgr);
|
||||
goto cleanup;
|
||||
}
|
||||
(void)isc_stdio_close(fp);
|
||||
fp = NULL;
|
||||
#else /* HAVE_LMDB */
|
||||
LOCK(&view->new_zone_lock);
|
||||
locked = true;
|
||||
/* Make sure we can open the NZD database */
|
||||
@@ -13413,7 +13098,6 @@ do_modzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view,
|
||||
isc_loopmgr_resume(named_g_loopmgr);
|
||||
goto cleanup;
|
||||
}
|
||||
#endif /* HAVE_LMDB */
|
||||
|
||||
/* Reconfigure the zone */
|
||||
dns_view_thaw(view);
|
||||
@@ -13441,21 +13125,6 @@ do_modzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view,
|
||||
CHECK(dns_view_findzone(view, name, DNS_ZTFIND_EXACT, &zone));
|
||||
}
|
||||
|
||||
#ifndef HAVE_LMDB
|
||||
/* Remove old zone from configuration (and NZF file if applicable) */
|
||||
if (added) {
|
||||
result = delete_zoneconf(view, cfg->add_parser, cfg->nzf_config,
|
||||
dns_zone_getorigin(zone),
|
||||
nzf_writeconf);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
TCHECK(putstr(text, "former zone configuration "
|
||||
"not deleted: "));
|
||||
TCHECK(putstr(text, isc_result_totext(result)));
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_LMDB */
|
||||
|
||||
if (!added) {
|
||||
if (cfg->vconfig == NULL) {
|
||||
result = delete_zoneconf(
|
||||
@@ -13512,24 +13181,9 @@ do_modzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view,
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
#ifndef HAVE_LMDB
|
||||
/* Store the new zone configuration; also in NZF if applicable */
|
||||
z = UNCONST(zoneobj);
|
||||
CHECK(cfg_parser_mapadd(cfg->add_parser, cfg->nzf_config, z, "zone"));
|
||||
#endif /* HAVE_LMDB */
|
||||
|
||||
if (added) {
|
||||
#ifdef HAVE_LMDB
|
||||
CHECK(nzd_open(view, 0, &txn, &dbi));
|
||||
CHECK(nzd_save(&txn, dbi, zone, zoneobj));
|
||||
#else /* ifdef HAVE_LMDB */
|
||||
result = nzf_append(view, zoneobj);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
TCHECK(putstr(text, "\nNew zone config not saved: "));
|
||||
TCHECK(putstr(text, isc_result_totext(result)));
|
||||
goto cleanup;
|
||||
}
|
||||
#endif /* HAVE_LMDB */
|
||||
|
||||
TCHECK(putstr(text, "zone '"));
|
||||
TCHECK(putstr(text, zname));
|
||||
@@ -13543,18 +13197,12 @@ do_modzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view,
|
||||
|
||||
cleanup:
|
||||
|
||||
#ifndef HAVE_LMDB
|
||||
if (fp != NULL) {
|
||||
(void)isc_stdio_close(fp);
|
||||
}
|
||||
#else /* HAVE_LMDB */
|
||||
if (txn != NULL) {
|
||||
(void)nzd_close(&txn, false);
|
||||
}
|
||||
if (locked) {
|
||||
UNLOCK(&view->new_zone_lock);
|
||||
}
|
||||
#endif /* HAVE_LMDB */
|
||||
|
||||
if (zone != NULL) {
|
||||
dns_zone_detach(&zone);
|
||||
@@ -13594,12 +13242,7 @@ named_server_changezone(named_server_t *server, char *command,
|
||||
&redirect, text));
|
||||
|
||||
/* Are we accepting new zones in this view? */
|
||||
#ifdef HAVE_LMDB
|
||||
if (view->new_zone_db == NULL)
|
||||
#else /* ifdef HAVE_LMDB */
|
||||
if (view->new_zone_file == NULL)
|
||||
#endif /* HAVE_LMDB */
|
||||
{
|
||||
if (view->new_zone_db == NULL) {
|
||||
(void)putstr(text, "Not allowing new zones in view '");
|
||||
(void)putstr(text, view->name);
|
||||
(void)putstr(text, "'");
|
||||
@@ -13629,8 +13272,8 @@ named_server_changezone(named_server_t *server, char *command,
|
||||
}
|
||||
|
||||
if (addzone) {
|
||||
CHECK(do_addzone(server, cfg, view, dnsname, zoneconf, zoneobj,
|
||||
redirect, text));
|
||||
CHECK(do_addzone(server, cfg, view, dnsname, zoneobj, redirect,
|
||||
text));
|
||||
} else {
|
||||
CHECK(do_modzone(server, cfg, view, dnsname, zonename, zoneobj,
|
||||
redirect, text));
|
||||
@@ -13693,10 +13336,8 @@ rmzone(void *arg) {
|
||||
dns_db_t *dbp = NULL;
|
||||
bool added;
|
||||
isc_result_t result;
|
||||
#ifdef HAVE_LMDB
|
||||
MDB_txn *txn = NULL;
|
||||
MDB_dbi dbi;
|
||||
#endif /* ifdef HAVE_LMDB */
|
||||
|
||||
REQUIRE(dz != NULL);
|
||||
|
||||
@@ -13718,7 +13359,6 @@ rmzone(void *arg) {
|
||||
catz = dns_zone_get_parentcatz(zone);
|
||||
|
||||
if (added && catz == NULL && cfg != NULL) {
|
||||
#ifdef HAVE_LMDB
|
||||
/* Make sure we can open the NZD database */
|
||||
LOCK(&view->new_zone_lock);
|
||||
result = nzd_open(view, 0, &txn, &dbi);
|
||||
@@ -13742,17 +13382,6 @@ rmzone(void *arg) {
|
||||
(void)nzd_close(&txn, false);
|
||||
}
|
||||
UNLOCK(&view->new_zone_lock);
|
||||
#else /* ifdef HAVE_LMDB */
|
||||
result = delete_zoneconf(view, cfg->add_parser, cfg->nzf_config,
|
||||
dns_zone_getorigin(zone),
|
||||
nzf_writeconf);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
isc_log_write(NAMED_LOGCATEGORY_GENERAL,
|
||||
NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
|
||||
"unable to delete zone configuration: %s",
|
||||
isc_result_totext(result));
|
||||
}
|
||||
#endif /* HAVE_LMDB */
|
||||
}
|
||||
|
||||
if (!added && cfg != NULL) {
|
||||
@@ -14061,9 +13690,7 @@ named_server_showzone(named_server_t *server, isc_lex_t *lex,
|
||||
dns_view_t *view = NULL;
|
||||
dns_zone_t *zone = NULL;
|
||||
ns_cfgctx_t *cfg = NULL;
|
||||
#ifdef HAVE_LMDB
|
||||
cfg_obj_t *nzconfig = NULL;
|
||||
#endif /* HAVE_LMDB */
|
||||
bool added, redirect;
|
||||
ns_dzarg_t dzarg;
|
||||
|
||||
@@ -14103,12 +13730,6 @@ named_server_showzone(named_server_t *server, isc_lex_t *lex,
|
||||
redirect);
|
||||
}
|
||||
|
||||
#ifndef HAVE_LMDB
|
||||
if (zconfig == NULL && cfg->nzf_config != NULL) {
|
||||
zconfig = find_name_in_list_from_map(cfg->nzf_config, "zone",
|
||||
zonename, redirect);
|
||||
}
|
||||
#else /* HAVE_LMDB */
|
||||
if (zconfig == NULL) {
|
||||
const cfg_obj_t *zlist = NULL;
|
||||
CHECK(get_newzone_config(view, zonename, &nzconfig));
|
||||
@@ -14119,7 +13740,6 @@ named_server_showzone(named_server_t *server, isc_lex_t *lex,
|
||||
|
||||
zconfig = cfg_listelt_value(cfg_list_first(zlist));
|
||||
}
|
||||
#endif /* HAVE_LMDB */
|
||||
|
||||
if (zconfig == NULL) {
|
||||
CHECK(ISC_R_NOTFOUND);
|
||||
@@ -14137,11 +13757,10 @@ named_server_showzone(named_server_t *server, isc_lex_t *lex,
|
||||
result = ISC_R_SUCCESS;
|
||||
|
||||
cleanup:
|
||||
#ifdef HAVE_LMDB
|
||||
if (nzconfig != NULL) {
|
||||
cfg_obj_destroy(named_g_addparser, &nzconfig);
|
||||
}
|
||||
#endif /* HAVE_LMDB */
|
||||
|
||||
if (isc_buffer_usedlength(*text) > 0) {
|
||||
(void)putnull(text);
|
||||
}
|
||||
|
||||
+7
-8
@@ -133,14 +133,13 @@ Currently supported commands are:
|
||||
string specified on the command line is the zone configuration text
|
||||
that would ordinarily be placed in :iscman:`named.conf`.
|
||||
|
||||
The configuration is saved in a file called ``viewname.nzf`` (or, if
|
||||
:iscman:`named` is compiled with liblmdb, an LMDB database file called
|
||||
``viewname.nzd``). ``viewname`` is the name of the view, unless the view
|
||||
name contains characters that are incompatible with use as a file
|
||||
name, in which case a cryptographic hash of the view name is used
|
||||
instead. When :iscman:`named` is restarted, the file is loaded into
|
||||
the view configuration so that zones that were added can persist
|
||||
after a restart.
|
||||
The configuration is saved in an LMDB database called
|
||||
``viewname.nzf``. ``viewname`` is the name of the view, unless the
|
||||
view name contains characters that are incompatible with use as a
|
||||
file name, in which case a cryptographic hash of the view name is
|
||||
used instead. When :iscman:`named` is restarted, the file is loaded
|
||||
into the view configuration so that zones that were added can
|
||||
persist after a restart.
|
||||
|
||||
This sample ``addzone`` command adds the zone ``example.com`` to
|
||||
the default view:
|
||||
|
||||
@@ -36,8 +36,8 @@ n=$((n + 1))
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
# When LMDB support is compiled in, this tests that migration from
|
||||
# NZF to NZD occurs during named startup
|
||||
# This tests that migration from NZF to NZD occurs during named
|
||||
# startup
|
||||
echo_i "checking previously added zone ($n)"
|
||||
ret=0
|
||||
$DIG $DIGOPTS @10.53.0.2 a.previous.example a >dig.out.ns2.$n || ret=1
|
||||
@@ -47,13 +47,11 @@ n=$((n + 1))
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
if $FEATURETEST --with-lmdb; then
|
||||
echo_i "checking that existing NZF file was renamed after migration ($n)"
|
||||
[ -e ns2/3bf305731dd26307.nzf~ ] || ret=1
|
||||
n=$((n + 1))
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
fi
|
||||
echo_i "checking that existing NZF file was renamed after migration ($n)"
|
||||
[ -e ns2/3bf305731dd26307.nzf~ ] || ret=1
|
||||
n=$((n + 1))
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "adding new zone ($n)"
|
||||
ret=0
|
||||
@@ -126,16 +124,6 @@ n=$((n + 1))
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
if ! $FEATURETEST --with-lmdb; then
|
||||
echo_i "verifying no comments in NZF file ($n)"
|
||||
ret=0
|
||||
hcount=$(grep "^# New zone file for view: _default" ns2/3bf305731dd26307.nzf | wc -l)
|
||||
[ $hcount -eq 0 ] || ret=1
|
||||
n=$((n + 1))
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
fi
|
||||
|
||||
echo_i "checking rndc showzone with previously added zone ($n)"
|
||||
ret=0
|
||||
$RNDCCMD 10.53.0.2 showzone previous.example >rndc.out.ns2.$n
|
||||
@@ -145,13 +133,11 @@ n=$((n + 1))
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
if $FEATURETEST --with-lmdb; then
|
||||
echo_i "checking zone is present in NZD ($n)"
|
||||
ret=0
|
||||
$NZD2NZF ns2/_default.nzd | grep previous.example >/dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
fi
|
||||
echo_i "checking zone is present in NZD ($n)"
|
||||
ret=0
|
||||
$NZD2NZF ns2/_default.nzd | grep previous.example >/dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "deleting previously added zone ($n)"
|
||||
ret=0
|
||||
@@ -171,22 +157,10 @@ check_nzd2nzf() (
|
||||
&& ! grep previous.example nzd2nzf.out.$n >/dev/null
|
||||
)
|
||||
|
||||
if $FEATURETEST --with-lmdb; then
|
||||
echo_i "checking zone was deleted from NZD ($n)"
|
||||
retry_quiet 10 check_nzd2nzf || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
fi
|
||||
|
||||
if ! $FEATURETEST --with-lmdb; then
|
||||
echo_i "checking NZF file now has comment ($n)"
|
||||
ret=0
|
||||
hcount=$(grep "^# New zone file for view: _default" ns2/3bf305731dd26307.nzf | wc -l)
|
||||
[ $hcount -eq 1 ] || ret=1
|
||||
n=$((n + 1))
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
fi
|
||||
echo_i "checking zone was deleted from NZD ($n)"
|
||||
retry_quiet 10 check_nzd2nzf || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "deleting newly added zone added.example ($n)"
|
||||
ret=0
|
||||
@@ -508,24 +482,12 @@ n=$((n + 1))
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
if ! $FEATURETEST --with-lmdb; then
|
||||
echo_i "checking new NZF file has comment ($n)"
|
||||
ret=0
|
||||
hcount=$(grep "^# New zone file for view: external" ns2/external.nzf | wc -l)
|
||||
[ $hcount -eq 1 ] || ret=1
|
||||
n=$((n + 1))
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
fi
|
||||
|
||||
if $FEATURETEST --with-lmdb; then
|
||||
echo_i "verifying added.example in external view created an external.nzd DB ($n)"
|
||||
ret=0
|
||||
[ -e ns2/external.nzd ] || ret=1
|
||||
n=$((n + 1))
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
fi
|
||||
echo_i "verifying added.example in external view created an external.nzd DB ($n)"
|
||||
ret=0
|
||||
[ -e ns2/external.nzd ] || ret=1
|
||||
n=$((n + 1))
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "checking rndc reload causes named to reload the external view's new zone config ($n)"
|
||||
ret=0
|
||||
@@ -544,11 +506,7 @@ status=$((status + ret))
|
||||
|
||||
echo_i "checking rndc showzone with newly added zone ($n)"
|
||||
_check_rndc_showzone_newly_added() (
|
||||
if ! $FEATURETEST --with-lmdb; then
|
||||
expected='zone "added.example" in external { type primary; file "added.db"; };'
|
||||
else
|
||||
expected='zone "added.example" { type primary; file "added.db"; };'
|
||||
fi
|
||||
expected='zone "added.example" { type primary; file "added.db"; };'
|
||||
$RNDCCMD 10.53.0.2 showzone added.example in external >rndc.out.ns2.$n 2>/dev/null \
|
||||
&& [ "$(cat rndc.out.ns2.$n)" = "$expected" ]
|
||||
)
|
||||
@@ -651,13 +609,8 @@ n=$((n + 1))
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
if $FEATURETEST --with-lmdb; then
|
||||
echo_i "checking NZD file was created in new-zones-directory ($n)"
|
||||
expect=ns2/new-zones/directory.nzd
|
||||
else
|
||||
echo_i "checking NZF file was created in new-zones-directory ($n)"
|
||||
expect=ns2/new-zones/directory.nzf
|
||||
fi
|
||||
echo_i "checking NZD file was created in new-zones-directory ($n)"
|
||||
expect=ns2/new-zones/directory.nzd
|
||||
$RNDCCMD 10.53.0.2 sync 'added.example IN directory' 2>&1 | sed 's/^/I:ns2 /'
|
||||
sleep 2
|
||||
[ -e "$expect" ] || ret=1
|
||||
@@ -693,19 +646,6 @@ n=$((n + 1))
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
if ! $FEATURETEST --with-lmdb; then
|
||||
echo_i "check that addzone is fully reversed on failure (--with-lmdb=no) ($n)"
|
||||
ret=0
|
||||
$RNDCCMD 10.53.0.3 addzone "test1.baz" '{ type primary; file "e.db"; };' >/dev/null 2>&1 || ret=1
|
||||
$RNDCCMD 10.53.0.3 addzone "test2.baz" '{ type primary; file "dne.db"; };' >/dev/null 2>&1 && ret=1
|
||||
$RNDCCMD 10.53.0.3 addzone "test3.baz" '{ type primary; file "e.db"; };' >/dev/null 2>&1 || ret=1
|
||||
$RNDCCMD 10.53.0.3 delzone "test3.baz" >/dev/null 2>&1 || ret=1
|
||||
grep test2.baz ns3/_default.nzf >/dev/null && ret=1
|
||||
n=$((n + 1))
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
fi
|
||||
|
||||
_check_version_bind() (
|
||||
$DIG $DIGOPTS @10.53.0.3 version.bind txt ch >dig.out.test$n \
|
||||
&& grep "status: NOERROR" dig.out.test$n >/dev/null
|
||||
|
||||
@@ -54,7 +54,6 @@ usage(void) {
|
||||
fprintf(stderr, "\t--tsan\n");
|
||||
fprintf(stderr, "\t--with-dlz-filesystem\n");
|
||||
fprintf(stderr, "\t--with-libidn2\n");
|
||||
fprintf(stderr, "\t--with-lmdb\n");
|
||||
fprintf(stderr, "\t--with-libnghttp2\n");
|
||||
fprintf(stderr, "\t--with-zlib\n");
|
||||
}
|
||||
@@ -238,14 +237,6 @@ main(int argc, char **argv) {
|
||||
#endif /* ifdef HAVE_LIBIDN2 */
|
||||
}
|
||||
|
||||
if (strcmp(argv[1], "--with-lmdb") == 0) {
|
||||
#ifdef HAVE_LMDB
|
||||
return 0;
|
||||
#else /* ifdef HAVE_LMDB */
|
||||
return 1;
|
||||
#endif /* ifdef HAVE_LMDB */
|
||||
}
|
||||
|
||||
if (strcmp(argv[1], "--with-libnghttp2") == 0) {
|
||||
#ifdef HAVE_LIBNGHTTP2
|
||||
return 0;
|
||||
|
||||
@@ -13,8 +13,5 @@
|
||||
|
||||
. ../conf.sh
|
||||
|
||||
$FEATURETEST --with-lmdb || {
|
||||
echo_i "This test requires LMDB support." >&2
|
||||
exit 255
|
||||
}
|
||||
$FEATURETEST
|
||||
exit 0
|
||||
|
||||
+6
-17
@@ -2,18 +2,21 @@ include $(top_srcdir)/Makefile.top
|
||||
|
||||
AM_CPPFLAGS += \
|
||||
$(LIBISC_CFLAGS) \
|
||||
$(LIBDNS_CFLAGS)
|
||||
$(LIBDNS_CFLAGS) \
|
||||
$(LMDB_CFLAGS)
|
||||
|
||||
LDADD += \
|
||||
$(LIBDNS_LIBS) \
|
||||
$(LIBISC_LIBS)
|
||||
$(LIBISC_LIBS) \
|
||||
$(LMDB_LIBS)
|
||||
|
||||
bin_PROGRAMS = \
|
||||
arpaname \
|
||||
mdig \
|
||||
named-journalprint \
|
||||
named-rrchecker \
|
||||
nsec3hash
|
||||
nsec3hash \
|
||||
named-nzd2nzf
|
||||
|
||||
arpaname_LDADD = \
|
||||
$(LIBISC_LIBS)
|
||||
@@ -32,17 +35,3 @@ dnstap_read_LDADD = \
|
||||
$(LIBISC_LIBS) \
|
||||
$(DNSTAP_LIBS)
|
||||
endif
|
||||
|
||||
if HAVE_LMDB
|
||||
bin_PROGRAMS += \
|
||||
named-nzd2nzf
|
||||
|
||||
named_nzd2nzf_CPPFLAGS = \
|
||||
$(AM_CPPFLAGS) \
|
||||
$(LMDB_CFLAGS)
|
||||
|
||||
named_nzd2nzf_LDADD = \
|
||||
$(LIBISC_LIBS) \
|
||||
$(LMDB_LIBS)
|
||||
|
||||
endif
|
||||
|
||||
@@ -11,14 +11,11 @@
|
||||
* information regarding copyright ownership.
|
||||
*/
|
||||
|
||||
#ifndef HAVE_LMDB
|
||||
#error This program requires the LMDB library.
|
||||
#endif /* ifndef HAVE_LMDB */
|
||||
|
||||
#include <lmdb.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <isc/lmdb.h>
|
||||
|
||||
#include <dns/view.h>
|
||||
|
||||
int
|
||||
|
||||
+7
-37
@@ -749,43 +749,13 @@ AC_SUBST([KRB5_CFLAGS])
|
||||
AC_SUBST([KRB5_LIBS])
|
||||
|
||||
#
|
||||
# was --with-lmdb specified?
|
||||
# LMDB is mandatory
|
||||
#
|
||||
PKG_CHECK_MODULES([LMDB], [lmdb])
|
||||
AX_SAVE_FLAGS([lmdb])
|
||||
|
||||
# [pairwise: --with-lmdb=auto, --with-lmdb=yes, --without-lmdb]
|
||||
AC_ARG_WITH([lmdb],
|
||||
[AS_HELP_STRING([--with-lmdb=@<:@PATH@:>@],
|
||||
[use LMDB library @<:@default=auto@:>@, optionally specify the prefix for lmdb library])],
|
||||
[:],
|
||||
[with_lmdb="auto"])
|
||||
|
||||
ac_lib_lmdb_found=no
|
||||
AS_CASE([$with_lmdb],
|
||||
[no],[],
|
||||
[auto|yes], [PKG_CHECK_MODULES([LMDB], [lmdb],
|
||||
[ac_lib_lmdb_found=yes],
|
||||
[for ac_lib_lmdb_path in /usr /usr/local /opt /opt/local; do
|
||||
AX_LIB_LMDB([$ac_lib_lmdb_path],
|
||||
[ac_lib_lmdb_found=yes
|
||||
break])
|
||||
done
|
||||
])],
|
||||
[AX_LIB_LMDB([$with_lmdb],[ac_lib_lmdb_found=yes])])
|
||||
|
||||
# don't fail when in automatic mode
|
||||
AS_IF([test "$with_lmdb" = "auto" && test "$ac_lib_lmdb_found" = "no"],
|
||||
[with_lmdb=no])
|
||||
|
||||
# hard fail when LMDB requested, but not found
|
||||
AS_IF([test "$with_lmdb" != "no" && test "$ac_lib_lmdb_found" != "yes"],
|
||||
[AC_MSG_ERROR([LMDB requested, but not found])])
|
||||
|
||||
AS_IF([test "$ac_lib_lmdb_found" = "yes"],
|
||||
[AC_DEFINE([HAVE_LMDB], [1], [Use lmdb library])])
|
||||
|
||||
AC_SUBST([LMDB_CFLAGS])
|
||||
AC_SUBST([LMDB_LIBS])
|
||||
AM_CONDITIONAL([HAVE_LMDB], [test -n "$LMDB_LIBS"])
|
||||
CFLAGS="$CFLAGS $LMDB_CFLAGS"
|
||||
LIBS="$LIBS $LMDB_LIBS"
|
||||
|
||||
#
|
||||
# was --with-libxml2 specified?
|
||||
@@ -1512,7 +1482,7 @@ report() {
|
||||
test -z "$LIBXML2_LIBS" || echo " XML statistics (--with-libxml2)"
|
||||
test -z "$JSON_C_LIBS" || echo " JSON statistics (--with-json-c): $JSON_C_CFLAGS $JSON_C_LIBS"
|
||||
test -z "$ZLIB_LIBS" || echo " HTTP zlib compression (--with-zlib)"
|
||||
test -z "$LMDB_LIBS" || echo " LMDB database to store configuration for 'addzone' zones (--with-lmdb)"
|
||||
test -z "$LMDB_LIBS" || echo " LMDB database"
|
||||
test -z "$LIBIDN2_LIBS" || echo " IDN support (--with-libidn2)"
|
||||
fi
|
||||
|
||||
@@ -1574,7 +1544,7 @@ report() {
|
||||
test -z "$LIBXML2_LIBS" && echo " XML statistics (--with-libxml2)"
|
||||
test -z "$JSON_C_LIBS" && echo " JSON statistics (--with-json-c)"
|
||||
test -z "$ZLIB_LIBS" && echo " HTTP zlib compression (--with-zlib)"
|
||||
test -z "$LMDB_LIBS" && echo " LMDB database to store configuration for 'addzone' zones (--with-lmdb)"
|
||||
test -z "$LMDB_LIBS" && echo " LMDB database"
|
||||
test -z "$LIBIDN2_LIBS" && echo " IDN support (--with-libidn2)"
|
||||
|
||||
echo "-------------------------------------------------------------------------------"
|
||||
|
||||
@@ -116,11 +116,6 @@ be linked against ``zlib`` (https://zlib.net/). If this is installed in
|
||||
a nonstandard location, specify the prefix using
|
||||
``--with-zlib=/prefix``.
|
||||
|
||||
To support storing configuration data for runtime-added zones in an LMDB
|
||||
database, the server must be linked with ``liblmdb``
|
||||
(https://github.com/LMDB/lmdb). If this is installed in a nonstandard
|
||||
location, specify the prefix using ``--with-lmdb=/prefix``.
|
||||
|
||||
To support MaxMind GeoIP2 location-based ACLs, the server must be linked
|
||||
with ``libmaxminddb`` (https://maxmind.github.io/libmaxminddb/). This is
|
||||
turned on by default if the library is found; if the library is
|
||||
|
||||
+13
-16
@@ -1360,12 +1360,11 @@ default is used.
|
||||
:tags: server
|
||||
:short: Sets a maximum size for the memory map of the new-zone database in LMDB database format.
|
||||
|
||||
When :iscman:`named` is built with liblmdb, this option sets a maximum size
|
||||
for the memory map of the new-zone database (NZD) in LMDB database
|
||||
format. This database is used to store configuration information for
|
||||
zones added using :option:`rndc addzone`. Note that this is not the NZD
|
||||
database file size, but the largest size that the database may grow
|
||||
to.
|
||||
This option sets a maximum size for the memory map of the new-zone
|
||||
database (NZD) in LMDB database format. This database is used to
|
||||
store configuration information for zones added using :option:`rndc
|
||||
addzone`. Note that this is not the NZD database file size, but the
|
||||
largest size that the database may grow to.
|
||||
|
||||
Because the database file is memory-mapped, its size is limited by
|
||||
the address space of the :iscman:`named` process. The default of 32 megabytes
|
||||
@@ -1994,17 +1993,15 @@ Boolean Options
|
||||
|
||||
Newly added zones' configuration parameters are stored so that they
|
||||
can persist after the server is restarted. The configuration
|
||||
information is saved in a file called ``viewname.nzf`` (or, if
|
||||
:iscman:`named` is compiled with liblmdb, in an LMDB database file called
|
||||
``viewname.nzd``). "viewname" is the name of the view, unless the view
|
||||
name contains characters that are incompatible with use as a file
|
||||
name, in which case a cryptographic hash of the view name is used
|
||||
instead.
|
||||
information is saved in an LMDB database file called
|
||||
``viewname.nzf``. "viewname" is the name of the view, unless the
|
||||
view name contains characters that are incompatible with use as a
|
||||
file name, in which case a cryptographic hash of the view name is
|
||||
used instead.
|
||||
|
||||
Configurations for zones added at runtime are stored either in
|
||||
a new-zone file (NZF) or a new-zone database (NZD), depending on
|
||||
whether :iscman:`named` was linked with liblmdb at compile time. See
|
||||
:ref:`man_rndc` for further details about :option:`rndc addzone`.
|
||||
Configurations for zones added at runtime are stored either in a
|
||||
new-zone database (NZD). See :ref:`man_rndc` for further details
|
||||
about :option:`rndc addzone`.
|
||||
|
||||
.. namedconf:statement:: auth-nxdomain
|
||||
:tags: query
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
|
||||
## cfgmgr
|
||||
|
||||
### Concepts
|
||||
|
||||
cfgmgr is a library to (in-memory) store and to interact with
|
||||
configuration data in a hierarchical way. A configuration clause can
|
||||
be seen as a "box" holding properties accessible by name. A property
|
||||
can be of various types (boolean, unsigned integer, string, sockaddr,
|
||||
etc.) as well as a clause, making possible to have nested
|
||||
clauses. Furthermore, a clause can be repeated: it is possible to have
|
||||
several clauses of the same name (or "several instances") at the same
|
||||
level (i.e. in the same parent clause).
|
||||
|
||||
### API
|
||||
|
||||
Public API usage and concepts are documented in
|
||||
lib/isc/include/isc/cfgmgr.h. The API is thread-safe and transaction
|
||||
based. In order to access to a clause, it must be opened (which starts
|
||||
a read or read-write transaction) and eventually closed (which commit
|
||||
the transaction, if read-write). Changes made during a transaction are
|
||||
visible by the current thread only. Opening nested clauses doesn't
|
||||
create sub-transactions: there is only one transaction at the time for
|
||||
a given thread opening a top-level clause.
|
||||
|
||||
### Implementation
|
||||
|
||||
cfgmgr is essentially a wrapper abstracting LMDB, which is providing
|
||||
the storage, thread-safe and transactional access. Because LMDB also
|
||||
write data on disk and cfgmgr intents to be in-memory only, the LMDB
|
||||
files are immediately deleted during the initialization of cfgmgr.
|
||||
|
||||
#### Nested and repeatable clauses
|
||||
|
||||
LDBM itself is "flat": it stores key-values associations. In order to
|
||||
model nested clauses, cfgmgr dynamically build the key for each value
|
||||
using a namespace dot based notation. For example:
|
||||
|
||||
```
|
||||
foo {
|
||||
bar {
|
||||
gee: 55;
|
||||
};
|
||||
baz: "abc";
|
||||
};
|
||||
```
|
||||
|
||||
is implemented with the following key-values:
|
||||
|
||||
```
|
||||
foo.XXX.bar.YYY.gee: 55
|
||||
foo.XXX.baz: "abc"
|
||||
```
|
||||
|
||||
The "XXX" and "YYY" are random numbers identifying a specific clause
|
||||
instance. Let's make the configuration a bit more complex:
|
||||
|
||||
```
|
||||
foo {
|
||||
bar {
|
||||
gee: 55;
|
||||
};
|
||||
bar {
|
||||
gee: 234;
|
||||
};
|
||||
baz: "abc";
|
||||
};
|
||||
```
|
||||
|
||||
This is implemented with the following key-values:
|
||||
|
||||
```
|
||||
foo.XXX.bar.YYY.gee: 55
|
||||
foo.XXX.bar.ZZZ.gee: 234
|
||||
foo.XXX.baz: "abc"
|
||||
```
|
||||
|
||||
This makes it easy to differentiate between the two instances of the
|
||||
"bar" clause: one is "bar.YYY" while the other is "bar.ZZZ".
|
||||
|
||||
The reason a number is used (instead of a name or something else) is
|
||||
because LMDB sort the key in a lexicographical way. Also, given a key,
|
||||
LMDB is able to give the first key which is equal or bigger. This is
|
||||
the crux of how cfgmgr can jump to the next instance of a same clause
|
||||
(function `isc_cfgmgr_nextclause`) in one lookup. It takes the key of
|
||||
the current clause, increments it by one (which is way simpler than
|
||||
incrementing a "string by one"), and asks LMDB to give it back the
|
||||
next key equal or bigger than the new one. Example:
|
||||
|
||||
- current clause is `foo.1234`
|
||||
- cfgmgr then creates the key prefix `foo.1235.`
|
||||
- LMDB gives the first key bigger or equals to `foo.1235.`
|
||||
|
||||
No matter what's inside `foo.1234` (so no matter the keys starting by
|
||||
`foo.1234.`, LMDB returns the first key starting by _at least_
|
||||
`foo.1235`, which would be the first key of the next instance of the
|
||||
same clause. (If there is no other "foo" clause, nothing is found).
|
||||
|
||||
### List properties
|
||||
|
||||
named configuration format supports properties holding a list of
|
||||
values. cfgmgr approach to implement lists uses the fact that LMDB
|
||||
allows duplicate keys and it stores a newly duplicated key after the
|
||||
previous ones. A clause "foo" with a property "bar" holding a list of
|
||||
integers is implemented by cfgmgr in the following way in LMDB:
|
||||
|
||||
```
|
||||
foo.XXX.bar: 23
|
||||
foo.XXX.bar: 43
|
||||
```
|
||||
|
||||
(Note there would be another way as LMDB enables to store several
|
||||
values for a given key, but it's not flexible enough for cfgmgr: it
|
||||
requires providing ahead the size used by all the values. It means
|
||||
that adding a new list element would require to allocate a buffer of
|
||||
the existing size plus the size of the new element, delete the
|
||||
existing key-value from LMDB, and add it again. Dup keys seems to be a
|
||||
better fit here.)
|
||||
|
||||
### Inheritance (proposal/speculative)
|
||||
|
||||
Some named configuration clauses have a concept of inheritance. For
|
||||
instance, if a looked-up property is not found in a "view" clause, the
|
||||
property will be looked-up in the "options" clause. cfgmgr currently
|
||||
doesn't support inheritance, so the following is an API/implementation
|
||||
proposal to support-ish it:
|
||||
|
||||
- a new API `isc_cfgmgr_fallback("A")` could be called while a clause
|
||||
"B" is opened. Then, cfgmgr would know that if a looked-up property
|
||||
of a clause "B" is not found, then it would internally and
|
||||
synchronously try to lookup the same property from within the clause
|
||||
"A". So this can be seen as "B inherits A properties" from the
|
||||
caller perspective. This API could be used once-per clause (no
|
||||
"multiple inheritance"), and it would be possible to break this
|
||||
relation if needed by calling `isc_cfgmgr_fallback(NULL)`.
|
||||
|
||||
- internally, `isc_cfgmgr_fallback` would create a new LMDB key-value
|
||||
`B.0.fallback: "A"`. So when cfgmgr opens a clause it can figure out
|
||||
if it needs to lookup for another clause in case a looked-up
|
||||
property is not found.
|
||||
|
||||
- From performance perspective in would make one extra LMDB lookup
|
||||
only if a property name is not found for a clause "B" (so this is
|
||||
likely acceptable). (And of course one extra lookup when opening a
|
||||
clause to find the "clausename.0.fallback" key, but again, that's
|
||||
acceptable.)
|
||||
|
||||
- limitation 1: if "B" has multiple instances, only the first instance
|
||||
values would be used. (Which I think is fine for named use case:
|
||||
"options" is not a repeatable clause).
|
||||
|
||||
- limitation 2: There won't be any check to make sure "A" actually
|
||||
exists when `isc_cfgmgr_fallback` is called, so can be done at any
|
||||
tine even if "A" doesn't exists yet. It makes initialization time
|
||||
order-independent (so actually not really a limitation), and more
|
||||
importantly it avoids falling into a rabbit hole, for instance, to
|
||||
make sure that if we delete "A" it would remove all the
|
||||
`"other-clause-names.0.fallback: "A"` keys. This is actually the
|
||||
reason I want to call it "fallback" in the API and not
|
||||
"inheritance". It's basically just a "second-chance" if a value is
|
||||
not found, but nothing more.
|
||||
+1
-5
@@ -100,6 +100,7 @@ man_MANS = \
|
||||
named-checkzone.1 \
|
||||
named-compilezone.1 \
|
||||
named-journalprint.1 \
|
||||
named-nzd2nzf.1 \
|
||||
named.8 \
|
||||
nsec3hash.1 \
|
||||
rndc-confgen.8 \
|
||||
@@ -111,11 +112,6 @@ man_MANS += \
|
||||
dnstap-read.1
|
||||
endif HAVE_DNSTAP
|
||||
|
||||
if HAVE_LMDB
|
||||
man_MANS += \
|
||||
named-nzd2nzf.1
|
||||
endif HAVE_LMDB
|
||||
|
||||
MANPAGES_IN = \
|
||||
$(man_MANS:=in) \
|
||||
dnstap-read.1in \
|
||||
|
||||
+2
-2
@@ -165,7 +165,7 @@ options {
|
||||
lame-ttl <duration>;
|
||||
listen-on [ port <integer> ] [ proxy <string> ] [ tls <string> ] [ http <string> ] { <address_match_element>; ... }; // may occur multiple times
|
||||
listen-on-v6 [ port <integer> ] [ proxy <string> ] [ tls <string> ] [ http <string> ] { <address_match_element>; ... }; // may occur multiple times
|
||||
lmdb-mapsize <sizeval>; // optional (only available if configured)
|
||||
lmdb-mapsize <sizeval>;
|
||||
managed-keys-directory <quoted_string>;
|
||||
masterfile-format ( raw | text );
|
||||
masterfile-style ( full | relative );
|
||||
@@ -449,7 +449,7 @@ view <string> [ <class> ] {
|
||||
}; // may occur multiple times
|
||||
key-directory <quoted_string>;
|
||||
lame-ttl <duration>;
|
||||
lmdb-mapsize <sizeval>; // optional (only available if configured)
|
||||
lmdb-mapsize <sizeval>;
|
||||
masterfile-format ( raw | text );
|
||||
masterfile-style ( full | relative );
|
||||
match-clients { <address_match_element>; ... };
|
||||
|
||||
+2
-5
@@ -267,6 +267,7 @@ libdns_la_CPPFLAGS = \
|
||||
$(LIBISC_CFLAGS) \
|
||||
$(LIBURCU_CFLAGS) \
|
||||
$(LIBUV_CFLAGS) \
|
||||
$(LMDB_CFLAGS) \
|
||||
$(OPENSSL_CFLAGS)
|
||||
|
||||
libdns_la_LDFLAGS = \
|
||||
@@ -277,6 +278,7 @@ libdns_la_LIBADD = \
|
||||
$(LIBISC_LIBS) \
|
||||
$(LIBURCU_LIBS) \
|
||||
$(LIBUV_LIBS) \
|
||||
$(LMDB_LIBS) \
|
||||
$(OPENSSL_LIBS)
|
||||
|
||||
if HAVE_JSON_C
|
||||
@@ -326,11 +328,6 @@ libdns_la_CPPFLAGS += $(DNSTAP_CFLAGS)
|
||||
libdns_la_LIBADD += $(DNSTAP_LIBS)
|
||||
endif
|
||||
|
||||
if HAVE_LMDB
|
||||
libdns_la_CPPFLAGS += $(LMDB_CFLAGS)
|
||||
libdns_la_LIBADD += $(LMDB_LIBS)
|
||||
endif
|
||||
|
||||
if !HAVE_SYSTEMTAP
|
||||
DTRACE_DEPS = libdns_la-xfrin.lo
|
||||
DTRACE_OBJS = .libs/libdns_la-xfrin.$(OBJEXT)
|
||||
|
||||
@@ -249,19 +249,6 @@ struct dns_view {
|
||||
#define DNS_VIEWATTR_ADBSHUTDOWN 0x02
|
||||
#define DNS_VIEWATTR_REQSHUTDOWN 0x04
|
||||
|
||||
#ifdef HAVE_LMDB
|
||||
#define DNS_LMDB_COMMON_FLAGS (MDB_CREATE | MDB_NOSUBDIR | MDB_NOLOCK)
|
||||
#ifndef __OpenBSD__
|
||||
#define DNS_LMDB_FLAGS (DNS_LMDB_COMMON_FLAGS)
|
||||
#else /* __OpenBSD__ */
|
||||
/*
|
||||
* OpenBSD does not have a unified buffer cache, which requires both reads and
|
||||
* writes to be performed using mmap().
|
||||
*/
|
||||
#define DNS_LMDB_FLAGS (DNS_LMDB_COMMON_FLAGS | MDB_WRITEMAP)
|
||||
#endif /* __OpenBSD__ */
|
||||
#endif /* HAVE_LMDB */
|
||||
|
||||
isc_result_t
|
||||
dns_view_create(isc_mem_t *mctx, isc_loopmgr_t *loopmgr,
|
||||
dns_dispatchmgr_t *dispmgr, dns_rdataclass_t rdclass,
|
||||
|
||||
+2
-18
@@ -17,15 +17,12 @@
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef HAVE_LMDB
|
||||
#include <lmdb.h>
|
||||
#endif /* ifdef HAVE_LMDB */
|
||||
|
||||
#include <isc/atomic.h>
|
||||
#include <isc/dir.h>
|
||||
#include <isc/file.h>
|
||||
#include <isc/hash.h>
|
||||
#include <isc/lex.h>
|
||||
#include <isc/lmdb.h>
|
||||
#include <isc/md.h>
|
||||
#include <isc/result.h>
|
||||
#include <isc/stats.h>
|
||||
@@ -392,7 +389,6 @@ destroy(dns_view_t *view) {
|
||||
isc_mem_free(view->mctx, view->new_zone_dir);
|
||||
view->new_zone_dir = NULL;
|
||||
}
|
||||
#ifdef HAVE_LMDB
|
||||
if (view->new_zone_dbenv != NULL) {
|
||||
mdb_env_close((MDB_env *)view->new_zone_dbenv);
|
||||
view->new_zone_dbenv = NULL;
|
||||
@@ -401,7 +397,6 @@ destroy(dns_view_t *view) {
|
||||
isc_mem_free(view->mctx, view->new_zone_db);
|
||||
view->new_zone_db = NULL;
|
||||
}
|
||||
#endif /* HAVE_LMDB */
|
||||
dns_fwdtable_destroy(&view->fwdtable);
|
||||
dns_aclenv_detach(&view->aclenv);
|
||||
if (view->failcache != NULL) {
|
||||
@@ -1787,14 +1782,8 @@ 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);
|
||||
@@ -1804,7 +1793,6 @@ dns_view_setnewzones(dns_view_t *view, bool allow, void *cfgctx,
|
||||
view->new_zone_file = NULL;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LMDB
|
||||
if (view->new_zone_dbenv != NULL) {
|
||||
mdb_env_close((MDB_env *)view->new_zone_dbenv);
|
||||
view->new_zone_dbenv = NULL;
|
||||
@@ -1814,7 +1802,6 @@ dns_view_setnewzones(dns_view_t *view, bool allow, void *cfgctx,
|
||||
isc_mem_free(view->mctx, view->new_zone_db);
|
||||
view->new_zone_db = NULL;
|
||||
}
|
||||
#endif /* HAVE_LMDB */
|
||||
|
||||
if (view->new_zone_config != NULL) {
|
||||
view->cfg_destroy(&view->new_zone_config);
|
||||
@@ -1830,7 +1817,6 @@ dns_view_setnewzones(dns_view_t *view, bool allow, void *cfgctx,
|
||||
|
||||
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)));
|
||||
|
||||
@@ -1866,7 +1852,6 @@ dns_view_setnewzones(dns_view_t *view, bool allow, void *cfgctx,
|
||||
|
||||
view->new_zone_dbenv = env;
|
||||
env = NULL;
|
||||
#endif /* HAVE_LMDB */
|
||||
|
||||
view->new_zone_config = cfgctx;
|
||||
view->cfg_destroy = cfg_destroy;
|
||||
@@ -1878,7 +1863,6 @@ cleanup:
|
||||
view->new_zone_file = NULL;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LMDB
|
||||
if (view->new_zone_db != NULL) {
|
||||
isc_mem_free(view->mctx, view->new_zone_db);
|
||||
view->new_zone_db = NULL;
|
||||
@@ -1886,7 +1870,7 @@ cleanup:
|
||||
if (env != NULL) {
|
||||
mdb_env_close(env);
|
||||
}
|
||||
#endif /* HAVE_LMDB */
|
||||
|
||||
view->new_zone_config = NULL;
|
||||
view->cfg_destroy = NULL;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ libisc_la_HEADERS = \
|
||||
include/isc/base32.h \
|
||||
include/isc/base64.h \
|
||||
include/isc/buffer.h \
|
||||
include/isc/cfgmgr.h \
|
||||
include/isc/commandline.h \
|
||||
include/isc/condition.h \
|
||||
include/isc/counter.h \
|
||||
@@ -44,6 +45,7 @@ libisc_la_HEADERS = \
|
||||
include/isc/lang.h \
|
||||
include/isc/lex.h \
|
||||
include/isc/list.h \
|
||||
include/isc/lmdb.h \
|
||||
include/isc/log.h \
|
||||
include/isc/loop.h \
|
||||
include/isc/magic.h \
|
||||
@@ -122,6 +124,7 @@ libisc_la_SOURCES = \
|
||||
backtrace.c \
|
||||
base32.c \
|
||||
base64.c \
|
||||
cfgmgr.c \
|
||||
commandline.c \
|
||||
condition.c \
|
||||
counter.c \
|
||||
@@ -214,6 +217,7 @@ libisc_la_CPPFLAGS = \
|
||||
$(AM_CPPFLAGS) \
|
||||
$(LIBISC_CFLAGS) \
|
||||
$(LIBUV_CFLAGS) \
|
||||
$(LMDB_CFLAGS) \
|
||||
$(OPENSSL_CFLAGS) \
|
||||
$(ZLIB_CFLAGS)
|
||||
|
||||
@@ -223,6 +227,7 @@ libisc_la_LDFLAGS = \
|
||||
|
||||
libisc_la_LIBADD = \
|
||||
$(LIBUV_LIBS) \
|
||||
$(LMDB_LIBS) \
|
||||
$(OPENSSL_LIBS) \
|
||||
$(ZLIB_LIBS)
|
||||
|
||||
|
||||
@@ -0,0 +1,765 @@
|
||||
/*
|
||||
* 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 <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <isc/cfgmgr.h>
|
||||
#include <isc/list.h>
|
||||
#include <isc/lmdb.h>
|
||||
#include <isc/mem.h>
|
||||
#include <isc/random.h>
|
||||
#include <isc/rwlock.h>
|
||||
#include <isc/thread.h>
|
||||
#include <isc/util.h>
|
||||
|
||||
/*
|
||||
* See MDB_MAXKEYSIZE documentation, but not accessible as defined in
|
||||
* internal implementation. Having key with longer size won't work
|
||||
* with LMDB. The value is 511 by default.
|
||||
*/
|
||||
#define BUFLEN 511
|
||||
|
||||
typedef struct openedclause openedclause_t;
|
||||
struct openedclause {
|
||||
char *name;
|
||||
uint32_t id;
|
||||
ISC_LINK(openedclause_t) link;
|
||||
};
|
||||
typedef ISC_LIST(openedclause_t) openedclauses_t;
|
||||
|
||||
typedef struct {
|
||||
openedclauses_t openedclauses;
|
||||
char *prefix;
|
||||
char *buffer;
|
||||
MDB_cursor *cursor;
|
||||
MDB_txn *txn;
|
||||
bool readonly;
|
||||
} context_t;
|
||||
|
||||
static isc_rwlock_t isc__cfgmgr_lock;
|
||||
static isc_mem_t *isc__cfgmgr_mctx = NULL;
|
||||
static MDB_env *isc__cfgmgr_env = NULL;
|
||||
static thread_local context_t isc__cfgmgr_ctx =
|
||||
(context_t){ .openedclauses = ISC_LIST_INITIALIZER,
|
||||
.prefix = NULL,
|
||||
.buffer = NULL,
|
||||
.cursor = NULL,
|
||||
.txn = NULL,
|
||||
.readonly = false };
|
||||
|
||||
static unsigned long
|
||||
isc__cfgmgr_parseid(const char *dbkey) {
|
||||
uint32_t id = 0;
|
||||
size_t idstarts;
|
||||
size_t idends;
|
||||
size_t keylen;
|
||||
|
||||
REQUIRE(isc__cfgmgr_ctx.buffer != NULL);
|
||||
REQUIRE(dbkey != NULL);
|
||||
|
||||
/*
|
||||
* starts checking after the prefix dot delimiter, i.e. if
|
||||
* prefix is "foo" then the key will be "foo.1235..." so the
|
||||
* start of the id (character 1) is at character index 4
|
||||
*/
|
||||
idstarts = strlen(isc__cfgmgr_ctx.buffer);
|
||||
INSIST(idstarts > 0 && isc__cfgmgr_ctx.buffer[idstarts - 1] == '.');
|
||||
idends = idstarts;
|
||||
|
||||
/*
|
||||
* Cutting the key form the dot after the identifier
|
||||
*/
|
||||
keylen = strlen(dbkey);
|
||||
REQUIRE(keylen > idends);
|
||||
while (dbkey[idends] != '.') {
|
||||
idends++;
|
||||
INSIST(keylen > idends);
|
||||
}
|
||||
|
||||
/*
|
||||
* strtoul stops as soon as it doesn't encounter a non-digit
|
||||
* number, so no need to get an extra buffer, copy the dbkey
|
||||
* and add a null byte after the last digit.
|
||||
*/
|
||||
id = strtoul(dbkey + idstarts, NULL, 10);
|
||||
ENSURE(id > 0);
|
||||
return id;
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
isc_cfgmgr_init(isc_mem_t *mctx, const char *dbpath) {
|
||||
int result = ISC_R_SUCCESS;
|
||||
char dbname[BUFLEN];
|
||||
char dblockname[BUFLEN];
|
||||
uint32_t random;
|
||||
|
||||
REQUIRE(ISC_LIST_EMPTY(isc__cfgmgr_ctx.openedclauses));
|
||||
REQUIRE(isc__cfgmgr_ctx.prefix == NULL);
|
||||
REQUIRE(isc__cfgmgr_ctx.buffer == NULL);
|
||||
REQUIRE(isc__cfgmgr_ctx.cursor == NULL);
|
||||
REQUIRE(isc__cfgmgr_ctx.txn == NULL);
|
||||
REQUIRE(isc__cfgmgr_mctx == NULL);
|
||||
REQUIRE(isc__cfgmgr_env == NULL);
|
||||
REQUIRE(mctx != NULL);
|
||||
REQUIRE(dbpath != NULL);
|
||||
|
||||
isc_rwlock_init(&isc__cfgmgr_lock);
|
||||
|
||||
isc_mem_attach(mctx, &isc__cfgmgr_mctx);
|
||||
INSIST(isc__cfgmgr_mctx != NULL);
|
||||
|
||||
result = mdb_env_create(&isc__cfgmgr_env);
|
||||
if (result != 0) {
|
||||
result = ISC_R_FAILURE;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
INSIST(isc__cfgmgr_env != NULL);
|
||||
random = isc_random32();
|
||||
|
||||
/*
|
||||
* Because LMDB also create a <dbname>-lock file, we also need
|
||||
* to build it (even if not passed to LMDB itself) so we can
|
||||
* immediately delete it
|
||||
*/
|
||||
REQUIRE(snprintf(dbname, BUFLEN, "%s-%" PRIu32, dbpath, random) <
|
||||
BUFLEN);
|
||||
REQUIRE(snprintf(dblockname, BUFLEN, "%s-%" PRIu32 "-lock", dbpath,
|
||||
random) < BUFLEN);
|
||||
|
||||
/*
|
||||
* Using MDB_NOSYNC as it avoids force disk flush after a
|
||||
* transaction. It's quicker and in our case we don't need it
|
||||
* as we delete the only link to the inode right away (so disk
|
||||
* corruption doesn't matter: as soon as the process is dead,
|
||||
* the disk data is dead as well)
|
||||
*/
|
||||
result = mdb_env_open(isc__cfgmgr_env, dbname,
|
||||
MDB_NOSYNC | MDB_NOSUBDIR | MDB_NOLOCK, 0600);
|
||||
if (result != 0) {
|
||||
result = ISC_R_FAILURE;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
remove(dbname);
|
||||
remove(dblockname);
|
||||
|
||||
goto out;
|
||||
|
||||
cleanup:
|
||||
if (isc__cfgmgr_env != NULL) {
|
||||
mdb_env_close(isc__cfgmgr_env);
|
||||
isc__cfgmgr_env = NULL;
|
||||
}
|
||||
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
isc_cfgmgr_deinit(void) {
|
||||
/*
|
||||
* Well, I'm on the fence about those context checks for the
|
||||
* deinit function: it's good to have, but because the context
|
||||
* data is thread specific, it doesn't means there isn't a
|
||||
* thread somewhere which haven't released its opened clauses
|
||||
* and not the one calling isc_cfgmgr_deinit, then we'll leak
|
||||
* those - even if unlikely as this function should be call
|
||||
* late in shutdown flow. (That said it's just an extra clue,
|
||||
* because destroying the context will assert anyway, as some
|
||||
* memory would not be released yet).
|
||||
*/
|
||||
REQUIRE(ISC_LIST_EMPTY(isc__cfgmgr_ctx.openedclauses));
|
||||
REQUIRE(isc__cfgmgr_ctx.prefix == NULL);
|
||||
REQUIRE(isc__cfgmgr_ctx.buffer == NULL);
|
||||
REQUIRE(isc__cfgmgr_ctx.cursor == NULL);
|
||||
REQUIRE(isc__cfgmgr_ctx.txn == NULL);
|
||||
REQUIRE(isc__cfgmgr_mctx != NULL);
|
||||
REQUIRE(isc__cfgmgr_env != NULL);
|
||||
mdb_env_close(isc__cfgmgr_env);
|
||||
isc__cfgmgr_env = NULL;
|
||||
isc_mem_detach(&isc__cfgmgr_mctx);
|
||||
INSIST(isc__cfgmgr_mctx == NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
isc__cfgmgr_buildkey(const char *name, bool trailingdot) {
|
||||
size_t written;
|
||||
const char *prefix = "";
|
||||
const char *dot = trailingdot ? "." : "";
|
||||
|
||||
REQUIRE(name != NULL);
|
||||
REQUIRE(isc__cfgmgr_ctx.buffer != NULL);
|
||||
REQUIRE(isc__cfgmgr_ctx.prefix != NULL);
|
||||
|
||||
if (ISC_LIST_EMPTY(isc__cfgmgr_ctx.openedclauses) == false) {
|
||||
prefix = isc__cfgmgr_ctx.prefix;
|
||||
}
|
||||
|
||||
written = snprintf(isc__cfgmgr_ctx.buffer, BUFLEN, "%s%s%s", prefix,
|
||||
name, dot);
|
||||
INSIST(written <= BUFLEN);
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
isc__cfgmgr_findclause(const char *name, unsigned long *id) {
|
||||
isc_result_t result = ISC_R_SUCCESS;
|
||||
MDB_val dbkey;
|
||||
size_t dotpos = 0;
|
||||
|
||||
REQUIRE(name != NULL);
|
||||
|
||||
isc__cfgmgr_buildkey(name, true);
|
||||
dotpos = strlen(isc__cfgmgr_ctx.buffer) - 1;
|
||||
dbkey = (MDB_val){ .mv_size = strlen(isc__cfgmgr_ctx.buffer) + 1,
|
||||
.mv_data = (char *)isc__cfgmgr_ctx.buffer };
|
||||
|
||||
/*
|
||||
* Let's use LMDB prefix search because the first clause
|
||||
* key/val won't just have the "name.id" prefix, but also the
|
||||
* id and the first property name (so "name.id.prop").
|
||||
*/
|
||||
if (mdb_cursor_get(isc__cfgmgr_ctx.cursor, &dbkey, NULL,
|
||||
MDB_SET_RANGE) != 0)
|
||||
{
|
||||
result = ISC_R_NOTFOUND;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* LMDB found a key which starts by "prefix", so let's make
|
||||
* sure it's actually the same prefix by checking the found
|
||||
* key has an immediate leading dot
|
||||
*/
|
||||
if (dbkey.mv_size <= dotpos || ((char *)dbkey.mv_data)[dotpos] != '.') {
|
||||
result = ISC_R_NOTFOUND;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* We found the clause. Let's extract its ID
|
||||
*/
|
||||
*id = isc__cfgmgr_parseid(dbkey.mv_data);
|
||||
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
isc__cfgmgr_updateprefix(void) {
|
||||
size_t written = 0;
|
||||
|
||||
REQUIRE(isc__cfgmgr_ctx.prefix != NULL);
|
||||
|
||||
if (ISC_LIST_EMPTY(isc__cfgmgr_ctx.openedclauses)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (openedclause_t *clause =
|
||||
ISC_LIST_TAIL(isc__cfgmgr_ctx.openedclauses);
|
||||
clause != NULL; clause = ISC_LIST_PREV(clause, link))
|
||||
{
|
||||
written += snprintf(isc__cfgmgr_ctx.prefix + written,
|
||||
BUFLEN - written, "%s.%" PRIu32 ".",
|
||||
clause->name, clause->id);
|
||||
INSIST(written <= BUFLEN);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
isc__cfgmgr_pushclause(const char *name, unsigned long id) {
|
||||
openedclause_t *clause = isc_mem_get(isc__cfgmgr_mctx, sizeof(*clause));
|
||||
|
||||
*clause = (openedclause_t){
|
||||
.name = isc_mem_allocate(isc__cfgmgr_mctx, strlen(name) + 1),
|
||||
.id = id,
|
||||
.link = ISC_LINK_INITIALIZER,
|
||||
};
|
||||
strcpy(clause->name, name);
|
||||
ENSURE(clause->id > 0);
|
||||
ISC_LIST_PREPEND(isc__cfgmgr_ctx.openedclauses, clause, link);
|
||||
isc__cfgmgr_updateprefix();
|
||||
}
|
||||
|
||||
static void
|
||||
isc__cfgmgr_freectx(void) {
|
||||
REQUIRE(isc__cfgmgr_ctx.buffer != NULL &&
|
||||
isc__cfgmgr_ctx.prefix != NULL);
|
||||
|
||||
isc_mem_free(isc__cfgmgr_mctx, isc__cfgmgr_ctx.buffer);
|
||||
isc_mem_free(isc__cfgmgr_mctx, isc__cfgmgr_ctx.prefix);
|
||||
isc__cfgmgr_ctx.txn = NULL;
|
||||
isc__cfgmgr_ctx.cursor = NULL;
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
isc__cfgmgr_starttransaction(bool readonly) {
|
||||
MDB_dbi dbi;
|
||||
|
||||
isc_rwlock_lock(&isc__cfgmgr_lock,
|
||||
readonly ? isc_rwlocktype_read : isc_rwlocktype_write);
|
||||
|
||||
if (mdb_txn_begin(isc__cfgmgr_env, NULL, readonly ? MDB_RDONLY : 0,
|
||||
&isc__cfgmgr_ctx.txn) != 0)
|
||||
{
|
||||
goto failure;
|
||||
}
|
||||
INSIST(isc__cfgmgr_ctx.txn != NULL);
|
||||
|
||||
if (mdb_dbi_open(isc__cfgmgr_ctx.txn, NULL, MDB_CREATE | MDB_DUPSORT,
|
||||
&dbi) != 0)
|
||||
{
|
||||
goto failure;
|
||||
}
|
||||
|
||||
if (mdb_cursor_open(isc__cfgmgr_ctx.txn, dbi,
|
||||
&isc__cfgmgr_ctx.cursor) != 0)
|
||||
{
|
||||
goto failure;
|
||||
}
|
||||
INSIST(isc__cfgmgr_ctx.cursor != NULL);
|
||||
isc__cfgmgr_ctx.readonly = readonly;
|
||||
isc__cfgmgr_ctx.buffer = isc_mem_allocate(isc__cfgmgr_mctx, BUFLEN);
|
||||
isc__cfgmgr_ctx.prefix = isc_mem_allocate(isc__cfgmgr_mctx, BUFLEN);
|
||||
|
||||
return ISC_R_SUCCESS;
|
||||
|
||||
failure:
|
||||
if (isc__cfgmgr_ctx.txn) {
|
||||
mdb_txn_abort(isc__cfgmgr_ctx.txn);
|
||||
isc__cfgmgr_freectx();
|
||||
isc_rwlock_unlock(&isc__cfgmgr_lock,
|
||||
isc__cfgmgr_ctx.readonly
|
||||
? isc_rwlocktype_read
|
||||
: isc_rwlocktype_write);
|
||||
}
|
||||
ENSURE(isc__cfgmgr_ctx.buffer == NULL &&
|
||||
isc__cfgmgr_ctx.prefix == NULL);
|
||||
|
||||
return ISC_R_FAILURE;
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
isc__cfgmgr_opentoplevel(const char *name, bool readonly) {
|
||||
isc_result_t result = ISC_R_SUCCESS;
|
||||
unsigned long id = 0;
|
||||
|
||||
REQUIRE(isc__cfgmgr_env != NULL);
|
||||
REQUIRE(name != NULL);
|
||||
REQUIRE(ISC_LIST_EMPTY(isc__cfgmgr_ctx.openedclauses));
|
||||
REQUIRE(isc__cfgmgr_ctx.prefix == NULL);
|
||||
REQUIRE(isc__cfgmgr_ctx.buffer == NULL);
|
||||
REQUIRE(isc__cfgmgr_ctx.txn == NULL);
|
||||
REQUIRE(isc__cfgmgr_ctx.cursor == NULL);
|
||||
|
||||
/*
|
||||
* We're opening a clause at top-level, so let's start a
|
||||
* transaction
|
||||
*/
|
||||
result = isc__cfgmgr_starttransaction(readonly);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now let's try to find the clause...
|
||||
*/
|
||||
result = isc__cfgmgr_findclause(name, &id);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/*
|
||||
* The clause is found, let's enqueue the clause in
|
||||
* context. the clause is now opened
|
||||
*/
|
||||
isc__cfgmgr_pushclause(name, id);
|
||||
goto out;
|
||||
|
||||
cleanup:
|
||||
if (isc__cfgmgr_ctx.txn) {
|
||||
mdb_cursor_close(isc__cfgmgr_ctx.cursor);
|
||||
mdb_txn_abort(isc__cfgmgr_ctx.txn);
|
||||
isc__cfgmgr_freectx();
|
||||
isc_rwlock_unlock(&isc__cfgmgr_lock,
|
||||
isc__cfgmgr_ctx.readonly
|
||||
? isc_rwlocktype_read
|
||||
: isc_rwlocktype_write);
|
||||
}
|
||||
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
isc__cfgmgr_opennested(const char *name) {
|
||||
isc_result_t result = ISC_R_SUCCESS;
|
||||
unsigned long id = 0;
|
||||
|
||||
REQUIRE(isc__cfgmgr_env != NULL);
|
||||
REQUIRE(name != NULL);
|
||||
REQUIRE(ISC_LIST_EMPTY(isc__cfgmgr_ctx.openedclauses) == false);
|
||||
REQUIRE(isc__cfgmgr_ctx.prefix != NULL);
|
||||
REQUIRE(isc__cfgmgr_ctx.buffer != NULL);
|
||||
REQUIRE(isc__cfgmgr_ctx.txn != NULL);
|
||||
REQUIRE(isc__cfgmgr_ctx.cursor != NULL);
|
||||
|
||||
result = isc__cfgmgr_findclause(name, &id);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
isc__cfgmgr_pushclause(name, id);
|
||||
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
isc_cfgmgr_openrw(const char *name) {
|
||||
return isc__cfgmgr_opentoplevel(name, false);
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
isc_cfgmgr_open(const char *name) {
|
||||
if (ISC_LIST_EMPTY(isc__cfgmgr_ctx.openedclauses)) {
|
||||
return isc__cfgmgr_opentoplevel(name, true);
|
||||
}
|
||||
|
||||
return isc__cfgmgr_opennested(name);
|
||||
}
|
||||
|
||||
static void
|
||||
popclause(void) {
|
||||
openedclause_t *clause = ISC_LIST_HEAD(isc__cfgmgr_ctx.openedclauses);
|
||||
|
||||
ISC_LIST_UNLINK(isc__cfgmgr_ctx.openedclauses, clause, link);
|
||||
isc_mem_free(isc__cfgmgr_mctx, clause->name);
|
||||
isc_mem_put(isc__cfgmgr_mctx, clause, sizeof(*clause));
|
||||
isc__cfgmgr_updateprefix();
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
isc_cfgmgr_close(void) {
|
||||
isc_result_t result = ISC_R_SUCCESS;
|
||||
|
||||
REQUIRE(isc__cfgmgr_env != NULL);
|
||||
REQUIRE(ISC_LIST_EMPTY(isc__cfgmgr_ctx.openedclauses) == false);
|
||||
REQUIRE(isc__cfgmgr_ctx.prefix != NULL);
|
||||
REQUIRE(isc__cfgmgr_ctx.buffer != NULL);
|
||||
REQUIRE(isc__cfgmgr_ctx.txn != NULL);
|
||||
REQUIRE(isc__cfgmgr_ctx.cursor != NULL);
|
||||
|
||||
popclause();
|
||||
if (ISC_LIST_EMPTY(isc__cfgmgr_ctx.openedclauses)) {
|
||||
mdb_cursor_close(isc__cfgmgr_ctx.cursor);
|
||||
|
||||
if (mdb_txn_commit(isc__cfgmgr_ctx.txn) != 0) {
|
||||
result = ISC_R_FAILURE;
|
||||
}
|
||||
|
||||
isc__cfgmgr_freectx();
|
||||
isc_rwlock_unlock(&isc__cfgmgr_lock,
|
||||
isc__cfgmgr_ctx.readonly
|
||||
? isc_rwlocktype_read
|
||||
: isc_rwlocktype_write);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
isc_cfgmgr_delclause(void) {
|
||||
MDB_val dbkey;
|
||||
|
||||
REQUIRE(isc__cfgmgr_env != NULL);
|
||||
REQUIRE(ISC_LIST_EMPTY(isc__cfgmgr_ctx.openedclauses) == false);
|
||||
REQUIRE(isc__cfgmgr_ctx.prefix != NULL);
|
||||
REQUIRE(isc__cfgmgr_ctx.buffer != NULL);
|
||||
REQUIRE(isc__cfgmgr_ctx.txn != NULL);
|
||||
REQUIRE(isc__cfgmgr_ctx.cursor != NULL);
|
||||
REQUIRE(isc__cfgmgr_ctx.readonly == false);
|
||||
|
||||
dbkey = (MDB_val){ .mv_size = strlen(isc__cfgmgr_ctx.prefix) + 1,
|
||||
.mv_data = isc__cfgmgr_ctx.prefix };
|
||||
do {
|
||||
/*
|
||||
* even though the key is modified by mdb_cursor_get
|
||||
* on each run (and is the exact current key) we're
|
||||
* good: MDB_SET_RANGE of the current key will point
|
||||
* to the next one with the same prefix as soon it
|
||||
* gets deleted
|
||||
*/
|
||||
int mdbres = mdb_cursor_get(isc__cfgmgr_ctx.cursor, &dbkey,
|
||||
NULL, MDB_SET_RANGE);
|
||||
if (mdbres == MDB_NOTFOUND) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (strncmp(isc__cfgmgr_ctx.prefix, dbkey.mv_data,
|
||||
strlen(isc__cfgmgr_ctx.prefix)) != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* NDB_NODUPDATA not strictly needed here, but we
|
||||
* avoid extra iterations if there are lists in the
|
||||
* clause
|
||||
*/
|
||||
REQUIRE(mdb_cursor_del(isc__cfgmgr_ctx.cursor, MDB_NODUPDATA) ==
|
||||
0);
|
||||
} while (1);
|
||||
|
||||
return isc_cfgmgr_close();
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
isc_cfgmgr_newclause(const char *name) {
|
||||
isc_result_t result = ISC_R_SUCCESS;
|
||||
|
||||
REQUIRE(name != NULL);
|
||||
REQUIRE(isc__cfgmgr_env != NULL);
|
||||
|
||||
if (isc__cfgmgr_ctx.txn == NULL || isc__cfgmgr_ctx.cursor == NULL) {
|
||||
REQUIRE(isc__cfgmgr_ctx.prefix == NULL);
|
||||
REQUIRE(isc__cfgmgr_ctx.buffer == NULL);
|
||||
REQUIRE(isc__cfgmgr_ctx.txn == NULL);
|
||||
REQUIRE(isc__cfgmgr_ctx.cursor == NULL);
|
||||
result = isc__cfgmgr_starttransaction(false);
|
||||
}
|
||||
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
INSIST(isc__cfgmgr_ctx.txn != NULL);
|
||||
INSIST(isc__cfgmgr_ctx.buffer != NULL);
|
||||
INSIST(isc__cfgmgr_ctx.cursor != NULL);
|
||||
INSIST(isc__cfgmgr_ctx.readonly == false);
|
||||
isc__cfgmgr_pushclause(name, isc_random32());
|
||||
INSIST(isc__cfgmgr_ctx.prefix != NULL);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
isc_cfgmgr_nextclause(void) {
|
||||
isc_result_t result = ISC_R_SUCCESS;
|
||||
MDB_val dbkey;
|
||||
uint32_t id;
|
||||
size_t idstarts = 0;
|
||||
size_t written = 0;
|
||||
|
||||
REQUIRE(isc__cfgmgr_env != NULL);
|
||||
REQUIRE(ISC_LIST_EMPTY(isc__cfgmgr_ctx.openedclauses) == false);
|
||||
REQUIRE(isc__cfgmgr_ctx.prefix != NULL);
|
||||
REQUIRE(isc__cfgmgr_ctx.buffer != NULL);
|
||||
REQUIRE(isc__cfgmgr_ctx.txn != NULL);
|
||||
REQUIRE(isc__cfgmgr_ctx.cursor != NULL);
|
||||
|
||||
/*
|
||||
* Let's pick the very next id (even if doesn't exists) of the
|
||||
* current clause
|
||||
*/
|
||||
id = ISC_LIST_HEAD(isc__cfgmgr_ctx.openedclauses)->id + 1;
|
||||
|
||||
/*
|
||||
* Variant of updateprefix, but this time we put a incremented
|
||||
* key for the currently opened clause. We also keep track of
|
||||
* when the current clause id starts.
|
||||
*/
|
||||
for (openedclause_t *clause =
|
||||
ISC_LIST_TAIL(isc__cfgmgr_ctx.openedclauses);
|
||||
clause != NULL; clause = ISC_LIST_PREV(clause, link))
|
||||
{
|
||||
bool last = ISC_LIST_PREV(clause, link) == NULL;
|
||||
|
||||
if (last) {
|
||||
idstarts = written + strlen(clause->name) + 1;
|
||||
}
|
||||
written += snprintf(isc__cfgmgr_ctx.buffer + written,
|
||||
BUFLEN - written, "%s.%" PRIu32 ".",
|
||||
clause->name, last ? id : clause->id);
|
||||
INSIST(written <= BUFLEN);
|
||||
}
|
||||
INSIST(idstarts > 0);
|
||||
dbkey = (MDB_val){ .mv_size = strlen(isc__cfgmgr_ctx.buffer) + 1,
|
||||
.mv_data = isc__cfgmgr_ctx.buffer };
|
||||
|
||||
/*
|
||||
* Looking for similar prefix name but with a bigger
|
||||
* id. Thanks for LMDB sort and MDB_SET_RANGE, we'll bump to
|
||||
* the first key/val of the next clause of the same type
|
||||
*/
|
||||
if (mdb_cursor_get(isc__cfgmgr_ctx.cursor, &dbkey, NULL,
|
||||
MDB_SET_RANGE) != 0)
|
||||
{
|
||||
result = ISC_R_NOMORE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Let's check if next found clause is same name
|
||||
*/
|
||||
REQUIRE(idstarts < BUFLEN);
|
||||
if (strncmp(isc__cfgmgr_ctx.buffer, dbkey.mv_data, idstarts) != 0) {
|
||||
result = ISC_R_NOMORE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets the actual id of the next clause (so let's get rid of
|
||||
* the fake id part from the prefix). Instead of pop/push a
|
||||
* new clause, let's simply replace the id and update the
|
||||
* prefix.
|
||||
*/
|
||||
isc__cfgmgr_ctx.buffer[idstarts] = 0;
|
||||
ISC_LIST_HEAD(isc__cfgmgr_ctx.openedclauses)->id =
|
||||
isc__cfgmgr_parseid(dbkey.mv_data);
|
||||
isc__cfgmgr_updateprefix();
|
||||
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
isc__cfgmgr_getval(const char *name, isc_cfgmgr_val_t *value) {
|
||||
isc_result_t result = ISC_R_SUCCESS;
|
||||
MDB_val dbkey;
|
||||
MDB_val dbval;
|
||||
const int opt = name == NULL ? MDB_NEXT_DUP : MDB_SET;
|
||||
|
||||
REQUIRE(isc__cfgmgr_env != NULL);
|
||||
REQUIRE(ISC_LIST_EMPTY(isc__cfgmgr_ctx.openedclauses) == false);
|
||||
REQUIRE(isc__cfgmgr_ctx.prefix != NULL);
|
||||
REQUIRE(isc__cfgmgr_ctx.buffer != NULL);
|
||||
REQUIRE(isc__cfgmgr_ctx.txn != NULL);
|
||||
REQUIRE(isc__cfgmgr_ctx.cursor != NULL);
|
||||
REQUIRE(value != NULL);
|
||||
|
||||
if (name != NULL) {
|
||||
isc__cfgmgr_buildkey(name, false);
|
||||
}
|
||||
dbkey = (MDB_val){
|
||||
.mv_size = name == NULL ? 0
|
||||
: strlen(isc__cfgmgr_ctx.buffer) + 1,
|
||||
.mv_data = name == NULL ? NULL : isc__cfgmgr_ctx.buffer
|
||||
};
|
||||
if (mdb_cursor_get(isc__cfgmgr_ctx.cursor, &dbkey, &dbval, opt) != 0) {
|
||||
result = opt == MDB_NEXT_DUP ? ISC_R_NOMORE : ISC_R_NOTFOUND;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(value, dbval.mv_data, sizeof(*value));
|
||||
if (value->type == ISC_CFGMGR_STRING) {
|
||||
value->string = ((char *)dbval.mv_data) + sizeof(value->type);
|
||||
}
|
||||
INSIST(value->type != ISC_CFGMGR_UNDEFINED);
|
||||
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
isc_cfgmgr_getval(const char *name, isc_cfgmgr_val_t *value) {
|
||||
return isc__cfgmgr_getval(name, value);
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
isc_cfgmgr_getnextlistval(isc_cfgmgr_val_t *value) {
|
||||
return isc__cfgmgr_getval(NULL, value);
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
isc__cfgmgr_setval(const char *name, const isc_cfgmgr_val_t *value, bool list) {
|
||||
isc_result_t result = ISC_R_SUCCESS;
|
||||
MDB_val dbkey;
|
||||
MDB_val dbval;
|
||||
|
||||
REQUIRE(isc__cfgmgr_env != NULL);
|
||||
REQUIRE(ISC_LIST_EMPTY(isc__cfgmgr_ctx.openedclauses) == false);
|
||||
REQUIRE(isc__cfgmgr_ctx.prefix != NULL);
|
||||
REQUIRE(isc__cfgmgr_ctx.buffer != NULL);
|
||||
REQUIRE(isc__cfgmgr_ctx.txn != NULL);
|
||||
REQUIRE(isc__cfgmgr_ctx.cursor != NULL);
|
||||
REQUIRE(isc__cfgmgr_ctx.readonly == false);
|
||||
REQUIRE(name != NULL);
|
||||
REQUIRE((value != NULL && value->type != ISC_CFGMGR_UNDEFINED) ||
|
||||
(value == NULL && list == false));
|
||||
|
||||
isc__cfgmgr_buildkey(name, false);
|
||||
dbkey = (MDB_val){ .mv_size = strlen(isc__cfgmgr_ctx.buffer) + 1,
|
||||
.mv_data = isc__cfgmgr_ctx.buffer };
|
||||
if (value == NULL) {
|
||||
if (mdb_cursor_get(isc__cfgmgr_ctx.cursor, &dbkey, NULL,
|
||||
MDB_SET) == MDB_NOTFOUND)
|
||||
{
|
||||
result = ISC_R_NOTFOUND;
|
||||
goto out;
|
||||
}
|
||||
|
||||
REQUIRE(mdb_cursor_del(isc__cfgmgr_ctx.cursor, MDB_NODUPDATA) ==
|
||||
0);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (value->type == ISC_CFGMGR_STRING) {
|
||||
dbval.mv_size = sizeof(*value) + strlen(value->string) + 1;
|
||||
dbval.mv_data = isc_mem_allocate(isc__cfgmgr_mctx,
|
||||
dbval.mv_size);
|
||||
memcpy(dbval.mv_data, value, sizeof(value->type));
|
||||
strcpy(((char *)dbval.mv_data) + sizeof(value->type),
|
||||
value->string);
|
||||
} else {
|
||||
dbval = (MDB_val){ .mv_size = sizeof(*value),
|
||||
/*
|
||||
* LMDB won't modify the mv_data buffer but
|
||||
* its API is designed w/o the const buffer.
|
||||
*/
|
||||
.mv_data = (void *)value };
|
||||
}
|
||||
|
||||
if (list == false) {
|
||||
/*
|
||||
* Can't use MDB_NOOVERWRITE as it would override the
|
||||
* data if the key/value already exists. Making a
|
||||
* value copy ahead just in case is likely more
|
||||
* expensive than an extra lookup
|
||||
*/
|
||||
if (mdb_cursor_get(isc__cfgmgr_ctx.cursor, &dbkey, NULL,
|
||||
MDB_SET) != MDB_NOTFOUND)
|
||||
{
|
||||
REQUIRE(mdb_cursor_del(isc__cfgmgr_ctx.cursor, 0) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
REQUIRE(mdb_cursor_put(isc__cfgmgr_ctx.cursor, &dbkey, &dbval, 0) == 0);
|
||||
|
||||
if (value->type == ISC_CFGMGR_STRING) {
|
||||
isc_mem_free(isc__cfgmgr_mctx, dbval.mv_data);
|
||||
}
|
||||
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
isc_cfgmgr_setval(const char *name, const isc_cfgmgr_val_t *value) {
|
||||
return isc__cfgmgr_setval(name, value, false);
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
isc_cfgmgr_setnextlistval(const char *name, const isc_cfgmgr_val_t *value) {
|
||||
return isc__cfgmgr_setval(name, value, true);
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* 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 <isc/sockaddr.h>
|
||||
#include <isc/types.h>
|
||||
|
||||
/*
|
||||
* Supported data types for read/write operations from/to cfgmgr.
|
||||
*/
|
||||
typedef enum isc_cfgmgr_type {
|
||||
ISC_CFGMGR_UNDEFINED = 0,
|
||||
ISC_CFGMGR_STRING,
|
||||
ISC_CFGMGR_BOOLEAN,
|
||||
ISC_CFGMGR_NONE,
|
||||
ISC_CFGMGR_SOCKADDR,
|
||||
ISC_CFGMGR_UINT32
|
||||
} isc_cfgmgr_type_t;
|
||||
|
||||
/*
|
||||
* Generic value holding the actual value and type value for
|
||||
* read/write from/to cfgmgr.
|
||||
*
|
||||
* cfgmgr_type_t::NONE doesn't have associated value,
|
||||
*/
|
||||
typedef struct isc_cfgmgr_val {
|
||||
isc_cfgmgr_type_t type;
|
||||
union {
|
||||
const char *string;
|
||||
bool boolean;
|
||||
isc_sockaddr_t sockaddr;
|
||||
uint32_t uint32;
|
||||
};
|
||||
} isc_cfgmgr_val_t;
|
||||
|
||||
/*
|
||||
* Get the property "name" in the opened clause into the caller
|
||||
* allocated "value" and returns ISC_R_SUCCESS. Returns ISC_R_NOTFOUND
|
||||
* and "*value" is not mutated if "name" is not found. Changes being
|
||||
* made by other threads aren't visible until the clause (and its
|
||||
* parent, if nested) is closed. If "name" is a list property, get its
|
||||
* head.
|
||||
*/
|
||||
isc_result_t
|
||||
isc_cfgmgr_getval(const char *name, isc_cfgmgr_val_t *value);
|
||||
|
||||
/*
|
||||
* Write "value" into the property "name" in the opened clause and
|
||||
* returns ISC_R_SUCCESS. If the property already exists, it is
|
||||
* overridden and even if the type is different. If "value" is NULL
|
||||
* and the property exists, it will be deleted (applies for list
|
||||
* properties as well), otherwise, it returns ISC_R_NOTFOUND. Changes
|
||||
* being made can be visible only by the current thread until the
|
||||
* clause (and its parent, if nested) is closed.
|
||||
*/
|
||||
isc_result_t
|
||||
isc_cfgmgr_setval(const char *name, const isc_cfgmgr_val_t *value);
|
||||
|
||||
/*
|
||||
* Same as isc_cfgmgr_getval but applies for elements after the head
|
||||
* of a list property. The head is read using isc_cfgmgr_getval as any
|
||||
* other value, then subsequents calls to isc_cfgmgr_getnextlistval
|
||||
* will get the next elements in the list. When the end of the list is
|
||||
* reached, ISC_R_NOMORE is returned. Calls to
|
||||
* isc_cfgmgr_getnextlistval name has to be made in immediate sequence
|
||||
* (without intermediate isc_cfgmgr_{set,get}val calls) to retrieve
|
||||
* each list element.
|
||||
*/
|
||||
isc_result_t
|
||||
isc_cfgmgr_getnextlistval(isc_cfgmgr_val_t *value);
|
||||
|
||||
/*
|
||||
* Same as isc_cfgmgr_setval but applies for a list property. Writes
|
||||
* by appending "*value" at the end of the list property "name" in the
|
||||
* opened clause and returns ISC_R_SUCCESS. If "name" property wasn't
|
||||
* existing before (or wasn't a list) it's overriden. It is not
|
||||
* possible to delete individual list element, only the whole list can
|
||||
* be removed using isc_cfgmgr_setval.
|
||||
*/
|
||||
isc_result_t
|
||||
isc_cfgmgr_setnextlistval(const char *name, const isc_cfgmgr_val_t *value);
|
||||
|
||||
/*
|
||||
* If the opened clause is a repeatable clause (i.e. view, acl, etc.),
|
||||
* internally closes the opened clause and open the next clause of the
|
||||
* same type and returns ISC_R_SUCCESS. When there is no next clause
|
||||
* of the same type, ISC_R_NOMORE is returned.
|
||||
*/
|
||||
isc_result_t
|
||||
isc_cfgmgr_nextclause(void);
|
||||
|
||||
/*
|
||||
* If used at top-level, create and open as read-write a new clause
|
||||
* "name". If used inside an opened parent clause, then the parent (or
|
||||
* parent or the parent, recursively) clause must have been opened
|
||||
* read-write (so using isc_cfgmgr_openrw or isc_cfgmgr_newclause).
|
||||
*
|
||||
* Returns ISC_R_SUCCESS or ISC_R_FAILURE if there is no transaction
|
||||
* and it fails creating one. Note that in order to have the new
|
||||
* clause actually written in cfgmgr, at least one property needs to
|
||||
* be set to that clause.
|
||||
*/
|
||||
isc_result_t
|
||||
isc_cfgmgr_newclause(const char *name);
|
||||
|
||||
/*
|
||||
* Delete and close the opened clause. (And thus all its properties,
|
||||
* including nested clauses). If the clause was nested, the currently
|
||||
* opened clause is now the parent clause. Otherwise, no clause is
|
||||
* opened. Returns ISC_R_SUCCESS.
|
||||
*/
|
||||
isc_result_t
|
||||
isc_cfgmgr_delclause(void);
|
||||
|
||||
/*
|
||||
* Close the currently opened clause and returns ISC_R_SUCCESS. If the
|
||||
* closed clause was nested, the currently opened clause is now the
|
||||
* parent clause. If top-level clause was opened with
|
||||
* isc_cfgmgr_openrw or isc_cfgmgr_newclause, closing the top-level
|
||||
* clause will applies all modifications done inside the clause (and
|
||||
* inside the nested clauses). If something is going wrong while
|
||||
* writing the modifications ISC_R_FAILURE is returned and all
|
||||
* modification made are discarded.
|
||||
*/
|
||||
isc_result_t
|
||||
isc_cfgmgr_close(void);
|
||||
|
||||
/*
|
||||
* Open the top-level clause "name" for reading and writing and
|
||||
* returns ISC_R_SUCCESS. If the clause "name" is not found, returns
|
||||
* ISC_R_NOTFOUND. If there is an issue creating a transaction, it
|
||||
* returns ISC_R_FAILURE.
|
||||
*
|
||||
* This call will block if another thread has already a clause opened
|
||||
* for reading and writting. Use isc_cfgmgr_openro for reading only.
|
||||
*/
|
||||
isc_result_t
|
||||
isc_cfgmgr_openrw(const char *name);
|
||||
|
||||
/*
|
||||
* Open the clause "name" and returns ISC_R_SUCCES or ISC_R_NOTFOUND
|
||||
* is the clause is not found. Two possible cases:
|
||||
*
|
||||
* - if called at top-level, it open the top-level clause as read
|
||||
* only. Returns ISC_R_FAILURE if there is an issue creating the
|
||||
* transaction.
|
||||
*
|
||||
* - if called form within an opened clause, it open it with the same
|
||||
* access than the already opened clause.
|
||||
*/
|
||||
isc_result_t
|
||||
isc_cfgmgr_open(const char *name);
|
||||
|
||||
/*
|
||||
* Initialize cfgmgr. Must be called before any other function. It is
|
||||
* possible to re-initialize cfgmgr only after calling
|
||||
* isc_cfgmgr_deinit (this drops all the data written in
|
||||
* cfgmgr). Returns ISC_R_SUCCESS or ISC_R_FAILURE if there is an
|
||||
* issue initializing the internal database.
|
||||
*/
|
||||
isc_result_t
|
||||
isc_cfgmgr_init(isc_mem_t *mctx, const char *dbpath);
|
||||
|
||||
/*
|
||||
* Destroy all cfgmgr data and free memory. Must be called only after
|
||||
* isc_cfgmgr_init and no function must be called after that one
|
||||
* (except isc_cfgmgr_init to re-initialize cfgmgr again).
|
||||
*/
|
||||
void
|
||||
isc_cfgmgr_deinit(void);
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
#define DNS_LMDB_COMMON_FLAGS (MDB_NOSUBDIR | MDB_NOLOCK)
|
||||
#ifndef __OpenBSD__
|
||||
#define DNS_LMDB_FLAGS (DNS_LMDB_COMMON_FLAGS)
|
||||
#else /* __OpenBSD__ */
|
||||
/*
|
||||
* OpenBSD does not have a unified buffer cache, which requires both reads and
|
||||
* writes to be performed using mmap().
|
||||
*/
|
||||
#define DNS_LMDB_FLAGS (DNS_LMDB_COMMON_FLAGS | MDB_WRITEMAP)
|
||||
#endif /* __OpenBSD__ */
|
||||
@@ -2061,11 +2061,7 @@ static cfg_clausedef_t view_clauses[] = {
|
||||
{ "ipv4only-server", &cfg_type_astring, 0 },
|
||||
{ "ixfr-from-differences", &cfg_type_ixfrdifftype, 0 },
|
||||
{ "lame-ttl", &cfg_type_duration, 0 },
|
||||
#ifdef HAVE_LMDB
|
||||
{ "lmdb-mapsize", &cfg_type_sizeval, CFG_CLAUSEFLAG_OPTIONAL },
|
||||
#else /* ifdef HAVE_LMDB */
|
||||
{ "lmdb-mapsize", &cfg_type_sizeval, CFG_CLAUSEFLAG_NOTCONFIGURED },
|
||||
#endif /* ifdef HAVE_LMDB */
|
||||
{ "lmdb-mapsize", &cfg_type_sizeval, 0 },
|
||||
{ "max-acache-size", NULL, CFG_CLAUSEFLAG_ANCIENT },
|
||||
{ "max-cache-size", &cfg_type_sizeorpercent, 0 },
|
||||
{ "max-cache-ttl", &cfg_type_duration, 0 },
|
||||
|
||||
@@ -15,6 +15,7 @@ check_PROGRAMS = \
|
||||
ascii_test \
|
||||
async_test \
|
||||
buffer_test \
|
||||
cfgmgr_test \
|
||||
counter_test \
|
||||
dnsstream_utils_test \
|
||||
errno_test \
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user