diff --git a/CHANGES b/CHANGES
index f2fa4f4fbe..7ef9c6af45 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,9 @@
+4380. [experimental] Added a "zone-directory" option to "catalog-zones"
+ syntax, allowing local masterfiles for slaves
+ that are provisioned by catalog zones to be stored
+ in a directory other than the server's working
+ directory. [RT #42527]
+
4379. [bug] An INSIST could be triggered if a zone contains
RRSIG records with expiry fields that loop
using serial number arithmetic. [RT #40571]
diff --git a/bin/named/server.c b/bin/named/server.c
index 58ab7c06be..cd9ed0d17c 100644
--- a/bin/named/server.c
+++ b/bin/named/server.c
@@ -2367,6 +2367,11 @@ configure_catz_zone(dns_view_t *view, const cfg_obj_t *config,
result = ns_config_getipandkeylist(config, obj,
view->mctx, &opts->masters);
+ obj = cfg_tuple_get(catz_obj, "zone-directory");
+ if (obj != NULL)
+ opts->zonedir = isc_mem_strdup(view->mctx,
+ cfg_obj_asstring(obj));
+
obj = cfg_tuple_get(catz_obj, "in-memory");
if (obj != NULL && cfg_obj_isboolean(obj))
opts->in_memory = cfg_obj_asboolean(obj);
diff --git a/bin/tests/system/catz/clean.sh b/bin/tests/system/catz/clean.sh
index bf1b2d5aa9..9912e55dcd 100644
--- a/bin/tests/system/catz/clean.sh
+++ b/bin/tests/system/catz/clean.sh
@@ -17,7 +17,8 @@ rm -f nsupdate.out.*
rm -f ns*/named.memstats
rm -f ns*/named.run
rm -f ns*/named.lock
-rm -f ns{1,2}/*dom*example.db
+rm -f ns1/*dom*example.db
rm -f ns{1,2}/catalog.example.db
+rm -rf ns2/zonedir
rm -f ns*/*.jnl
rm -f ns*/*.nzf
diff --git a/bin/tests/system/catz/ns2/named.conf b/bin/tests/system/catz/ns2/named.conf
index 4cd508d78b..a641a5c160 100644
--- a/bin/tests/system/catz/ns2/named.conf
+++ b/bin/tests/system/catz/ns2/named.conf
@@ -32,7 +32,10 @@ options {
recursion no;
serial-query-rate 100;
catalog-zones {
- zone "catalog.example" default-masters { 10.53.0.1; };
+ zone "catalog.example"
+ default-masters { 10.53.0.1; }
+ in-memory no
+ zone-directory "zonedir";
};
};
diff --git a/bin/tests/system/catz/setup.sh b/bin/tests/system/catz/setup.sh
index 8e0d3e8d86..7a3dbcdb7d 100644
--- a/bin/tests/system/catz/setup.sh
+++ b/bin/tests/system/catz/setup.sh
@@ -20,3 +20,4 @@ SYSTEMTESTTOP=..
$SHELL clean.sh
cat ns1/catalog.example.db.in > ns1/catalog.example.db
+mkdir ns2/zonedir
diff --git a/bin/tests/system/catz/tests.sh b/bin/tests/system/catz/tests.sh
index c72b99c690..4546d89ef3 100644
--- a/bin/tests/system/catz/tests.sh
+++ b/bin/tests/system/catz/tests.sh
@@ -301,5 +301,13 @@ grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
+n=`expr $n + 1`
+echo "I:checking that zone-directory is populated ($n)"
+ret=0
+[ -f "ns2/zonedir/__catz___default_catalog.example_dom3.example.db" ] || ret=1
+[ -f "ns2/zonedir/__catz___default_catalog.example_dom4.example.db" ] || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
echo "I:exit status: $status"
exit $status
diff --git a/doc/arm/catz.xml b/doc/arm/catz.xml
index 87cf5fbaec..6f19f90eb2 100644
--- a/doc/arm/catz.xml
+++ b/doc/arm/catz.xml
@@ -110,7 +110,11 @@
catalog-zones {
- zone "catalog.example" default-masters { 10.53.0.1; } in-memory true min-update-interval 10;
+ zone "catalog.example"
+ default-masters { 10.53.0.1; }
+ in-memory no
+ zone-directory "catzones"
+ min-update-interval 10;
};
@@ -119,6 +123,10 @@ catalog-zones {
properly configured in the same view. In most configurations, it would
be a slave zone.
+
+ The options following the zone name are not required, and may be
+ specified in any order:
+
The option defines the default masters
for member zones listed in a catalog zone. This can be overridden by
@@ -134,6 +142,14 @@ catalog-zones {
will be stored locally in a file whose name is automatically generated
from the view name, catalog zone name, and member zone name.
+
+ The option causes local copies of
+ member zones' master files (if is not set
+ to yes) to be stored in the specified directory.
+ The default is to store zone files in the server's working directory.
+ A non-absolute pathname in is
+ assumed to be relative to the working directory.
+
The option sets the minimum
interval between processing of updates to catalog zones, in seconds.
diff --git a/lib/dns/catz.c b/lib/dns/catz.c
index 46b467962e..9364ff1fc0 100644
--- a/lib/dns/catz.c
+++ b/lib/dns/catz.c
@@ -96,12 +96,17 @@ dns_catz_options_init(dns_catz_options_t *options) {
options->in_memory = ISC_FALSE;
options->min_update_interval = 5;
+ options->zonedir = NULL;
}
void
dns_catz_options_free(dns_catz_options_t *options, isc_mem_t *mctx) {
if (options->masters.count > 0)
dns_ipkeylist_clear(mctx, &options->masters);
+ if (options->zonedir != NULL) {
+ isc_mem_free(mctx, options->zonedir);
+ options->zonedir = NULL;
+ }
}
isc_result_t
@@ -112,10 +117,17 @@ dns_catz_options_copy(isc_mem_t *mctx, const dns_catz_options_t *src,
REQUIRE(dst != NULL);
REQUIRE(dst->masters.count == 0);
- if (src->masters.count != 0) {
+ if (src->masters.count != 0)
dns_ipkeylist_copy(mctx, &src->masters, &dst->masters);
+
+ if (dst->zonedir != NULL) {
+ isc_mem_free(mctx, dst->zonedir);
+ dst->zonedir = NULL;
}
+ if (src->zonedir != NULL)
+ dst->zonedir = isc_mem_strdup(mctx, src->zonedir);
+
return (ISC_R_SUCCESS);
}
@@ -125,6 +137,8 @@ dns_catz_options_setdefault(isc_mem_t *mctx, const dns_catz_options_t *defaults,
{
if (opts->masters.count == 0)
dns_catz_options_copy(mctx, defaults, opts);
+ else if (defaults->zonedir != NULL)
+ opts->zonedir = isc_mem_strdup(mctx, defaults->zonedir);
/* This option is always taken from config, so it's always 'default' */
opts->in_memory = defaults->in_memory;
@@ -234,7 +248,6 @@ dns_catz_entry_cmp(const dns_catz_entry_t *ea, const dns_catz_entry_t *eb) {
return (ISC_TRUE);
}
-
dns_name_t *
dns_catz_zone_getname(dns_catz_zone_t *zone) {
REQUIRE(zone != NULL);
@@ -1072,6 +1085,7 @@ dns_catz_generate_masterfilename(dns_catz_zone_t *zone, dns_catz_entry_t *entry,
isc_sha256_t sha256;
isc_region_t r;
isc_result_t result;
+ size_t rlen;
REQUIRE(zone != NULL);
REQUIRE(entry != NULL);
@@ -1079,26 +1093,38 @@ dns_catz_generate_masterfilename(dns_catz_zone_t *zone, dns_catz_entry_t *entry,
result = isc_buffer_allocate(zone->catzs->mctx, &tbuf,
strlen(zone->catzs->view->name) +
- 2*DNS_NAME_FORMATSIZE + 2);
+ 2 * DNS_NAME_FORMATSIZE + 2);
if (result != ISC_R_SUCCESS)
return (result);
INSIST(tbuf != NULL);
+
isc_buffer_putstr(tbuf, zone->catzs->view->name);
isc_buffer_putstr(tbuf, "_");
result = dns_name_totext(&zone->name, ISC_TRUE, tbuf);
if (result != ISC_R_SUCCESS)
goto cleanup;
+
isc_buffer_putstr(tbuf, "_");
result = dns_name_totext(&entry->name, ISC_TRUE, tbuf);
if (result != ISC_R_SUCCESS)
goto cleanup;
- result = isc_buffer_reserve(buffer, strlen("__catz__") +
- ISC_SHA256_DIGESTSTRINGLENGTH +
- strlen(".db"));
+ /* __catz__.db */
+ rlen = ISC_SHA256_DIGESTSTRINGLENGTH + 12;
+
+ /* optionally prepend with / */
+ if (entry->opts.zonedir != NULL)
+ rlen += strlen(entry->opts.zonedir) + 1;
+
+ result = isc_buffer_reserve(buffer, rlen);
if (result != ISC_R_SUCCESS)
goto cleanup;
+ if (entry->opts.zonedir != NULL) {
+ isc_buffer_putstr(*buffer, entry->opts.zonedir);
+ isc_buffer_putstr(*buffer, "/");
+ }
+
isc_buffer_usedregion(tbuf, &r);
isc_buffer_putstr(*buffer, "__catz__");
if (tbuf->used > ISC_SHA256_DIGESTSTRINGLENGTH) {
@@ -1110,11 +1136,13 @@ dns_catz_generate_masterfilename(dns_catz_zone_t *zone, dns_catz_entry_t *entry,
} else {
isc_buffer_copyregion(*buffer, &r);
}
+
isc_buffer_putstr(*buffer, ".db");
result = ISC_R_SUCCESS;
cleanup:
- isc_buffer_free(&tbuf);
+ if (tbuf != NULL)
+ isc_buffer_free(&tbuf);
return (result);
}
diff --git a/lib/dns/include/dns/catz.h b/lib/dns/include/dns/catz.h
index 7cff8b9b33..73bd25028e 100644
--- a/lib/dns/include/dns/catz.h
+++ b/lib/dns/include/dns/catz.h
@@ -51,12 +51,15 @@ struct dns_catz_entry_options {
/*
* Options that can be overriden in catalog zone
*/
- /* masters definition */
+ /* default-masters definition */
dns_ipkeylist_t masters;
/*
* Options that are only set in named.conf
*/
+ /* zone-directory definition */
+ char *zonedir;
+
/* zone should not be stored on disk (no 'file' statement in def */
isc_boolean_t in_memory;
/*
diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c
index a3ca7c8163..f45eec6483 100644
--- a/lib/isccfg/namedconf.c
+++ b/lib/isccfg/namedconf.c
@@ -1484,6 +1484,7 @@ static cfg_type_t cfg_type_catz_zone = {
static cfg_tuplefielddef_t catz_zone_fields[] = {
{ "zone name", &cfg_type_catz_zone, 0 },
{ "default-masters", &cfg_type_namesockaddrkeylist, 0 },
+ { "zone-directory", &cfg_type_qstring, 0 },
{ "in-memory", &cfg_type_boolean, 0 },
{ "min-update-interval", &cfg_type_uint32, 0 },
{ NULL, NULL, 0 }