]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
4387. [test] Rewritten test suite for catalog zones. [RT #42676]
authorWitold Krecicki <wpk@isc.org>
Wed, 22 Jun 2016 08:49:25 +0000 (10:49 +0200)
committerWitold Krecicki <wpk@isc.org>
Wed, 22 Jun 2016 08:50:09 +0000 (10:50 +0200)
4386. [func] Support for master entries with TSIG keys in catalog
zones. [RT #42577]

18 files changed:
CHANGES
bin/named/config.c
bin/named/server.c
bin/named/zoneconf.c
bin/tests/system/catz/clean.sh
bin/tests/system/catz/ns1/named.conf
bin/tests/system/catz/ns2/named.conf.in [moved from bin/tests/system/catz/ns2/named.conf with 83% similarity]
bin/tests/system/catz/ns3/dom5.example.db [moved from bin/tests/system/catz/ns3/dom4.example.db with 100% similarity]
bin/tests/system/catz/ns3/dom6.example.db [new file with mode: 0644]
bin/tests/system/catz/ns3/named.conf
bin/tests/system/catz/setup.sh
bin/tests/system/catz/tests.sh
lib/dns/catz.c
lib/dns/include/dns/ipkeylist.h
lib/dns/include/dns/zone.h
lib/dns/ipkeylist.c
lib/dns/name.c
lib/dns/zone.c

diff --git a/CHANGES b/CHANGES
index e2db12abd37a3fa0acdb3aa36ab47c57784d2272..f831e1bb981d6e3b7b753d805ae55991259362a8 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,8 @@
+4389.  [test]          Rewritten test suite for catalog zones. [RT #42676]
+
+4388.  [func]          Support for master entries with TSIG keys in catalog
+                       zones. [RT #42577]
+
 4387.  [bug]           Change 4336 was not complete leading to SERVFAIL
                        being return as NS records expired. [RT #42683]
 
index fb061e7d3d9733676abc70c34dcc2b98c819a713..e9c2d599a30e0d40d31dac68f5efb030452e3b62 100644 (file)
@@ -600,9 +600,12 @@ ns_config_getipandkeylist(const cfg_obj_t *config, const cfg_obj_t *list,
        } *stack = NULL;
 
        REQUIRE(ipkl != NULL);
+       REQUIRE(ipkl->count == 0);
        REQUIRE(ipkl->addrs == NULL);
        REQUIRE(ipkl->keys == NULL);
        REQUIRE(ipkl->dscps == NULL);
+       REQUIRE(ipkl->labels == NULL);
+       REQUIRE(ipkl->allocated == 0);
 
        /*
         * Get system defaults.
@@ -859,6 +862,7 @@ ns_config_getipandkeylist(const cfg_obj_t *config, const cfg_obj_t *list,
        ipkl->dscps = dscps;
        ipkl->keys = keys;
        ipkl->count = addrcount;
+       ipkl->allocated = addrcount;
 
        return (ISC_R_SUCCESS);
 
index 830369d63ea2e11979e2fb5c0304a7e4450f2182..8fc8ff6df499d521fccdbe2a344ae1d0d24a3f2f 100644 (file)
@@ -2046,12 +2046,31 @@ catz_addmodzone_taskaction(isc_task_t *task, isc_event_t *event0) {
                if (result != ISC_R_SUCCESS) {
                        isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
                                      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
-                                     "catz: error \"%s\" while trying to"
+                                     "catz: error \"%s\" while trying to "
                                      "modify zone \"%s\"",
                                      isc_result_totext(result),
                                      nameb);
                        goto cleanup;
                } else {
+                       if (!dns_zone_getadded(zone)) {
+                               isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+                                             NS_LOGMODULE_SERVER,
+                                             ISC_LOG_WARNING,
+                                             "catz: catz_addmodzone_taskaction: "
+                                             "zone '%s' is not a dynamically "
+                                             "added zone",
+                                             nameb);
+                               goto cleanup;
+                       }
+                       if (dns_zone_get_parentcatz(zone) != ev->origin) {
+                               isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+                                             NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
+                                             "catz: catz_delzone_taskaction: "
+                                             "zone '%s' exists in multiple "
+                                             "catalog zones",
+                                             nameb);
+                               goto cleanup;
+                       }
                        dns_zone_detach(&zone);
                }
 
@@ -2059,7 +2078,7 @@ catz_addmodzone_taskaction(isc_task_t *task, isc_event_t *event0) {
                if (result != ISC_R_NOTFOUND && result != DNS_R_PARTIALMATCH) {
                        isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
                                      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
-                                     "catz: error \"%s\" while trying to"
+                                     "catz: error \"%s\" while trying to "
                                      "add zone \"%s\"",
                                      isc_result_totext(result),
                                      nameb);
@@ -2141,6 +2160,7 @@ catz_addmodzone_taskaction(isc_task_t *task, isc_event_t *event0) {
 
        /* Flag the zone as having been added at runtime */
        dns_zone_setadded(zone, ISC_TRUE);
+       dns_zone_set_parentcatz(zone, ev->origin);
 
  cleanup:
        if (zone != NULL)
@@ -2177,8 +2197,6 @@ catz_delzone_taskaction(isc_task_t *task, isc_event_t *event0) {
                goto cleanup;
        }
 
-       /* TODO make other flag for CZ zones */
-       /* TODO2 make sure that we delete only 'own' zones */
        if (!dns_zone_getadded(zone)) {
                isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
                              NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
@@ -2188,6 +2206,15 @@ catz_delzone_taskaction(isc_task_t *task, isc_event_t *event0) {
                goto cleanup;
        }
 
+       if (dns_zone_get_parentcatz(zone) != ev->origin) {
+               isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+                             NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
+                             "catz: catz_delzone_taskaction: zone "
+                             "'%s' exists in multiple catalog zones",
+                             cname);
+               goto cleanup;
+       }
+
        /* Stop answering for this zone */
        if (dns_zone_getdb(zone, &dbp) == ISC_R_SUCCESS) {
                dns_db_detach(&dbp);
@@ -2196,7 +2223,8 @@ catz_delzone_taskaction(isc_task_t *task, isc_event_t *event0) {
 
        CHECK(dns_zt_unmount(ev->view->zonetable, zone));
        file = dns_zone_getfile(zone);
-       isc_file_remove(file);
+       if (file != NULL)
+               isc_file_remove(file);
 
        isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
                      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
@@ -5408,8 +5436,11 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
                dns_db_t *db = NULL;
 
                tresult = dns_zone_getdb(zone, &db);
-               if (tresult == ISC_R_SUCCESS)
+               if (tresult == ISC_R_SUCCESS) {
                        dns_catz_dbupdate_callback(db, view->catzs);
+                       dns_db_detach(&db);
+               }
+
        }
 
        /*
index 6e81c0835adc956f175ac31413db271089da9635..e21bf963c6fbfb2e554a74d3b32f040238ffe8a6 100644 (file)
@@ -1148,10 +1148,8 @@ ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig,
                      ztype == dns_zone_master)))
                {
                        dns_ipkeylist_t ipkl;
-                       ipkl.count = 0;
-                       ipkl.addrs = NULL;
-                       ipkl.dscps = NULL;
-                       ipkl.keys = NULL;
+                       dns_ipkeylist_init(&ipkl);
+
                        RETERR(ns_config_getipandkeylist(config, obj, mctx,
                                                         &ipkl));
                        result = dns_zone_setalsonotifydscpkeys(zone,
@@ -1623,10 +1621,8 @@ ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig,
                (void)cfg_map_get(zoptions, "masters", &obj);
                if (obj != NULL) {
                        dns_ipkeylist_t ipkl;
-                       ipkl.count = 0;
-                       ipkl.addrs = NULL;
-                       ipkl.dscps = NULL;
-                       ipkl.keys = NULL;
+                       dns_ipkeylist_init(&ipkl);
+
                        RETERR(ns_config_getipandkeylist(config, obj, mctx,
                                                         &ipkl));
                        result = dns_zone_setmasterswithkeys(mayberaw,
index 46dc9c7e8d797b64ccbc028166b8fb85bccf46af..516324d48b13f0dc72aa7047793c126ecdb7b420 100644 (file)
 # PERFORMANCE OF THIS SOFTWARE.
 
 rm -f dig.out.*
-rm -f nsupdate.out.*
+rm -f ns*/*.jnl
+rm -f ns*/*.nzf
+rm -f ns*/named.lock
 rm -f ns*/named.memstats
 rm -f ns*/named.run
-rm -f ns*/named.lock
 rm -f ns1/*dom*example.db
-rm -f ns{1,2}/catalog{1,2,3}.example.db
 rm -f ns2/__catz__*db
+rm -f ns2/named.conf
+rm -f ns3/dom{13,14}.example.db
+rm -f nsupdate.out.*
+rm -f ns{1,2,3}/catalog{1,2,3,4}.example.db
 rm -rf ns2/zonedir
-rm -f ns*/*.jnl
-rm -f ns*/*.nzf
index ecdbb0b2d02cb1e44e26fe4968af71972a3f9c67..521d147a6766db91fcd603d1c1ebf61788a5f1d0 100644 (file)
@@ -31,6 +31,7 @@ options {
        listen-on-v6 { none; };
        notify no;
        recursion no;
+       allow-transfer { any; };
 };
 
 zone "catalog1.example" {
@@ -42,20 +43,27 @@ zone "catalog1.example" {
        notify explicit;
 };
 
-zone "catalog2.example" {
+zone "catalog3.example" {
        type master;
-       file "catalog2.example.db";
+       file "catalog3.example.db";
        allow-transfer { any; };
        allow-update { any; };
        also-notify { 10.53.0.2; };
        notify explicit;
 };
 
-zone "catalog3.example" {
+zone "catalog4.example" {
        type master;
-       file "catalog3.example.db";
+       file "catalog4.example.db";
        allow-transfer { any; };
        allow-update { any; };
        also-notify { 10.53.0.2; };
        notify explicit;
 };
+
+/* catalog5 is missing on purpose */
+
+key tsig_key. {
+        secret "LSAnCU+Z";
+        algorithm hmac-md5;
+};
similarity index 83%
rename from bin/tests/system/catz/ns2/named.conf
rename to bin/tests/system/catz/ns2/named.conf.in
index d989b9ebd83d8a1ba950219ce358614f634a61c7..f2ea7cbbb12db94432aeb55d43c78d9ddd51c96e 100644 (file)
@@ -27,7 +27,7 @@ options {
        port 5300;
        pid-file "named.pid";
        listen-on { 10.53.0.2; };
-       listen-on-v6 { none; };
+       listen-on-v6 { fd92:7065:b8e:ffff::2; };
        notify no;
        recursion no;
        serial-query-rate 100;
@@ -42,6 +42,10 @@ options {
                zone "catalog3.example"
                        default-masters { 10.53.0.1; }
                        zone-directory "nonexistent";
+#T1            zone "catalog4.example"
+#T1                    default-masters { 10.53.0.1; };
+#T2            zone "catalog5.example"
+#T2                    default-masters { 10.53.0.1; };
        };
 };
 
@@ -54,7 +58,7 @@ zone "catalog1.example" {
 zone "catalog2.example" {
        type slave;
        file "catalog2.example.db";
-       masters { 10.53.0.1; };
+       masters { 10.53.0.3; };
 };
 
 zone "catalog3.example" {
@@ -62,3 +66,14 @@ zone "catalog3.example" {
        file "catalog3.example.db";
        masters { 10.53.0.1; };
 };
+
+zone "catalog4.example" {
+       type slave;
+       file "catalog4.example.db";
+       masters { 10.53.0.1; };
+};
+
+key tsig_key. {
+        secret "LSAnCU+Z";
+        algorithm hmac-md5;
+};
diff --git a/bin/tests/system/catz/ns3/dom6.example.db b/bin/tests/system/catz/ns3/dom6.example.db
new file mode 100644 (file)
index 0000000..2373dce
--- /dev/null
@@ -0,0 +1,16 @@
+; Copyright (C) 2016  Internet Systems Consortium, Inc. ("ISC")
+;
+; Permission to use, copy, modify, and/or distribute this software for any
+; purpose with or without fee is hereby granted, provided that the above
+; copyright notice and this permission notice appear in all copies.
+;
+; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+; AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+; PERFORMANCE OF THIS SOFTWARE.
+
+@ 3600 IN SOA . . 1 3600 3600 3600 3600
+@ IN NS invalid.
index 0b9e5b4e7ff6fbf17c1a2d9ac7f129e7ffd21f27..5338752144cc78f9d9c3fbb170ba6acd940b40d9 100644 (file)
@@ -25,16 +25,35 @@ options {
        notify-source 10.53.0.3;
        transfer-source 10.53.0.3;
        port 5300;
+       allow-new-zones yes;
        pid-file "named.pid";
+       provide-ixfr no;
        listen-on { 10.53.0.3; };
-       listen-on-v6 { none; };
+       listen-on-v6 { fd92:7065:b8e:ffff::3; };
        notify no;
        recursion no;
 };
 
-zone "dom4.example" {
+zone "catalog2.example" {
        type master;
-       file "dom4.example.db";
+       file "catalog2.example.db";
+       allow-transfer { any; };
+       allow-update { any; };
+       also-notify { 10.53.0.2; };
+       notify explicit;
+};
+
+zone "dom5.example" {
+       type master;
+       file "dom5.example.db";
+       allow-transfer { any; };
+       allow-update { any; };
+       notify explicit;
+};
+
+zone "dom6.example" {
+       type master;
+       file "dom6.example.db";
        allow-transfer { any; };
        allow-update { any; };
        notify explicit;
index 2b8716cdcb82b9d59fb47a4c5a560189fda936f4..c6607b0967898bad92a34c5778816b9e99785a7a 100644 (file)
@@ -20,6 +20,8 @@ SYSTEMTESTTOP=..
 $SHELL clean.sh
 
 cat ns1/catalog.example.db.in > ns1/catalog1.example.db
-cat ns1/catalog.example.db.in > ns1/catalog2.example.db
+cat ns1/catalog.example.db.in > ns3/catalog2.example.db
 cat ns1/catalog.example.db.in > ns1/catalog3.example.db
+cat ns1/catalog.example.db.in > ns1/catalog4.example.db
+cat ns2/named.conf.in > ns2/named.conf
 mkdir ns2/zonedir
index c37b3503974d8e5cc4d475e172bce08ae0533e36..bb06559e9e98ecad08ec5889038eb1c81476f70d 100644 (file)
@@ -19,47 +19,48 @@ SYSTEMTESTTOP=..
 
 status=0
 n=0
-
+##########################################################################
+echo "I:Testing adding/removing of domain in catalog zone"
 n=`expr $n + 1`
-echo "I:checking that dom1.example is not served by master ($n)"
+echo "I: checking that dom1.example is not served by master ($n)"
 ret=0
 $DIG soa dom1.example @10.53.0.1 -p 5300 > dig.out.test$n
 grep "status: REFUSED" dig.out.test$n > /dev/null || ret=1
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 n=`expr $n + 1`
-echo "I:Adding a domain dom1.example to master via RNDC ($n)"
+echo "I: Adding a domain dom1.example to master via RNDC ($n)"
 ret=0
 echo "@ 3600 IN SOA . . 1 3600 3600 3600 3600" > ns1/dom1.example.db
 echo "@ IN NS invalid." >> ns1/dom1.example.db
 $RNDC -c ../common/rndc.conf -s 10.53.0.1 -p 9953  addzone dom1.example '{type master; file "dom1.example.db";};' || ret=1
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 n=`expr $n + 1`
-echo "I:checking that dom1.example is now served by master ($n)"
+echo "I: checking that dom1.example is now served by master ($n)"
 ret=0
 $DIG soa dom1.example @10.53.0.1 -p 5300 > dig.out.test$n
 grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 cur=`awk 'BEGIN {l=0} /^/ {l++} END { print l }' ns2/named.run`
 
 n=`expr $n + 1`
-echo "I:Adding domain dom1.example to catalog1 zone ($n)"
+echo "I: Adding domain dom1.example to catalog1 zone ($n)"
 ret=0
 $NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
     server 10.53.0.1 5300
     update add e721433b6160b450260d4f54b3ec8bab30cb3b83.zones.catalog1.example 3600 IN PTR dom1.example.
     send
 END
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 n=`expr $n + 1`
-echo "I:waiting for slave to sync up ($n)"
+echo "I: waiting for slave to sync up ($n)"
 ret=1
 try=0
 while test $try -lt 45
@@ -81,30 +82,37 @@ do
     }
     try=`expr $try + 1`
 done
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 n=`expr $n + 1`
-echo "I:checking that dom1.example is served by slave ($n)"
+echo "I: checking that dom1.example is served by slave ($n)"
 ret=0
 $DIG soa dom1.example @10.53.0.2 -p 5300 > dig.out.test$n
 grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
-if [ $ret != 0 ]; then echo "I:failed"; fi
+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_catalog1.example_dom1.example.db" ] || ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 n=`expr $n + 1`
-echo "I:Removing domain dom1.example from catalog1 zone ($n)"
+echo "I: removing domain dom1.example from catalog1 zone ($n)"
 ret=0
 $NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
    server 10.53.0.1 5300
    update delete e721433b6160b450260d4f54b3ec8bab30cb3b83.zones.catalog1.example
    send
 END
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 n=`expr $n + 1`
-echo "I:waiting for slave to sync up ($n)"
+echo "I: waiting for slave to sync up ($n)"
 ret=1
 try=0
 while test $try -lt 45
@@ -116,37 +124,46 @@ do
     }
     try=`expr $try + 1`
 done
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 n=`expr $n + 1`
-echo "I:checking that dom1.example is not served by slave ($n)"
+echo "I: checking that dom1.example is not served by slave ($n)"
 ret=0
 $DIG soa dom1.example @10.53.0.2 -p 5300 > dig.out.test$n
 grep "status: REFUSED" dig.out.test$n > /dev/null || ret=1
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that zone-directory is emptied ($n)"
+ret=0
+[ -f "ns2/zonedir/__catz___default_catalog1.example_dom1.example.db" ] && ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
+##########################################################################
+echo "I:Testing various simple operations on domains, including using multiple catalog zones and garbage in zone"
 n=`expr $n + 1`
-echo "I:Adding a domain dom2.example to master via RNDC ($n)"
+echo "I: adding domain dom2.example to master via RNDC ($n)"
 ret=0
 echo "@ 3600 IN SOA . . 1 3600 3600 3600 3600" > ns1/dom2.example.db
 echo "@ IN NS invalid." >> ns1/dom2.example.db
 $RNDC -c ../common/rndc.conf -s 10.53.0.1 -p 9953  addzone dom2.example '{type master; file "dom2.example.db";};' || ret=1
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 n=`expr $n + 1`
-echo "I:Adding a domain dom5.example to master via RNDC ($n)"
+echo "I: adding domain dom4.example to master via RNDC ($n)"
 ret=0
-echo "@ 3600 IN SOA . . 1 3600 3600 3600 3600" > ns1/dom5.example.db
-echo "@ IN NS invalid." >> ns1/dom5.example.db
-$RNDC -c ../common/rndc.conf -s 10.53.0.1 -p 9953  addzone dom5.example '{type master; file "dom5.example.db";};' || ret=1
-if [ $ret != 0 ]; then echo "I:failed"; fi
+echo "@ 3600 IN SOA . . 1 3600 3600 3600 3600" > ns1/dom4.example.db
+echo "@ IN NS invalid." >> ns1/dom4.example.db
+$RNDC -c ../common/rndc.conf -s 10.53.0.1 -p 9953  addzone dom4.example '{type master; file "dom4.example.db";};' || ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 n=`expr $n + 1`
-echo "I:Adding domains dom2.example, dom3.example and some trash to catalog1 zone ($n)"
+echo "I: adding domains dom2.example, dom3.example and some garbage to catalog1 zone ($n)"
 ret=0
 $NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
     server 10.53.0.1 5300
@@ -156,31 +173,37 @@ $NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
     update add trash.catalog1.example 3600 IN A 1.2.3.4
     update add trash2.foo.catalog1.example 3600 IN A 1.2.3.4
     update add trash3.zones.catalog1.example 3600 IN NS a.dom2.example.
+    update add foobarbaz.b901f492f3ebf6c1e5b597e51766f02f0479eb03.zones.catalog1.example 3600 IN PTR dom3.example.
+    update add blahblah.636722929740e507aaf27c502812fc395d30fb17.zones.catalog1.example 3600 IN PTR dom2.example.
+    update add foobarbaz.b901f492f3ebf6c1e5b597e51766f02f0479eb03.zones.catalog1.example 3600 IN APL 1:1.2.3.4/30
+    update add blahblah.636722929740e507aaf27c502812fc395d30fb17.zones.catalog1.example 3600 IN TXT "blah blah"
+    update add version.catalog1.example 3600 IN A 1.2.3.4
     send
+
 END
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 n=`expr $n + 1`
-echo "I:Adding domains dom5.example to catalog2 zone ($n)"
+echo "I: adding domain dom4.example to catalog2 zone ($n)"
 ret=0
 $NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
-    server 10.53.0.1 5300
-    update add de26b88d855397a03f77ff1162fd055d8b419584.zones.catalog2.example 3600 IN PTR dom5.example.
+    server 10.53.0.3 5300
+    update add de26b88d855397a03f77ff1162fd055d8b419584.zones.catalog2.example 3600 IN PTR dom4.example.
     send
 END
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 
 n=`expr $n + 1`
-echo "I:waiting for slave to sync up ($n)"
+echo "I: waiting for slave to sync up ($n)"
 ret=1
 try=0
 while test $try -lt 45
 do
     sleep 1
-    sed -n "$cur,"'$p' < ns2/named.run | grep "catz: adding zone 'dom5.example' from catalog 'catalog2.example'" > /dev/null && {
+    sed -n "$cur,"'$p' < ns2/named.run | grep "catz: adding zone 'dom4.example' from catalog 'catalog2.example'" > /dev/null && {
        ret=0
        break
     }
@@ -190,51 +213,51 @@ try=0
 while test $try -lt 45
 do
     sleep 1
-    sed -n "$cur,"'$p' < ns2/named.run | grep "transfer of 'dom5.example/IN' from 10.53.0.1#5300: Transfer status: success" > /dev/null && {
+    sed -n "$cur,"'$p' < ns2/named.run | grep "transfer of 'dom4.example/IN' from 10.53.0.1#5300: Transfer status: success" > /dev/null && {
        ret=0
        break
     }
     try=`expr $try + 1`
 done
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 n=`expr $n + 1`
-echo "I:checking that dom5.example is served by slave ($n)"
+echo "I: checking that dom4.example is served by slave ($n)"
 ret=0
-$DIG soa dom5.example @10.53.0.2 -p 5300 > dig.out.test$n
+$DIG soa dom4.example @10.53.0.2 -p 5300 > dig.out.test$n
 grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 
 n=`expr $n + 1`
-echo "I:checking that dom3.example is not served by master ($n)"
+echo "I: checking that dom3.example is not served by master ($n)"
 ret=0
 $DIG soa dom3.example @10.53.0.1 -p 5300 > dig.out.test$n
 grep "status: REFUSED" dig.out.test$n > /dev/null || ret=1
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 n=`expr $n + 1`
-echo "I:Adding a domain dom3.example to master via RNDC ($n)"
+echo "I: adding a domain dom3.example to master via RNDC ($n)"
 ret=0
 echo "@ 3600 IN SOA . . 1 3600 3600 3600 3600" > ns1/dom3.example.db
 echo "@ IN NS invalid." >> ns1/dom3.example.db
-$RNDC -c ../common/rndc.conf -s 10.53.0.1 -p 9953  addzone dom3.example '{type master; file "dom3.example.db"; also-notify { 10.53.0.2; }; };' || ret=1
-if [ $ret != 0 ]; then echo "I:failed"; fi
+$RNDC -c ../common/rndc.conf -s 10.53.0.1 -p 9953  addzone dom3.example '{type master; file "dom3.example.db"; also-notify { 10.53.0.2; }; notify explicit; };' || ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 n=`expr $n + 1`
-echo "I:checking that dom3.example is served by master ($n)"
+echo "I: checking that dom3.example is served by master ($n)"
 ret=0
-$DIG soa dom2.example @10.53.0.1 -p 5300 > dig.out.test$n
+$DIG soa dom3.example @10.53.0.1 -p 5300 > dig.out.test$n
 grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 n=`expr $n + 1`
-echo "I:waiting for slave to sync up ($n)"
+echo "I: waiting for slave to sync up ($n)"
 ret=1
 try=0
 while test $try -lt 45
@@ -256,161 +279,278 @@ do
     }
     try=`expr $try + 1`
 done
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 n=`expr $n + 1`
-echo "I:checking that dom3.example is served by slave ($n)"
+echo "I: checking that dom3.example is served by slave ($n)"
 ret=0
 $DIG soa dom3.example @10.53.0.2 -p 5300 > dig.out.test$n
 grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: removing all records from catalog1 zone ($n)"
+ret=0
+$NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
+    server 10.53.0.1 5300
+    update delete 636722929740e507aaf27c502812fc395d30fb17.zones.catalog1.example 3600 IN PTR dom2.example.
+    update delete b901f492f3ebf6c1e5b597e51766f02f0479eb03.zones.catalog1.example 3600 IN PTR dom3.example.
+    update delete e721433b6160b450260d4f54b3ec8bab30cb3b83.zones.catalog1.example 3600 IN NS foo.bar.
+    update delete trash.catalog1.example 3600 IN A 1.2.3.4
+    update delete trash2.foo.catalog1.example 3600 IN A 1.2.3.4
+    update delete trash3.zones.catalog1.example 3600 IN NS a.dom2.example.
+    update delete foobarbaz.b901f492f3ebf6c1e5b597e51766f02f0479eb03.zones.catalog1.example 3600 IN PTR dom3.example.
+    update delete blahblah.636722929740e507aaf27c502812fc395d30fb17.zones.catalog1.example 3600 IN PTR dom2.example.
+    update delete foobarbaz.b901f492f3ebf6c1e5b597e51766f02f0479eb03.zones.catalog1.example 3600 IN APL 1:1.2.3.4/30
+    update delete blahblah.636722929740e507aaf27c502812fc395d30fb17.zones.catalog1.example 3600 IN TXT "blah blah"
+    update delete version.catalog1.example 3600 IN A 1.2.3.4
+    send
+
+END
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: removing all records from catalog2 zone ($n)"
+ret=0
+$NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
+    server 10.53.0.3 5300
+    update delete de26b88d855397a03f77ff1162fd055d8b419584.zones.catalog2.example 3600 IN PTR dom4.example.
+    send
+END
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
+##########################################################################
+echo "I:Testing masters suboption and random labels"
 n=`expr $n + 1`
-echo "I:Adding dom4.example with 'masters' defined and a random label ($n)"
+echo "I: adding dom5.example with 'masters' suboption set and a random label ($n)"
 ret=0
 $NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
     server 10.53.0.1 5300
-    update add somerandomlabel.zones.catalog1.example 3600 IN PTR dom4.example.
+    update add somerandomlabel.zones.catalog1.example 3600 IN PTR dom5.example.
     update add masters.somerandomlabel.zones.catalog1.example 3600 IN A 10.53.0.3
     send
 END
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 n=`expr $n + 1`
-echo "I:waiting for slave to sync up ($n)"
+echo "I: waiting for slave to sync up ($n)"
 ret=1
 try=0
 while test $try -lt 45
 do
     sleep 1
-    sed -n "$cur,"'$p' < ns2/named.run | grep "catz: adding zone 'dom4.example' from catalog 'catalog1.example'" > /dev/null && {
+    sed -n "$cur,"'$p' < ns2/named.run | grep "catz: adding zone 'dom5.example' from catalog 'catalog1.example'" > /dev/null && {
        ret=0
        break
     }
     try=`expr $try + 1`
 done
+
 try=0
 while test $try -lt 45
 do
     sleep 1
-    sed -n "$cur,"'$p' < ns2/named.run | grep "transfer of 'dom4.example/IN' from 10.53.0.3#5300: Transfer status: success" > /dev/null && {
+    sed -n "$cur,"'$p' < ns2/named.run | grep "transfer of 'dom5.example/IN' from 10.53.0.3#5300: Transfer status: success" > /dev/null && {
        ret=0
        break
     }
     try=`expr $try + 1`
 done
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 n=`expr $n + 1`
-echo "I:checking that dom4.example is served by slave ($n)"
+echo "I: checking that dom5.example is served by slave ($n)"
 ret=0
-$DIG soa dom4.example @10.53.0.2 -p 5300 > dig.out.test$n
+$DIG soa dom5.example @10.53.0.2 -p 5300 > dig.out.test$n
 grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 n=`expr $n + 1`
-echo "I:Removing domain dom2.example from catalog1 zone ($n)"
+echo "I: removing dom5.example ($n)"
 ret=0
 $NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
     server 10.53.0.1 5300
-    update delete 636722929740e507aaf27c502812fc395d30fb17.zones.catalog1.example
+    update delete somerandomlabel.zones.catalog1.example 3600 IN PTR dom5.example.
+    update delete masters.somerandomlabel.zones.catalog1.example 3600 IN A 10.53.0.3
     send
 END
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 n=`expr $n + 1`
-echo "I:waiting for slave to sync up ($n)"
+echo "I: waiting for slave to sync up ($n)"
 ret=1
 try=0
 while test $try -lt 45
 do
     sleep 1
-    sed -n "$cur,"'$p' < ns2/named.run | grep "catz: deleting zone 'dom2.example' from catalog 'catalog1.example'" > /dev/null && {
+    sed -n "$cur,"'$p' < ns2/named.run | grep "catz: deleting zone 'dom5.example' from catalog 'catalog1.example'" > /dev/null && {
        ret=0
        break
     }
     try=`expr $try + 1`
 done
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
-
 n=`expr $n + 1`
-echo "I:checking that dom2.example is not served by slave ($n)"
+echo "I: checking that dom5.example is no longer served by slave ($n)"
 ret=0
-$DIG soa dom2.example @10.53.0.2 -p 5300 > dig.out.test$n
+$DIG soa dom5.example @10.53.0.2 -p 5300 > dig.out.test$n
 grep "status: REFUSED" dig.out.test$n > /dev/null || ret=1
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
+
+##########################################################################
+echo "I:Testing masters global option"
 n=`expr $n + 1`
-echo "I:checking that dom3.example is still served by slave ($n)"
+echo "I: adding dom6.example and global masters option ($n)"
 ret=0
-$DIG soa dom3.example @10.53.0.2 -p 5300 > dig.out.test$n
+$NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
+    server 10.53.0.1 5300
+    update add masters.catalog1.example 3600 IN A 10.53.0.3
+    update add masters.catalog1.example 3600 IN AAAA  fd92:7065:b8e:ffff::3
+    update add 4346f565b4d63ddb99e5d2497ff22d04e878e8f8.zones.catalog1.example 3600 IN PTR dom6.example.
+    send
+END
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: waiting for slave to sync up ($n)"
+ret=1
+try=0
+while test $try -lt 45
+do
+    sleep 1
+    sed -n "$cur,"'$p' < ns2/named.run | grep "catz: adding zone 'dom6.example' from catalog 'catalog1.example'" > /dev/null && {
+       ret=0
+       break
+    }
+    try=`expr $try + 1`
+done
+
+try=0
+while test $try -lt 45
+do
+    sleep 1
+    sed -n "$cur,"'$p' < ns2/named.run | grep "transfer of 'dom6.example/IN' from " > /dev/null && {
+       ret=0
+       break
+    }
+    try=`expr $try + 1`
+done
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that dom6.example is served by slave ($n)"
+ret=0
+$DIG soa dom6.example @10.53.0.2 -p 5300 > dig.out.test$n
 grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: removing dom6.example ($n)"
+ret=0
+$NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
+    server 10.53.0.1 5300
+    update delete masters.catalog1.example 3600 IN A 10.53.0.3
+    update delete masters.catalog1.example 3600 IN AAAA  fd92:7065:b8e:ffff::3
+    update delete 4346f565b4d63ddb99e5d2497ff22d04e878e8f8.zones.catalog1.example 3600 IN PTR dom6.example.
+    send
+END
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: waiting for slave to sync up ($n)"
+ret=1
+try=0
+while test $try -lt 45
+do
+    sleep 1
+    sed -n "$cur,"'$p' < ns2/named.run | grep "catz: deleting zone 'dom6.example' from catalog 'catalog1.example'" > /dev/null && {
+       ret=0
+       break
+    }
+    try=`expr $try + 1`
+done
+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)"
+echo "I: checking that dom6.example is no longer served by slave ($n)"
 ret=0
-[ -f "ns2/zonedir/__catz___default_catalog1.example_dom3.example.db" ] || ret=1
-[ -f "ns2/zonedir/__catz___default_catalog1.example_dom4.example.db" ] || ret=1
-if [ $ret != 0 ]; then echo "I:failed"; fi
+$DIG soa dom6.example @10.53.0.2 -p 5300 > dig.out.test$n
+grep "status: REFUSED" 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 a missing zone directory forces in-memory ($n)"
+echo "I:Checking that a missing zone directory forces in-memory ($n)"
 ret=0
 grep "'nonexistent' not found; zone files will not be saved" ns2/named.run > /dev/null || ret=1
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
+##########################################################################
+echo "I:Testing allow-query and allow-transfer ACLs"
 n=`expr $n + 1`
-echo "I:Adding a domain dom6.example to master via RNDC ($n)"
+echo "I: adding domains dom7.example and dom8.example to master via RNDC ($n)"
 ret=0
-echo "@ 3600 IN SOA . . 1 3600 3600 3600 3600" > ns1/dom6.example.db
-echo "@ IN NS invalid." >> ns1/dom6.example.db
-$RNDC -c ../common/rndc.conf -s 10.53.0.1 -p 9953  addzone dom6.example '{type master; file "dom6.example.db";};' || ret=1
-if [ $ret != 0 ]; then echo "I:failed"; fi
+echo "@ 3600 IN SOA . . 1 3600 3600 3600 3600" > ns1/dom7.example.db
+echo "@ IN NS invalid." >> ns1/dom7.example.db
+$RNDC -c ../common/rndc.conf -s 10.53.0.1 -p 9953  addzone dom7.example '{type master; file "dom7.example.db";};' || ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+echo "@ 3600 IN SOA . . 1 3600 3600 3600 3600" > ns1/dom8.example.db
+echo "@ IN NS invalid." >> ns1/dom8.example.db
+$RNDC -c ../common/rndc.conf -s 10.53.0.1 -p 9953  addzone dom8.example '{type master; file "dom8.example.db";};' || ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 n=`expr $n + 1`
-echo "I:checking that dom6.example is now served by master ($n)"
+echo "I: checking that dom7.example is now served by master ($n)"
 ret=0
-$DIG soa dom6.example @10.53.0.1 -p 5300 > dig.out.test$n
+$DIG soa dom7.example @10.53.0.1 -p 5300 > dig.out.test$n
 grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 cur=`awk 'BEGIN {l=0} /^/ {l++} END { print l }' ns2/named.run`
 
 n=`expr $n + 1`
-echo "I:Adding domain dom6.example to catalog1 zone with an allow-query statement ($n)"
+echo "I: adding domain dom7.example to catalog1 zone with an allow-query statement ($n)"
 ret=0
 $NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
     server 10.53.0.1 5300
-    update add 4346f565b4d63ddb99e5d2497ff22d04e878e8f8.zones.catalog1.example 3600 IN PTR dom6.example.
-    update add allow-query.4346f565b4d63ddb99e5d2497ff22d04e878e8f8.zones.catalog1.example 3600 IN APL 1:10.53.0.1/32 !1:10.53.0.0/30 1:0.0.0.0/0
+    update add 78833ec3c0059fd4540fee81c7eaddce088e7cd7.zones.catalog1.example 3600 IN PTR dom7.example.
+    update add allow-query.78833ec3c0059fd4540fee81c7eaddce088e7cd7.zones.catalog1.example 3600 IN APL 1:10.53.0.1/32 !1:10.53.0.0/30 1:0.0.0.0/0
     send
 END
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 n=`expr $n + 1`
-echo "I:waiting for slave to sync up ($n)"
+echo "I: waiting for slave to sync up ($n)"
 ret=1
 try=0
 while test $try -lt 45
 do
     sleep 1
-    sed -n "$cur,"'$p' < ns2/named.run | grep "catz: adding zone 'dom6.example' from catalog 'catalog1.example'" > /dev/null && {
+    sed -n "$cur,"'$p' < ns2/named.run | grep "catz: adding zone 'dom7.example' from catalog 'catalog1.example'" > /dev/null && {
        ret=0
        break
     }
@@ -420,104 +560,104 @@ try=0
 while test $try -lt 45
 do
     sleep 1
-    sed -n "$cur,"'$p' < ns2/named.run | grep "transfer of 'dom6.example/IN' from 10.53.0.1#5300: Transfer status: success" > /dev/null && {
+    sed -n "$cur,"'$p' < ns2/named.run | grep "transfer of 'dom7.example/IN' from 10.53.0.1#5300: Transfer status: success" > /dev/null && {
        ret=0
        break
     }
     try=`expr $try + 1`
 done
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 n=`expr $n + 1`
-echo "I:checking that dom6.example is accessible from 10.53.0.1 ($n)"
+echo "I: checking that dom7.example is accessible from 10.53.0.1 ($n)"
 ret=0
-$DIG soa dom6.example -b 10.53.0.1 @10.53.0.2 -p 5300 > dig.out.test$n
+$DIG soa dom7.example -b 10.53.0.1 @10.53.0.2 -p 5300 > dig.out.test$n
 grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 n=`expr $n + 1`
-echo "I:checking that dom6.example is not accessible from 10.53.0.2 ($n)"
+echo "I: checking that dom7.example is not accessible from 10.53.0.2 ($n)"
 ret=0
-$DIG soa dom6.example -b 10.53.0.2 @10.53.0.2 -p 5300 > dig.out.test$n
+$DIG soa dom7.example -b 10.53.0.2 @10.53.0.2 -p 5300 > dig.out.test$n
 grep "status: REFUSED" dig.out.test$n > /dev/null || ret=1
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 n=`expr $n + 1`
-echo "I:checking that dom6.example is accessible from 10.53.0.5 ($n)"
+echo "I: checking that dom7.example is accessible from 10.53.0.5 ($n)"
 ret=0
-$DIG soa dom6.example -b 10.53.0.5 @10.53.0.2 -p 5300 > dig.out.test$n
+$DIG soa dom7.example -b 10.53.0.5 @10.53.0.2 -p 5300 > dig.out.test$n
 grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 cur=`awk 'BEGIN {l=0} /^/ {l++} END { print l }' ns2/named.run`
 n=`expr $n + 1`
-echo "I:Adding global allow-query and allow-domain ACLs ($n)"
+echo "I: adding dom8.example domain and global allow-query and allow-transfer ACLs ($n)"
 ret=0
 $NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
     server 10.53.0.1 5300
-    update add 636722929740e507aaf27c502812fc395d30fb17.zones.catalog1.example 3600 IN PTR dom2.example.
+    update add cba95222e308baba42417be6021026fdf20827b6.zones.catalog1.example 3600 IN PTR dom8.example
     update add allow-query.catalog1.example 3600 IN APL 1:10.53.0.1/32
     update add allow-transfer.catalog1.example 3600 IN APL 1:10.53.0.2/32
     send
 END
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 n=`expr $n + 1`
-echo "I:waiting for slave to sync up ($n)"
+echo "I: waiting for slave to sync up ($n)"
 ret=1
 try=0
 while test $try -lt 45
 do
     sleep 1
-    sed -n "$cur,"'$p' < ns2/named.run | grep "catz: adding zone 'dom2.example' from catalog 'catalog1.example'" > /dev/null && {
+    sed -n "$cur,"'$p' < ns2/named.run | grep "catz: update_from_db: new zone merged" > /dev/null && {
        ret=0
        break
     }
     try=`expr $try + 1`
 done
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 n=`expr $n + 1`
-echo "I:checking that dom3.example is accessible from 10.53.0.1 ($n)"
+echo "I: checking that dom8.example is accessible from 10.53.0.1 ($n)"
 ret=0
-$DIG soa dom3.example -b 10.53.0.1 @10.53.0.2 -p 5300 > dig.out.test$n
+$DIG soa dom8.example -b 10.53.0.1 @10.53.0.2 -p 5300 > dig.out.test$n
 grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 n=`expr $n + 1`
-echo "I:checking that dom3.example is not accessible from 10.53.0.2 ($n)"
+echo "I: checking that dom8.example is not accessible from 10.53.0.2 ($n)"
 ret=0
-$DIG soa dom3.example -b 10.53.0.2 @10.53.0.2 -p 5300 > dig.out.test$n
+$DIG soa dom8.example -b 10.53.0.2 @10.53.0.2 -p 5300 > dig.out.test$n
 grep "status: REFUSED" dig.out.test$n > /dev/null || ret=1
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 n=`expr $n + 1`
-echo "I:checking that dom3.example is not AXFR accessible from 10.53.0.1 ($n)"
+echo "I: checking that dom8.example is not AXFR accessible from 10.53.0.1 ($n)"
 ret=0
-$DIG axfr dom3.example -b 10.53.0.1 @10.53.0.2 -p 5300 > dig.out.test$n
+$DIG axfr dom8.example -b 10.53.0.1 @10.53.0.2 -p 5300 > dig.out.test$n
 grep "Transfer failed." dig.out.test$n > /dev/null || ret=1
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 n=`expr $n + 1`
-echo "I:checking that dom3.example is AXFR accessible from 10.53.0.2 ($n)"
+echo "I: checking that dom8.example is AXFR accessible from 10.53.0.2 ($n)"
 ret=0
-$DIG axfr dom3.example -b 10.53.0.2 @10.53.0.2 -p 5300 > dig.out.test$n
+$DIG axfr dom8.example -b 10.53.0.2 @10.53.0.2 -p 5300 > dig.out.test$n
 grep -v "Transfer failed." dig.out.test$n > /dev/null || ret=1
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 cur=`awk 'BEGIN {l=0} /^/ {l++} END { print l }' ns2/named.run`
 n=`expr $n + 1`
-echo "I:Deleting global allow-query and allow-domain ACLs ($n)"
+echo "I: deleting global allow-query and allow-domain ACLs ($n)"
 ret=0
 $NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
     server 10.53.0.1 5300
@@ -525,7 +665,7 @@ $NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
     update delete allow-transfer.catalog1.example 3600 IN APL 1:10.53.0.2/32
     send
 END
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 ret=0
 try=0
@@ -538,42 +678,982 @@ do
     }
     try=`expr $try + 1`
 done
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 n=`expr $n + 1`
-echo "I:checking that dom3.example is accessible from 10.53.0.1 ($n)"
+echo "I: checking that dom8.example is accessible from 10.53.0.1 ($n)"
 ret=0
-$DIG soa dom3.example -b 10.53.0.1 @10.53.0.2 -p 5300 > dig.out.test$n
+$DIG soa dom8.example -b 10.53.0.1 @10.53.0.2 -p 5300 > dig.out.test$n
 grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 n=`expr $n + 1`
-echo "I:checking that dom3.example is accessible from 10.53.0.2 ($n)"
+echo "I: checking that dom8.example is accessible from 10.53.0.2 ($n)"
 ret=0
-$DIG soa dom3.example -b 10.53.0.2 @10.53.0.2 -p 5300 > dig.out.test$n
+$DIG soa dom8.example -b 10.53.0.2 @10.53.0.2 -p 5300 > dig.out.test$n
 grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 n=`expr $n + 1`
-echo "I:checking that dom3.example is AXFR accessible from 10.53.0.1 ($n)"
+echo "I: checking that dom8.example is AXFR accessible from 10.53.0.1 ($n)"
 ret=0
-$DIG axfr dom3.example -b 10.53.0.1 @10.53.0.2 -p 5300 > dig.out.test$n
+$DIG axfr dom8.example -b 10.53.0.1 @10.53.0.2 -p 5300 > dig.out.test$n
 grep -v "Transfer failed." dig.out.test$n > /dev/null || ret=1
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
 status=`expr $status + $ret`
 
 n=`expr $n + 1`
-echo "I:checking that dom3.example is AXFR accessible from 10.53.0.2 ($n)"
+echo "I: checking that dom8.example is AXFR accessible from 10.53.0.2 ($n)"
 ret=0
-$DIG axfr dom3.example -b 10.53.0.2 @10.53.0.2 -p 5300 > dig.out.test$n
+$DIG axfr dom8.example -b 10.53.0.2 @10.53.0.2 -p 5300 > dig.out.test$n
 grep -v "Transfer failed." dig.out.test$n > /dev/null || ret=1
-if [ $ret != 0 ]; then echo "I:failed"; fi
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+
+##########################################################################
+echo "I:Testing TSIG keys for masters set per-domain"
+n=`expr $n + 1`
+echo "I: adding a domain dom9.example to master via RNDC, with transfers allowed only with TSIG key ($n)"
+ret=0
+echo "@ 3600 IN SOA . . 1 3600 3600 3600 3600" > ns1/dom9.example.db
+echo "@ IN NS invalid." >> ns1/dom9.example.db
+$RNDC -c ../common/rndc.conf -s 10.53.0.1 -p 9953 addzone dom9.example '{type master; file "dom9.example.db"; allow-transfer { key tsig_key; }; };' || ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that dom9.example is now served by master ($n)"
+ret=0
+$DIG soa dom9.example @10.53.0.1 -p 5300 > dig.out.test$n
+grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+cur=`awk 'BEGIN {l=0} /^/ {l++} END { print l }' ns2/named.run`
+
+n=`expr $n + 1`
+echo "I: adding domain dom9.example to catalog1 zone with masters and tsig key($n)"
+ret=0
+$NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
+    server 10.53.0.1 5300
+    update add f0f989bc71c5c8ca3a1eb9c9ab5246521907e3af.zones.catalog1.example 3600 IN PTR dom9.example.
+    update add label1.masters.f0f989bc71c5c8ca3a1eb9c9ab5246521907e3af.zones.catalog1.example 3600 IN A 10.53.0.1
+    update add label1.masters.f0f989bc71c5c8ca3a1eb9c9ab5246521907e3af.zones.catalog1.example 3600 IN TXT "tsig_key"
+    send
+END
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: waiting for slave to sync up ($n)"
+ret=1
+try=0
+while test $try -lt 45
+do
+    sleep 1
+    sed -n "$cur,"'$p' < ns2/named.run | grep "catz: adding zone 'dom9.example' from catalog 'catalog1.example'" > /dev/null && {
+       ret=0
+       break
+    }
+    try=`expr $try + 1`
+done
+try=0
+while test $try -lt 45
+do
+    sleep 1
+    sed -n "$cur,"'$p' < ns2/named.run | grep "transfer of 'dom9.example/IN' from 10.53.0.1#5300: Transfer status: success" > /dev/null && {
+       ret=0
+       break
+    }
+    try=`expr $try + 1`
+done
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that dom9.example is accessible on slave ($n)"
+ret=0
+$DIG soa dom9.example @10.53.0.2 -p 5300 > dig.out.test$n
+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: deleting domain dom9.example from catalog1 zone($n)"
+ret=0
+$NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
+    server 10.53.0.1 5300
+    update delete f0f989bc71c5c8ca3a1eb9c9ab5246521907e3af.zones.catalog1.example 3600 IN PTR dom9.example.
+    update delete label1.masters.f0f989bc71c5c8ca3a1eb9c9ab5246521907e3af.zones.catalog1.example 3600 IN A 10.53.0.1
+    update delete label1.masters.f0f989bc71c5c8ca3a1eb9c9ab5246521907e3af.zones.catalog1.example 3600 IN TXT "tsig_key"
+    send
+END
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: waiting for slave to sync up ($n)"
+ret=1
+try=0
+while test $try -lt 45
+do
+    sleep 1
+    sed -n "$cur,"'$p' < ns2/named.run | grep "catz: deleting zone 'dom9.example' from catalog 'catalog1.example'" > /dev/null && {
+       ret=0
+       break
+    }
+    try=`expr $try + 1`
+done
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that dom9.example is no longer accessible on slave ($n)"
+ret=0
+$DIG soa dom9.example @10.53.0.2 -p 5300 > dig.out.test$n
+grep "status: REFUSED" dig.out.test$n > /dev/null || ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+##########################################################################
+echo "I:Testing very long domain in catalog"
+n=`expr $n + 1`
+echo "I: checking that this.is.a.very.very.long.long.long.domain.that.will.cause.catalog.zones.to.generate.hash.instead.of.using.regular.filename.dom10.example is not served by master ($n)"
+ret=0
+$DIG soa this.is.a.very.very.long.long.long.domain.that.will.cause.catalog.zones.to.generate.hash.instead.of.using.regular.filename.dom10.example @10.53.0.1 -p 5300 > dig.out.test$n
+grep "status: REFUSED" 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: Adding a domain this.is.a.very.very.long.long.long.domain.that.will.cause.catalog.zones.to.generate.hash.instead.of.using.regular.filename.dom10.example to master via RNDC ($n)"
+ret=0
+echo "@ 3600 IN SOA . . 1 3600 3600 3600 3600" > ns1/dom10.example.db
+echo "@ IN NS invalid." >> ns1/dom10.example.db
+$RNDC -c ../common/rndc.conf -s 10.53.0.1 -p 9953  addzone this.is.a.very.very.long.long.long.domain.that.will.cause.catalog.zones.to.generate.hash.instead.of.using.regular.filename.dom10.example '{type master; file "dom10.example.db";};' || ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that this.is.a.very.very.long.long.long.domain.that.will.cause.catalog.zones.to.generate.hash.instead.of.using.regular.filename.dom10.example is now served by master ($n)"
+ret=0
+$DIG soa this.is.a.very.very.long.long.long.domain.that.will.cause.catalog.zones.to.generate.hash.instead.of.using.regular.filename.dom10.example @10.53.0.1 -p 5300 > dig.out.test$n
+grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+cur=`awk 'BEGIN {l=0} /^/ {l++} END { print l }' ns2/named.run`
+
+n=`expr $n + 1`
+echo "I: Adding domain this.is.a.very.very.long.long.long.domain.that.will.cause.catalog.zones.to.generate.hash.instead.of.using.regular.filename.dom10.example to catalog1 zone ($n)"
+ret=0
+$NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
+    server 10.53.0.1 5300
+    update add 825f48b1ce1b4cf5a041d20255a0c8e98d114858.zones.catalog1.example 3600 IN PTR this.is.a.very.very.long.long.long.domain.that.will.cause.catalog.zones.to.generate.hash.instead.of.using.regular.filename.dom10.example.
+    send
+END
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: waiting for slave to sync up ($n)"
+ret=1
+try=0
+while test $try -lt 45
+do
+    sleep 1
+    sed -n "$cur,"'$p' < ns2/named.run | grep "catz: adding zone 'this.is.a.very.very.long.long.long.domain.that.will.cause.catalog.zones.to.generate.hash.instead.of.using.regular.filename.dom10.example' from catalog 'catalog1.example'" > /dev/null && {
+       ret=0
+       break
+    }
+    try=`expr $try + 1`
+done
+try=0
+while test $try -lt 45
+do
+    sleep 1
+    sed -n "$cur,"'$p' < ns2/named.run | grep "transfer of 'this.is.a.very.very.long.long.long.domain.that.will.cause.catalog.zones.to.generate.hash.instead.of.using.regular.filename.dom10.example/IN' from 10.53.0.1#5300: Transfer status: success" > /dev/null && {
+       ret=0
+       break
+    }
+    try=`expr $try + 1`
+done
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that this.is.a.very.very.long.long.long.domain.that.will.cause.catalog.zones.to.generate.hash.instead.of.using.regular.filename.dom10.example is served by slave ($n)"
+ret=0
+$DIG soa this.is.a.very.very.long.long.long.domain.that.will.cause.catalog.zones.to.generate.hash.instead.of.using.regular.filename.dom10.example @10.53.0.2 -p 5300 > dig.out.test$n
+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 with a hashed filename($n)"
+ret=0
+[ -f "ns2/zonedir/__catz__4d70696f2335687069467f11f5d5378c480383f97782e553fb2d04a7bb2a23ed.db" ] || ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: removing domain this.is.a.very.very.long.long.long.domain.that.will.cause.catalog.zones.to.generate.hash.instead.of.using.regular.filename.dom10.example from catalog1 zone ($n)"
+ret=0
+$NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
+   server 10.53.0.1 5300
+   update delete 825f48b1ce1b4cf5a041d20255a0c8e98d114858.zones.catalog1.example
+   send
+END
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: waiting for slave to sync up ($n)"
+ret=1
+try=0
+while test $try -lt 45
+do
+    sleep 1
+    sed -n "$cur,"'$p' < ns2/named.run | grep "catz: deleting zone 'this.is.a.very.very.long.long.long.domain.that.will.cause.catalog.zones.to.generate.hash.instead.of.using.regular.filename.dom10.example' from catalog 'catalog1.example'" > /dev/null && {
+       ret=0
+       break
+    }
+    try=`expr $try + 1`
+done
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that this.is.a.very.very.long.long.long.domain.that.will.cause.catalog.zones.to.generate.hash.instead.of.using.regular.filename.dom10.example is not served by slave ($n)"
+ret=0
+$DIG soa this.is.a.very.very.long.long.long.domain.that.will.cause.catalog.zones.to.generate.hash.instead.of.using.regular.filename.dom10.example @10.53.0.2 -p 5300 > dig.out.test$n
+grep "status: REFUSED" 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 emptied ($n)"
+ret=0
+[ -f "ns2/zonedir/__catz__4d70696f2335687069467f11f5d5378c480383f97782e553fb2d04a7bb2a23ed.db" ] && ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
 
+##########################################################################
+echo "I:Testing adding a domain and a subdomain of it"
+n=`expr $n + 1`
+echo "I: checking that dom11.example is not served by master ($n)"
+ret=0
+$DIG soa dom11.example @10.53.0.1 -p 5300 > dig.out.test$n
+grep "status: REFUSED" 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: Adding a domain dom11.example to master via RNDC ($n)"
+ret=0
+echo "@ 3600 IN SOA . . 1 3600 3600 3600 3600" > ns1/dom11.example.db
+echo "@ IN NS invalid." >> ns1/dom11.example.db
+$RNDC -c ../common/rndc.conf -s 10.53.0.1 -p 9953  addzone dom11.example '{type master; file "dom11.example.db";};' || ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that dom11.example is now served by master ($n)"
+ret=0
+$DIG soa dom11.example @10.53.0.1 -p 5300 > dig.out.test$n
+grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+cur=`awk 'BEGIN {l=0} /^/ {l++} END { print l }' ns2/named.run`
+
+n=`expr $n + 1`
+echo "I: Adding domain dom11.example to catalog1 zone ($n)"
+ret=0
+$NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
+    server 10.53.0.1 5300
+    update add 0580d70e769c86c8b951a488d8b776627f427d7a.zones.catalog1.example 3600 IN PTR dom11.example.
+    send
+END
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: waiting for slave to sync up ($n)"
+ret=1
+try=0
+while test $try -lt 45
+do
+    sleep 1
+    sed -n "$cur,"'$p' < ns2/named.run | grep "catz: adding zone 'dom11.example' from catalog 'catalog1.example'" > /dev/null && {
+       ret=0
+       break
+    }
+    try=`expr $try + 1`
+done
+try=0
+while test $try -lt 45
+do
+    sleep 1
+    sed -n "$cur,"'$p' < ns2/named.run | grep "transfer of 'dom11.example/IN' from 10.53.0.1#5300: Transfer status: success" > /dev/null && {
+       ret=0
+       break
+    }
+    try=`expr $try + 1`
+done
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that dom11.example is served by slave ($n)"
+ret=0
+$DIG soa dom11.example @10.53.0.2 -p 5300 > dig.out.test$n
+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 subdomain.of.dom11.example is not served by master ($n)"
+ret=0
+$DIG soa subdomain.of.dom11.example @10.53.0.1 -p 5300 > dig.out.test$n
+grep "status: NXDOMAIN" 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: Adding a domain subdomain.of.dom11.example to master via RNDC ($n)"
+ret=0
+echo "@ 3600 IN SOA . . 1 3600 3600 3600 3600" > ns1/subdomain.of.dom11.example.db
+echo "@ IN NS invalid." >> ns1/subdomain.of.dom11.example.db
+$RNDC -c ../common/rndc.conf -s 10.53.0.1 -p 9953  addzone subdomain.of.dom11.example '{type master; file "subdomain.of.dom11.example.db";};' || ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that subdomain.of.dom11.example is now served by master ($n)"
+ret=0
+$DIG soa subdomain.of.dom11.example @10.53.0.1 -p 5300 > dig.out.test$n
+grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+cur=`awk 'BEGIN {l=0} /^/ {l++} END { print l }' ns2/named.run`
+
+n=`expr $n + 1`
+echo "I: Adding domain subdomain.of.dom11.example to catalog1 zone ($n)"
+ret=0
+$NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
+    server 10.53.0.1 5300
+    update add 25557e0bdd10cb3710199bb421b776df160f241e.zones.catalog1.example 3600 IN PTR subdomain.of.dom11.example.
+    send
+END
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: waiting for slave to sync up ($n)"
+ret=1
+try=0
+while test $try -lt 45
+do
+    sleep 1
+    sed -n "$cur,"'$p' < ns2/named.run | grep "catz: adding zone 'subdomain.of.dom11.example' from catalog 'catalog1.example'" > /dev/null && {
+       ret=0
+       break
+    }
+    try=`expr $try + 1`
+done
+try=0
+while test $try -lt 45
+do
+    sleep 1
+    sed -n "$cur,"'$p' < ns2/named.run | grep "transfer of 'subdomain.of.dom11.example/IN' from 10.53.0.1#5300: Transfer status: success" > /dev/null && {
+       ret=0
+       break
+    }
+    try=`expr $try + 1`
+done
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that subdomain.of.dom11.example is served by slave ($n)"
+ret=0
+$DIG soa subdomain.of.dom11.example @10.53.0.2 -p 5300 > dig.out.test$n
+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: removing domain dom11.example from catalog1 zone ($n)"
+ret=0
+$NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
+   server 10.53.0.1 5300
+   update delete 0580d70e769c86c8b951a488d8b776627f427d7a.zones.catalog1.example
+   send
+END
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: waiting for slave to sync up ($n)"
+ret=1
+try=0
+while test $try -lt 45
+do
+    sleep 1
+    sed -n "$cur,"'$p' < ns2/named.run | grep "catz: deleting zone 'dom11.example' from catalog 'catalog1.example'" > /dev/null && {
+       ret=0
+       break
+    }
+    try=`expr $try + 1`
+done
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that dom11.example is not served by slave ($n)"
+ret=0
+$DIG soa dom11.example @10.53.0.2 -p 5300 > dig.out.test$n
+grep "status: REFUSED" 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 subdomain.of.dom11.example is still served by slave ($n)"
+ret=0
+$DIG soa subdomain.of.dom11.example @10.53.0.2 -p 5300 > dig.out.test$n
+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: removing domain subdomain.of.dom11.example from catalog1 zone ($n)"
+ret=0
+$NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
+   server 10.53.0.1 5300
+   update delete 25557e0bdd10cb3710199bb421b776df160f241e.zones.catalog1.example
+   send
+END
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: waiting for slave to sync up ($n)"
+ret=1
+try=0
+while test $try -lt 45
+do
+    sleep 1
+    sed -n "$cur,"'$p' < ns2/named.run | grep "catz: deleting zone 'subdomain.of.dom11.example' from catalog 'catalog1.example'" > /dev/null && {
+       ret=0
+       break
+    }
+    try=`expr $try + 1`
+done
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that subdomain.of.dom11.example is not served by slave ($n)"
+ret=0
+$DIG soa subdomain.of.dom11.example @10.53.0.2 -p 5300 > dig.out.test$n
+grep "status: REFUSED" dig.out.test$n > /dev/null || ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+
+##########################################################################
+echo "I:Testing adding a catalog zone at runtime with rndc reconfig"
+n=`expr $n + 1`
+echo "I: checking that dom12.example is not served by master ($n)"
+ret=0
+$DIG soa dom12.example @10.53.0.1 -p 5300 > dig.out.test$n
+grep "status: REFUSED" 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: Adding a domain dom12.example to master via RNDC ($n)"
+ret=0
+echo "@ 3600 IN SOA . . 1 3600 3600 3600 3600" > ns1/dom12.example.db
+echo "@ IN NS invalid." >> ns1/dom12.example.db
+$RNDC -c ../common/rndc.conf -s 10.53.0.1 -p 9953  addzone dom12.example '{type master; file "dom12.example.db";};' || ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that dom12.example is now served by master ($n)"
+ret=0
+$DIG soa dom12.example @10.53.0.1 -p 5300 > dig.out.test$n
+grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+cur=`awk 'BEGIN {l=0} /^/ {l++} END { print l }' ns2/named.run`
+
+n=`expr $n + 1`
+echo "I: Adding domain dom12.example to catalog4 zone ($n)"
+ret=0
+$NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
+    server 10.53.0.1 5300
+    update add 871d51e5433543c0f6fb263c40f359fbc152c8ae.zones.catalog4.example 3600 IN PTR dom12.example.
+    send
+END
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that dom12.example is not served by slave ($n)"
+ret=0
+$DIG soa dom12.example @10.53.0.2 -p 5300 > dig.out.test$n
+grep "status: REFUSED" 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: reconfiguring slave - adding catalog4 catalog zone ($n)"
+ret=0
+cat ns2/named.conf.in |sed -e "s/^#T1//g" > ns2/named.conf
+$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 reconfig || ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: waiting for slave to sync up ($n)"
+ret=1
+try=0
+while test $try -lt 45
+do
+    sleep 1
+    sed -n "$cur,"'$p' < ns2/named.run | grep "catz: adding zone 'dom12.example' from catalog 'catalog4.example'" > /dev/null && {
+       ret=0
+       break
+    }
+    try=`expr $try + 1`
+done
+try=0
+while test $try -lt 45
+do
+    sleep 1
+    sed -n "$cur,"'$p' < ns2/named.run | grep "transfer of 'dom12.example/IN' from 10.53.0.1#5300: Transfer status: success" > /dev/null && {
+       ret=0
+       break
+    }
+    try=`expr $try + 1`
+done
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that dom12.example is served by slave ($n)"
+ret=0
+$DIG soa dom12.example @10.53.0.2 -p 5300 > dig.out.test$n
+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: reconfiguring slave - removing catalog4 catalog zone ($n)"
+ret=0
+cat ns2/named.conf.in > ns2/named.conf
+$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 reconfig || ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that dom12.example is not served by slave ($n)"
+ret=0
+$DIG soa dom12.example @10.53.0.2 -p 5300 > dig.out.test$n
+grep "status: REFUSED" 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: removing domain dom12.example from catalog4 zone ($n)"
+ret=0
+$NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
+    server 10.53.0.1 5300
+    update delete 871d51e5433543c0f6fb263c40f359fbc152c8ae.zones.catalog4.example 3600 IN PTR dom12.example.
+    send
+END
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+##########################################################################
+echo "I:Testing having a zone in two different catalogs"
+n=`expr $n + 1`
+echo "I: checking that dom13.example is not served by master ($n)"
+ret=0
+$DIG soa dom13.example @10.53.0.1 -p 5300 > dig.out.test$n
+grep "status: REFUSED" 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: Adding a domain dom13.example to master ns1 via RNDC ($n)"
+ret=0
+echo "@ 3600 IN SOA . . 1 3600 3600 3600 3600" > ns1/dom13.example.db
+echo "@ IN NS invalid." >> ns1/dom13.example.db
+echo "@ IN A 192.0.2.1" >> ns1/dom13.example.db
+$RNDC -c ../common/rndc.conf -s 10.53.0.1 -p 9953  addzone dom13.example '{type master; file "dom13.example.db";};' || ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that dom13.example is now served by master ns1 ($n)"
+ret=0
+$DIG soa dom13.example @10.53.0.1 -p 5300 > dig.out.test$n
+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: Adding a domain dom13.example to master ns3 via RNDC ($n)"
+ret=0
+echo "@ 3600 IN SOA . . 1 3600 3600 3600 3600" > ns3/dom13.example.db
+echo "@ IN NS invalid." >> ns3/dom13.example.db
+echo "@ IN A 192.0.2.2" >> ns3/dom13.example.db
+$RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 addzone dom13.example '{type master; file "dom13.example.db";};' || ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that dom13.example is now served by master ns3 ($n)"
+ret=0
+$DIG soa dom13.example @10.53.0.3 -p 5300 > dig.out.test$n
+grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+
+cur=`awk 'BEGIN {l=0} /^/ {l++} END { print l }' ns2/named.run`
+
+n=`expr $n + 1`
+echo "I: Adding domain dom13.example to catalog1 zone with ns1 as master($n)"
+ret=0
+$NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
+    server 10.53.0.1 5300
+    update add 8d7989c746b3f92b3bba2479e72afd977198363f.zones.catalog1.example 3600 IN PTR dom13.example.
+    update add masters.8d7989c746b3f92b3bba2479e72afd977198363f.zones.catalog1.example 3600 IN A 10.53.0.1
+    send
+END
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: waiting for slave to sync up ($n)"
+ret=1
+try=0
+while test $try -lt 45
+do
+    sleep 1
+    sed -n "$cur,"'$p' < ns2/named.run | grep "catz: adding zone 'dom13.example' from catalog 'catalog1.example'" > /dev/null && {
+       ret=0
+       break
+    }
+    try=`expr $try + 1`
+done
+try=0
+while test $try -lt 45
+do
+    sleep 1
+    sed -n "$cur,"'$p' < ns2/named.run | grep "transfer of 'dom13.example/IN' from 10.53.0.1#5300: Transfer status: success" > /dev/null && {
+       ret=0
+       break
+    }
+    try=`expr $try + 1`
+done
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+cur=`awk 'BEGIN {l=0} /^/ {l++} END { print l }' ns2/named.run`
+
+n=`expr $n + 1`
+echo "I: checking that dom13.example is served by slave and that it's the one from ns1 ($n)"
+ret=0
+$DIG a dom13.example @10.53.0.2 -p 5300 > dig.out.test$n
+grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+grep "192.0.2.1" 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: Adding domain dom13.example to catalog2 zone with ns3 as master($n)"
+ret=0
+$NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
+    server 10.53.0.3 5300
+    update add 8d7989c746b3f92b3bba2479e72afd977198363f.zones.catalog2.example 3600 IN PTR dom13.example.
+    update add masters.8d7989c746b3f92b3bba2479e72afd977198363f.zones.catalog2.example 3600 IN A 10.53.0.3
+    send
+END
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: waiting for slave to sync up ($n)"
+ret=1
+try=0
+while test $try -lt 45
+do
+    sleep 1
+    sed -n "$cur,"'$p' < ns2/named.run | grep "catz: update_from_db: new zone merged" > /dev/null && {
+       ret=0
+       break
+    }
+    try=`expr $try + 1`
+done
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that dom13.example is served by slave and that it's still the one from ns1 ($n)"
+ret=0
+$DIG a dom13.example @10.53.0.2 -p 5300 > dig.out.test$n
+grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+grep "192.0.2.1" dig.out.test$n > /dev/null || ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+cur=`awk 'BEGIN {l=0} /^/ {l++} END { print l }' ns2/named.run`
+
+n=`expr $n + 1`
+echo "I: Deleting domain dom13.example from catalog2 ($n)"
+ret=0
+$NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
+    server 10.53.0.3 5300
+    update delete 8d7989c746b3f92b3bba2479e72afd977198363f.zones.catalog2.example 3600 IN PTR dom13.example.
+    update delete masters.8d7989c746b3f92b3bba2479e72afd977198363f.zones.catalog2.example 3600 IN A 10.53.0.3
+    send
+END
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: waiting for slave to sync up ($n)"
+ret=1
+try=0
+while test $try -lt 45
+do
+    sleep 1
+    sed -n "$cur,"'$p' < ns2/named.run | grep "catz: update_from_db: new zone merged" > /dev/null && {
+       ret=0
+       break
+    }
+    try=`expr $try + 1`
+done
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that dom13.example is served by slave and that it's still the one from ns1 ($n)"
+ret=0
+$DIG a dom13.example @10.53.0.2 -p 5300 > dig.out.test$n
+grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+grep "192.0.2.1" 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: Deleting domain dom13.example from catalog1 ($n)"
+ret=0
+$NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
+    server 10.53.0.1 5300
+    update delete 8d7989c746b3f92b3bba2479e72afd977198363f.zones.catalog1.example 3600 IN PTR dom13.example.
+    update delete masters.8d7989c746b3f92b3bba2479e72afd977198363f.zones.catalog1.example 3600 IN A 10.53.0.2
+    send
+END
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: waiting for slave to sync up ($n)"
+ret=1
+try=0
+while test $try -lt 45
+do
+    sleep 1
+    sed -n "$cur,"'$p' < ns2/named.run | grep "catz: update_from_db: new zone merged" > /dev/null && {
+       ret=0
+       break
+    }
+    try=`expr $try + 1`
+done
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that dom13.example is no longer served by slave ($n)"
+ret=0
+$DIG a dom13.example @10.53.0.2 -p 5300 > dig.out.test$n
+grep "status: REFUSED" dig.out.test$n > /dev/null || ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+##########################################################################
+echo "I:Testing having a regular zone and a zone in catalog zone of the same name"
+n=`expr $n + 1`
+echo "I: checking that dom14.example is not served by master ($n)"
+ret=0
+$DIG soa dom14.example @10.53.0.1 -p 5300 > dig.out.test$n
+grep "status: REFUSED" 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: Adding a domain dom14.example to master ns1 via RNDC ($n)"
+ret=0
+echo "@ 3600 IN SOA . . 1 3600 3600 3600 3600" > ns1/dom14.example.db
+echo "@ IN NS invalid." >> ns1/dom14.example.db
+echo "@ IN A 192.0.2.1" >> ns1/dom14.example.db
+$RNDC -c ../common/rndc.conf -s 10.53.0.1 -p 9953  addzone dom14.example '{type master; file "dom14.example.db";};' || ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that dom14.example is now served by master ns1 ($n)"
+ret=0
+$DIG soa dom14.example @10.53.0.1 -p 5300 > dig.out.test$n
+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: Adding a domain dom14.example to master ns3 via RNDC ($n)"
+ret=0
+echo "@ 3600 IN SOA . . 1 3600 3600 3600 3600" > ns3/dom14.example.db
+echo "@ IN NS invalid." >> ns3/dom14.example.db
+echo "@ IN A 192.0.2.2" >> ns3/dom14.example.db
+$RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 addzone dom14.example '{type master; file "dom14.example.db";};' || ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that dom14.example is now served by master ns3 ($n)"
+ret=0
+$DIG soa dom14.example @10.53.0.3 -p 5300 > dig.out.test$n
+grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+cur=`awk 'BEGIN {l=0} /^/ {l++} END { print l }' ns2/named.run`
+
+n=`expr $n + 1`
+echo "I: Adding domain dom14.example with rndc with ns1 as master($n)"
+ret=0
+$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 addzone dom14.example '{type slave; masters {10.53.0.1;};};' || ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: waiting for slave to sync up ($n)"
+ret=1
+try=0
+while test $try -lt 45
+do
+    sleep 1
+    sed -n "$cur,"'$p' < ns2/named.run | grep "transfer of 'dom14.example/IN' from 10.53.0.1#5300: Transfer status: success" > /dev/null && {
+       ret=0
+       break
+    }
+    try=`expr $try + 1`
+done
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+cur=`awk 'BEGIN {l=0} /^/ {l++} END { print l }' ns2/named.run`
+
+n=`expr $n + 1`
+echo "I: checking that dom14.example is served by slave and that it's the one from ns1 ($n)"
+ret=0
+$DIG a dom14.example @10.53.0.2 -p 5300 > dig.out.test$n
+grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+grep "192.0.2.1" 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: Adding domain dom14.example to catalog2 zone with ns3 as master($n)"
+ret=0
+$NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
+    server 10.53.0.3 5300
+    update add 45e3d45ea5f7bd01c395ccbde6ae2e750a3ee8ab.zones.catalog2.example 3600 IN PTR dom14.example.
+    update add masters.45e3d45ea5f7bd01c395ccbde6ae2e750a3ee8ab.zones.catalog2.example 3600 IN A 10.53.0.3
+    send
+END
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: waiting for slave to sync up ($n)"
+ret=1
+try=0
+while test $try -lt 45
+do
+    sleep 1
+    sed -n "$cur,"'$p' < ns2/named.run | grep "catz: update_from_db: new zone merged" > /dev/null && {
+       ret=0
+       break
+    }
+    try=`expr $try + 1`
+done
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that dom14.example is served by slave and that it's still the one from ns1 ($n)"
+ret=0
+$DIG a dom14.example @10.53.0.2 -p 5300 > dig.out.test$n
+grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+grep "192.0.2.1" dig.out.test$n > /dev/null || ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+cur=`awk 'BEGIN {l=0} /^/ {l++} END { print l }' ns2/named.run`
+
+n=`expr $n + 1`
+echo "I: Deleting domain dom14.example from catalog2 ($n)"
+ret=0
+$NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
+    server 10.53.0.3 5300
+    update delete 45e3d45ea5f7bd01c395ccbde6ae2e750a3ee8ab.zones.catalog2.example 3600 IN PTR dom14.example.
+    update delete masters.45e3d45ea5f7bd01c395ccbde6ae2e750a3ee8ab.zones.catalog2.example 3600 IN A 10.53.0.3
+    send
+END
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: waiting for slave to sync up ($n)"
+ret=1
+try=0
+while test $try -lt 45
+do
+    sleep 1
+    sed -n "$cur,"'$p' < ns2/named.run | grep "catz: update_from_db: new zone merged" > /dev/null && {
+       ret=0
+       break
+    }
+    try=`expr $try + 1`
+done
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that dom14.example is served by slave and that it's still the one from ns1 ($n)"
+ret=0
+$DIG a dom14.example @10.53.0.2 -p 5300 > dig.out.test$n
+grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+grep "192.0.2.1" dig.out.test$n > /dev/null || ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
 
 echo "I:exit status: $status"
 [ $status -eq 0 ] || exit 1
index fc97c7fe178f105f846964cecd1027b65e4a1f64..e1ba568c724ac26de555e6203b390310c8aba5f7 100644 (file)
@@ -68,10 +68,18 @@ struct dns_catz_zone {
        isc_event_t             updateevent;
 
        isc_boolean_t           active;
+       isc_boolean_t           db_registered;
 
        isc_refcount_t          refs;
 };
 
+static isc_result_t
+catz_process_zones_entry(dns_catz_zone_t *zone, dns_rdataset_t *value,
+                        dns_label_t *mhash);
+static isc_result_t
+catz_process_zones_suboption(dns_catz_zone_t *zone, dns_rdataset_t *value,
+                            dns_label_t *mhash, dns_name_t *name);
+
 /*%
  * Collection of catalog zones for a view
  */
@@ -89,10 +97,10 @@ struct dns_catz_zones {
 
 void
 dns_catz_options_init(dns_catz_options_t *options) {
-       options->masters.addrs = NULL;
-       options->masters.dscps = NULL;
-       options->masters.keys = NULL;
-       options->masters.count = 0;
+       dns_ipkeylist_init(&options->masters);
+
+       options->allow_query = NULL;
+       options->allow_transfer = NULL;
 
        options->allow_query = NULL;
        options->allow_transfer = NULL;
@@ -120,7 +128,6 @@ isc_result_t
 dns_catz_options_copy(isc_mem_t *mctx, const dns_catz_options_t *src,
                      dns_catz_options_t *dst)
 {
-       /* TODO error handling */
        REQUIRE(dst != NULL);
        REQUIRE(dst->masters.count == 0);
        REQUIRE(dst->allow_query == NULL);
@@ -143,7 +150,6 @@ dns_catz_options_copy(isc_mem_t *mctx, const dns_catz_options_t *src,
        if (src->allow_transfer != NULL)
                isc_buffer_dup(mctx, &dst->allow_transfer, src->allow_transfer);
 
-
        return (ISC_R_SUCCESS);
 }
 
@@ -176,16 +182,17 @@ dns_catz_entry_new(isc_mem_t *mctx, const dns_name_t *domain,
        isc_result_t result;
 
        REQUIRE(nentryp != NULL && *nentryp == NULL);
-       REQUIRE(domain != NULL);
 
        nentry = isc_mem_get(mctx, sizeof(dns_catz_entry_t));
        if (nentry == NULL)
                return (ISC_R_NOMEMORY);
 
        dns_name_init(&nentry->name, NULL);
-       result = dns_name_dup(domain, mctx, &nentry->name);
-       if (result != ISC_R_SUCCESS)
-               goto cleanup;
+       if (domain != NULL) {
+               result = dns_name_dup(domain, mctx, &nentry->name);
+               if (result != ISC_R_SUCCESS)
+                       goto cleanup;
+       }
 
        dns_catz_options_init(&nentry->opts);
        isc_refcount_init(&nentry->refs, 1);
@@ -245,7 +252,8 @@ dns_catz_entry_detach(dns_catz_zone_t *zone, dns_catz_entry_t **entryp) {
        isc_refcount_decrement(&entry->refs, &refs);
        if (refs == 0) {
                dns_catz_options_free(&entry->opts, mctx);
-               dns_name_free(&entry->name, mctx);
+               if (dns_name_dynamic(&entry->name))
+                       dns_name_free(&entry->name, mctx);
                isc_refcount_destroy(&entry->refs);
                isc_mem_put(mctx, entry, sizeof(dns_catz_entry_t));
        }
@@ -261,6 +269,10 @@ dns_catz_entry_validate(const dns_catz_entry_t *entry) {
 isc_boolean_t
 dns_catz_entry_cmp(const dns_catz_entry_t *ea, const dns_catz_entry_t *eb) {
        isc_region_t ra, rb;
+
+       if (ea == eb)
+               return (ISC_TRUE);
+
        if (ea->opts.masters.count != eb->opts.masters.count)
                return (ISC_FALSE);
 
@@ -269,8 +281,7 @@ dns_catz_entry_cmp(const dns_catz_entry_t *ea, const dns_catz_entry_t *eb) {
                return (ISC_FALSE);
 
        /* If one is NULL and the other isn't, the entries don't match */
-       if ((ea->opts.allow_query == NULL) !=
-           (eb->opts.allow_query == NULL))
+       if ((ea->opts.allow_query == NULL) != (eb->opts.allow_query == NULL))
                return (ISC_FALSE);
 
        /* If one is non-NULL, then they both are */
@@ -322,12 +333,11 @@ dns_catz_zone_resetdefoptions(dns_catz_zone_t *zone) {
 isc_result_t
 dns_catz_zones_merge(dns_catz_zone_t *target, dns_catz_zone_t *newzone) {
        isc_result_t result;
-       isc_ht_iter_t *iter = NULL;
-       char cznamebuf[DNS_NAME_FORMATSIZE];
-       char znamebuf[DNS_NAME_FORMATSIZE];
-       isc_buffer_t czname;
-       isc_buffer_t zname;
+       isc_ht_iter_t *iter1 = NULL, *iter2 = NULL;
+       char czname[DNS_NAME_FORMATSIZE];
+       char zname[DNS_NAME_FORMATSIZE];
        dns_catz_zoneop_fn_t addzone, modzone, delzone;
+
        REQUIRE(target != NULL);
        REQUIRE(newzone != NULL);
 
@@ -345,35 +355,47 @@ dns_catz_zones_merge(dns_catz_zone_t *target, dns_catz_zone_t *newzone) {
        dns_catz_options_setdefault(target->catzs->mctx, &target->defoptions,
                                    &target->zoneoptions);
 
-       isc_buffer_init(&czname, cznamebuf, DNS_NAME_FORMATSIZE);
-       isc_buffer_init(&zname, znamebuf, DNS_NAME_FORMATSIZE);
-       dns_name_totext(&target->name, ISC_TRUE, &czname);
-       isc_buffer_putuint8(&czname, 0);
+       dns_name_format(&target->name, czname, DNS_NAME_FORMATSIZE);
+
+       result = isc_ht_iter_create(newzone->entries, &iter1);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+
+       result = isc_ht_iter_create(target->entries, &iter2);
+       if (result != ISC_R_SUCCESS) {
+               isc_ht_iter_destroy(&iter1);
+               return (result);
+       }
 
        /*
-        * first - walk the new zone and find all nodes that are not in the
-        * old zone, or are in both zones and are modified
+        * First - walk the new zone and find all nodes that are not in the
+        * old zone, or are in both zones and are modified.
         */
-       result = isc_ht_iter_create(newzone->entries, &iter);
-       INSIST(result == ISC_R_SUCCESS);
-
-       result = isc_ht_iter_first(iter);
-       for (result = isc_ht_iter_first(iter);
+       for (result = isc_ht_iter_first(iter1);
             result == ISC_R_SUCCESS;
-            result = isc_ht_iter_next(iter))
+            result = isc_ht_iter_next(iter1))
        {
                dns_catz_entry_t *nentry;
                dns_catz_entry_t *oentry;
                unsigned char * key;
                size_t keysize;
 
-               isc_ht_iter_current(iter, (void **) &nentry);
-               isc_ht_iter_currentkey(iter, &key, &keysize);
+               isc_ht_iter_current(iter1, (void **) &nentry);
+               isc_ht_iter_currentkey(iter1, &key, &keysize);
 
-               isc_buffer_clear(&zname);
-               dns_name_totext(&nentry->name, ISC_TRUE, &zname);
-               isc_buffer_putuint8(&zname, 0);
+               /*
+                * Spurious record that came from suboption without main
+                * record, ignored.
+                */
+               if (dns_name_countlabels(&nentry->name) == 0)
+                       continue;
+
+               dns_name_format(&nentry->name, zname, DNS_NAME_FORMATSIZE);
 
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                             DNS_LOGMODULE_MASTER, ISC_LOG_DEBUG(3),
+                             "catz: iterating over '%s' from catalog '%s'",
+                             zname, czname);
                dns_catz_options_setdefault(target->catzs->mctx,
                                            &target->zoneoptions,
                                            &nentry->opts);
@@ -388,7 +410,7 @@ dns_catz_zones_merge(dns_catz_zone_t *target, dns_catz_zone_t *newzone) {
                                      DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
                                      "catz: adding zone '%s' from catalog "
                                      "'%s' - %s",
-                                     znamebuf, cznamebuf,
+                                     zname, czname,
                                      isc_result_totext(result));
                        continue;
                }
@@ -401,7 +423,7 @@ dns_catz_zones_merge(dns_catz_zone_t *target, dns_catz_zone_t *newzone) {
                                      DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
                                      "catz: modifying zone '%s' from catalog "
                                      "'%s' - %s",
-                                     znamebuf, cznamebuf,
+                                     zname, czname,
                                      isc_result_totext(result));
                }
 
@@ -410,37 +432,32 @@ dns_catz_zones_merge(dns_catz_zone_t *target, dns_catz_zone_t *newzone) {
                RUNTIME_CHECK(result == ISC_R_SUCCESS);
        }
        RUNTIME_CHECK(result == ISC_R_NOMORE);
-       isc_ht_iter_destroy(&iter);
+       isc_ht_iter_destroy(&iter1);
 
        /*
-        * then - walk the old zone; only deleted entries should remain
+        * Then - walk the old zone; only deleted entries should remain.
         */
-       result = isc_ht_iter_create(target->entries, &iter);
-       INSIST(result == ISC_R_SUCCESS);
-
-       for (result = isc_ht_iter_first(iter);
+       for (result = isc_ht_iter_first(iter2);
             result == ISC_R_SUCCESS;
-            result = isc_ht_iter_delcurrent_next(iter))
+            result = isc_ht_iter_delcurrent_next(iter2))
        {
                dns_catz_entry_t *entry;
-               isc_ht_iter_current(iter, (void **) &entry);
+               isc_ht_iter_current(iter2, (void **) &entry);
 
-               isc_buffer_clear(&zname);
-               dns_name_totext(&entry->name, ISC_TRUE, &zname);
-               isc_buffer_putuint8(&zname, 0);
+               dns_name_format(&entry->name, zname, DNS_NAME_FORMATSIZE);
                result = delzone(entry, target, target->catzs->view,
                              target->catzs->taskmgr,
                              target->catzs->zmm->udata);
                isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
                              DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
                              "catz: deleting zone '%s' from catalog '%s' - %s",
-                             znamebuf, cznamebuf, isc_result_totext(result));
+                             zname, czname, isc_result_totext(result));
                dns_catz_entry_detach(target, &entry);
        }
        RUNTIME_CHECK(result == ISC_R_NOMORE);
-       isc_ht_iter_destroy(&iter);
+       isc_ht_iter_destroy(&iter2);
 
-       /* at this moment target->entries has to be be empty */
+       /* At this moment target->entries has to be be empty. */
        INSIST(isc_ht_count(target->entries) == 0);
        isc_ht_destroy(&target->entries);
 
@@ -506,7 +523,7 @@ void
 dns_catz_catzs_set_view(dns_catz_zones_t *catzs, dns_view_t *view) {
        REQUIRE(catzs != NULL);
        REQUIRE(view != NULL);
-       /* either it's a new one or it's being reconfigured */
+       /* Either it's a new one or it's being reconfigured. */
        REQUIRE(catzs->view == NULL || !strcmp(catzs->view->name, view->name));
 
        catzs->view = view;
@@ -514,7 +531,8 @@ dns_catz_catzs_set_view(dns_catz_zones_t *catzs, dns_view_t *view) {
 
 isc_result_t
 dns_catz_new_zone(dns_catz_zones_t *catzs, dns_catz_zone_t **zonep,
-                 const dns_name_t *name) {
+                 const dns_name_t *name)
+{
        isc_result_t result;
        dns_catz_zone_t *new_zone;
 
@@ -552,6 +570,7 @@ dns_catz_new_zone(dns_catz_zones_t *catzs, dns_catz_zone_t **zonep,
        dns_catz_options_init(&new_zone->defoptions);
        dns_catz_options_init(&new_zone->zoneoptions);
        new_zone->active = ISC_TRUE;
+       new_zone->db_registered = ISC_FALSE;
        new_zone->version = (isc_uint32_t)(-1);
        isc_refcount_init(&new_zone->refs, 1);
 
@@ -575,10 +594,15 @@ dns_catz_add_zone(dns_catz_zones_t *catzs, const dns_name_t *name,
 {
        dns_catz_zone_t *new_zone = NULL;
        isc_result_t result;
+       char zname[DNS_NAME_FORMATSIZE];
 
        REQUIRE(catzs != NULL);
        REQUIRE(name != NULL);
        REQUIRE(zonep != NULL && *zonep == NULL);
+       dns_name_format(name, zname, DNS_NAME_FORMATSIZE);
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                     DNS_LOGMODULE_MASTER, ISC_LOG_DEBUG(3),
+                     "catz: dns_catz_add_zone %s", zname);
 
        LOCK(&catzs->lock);
 
@@ -633,12 +657,14 @@ dns_catz_catzs_attach(dns_catz_zones_t *catzs, dns_catz_zones_t **catzsp) {
 void
 dns_catz_zone_attach(dns_catz_zone_t *zone, dns_catz_zone_t **zonep) {
        REQUIRE(zonep != NULL && *zonep == NULL);
+
        isc_refcount_increment(&zone->refs, NULL);
        *zonep = zone;
 }
 
 void
 dns_catz_zone_detach(dns_catz_zone_t **zonep) {
+       isc_result_t result;
        dns_catz_zone_t *zone;
        isc_ht_iter_t *iter = NULL;
        isc_mem_t *mctx;
@@ -651,8 +677,6 @@ dns_catz_zone_detach(dns_catz_zone_t **zonep) {
        isc_refcount_decrement(&zone->refs, &refs);
        if (refs == 0) {
                if (zone->entries != NULL) {
-                       isc_result_t result;
-
                        result = isc_ht_iter_create(zone->entries, &iter);
                        INSIST(result == ISC_R_SUCCESS);
                        for (result = isc_ht_iter_first(iter);
@@ -660,22 +684,36 @@ dns_catz_zone_detach(dns_catz_zone_t **zonep) {
                             result = isc_ht_iter_delcurrent_next(iter))
                        {
                                dns_catz_entry_t *entry;
+
                                isc_ht_iter_current(iter, (void **) &entry);
                                dns_catz_entry_detach(zone, &entry);
                        }
                        INSIST(result == ISC_R_NOMORE);
                        isc_ht_iter_destroy(&iter);
 
-                       /* the hashtable has to be empty now */
+                       /* The hashtable has to be empty now. */
                        INSIST(isc_ht_count(zone->entries) == 0);
                        isc_ht_destroy(&zone->entries);
                }
                mctx = zone->catzs->mctx;
                isc_timer_detach(&zone->updatetimer);
                isc_refcount_destroy(&zone->refs);
+               if (zone->db_registered == ISC_TRUE) {
+                       result = dns_db_updatenotify_unregister(zone->db,
+                                                   dns_catz_dbupdate_callback,
+                                                   zone->catzs);
+                       INSIST(result == ISC_R_SUCCESS);
+               }
+               if (zone->dbversion)
+                       dns_db_closeversion(zone->db, &zone->dbversion,
+                                           ISC_FALSE);
+               if (zone->db != NULL)
+                       dns_db_detach(&zone->db);
+
                dns_name_free(&zone->name, mctx);
                dns_catz_options_free(&zone->defoptions, mctx);
                dns_catz_options_free(&zone->zoneoptions, mctx);
+
                zone->catzs = NULL;
                isc_mem_put(mctx, zone, sizeof(dns_catz_zone_t));
        }
@@ -703,10 +741,10 @@ dns_catz_catzs_detach(dns_catz_zones_t ** catzsp) {
                        result = isc_ht_iter_create(catzs->zones, &iter);
                        INSIST(result == ISC_R_SUCCESS);
                        for (result = isc_ht_iter_first(iter);
-                            result == ISC_R_SUCCESS;
-                            result = isc_ht_iter_delcurrent_next(iter))
+                            result == ISC_R_SUCCESS;)
                        {
                                isc_ht_iter_current(iter, (void **) &zone);
+                               result = isc_ht_iter_delcurrent_next(iter);
                                dns_catz_zone_detach(&zone);
                        }
                        INSIST(result == ISC_R_NOMORE);
@@ -756,80 +794,92 @@ catz_get_option(const dns_label_t *option) {
 }
 
 static isc_result_t
-catz_process_global_list(dns_catz_zone_t *zone, catz_opt_t opt,
-                        dns_rdataset_t *value, dns_label_t *mhash)
+catz_process_zones(dns_catz_zone_t *zone, dns_rdataset_t *value,
+                  dns_name_t *name)
+{
+       dns_label_t mhash;
+       dns_name_t opt;
+
+       REQUIRE(zone != NULL);
+       REQUIRE(DNS_RDATASET_VALID(value));
+       REQUIRE(name != NULL);
+
+       if (value->rdclass != dns_rdataclass_in)
+
+       if (name->labels == 0)
+               return (ISC_R_FAILURE);
+
+       dns_name_getlabel(name, name->labels-1, &mhash);
+
+       if (name->labels == 1)
+               return (catz_process_zones_entry(zone, value, &mhash));
+       else {
+               dns_name_init(&opt, NULL);
+               dns_name_split(name, 1, &opt, NULL);
+               return (catz_process_zones_suboption(zone, value, &mhash, &opt));
+       }
+}
+
+static isc_result_t
+catz_process_zones_entry(dns_catz_zone_t *zone, dns_rdataset_t *value,
+                        dns_label_t *mhash)
 {
        isc_result_t result;
        dns_rdata_t rdata;
        dns_rdata_ptr_t ptr;
        dns_catz_entry_t *entry = NULL;
-       dns_name_t emptyname;
 
-       REQUIRE(zone != NULL);
-       REQUIRE(DNS_RDATASET_VALID(value));
-       REQUIRE(mhash != NULL);
-
-       dns_name_init(&emptyname, NULL);
-       switch (opt) {
-       case CATZ_OPT_ZONES:
-               if (value->rdclass != dns_rdataclass_in ||
-                   value->type != dns_rdatatype_ptr) {
-                       break;
-               }
+       /*
+        * We only take -first- value, as mhash must be
+        * different.
+        */
+       if (value->type != dns_rdatatype_ptr)
+               return (ISC_R_FAILURE);
 
-               /*
-                * We only take -first- value, as mhash must be
-                * different
-                */
-               result = dns_rdataset_first(value);
-               if (result != ISC_R_SUCCESS)
-                       break;
+       result = dns_rdataset_first(value);
+       if (result != ISC_R_SUCCESS)
+               return (ISC_R_FAILURE);
 
-               dns_rdata_init(&rdata);
-               dns_rdataset_current(value, &rdata);
+       dns_rdata_init(&rdata);
+       dns_rdataset_current(value, &rdata);
 
-               result = dns_rdata_tostruct(&rdata, &ptr, NULL);
-               RUNTIME_CHECK(result == ISC_R_SUCCESS);
+       result = dns_rdata_tostruct(&rdata, &ptr, NULL);
+       RUNTIME_CHECK(result == ISC_R_SUCCESS);
 
-               result = isc_ht_find(zone->entries, mhash->base,
-                                    mhash->length, (void **) &entry);
-               if (result == ISC_R_SUCCESS) {
-                       if (dns_name_countlabels(&entry->name) != 0) {
-                               /* we have a duplicate */
-                               dns_rdata_freestruct(&ptr);
-                               return (ISC_R_FAILURE);
-                       } else {
-                               result = dns_name_dup(&ptr.ptr,
-                                                     zone->catzs->mctx,
-                                                     &entry->name);
-                               if (result != ISC_R_SUCCESS) {
-                                       dns_rdata_freestruct(&ptr);
-                                       return (result);
-                               }
-                       }
+       result = isc_ht_find(zone->entries, mhash->base,
+                            mhash->length, (void **) &entry);
+       if (result == ISC_R_SUCCESS) {
+               if (dns_name_countlabels(&entry->name) != 0) {
+                       /* We have a duplicate. */
+                       dns_rdata_freestruct(&ptr);
+                       return (ISC_R_FAILURE);
                } else {
-                       result = dns_catz_entry_new(zone->catzs->mctx, &ptr.ptr,
-                                                   &entry);
+                       result = dns_name_dup(&ptr.ptr, zone->catzs->mctx,
+                                             &entry->name);
                        if (result != ISC_R_SUCCESS) {
                                dns_rdata_freestruct(&ptr);
                                return (result);
                        }
-
-                       result = isc_ht_add(zone->entries, mhash->base,
-                                           mhash->length, entry);
-                       if (result != ISC_R_SUCCESS) {
-                               dns_rdata_freestruct(&ptr);
-                               dns_catz_entry_detach(zone, &entry);
-                               return (result);
-                       }
+               }
+       } else {
+               result = dns_catz_entry_new(zone->catzs->mctx, &ptr.ptr,
+                                           &entry);
+               if (result != ISC_R_SUCCESS) {
+                       dns_rdata_freestruct(&ptr);
+                       return (result);
                }
 
-               dns_rdata_freestruct(&ptr);
-               break;
-       default:
-               return (ISC_R_FAILURE);
+               result = isc_ht_add(zone->entries, mhash->base,
+                                   mhash->length, entry);
+               if (result != ISC_R_SUCCESS) {
+                       dns_rdata_freestruct(&ptr);
+                       dns_catz_entry_detach(zone, &entry);
+                       return (result);
+               }
        }
 
+       dns_rdata_freestruct(&ptr);
+
        return (ISC_R_SUCCESS);
 }
 
@@ -866,6 +916,7 @@ catz_process_version(dns_catz_zone_t *zone, dns_rdataset_t *value) {
        result = dns_rdata_txt_current(&rdatatxt, &rdatastr);
        if (result != ISC_R_SUCCESS)
                goto cleanup;
+
        result = dns_rdata_txt_next(&rdatatxt);
        if (result != ISC_R_NOMORE) {
                result = ISC_R_FAILURE;
@@ -890,35 +941,164 @@ cleanup:
 }
 
 static isc_result_t
-catz_process_ipkl(dns_catz_zone_t *zone, dns_ipkeylist_t *ipkl,
-                 dns_rdataset_t *value)
+catz_process_masters(dns_catz_zone_t *zone, dns_ipkeylist_t *ipkl,
+                    dns_rdataset_t *value, dns_name_t *name)
 {
        isc_result_t result;
        dns_rdata_t rdata;
        dns_rdata_in_a_t rdata_a;
+       dns_rdata_in_aaaa_t rdata_aaaa;
+       dns_rdata_txt_t rdata_txt;
+       dns_rdata_txt_string_t rdatastr;
+       dns_name_t *keyname = NULL;
+       isc_mem_t *mctx;
+       char keycbuf[DNS_NAME_FORMATSIZE];
+       isc_buffer_t keybuf;
        unsigned int rcount;
+       unsigned int i;
 
        REQUIRE(zone != NULL);
        REQUIRE(ipkl != NULL);
        REQUIRE(DNS_RDATASET_VALID(value));
        REQUIRE(dns_rdataset_isassociated(value));
+       REQUIRE(name != NULL);
 
-       if (value->rdclass != dns_rdataclass_in ||
-           value->type != dns_rdatatype_a)
+       mctx = zone->catzs->mctx;
+       memset(&rdata_a, 0, sizeof(rdata_a));
+       memset(&rdata_aaaa, 0, sizeof(rdata_aaaa));
+       memset(&rdata_txt, 0, sizeof(rdata_txt));
+       isc_buffer_init(&keybuf, keycbuf, sizeof(keycbuf));
+
+       /*
+        * We have three possibilities here:
+        * - either empty name and IN A/IN AAAA record
+        * - label and IN A/IN AAAA
+        * - label and IN TXT - TSIG key name
+        */
+       if (value->rdclass != dns_rdataclass_in)
                return (ISC_R_FAILURE);
 
-       rcount = dns_rdataset_count(value);
+       if (name->labels > 0) {
+               isc_sockaddr_t sockaddr;
 
-       ipkl->addrs = isc_mem_reallocate(zone->catzs->mctx, ipkl->addrs,
-                                        (rcount * sizeof(isc_sockaddr_t)));
-       if (ipkl->addrs == NULL)
-               return (ISC_R_NOMEMORY);
+               /*
+                * We're pre-preparing the data once, we'll put it into
+                * the right spot in the masters array once we find it.
+                */
+               result = dns_rdataset_first(value);
+               RUNTIME_CHECK(result == ISC_R_SUCCESS);
+               dns_rdata_init(&rdata);
+               dns_rdataset_current(value, &rdata);
+               switch (value->type) {
+               case dns_rdatatype_a:
+                       result = dns_rdata_tostruct(&rdata, &rdata_a, NULL);
+                       RUNTIME_CHECK(result == ISC_R_SUCCESS);
+                       isc_sockaddr_fromin(&sockaddr, &rdata_a.in_addr, 0);
+                       break;
+               case dns_rdatatype_aaaa:
+                       result = dns_rdata_tostruct(&rdata, &rdata_aaaa, NULL);
+                       RUNTIME_CHECK(result == ISC_R_SUCCESS);
+                       isc_sockaddr_fromin6(&sockaddr, &rdata_aaaa.in6_addr, 0);
+                       break;
+               case dns_rdatatype_txt:
+                       result = dns_rdata_tostruct(&rdata, &rdata_txt, NULL);
+                       RUNTIME_CHECK(result == ISC_R_SUCCESS);
 
-       ipkl->keys = isc_mem_reallocate(zone->catzs->mctx, ipkl->keys,
-                                       (rcount * sizeof(dns_name_t *)));
-       if (ipkl->keys == NULL) {
-               isc_mem_free(zone->catzs->mctx, ipkl->addrs);
-               return (ISC_R_NOMEMORY);
+                       result = dns_rdata_txt_first(&rdata_txt);
+                       if (result != ISC_R_SUCCESS)
+                               return (result);
+
+                       result = dns_rdata_txt_current(&rdata_txt, &rdatastr);
+                       if (result != ISC_R_SUCCESS)
+                               return (result);
+
+                       result = dns_rdata_txt_next(&rdata_txt);
+                       if (result != ISC_R_NOMORE)
+                               return (ISC_R_FAILURE);
+
+                       /* rdatastr.length < DNS_NAME_MAXTEXT */
+                       keyname = isc_mem_get(mctx, sizeof(dns_name_t));
+                       if (keyname == NULL)
+                               return (ISC_R_NOMEMORY);
+                       dns_name_init(keyname, 0);
+                       memcpy(keycbuf, rdatastr.data, rdatastr.length);
+                       keycbuf[rdatastr.length] = 0;
+                       result = dns_name_fromstring(keyname, keycbuf, 0, mctx);
+                       if (result != ISC_R_SUCCESS) {
+                               dns_name_free(keyname, mctx);
+                               isc_mem_put(mctx, keyname, sizeof(dns_name_t));
+                               return (result);
+                       }
+                       break;
+               default:
+                       return (ISC_R_FAILURE);
+               }
+
+               /*
+                * We have to find the appropriate labeled record in masters
+                * if it exists.
+                * In common case we'll have no more than 3-4 records here so
+                * no optimization.
+                */
+               for (i = 0; i < ipkl->count; i++) {
+                       if (ipkl->labels != NULL &&
+                           !dns_name_compare(name, ipkl->labels[i]))
+                               break;
+               }
+
+               if (i < ipkl->count) { /* we have this record already */
+                       if (value->type == dns_rdatatype_txt)
+                               ipkl->keys[i] = keyname;
+                       else /* A/AAAA */
+                               memcpy(&ipkl->addrs[i], &sockaddr,
+                                      sizeof(isc_sockaddr_t));
+               } else {
+                       result = dns_ipkeylist_resize(mctx, ipkl,
+                                                     i+1);
+                       if (result != ISC_R_SUCCESS) {
+                               return (result);
+                       }
+
+                       ipkl->labels[i] = isc_mem_get(mctx, sizeof(dns_name_t));
+                       if (ipkl->labels == NULL)  {
+                               if (keyname != NULL) {
+                                       dns_name_free(keyname, mctx);
+                                       isc_mem_put(mctx, keyname,
+                                                   sizeof(dns_name_t));
+                               }
+                               return (ISC_R_NOMEMORY);
+                       }
+                       dns_name_init(ipkl->labels[i], NULL);
+                       result = dns_name_dup(name, mctx, ipkl->labels[i]);
+                       if (result != ISC_R_SUCCESS) {
+                               if (keyname != NULL) {
+                                       dns_name_free(keyname, mctx);
+                                       isc_mem_put(mctx, keyname,
+                                                   sizeof(dns_name_t));
+                               }
+                               return (result);
+                       }
+
+                       if (value->type == dns_rdatatype_txt)
+                               ipkl->keys[i] = keyname;
+                       else /* A/AAAA */
+                               memcpy(&ipkl->addrs[i], &sockaddr,
+                                      sizeof(isc_sockaddr_t));
+                       ipkl->count++;
+               }
+               return (ISC_R_SUCCESS);
+       }
+       /* else - 'simple' case - without labels */
+
+       if (value->type != dns_rdatatype_a &&
+           value->type != dns_rdatatype_aaaa)
+               return (ISC_R_FAILURE);
+
+       rcount = dns_rdataset_count(value) + ipkl->count;
+
+       result = dns_ipkeylist_resize(mctx, ipkl, rcount);
+       if (result != ISC_R_SUCCESS) {
+               return (result);
        }
 
        for (result = dns_rdataset_first(value);
@@ -927,16 +1107,22 @@ catz_process_ipkl(dns_catz_zone_t *zone, dns_ipkeylist_t *ipkl,
        {
                dns_rdata_init(&rdata);
                dns_rdataset_current(value, &rdata);
-
-               result = dns_rdata_tostruct(&rdata, &rdata_a, NULL);
-               RUNTIME_CHECK(result == ISC_R_SUCCESS);
-
                /*
                 * port 0 == take the default
                 */
-               isc_sockaddr_fromin(&ipkl->addrs[ipkl->count],
-                                   &rdata_a.in_addr, 0);
+               if (value->type == dns_rdatatype_a) {
+                       result = dns_rdata_tostruct(&rdata, &rdata_a, NULL);
+                       RUNTIME_CHECK(result == ISC_R_SUCCESS);
+                       isc_sockaddr_fromin(&ipkl->addrs[ipkl->count],
+                                           &rdata_a.in_addr, 0);
+               } else {
+                       result = dns_rdata_tostruct(&rdata, &rdata_aaaa, NULL);
+                       RUNTIME_CHECK(result == ISC_R_SUCCESS);
+                       isc_sockaddr_fromin6(&ipkl->addrs[ipkl->count],
+                                            &rdata_aaaa.in6_addr, 0);
+               }
                ipkl->keys[ipkl->count] = NULL;
+               ipkl->labels[ipkl->count] = NULL;
                ipkl->count++;
                dns_rdata_freestruct(&rdata_a);
        }
@@ -1022,22 +1208,30 @@ cleanup:
 }
 
 static isc_result_t
-catz_process_suboption(dns_catz_zone_t *zone, dns_label_t *mhash,
-                      catz_opt_t subopt, dns_rdataset_t *value)
+catz_process_zones_suboption(dns_catz_zone_t *zone, dns_rdataset_t *value,
+                            dns_label_t *mhash, dns_name_t *name)
 {
        isc_result_t result;
        dns_catz_entry_t *entry = NULL;
+       dns_label_t option;
+       dns_name_t prefix;
+       catz_opt_t opt;
 
        REQUIRE(zone != NULL);
        REQUIRE(mhash != NULL);
        REQUIRE(DNS_RDATASET_VALID(value));
 
+       if (name->labels == 0)
+               return (ISC_R_FAILURE);
+       dns_name_getlabel(name, name->labels - 1, &option);
+       opt = catz_get_option(&option);
+
        /*
-        * we're adding this entry now, in case the option is invalid we'll get
-        * rid of in verification phase
+        * We're adding this entry now, in case the option is invalid we'll get
+        * rid of it in verification phase.
         */
-       result = isc_ht_find(zone->entries, mhash->base,
-                            mhash->length, (void **) &entry);
+       result = isc_ht_find(zone->entries, mhash->base, mhash->length,
+                            (void **) &entry);
        if (result != ISC_R_SUCCESS) {
                result = dns_catz_entry_new(zone->catzs->mctx, NULL, &entry);
                if (result != ISC_R_SUCCESS)
@@ -1050,14 +1244,20 @@ catz_process_suboption(dns_catz_zone_t *zone, dns_label_t *mhash,
                }
        }
 
-       switch (subopt) {
+       dns_name_init(&prefix, NULL);
+       dns_name_split(name, 1, &prefix, NULL);
+       switch (opt) {
        case CATZ_OPT_MASTERS:
-               return (catz_process_ipkl(zone, &entry->opts.masters, value));
-               break;
+               return (catz_process_masters(zone, &entry->opts.masters, value,
+                                            &prefix));
        case CATZ_OPT_ALLOW_QUERY:
+               if (prefix.labels != 0)
+                       return (ISC_R_FAILURE);
                return (catz_process_apl(zone, &entry->opts.allow_query,
                                         value));
        case CATZ_OPT_ALLOW_TRANSFER:
+               if (prefix.labels != 0)
+                       return (ISC_R_FAILURE);
                return (catz_process_apl(zone, &entry->opts.allow_transfer,
                                         value));
        default:
@@ -1068,72 +1268,45 @@ catz_process_suboption(dns_catz_zone_t *zone, dns_label_t *mhash,
 }
 
 static isc_result_t
-catz_process_global_option(dns_catz_zone_t *zone, catz_opt_t option,
-                          dns_rdataset_t *value)
+catz_process_value(dns_catz_zone_t *zone, dns_name_t *name,
+                  dns_rdataset_t *rdataset)
 {
+       dns_label_t option;
+       dns_name_t prefix;
+       catz_opt_t opt;
+
        REQUIRE(zone != NULL);
-       REQUIRE(DNS_RDATASET_VALID(value));
+       REQUIRE(name != NULL);
+       REQUIRE(DNS_RDATASET_VALID(rdataset));
 
-       switch (option) {
+       dns_name_getlabel(name, name->labels - 1, &option);
+       opt = catz_get_option(&option);
+       dns_name_init(&prefix, NULL);
+       dns_name_split(name, 1, &prefix, NULL);
+       switch (opt) {
+       case CATZ_OPT_ZONES:
+               return (catz_process_zones(zone, rdataset, &prefix));
        case CATZ_OPT_MASTERS:
-               return (catz_process_ipkl(zone, &zone->zoneoptions.masters,
-                                         value));
-       case CATZ_OPT_VERSION:
-               return (catz_process_version(zone, value));
+               return (catz_process_masters(zone, &zone->zoneoptions.masters,
+                                            rdataset, &prefix));
        case CATZ_OPT_ALLOW_QUERY:
+               if (prefix.labels != 0)
+                       return (ISC_R_FAILURE);
                return (catz_process_apl(zone, &zone->zoneoptions.allow_query,
-                                        value));
+                                        rdataset));
        case CATZ_OPT_ALLOW_TRANSFER:
-               return (catz_process_apl(zone, &zone->zoneoptions.allow_transfer,
-                                        value));
+               if (prefix.labels != 0)
+                       return (ISC_R_FAILURE);
+               return (catz_process_apl(zone,
+                                        &zone->zoneoptions.allow_transfer,
+                                        rdataset));
+       case CATZ_OPT_VERSION:
+               if (prefix.labels != 0)
+                       return (ISC_R_FAILURE);
+               return (catz_process_version(zone, rdataset));
        default:
                return (ISC_R_FAILURE);
        }
-       return (ISC_R_FAILURE);
-}
-
-static isc_result_t
-catz_process_value(dns_catz_zone_t *zone, dns_name_t *value,
-                  dns_rdataset_t *rdataset)
-{
-       dns_label_t suboption, option, mhash;
-       catz_opt_t opt, subopt;
-
-       REQUIRE(zone != NULL);
-       REQUIRE(value != NULL);
-       REQUIRE(DNS_RDATASET_VALID(rdataset));
-
-       switch (value->labels) {
-       case 1:
-               /* Catalog zone-wide option */
-               dns_name_getlabel(value, 0, &option);
-               opt = catz_get_option(&option);
-               return (catz_process_global_option(zone, opt, rdataset));
-
-       case 2:
-               /* Global list (eg. 'zones') */
-               dns_name_getlabel(value, 0, &mhash);
-               dns_name_getlabel(value, 1, &option);
-               opt = catz_get_option(&option);
-               return (catz_process_global_list(zone, opt, rdataset, &mhash));
-
-       case 3:
-               /* Zone option */
-               dns_name_getlabel(value, 0, &suboption);
-               dns_name_getlabel(value, 1, &mhash);
-               dns_name_getlabel(value, 2, &option);
-               opt = catz_get_option(&option);
-               subopt = catz_get_option(&suboption);
-               if (opt == CATZ_OPT_ZONES)
-                       return (catz_process_suboption(zone, &mhash, subopt,
-                                                      rdataset));
-               break;
-
-       default:
-               break;
-       }
-
-       return (ISC_R_FAILURE);
 }
 
 isc_result_t
@@ -1349,7 +1522,6 @@ dns_catz_update_taskaction(isc_task_t *task, isc_event_t *event) {
        LOCK(&zone->catzs->lock);
        zone->updatepending = ISC_FALSE;
        dns_catz_update_from_db(zone->db, zone->catzs);
-       dns_db_detach(&zone->db);
        result = isc_timer_reset(zone->updatetimer, isc_timertype_inactive,
                                 NULL, NULL, ISC_TRUE);
        RUNTIME_CHECK(result == ISC_R_SUCCESS);
@@ -1379,20 +1551,34 @@ dns_catz_dbupdate_callback(dns_db_t *db, void *fn_arg) {
        if (result != ISC_R_SUCCESS)
                goto cleanup;
 
+       /* New zone came as AXFR */
+       if (zone->db != NULL && zone->db != db) {
+               if (zone->dbversion != NULL)
+                       dns_db_closeversion(zone->db, &zone->dbversion,
+                                                   ISC_FALSE);
+               dns_db_detach(&zone->db);
+               /*
+                * We're not registering db update callback, it will be
+                * registered at the end of update_from_db
+                */
+               zone->db_registered = ISC_FALSE;
+       }
+       if (zone->db == NULL)
+               dns_db_attach(db, &zone->db);
+
        if (zone->updatepending == ISC_FALSE) {
                zone->updatepending = ISC_TRUE;
                isc_time_now(&now);
                tdiff = isc_time_microdiff(&now, &zone->lastupdated)/1000000;
                if (tdiff < zone->defoptions.min_update_interval) {
                        isc_interval_t interval;
-
                        isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
                                      DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
                                      "catz: new zone version came too soon, "
                                      "deferring update");
                        isc_interval_set(&interval,
-                                        5 - (unsigned int)tdiff, 0);
-                       dns_db_attach(db, &zone->db);
+                                        zone->defoptions.min_update_interval -
+                                        (unsigned int)tdiff, 0);
                        dns_db_currentversion(db, &zone->dbversion);
                        result = isc_timer_reset(zone->updatetimer,
                                                 isc_timertype_once,
@@ -1402,7 +1588,6 @@ dns_catz_dbupdate_callback(dns_db_t *db, void *fn_arg) {
                } else {
                        isc_event_t *event;
 
-                       dns_db_attach(db, &zone->db);
                        dns_db_currentversion(db, &zone->dbversion);
                        ISC_EVENT_INIT(&zone->updateevent,
                                       sizeof(zone->updateevent), 0, NULL,
@@ -1413,11 +1598,12 @@ dns_catz_dbupdate_callback(dns_db_t *db, void *fn_arg) {
                        isc_task_send(catzs->updater, &event);
                }
        } else {
-               INSIST(db == zone->db);
                isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
                              DNS_LOGMODULE_MASTER, ISC_LOG_DEBUG(3),
                              "catz: update already queued");
-               dns_db_closeversion(zone->db, &zone->dbversion, ISC_FALSE);
+               if (zone->dbversion != NULL)
+                       dns_db_closeversion(zone->db, &zone->dbversion,
+                                           ISC_FALSE);
                dns_db_currentversion(zone->db, &zone->dbversion);
        }
 
@@ -1446,12 +1632,12 @@ dns_catz_update_from_db(dns_db_t *db, dns_catz_zones_t *catzs) {
        REQUIRE(catzs != NULL);
 
        /*
-        * Create a new catz in the same context as current catz
+        * Create a new catz in the same context as current catz.
         */
        dns_name_toregion(&db->origin, &r);
        result = isc_ht_find(catzs->zones, r.base, r.length, (void **)&oldzone);
        if (result != ISC_R_SUCCESS) {
-               /* this can happen if we remove the zone in the meantime */
+               /* This can happen if we remove the zone in the meantime. */
                isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
                              DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
                              "catz: zone '%s' not in config",
@@ -1503,7 +1689,7 @@ dns_catz_update_from_db(dns_db_t *db, dns_catz_zones_t *catzs) {
        name = dns_fixedname_name(&fixname);
 
        /*
-        * Iterate over database to fill the new zone
+        * Iterate over database to fill the new zone.
         */
        result = dns_dbiterator_first(it);
        if (result != ISC_R_SUCCESS) {
@@ -1542,21 +1728,27 @@ dns_catz_update_from_db(dns_db_t *db, dns_catz_zones_t *catzs) {
                                                         &rdataset);
                        if (result != ISC_R_SUCCESS) {
                                char cname[DNS_NAME_FORMATSIZE];
+                               char type[DNS_RDATATYPE_FORMATSIZE];
+                               char class[DNS_RDATACLASS_FORMATSIZE];
+
                                dns_name_format(name, cname,
                                                DNS_NAME_FORMATSIZE);
+                               dns_rdataclass_format(rdataset.rdclass, class,
+                                                     sizeof(class));
+                               dns_rdatatype_format(rdataset.type, type,
+                                                    sizeof(type));
                                isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
                                              DNS_LOGMODULE_MASTER,
                                              ISC_LOG_WARNING,
                                              "catz: unknown record in catalog "
-                                             "zone - %s (%s) - ignoring",
-                                             cname,
+                                             "zone - %s %s %s(%s) - ignoring",
+                                             cname, class, type,
                                              isc_result_totext(result));
                        }
                        dns_rdataset_disassociate(&rdataset);
                        if (result != ISC_R_SUCCESS) {
                                break;
                        }
-
                        result = dns_rdatasetiter_next(rdsiter);
                }
 
@@ -1573,9 +1765,10 @@ dns_catz_update_from_db(dns_db_t *db, dns_catz_zones_t *catzs) {
                      "catz: update_from_db: iteration finished");
 
        /*
-        * Finally merge new zone into old zone
+        * Finally merge new zone into old zone.
         */
        result = dns_catz_zones_merge(oldzone, newzone);
+       dns_catz_zone_detach(&newzone);
        if (result != ISC_R_SUCCESS) {
                isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
                              DNS_LOGMODULE_MASTER,
@@ -1586,11 +1779,23 @@ dns_catz_update_from_db(dns_db_t *db, dns_catz_zones_t *catzs) {
                return;
        }
 
-       dns_catz_zone_detach(&newzone);
-
        isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
                      DNS_LOGMODULE_MASTER, ISC_LOG_DEBUG(3),
                      "catz: update_from_db: new zone merged");
+
+       /*
+        * When we're doing reconfig and setting a new catalog zone
+        * from an existing zone we won't have a chance to set up
+        * update callback in zone_startload or axfr_makedb, but we will
+        * call onupdate() artificially so we can register the callback here.
+        */
+       if (oldzone->db_registered == ISC_FALSE) {
+               result = dns_db_updatenotify_register(db,
+                                                   dns_catz_dbupdate_callback,
+                                                   oldzone->catzs);
+               if (result == ISC_R_SUCCESS)
+                       oldzone->db_registered = ISC_TRUE;
+       }
 }
 
 void
@@ -1621,11 +1826,11 @@ dns_catz_postreconfig(dns_catz_zones_t *catzs) {
        isc_ht_iter_t *iter = NULL;
        dns_catz_zone_t *zone;
 
+       LOCK(&catzs->lock);
        result = isc_ht_iter_create(catzs->zones, &iter);
        INSIST(result == ISC_R_SUCCESS);
        for (result = isc_ht_iter_first(iter);
-            result == ISC_R_SUCCESS;
-            result = isc_ht_iter_next(iter))
+            result == ISC_R_SUCCESS;)
        {
                isc_ht_iter_current(iter, (void **) &zone);
                if (zone->active == ISC_FALSE) {
@@ -1639,7 +1844,7 @@ dns_catz_postreconfig(dns_catz_zones_t *catzs) {
 
                        /*
                         * Merge the old zone with an empty one to remove
-                        * all members
+                        * all members.
                         */
                        result = dns_catz_new_zone(catzs, &newzone,
                                                   &zone->name);
@@ -1647,11 +1852,15 @@ dns_catz_postreconfig(dns_catz_zones_t *catzs) {
                        dns_catz_zones_merge(zone, newzone);
                        dns_catz_zone_detach(&newzone);
 
-                       /* Make sure that we have an empty catalog zone */
+                       /* Make sure that we have an empty catalog zone. */
                        INSIST(isc_ht_count(zone->entries) == 0);
+                       result = isc_ht_iter_delcurrent_next(iter);
                        dns_catz_zone_detach(&zone);
+               } else {
+                       result = isc_ht_iter_next(iter);
                }
        }
+       UNLOCK(&catzs->lock);
        RUNTIME_CHECK(result == ISC_R_NOMORE);
        isc_ht_iter_destroy(&iter);
 }
index 55c094e54466460cee76f89d487eefcc5053f38c..769c74838245f69b35c43fbf83ff9908c3fdeb35 100644 (file)
@@ -28,9 +28,20 @@ struct dns_ipkeylist {
        isc_sockaddr_t          *addrs;
        isc_dscp_t              *dscps;
        dns_name_t              **keys;
+       dns_name_t              **labels;
        isc_uint32_t            count;
+       isc_uint32_t            allocated;
 };
 
+void
+dns_ipkeylist_init(dns_ipkeylist_t *ipkl);
+/*%<
+ * Reset ipkl to empty state
+ *
+ * Requires:
+ *\li  'ipkl' to be non NULL.
+ */
+
 void
 dns_ipkeylist_clear(isc_mem_t *mctx, dns_ipkeylist_t *ipkl);
 /*%<
@@ -41,8 +52,7 @@ dns_ipkeylist_clear(isc_mem_t *mctx, dns_ipkeylist_t *ipkl);
  *
  * Requires:
  *\li  'mctx' to be a valid memory context.
- *\li  'ipkl' to be non NULL and have its members `addrs` and `keys`
- *      allocated. 'dscps' might be NULL.
+ *\li  'ipkl' to be non NULL.
  */
 
 isc_result_t
@@ -61,5 +71,20 @@ dns_ipkeylist_copy(isc_mem_t *mctx, const dns_ipkeylist_t *src,
  *\li  #ISC_R_SUCCESS  -- success
  *\li  any other value -- failure
  */
+isc_result_t
+dns_ipkeylist_resize(isc_mem_t *mctx, dns_ipkeylist_t *ipkl, unsigned int n);
+/*%<
+ * Resize ipkl to contain n elements. Size (count) is not changed, and the
+ * added space is zeroed.
+ *
+ * Requires:
+ * \li 'mctx' to be a valid memory context.
+ * \li 'ipk' to be non NULL
+ * \li 'n' >= ipkl->count
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS if successs
+ * \li #ISC_R_NOMEMORY if there's no memory, ipkeylist is left untoched
+ */
 
 #endif
index 25db4cd5bacb8c4d9444c0bde7741f8613caf982..d708f368d6c672481de7b988668f30233d2d8bc1 100644 (file)
@@ -2415,6 +2415,27 @@ dns_zone_catz_enable_db(dns_zone_t *zone, dns_db_t *db);
  * \li 'zone' is a valid zone object
  * \li 'db' is not NULL
  */
+void
+dns_zone_set_parentcatz(dns_zone_t *zone, dns_catz_zone_t *catz);
+/*%<
+ * Set parent catalog zone for this zone
+ *
+ * Requires:
+ *
+ * \li 'zone' is a valid zone object
+ * \li 'catz' is not NULL
+ */
+
+dns_catz_zone_t *
+dns_zone_get_parentcatz(const dns_zone_t *zone);
+/*%<
+ * Get parent catalog zone for this zone
+ *
+ * Requires:
+ *
+ * \li 'zone' is a valid zone object
+ */
+
 
 void
 dns_zone_setstatlevel(dns_zone_t *zone, dns_zonestat_level_t level);
index 10770985c454e256db11cb854d4499abec9250ad..5beebfa56486fd6cf6222d2385795b448edea255 100644 (file)
 #include <dns/ipkeylist.h>
 #include <dns/name.h>
 
+void
+dns_ipkeylist_init(dns_ipkeylist_t *ipkl) {
+       ipkl->count = 0;
+       ipkl->allocated = 0;
+       ipkl->addrs = NULL;
+       ipkl->dscps = NULL;
+       ipkl->keys = NULL;
+       ipkl->labels = NULL;
+}
+
 void
 dns_ipkeylist_clear(isc_mem_t *mctx, dns_ipkeylist_t *ipkl) {
        isc_uint32_t i;
 
        REQUIRE(ipkl != NULL);
-       REQUIRE(ipkl->count == 0 || ipkl->keys != NULL);
 
-       if (ipkl->count == 0)
+       if (ipkl->allocated == 0)
                return;
 
        if (ipkl->addrs != NULL)
                isc_mem_put(mctx, ipkl->addrs,
-                           ipkl->count * sizeof(isc_sockaddr_t));
+                           ipkl->allocated * sizeof(isc_sockaddr_t));
 
        if (ipkl->dscps != NULL)
                isc_mem_put(mctx, ipkl->dscps,
-                           ipkl->count * sizeof(isc_dscp_t));
-
-       for (i = 0; i < ipkl->count; i++) {
-               if (ipkl->keys[i] == NULL)
-                       continue;
-               if (dns_name_dynamic(ipkl->keys[i]))
-                       dns_name_free(ipkl->keys[i], mctx);
-               isc_mem_put(mctx, ipkl->keys[i], sizeof(dns_name_t));
+                           ipkl->allocated * sizeof(isc_dscp_t));
+
+       if (ipkl->keys != NULL) {
+               for (i = 0; i < ipkl->allocated; i++) {
+                       if (ipkl->keys[i] == NULL)
+                               continue;
+                       if (dns_name_dynamic(ipkl->keys[i]))
+                               dns_name_free(ipkl->keys[i], mctx);
+                       isc_mem_put(mctx, ipkl->keys[i], sizeof(dns_name_t));
+               }
+               isc_mem_put(mctx, ipkl->keys,
+                           ipkl->allocated * sizeof(dns_name_t *));
        }
 
-       isc_mem_put(mctx, ipkl->keys, ipkl->count * sizeof(dns_name_t *));
+       if (ipkl->labels != NULL) {
+               for (i = 0; i < ipkl->allocated; i++) {
+                       if (ipkl->labels[i] == NULL)
+                               continue;
+                       if (dns_name_dynamic(ipkl->labels[i]))
+                               dns_name_free(ipkl->labels[i], mctx);
+                       isc_mem_put(mctx, ipkl->labels[i], sizeof(dns_name_t));
+               }
+               isc_mem_put(mctx, ipkl->labels,
+                           ipkl->allocated * sizeof(dns_name_t *));
+       }
 
-       ipkl->count = 0;
-       ipkl->addrs = NULL;
-       ipkl->dscps = NULL;
-       ipkl->keys = NULL;
+       dns_ipkeylist_init(ipkl);
 }
 
 isc_result_t
@@ -67,40 +87,24 @@ dns_ipkeylist_copy(isc_mem_t *mctx, const dns_ipkeylist_t *src,
        isc_uint32_t i;
 
        REQUIRE(dst != NULL);
-       REQUIRE(dst->count == 0 &&
-               dst->addrs == NULL && dst->keys == NULL && dst->dscps == NULL);
+       /* dst might be preallocated, we don't care, but it must be empty */
+       REQUIRE(dst->count == 0);
 
        if (src->count == 0)
                return (ISC_R_SUCCESS);
 
-       dst->count = src->count;
-
-       dst->addrs = isc_mem_get(mctx,
-                                src->count * sizeof(isc_sockaddr_t));
-       if (dst->addrs == NULL)
-               return (ISC_R_NOMEMORY);
+       result = dns_ipkeylist_resize(mctx, dst, src->count);
+       if (result != ISC_R_SUCCESS)
+               return (result);
 
        memmove(dst->addrs, src->addrs, src->count * sizeof(isc_sockaddr_t));
 
        if (src->dscps != NULL) {
-               dst->dscps = isc_mem_get(mctx,
-                                        src->count * sizeof(isc_dscp_t));
-               if (dst->dscps == NULL) {
-                       result = ISC_R_NOMEMORY;
-                       goto cleanup_addrs;
-               }
                memmove(dst->dscps, src->dscps,
                        src->count * sizeof(isc_dscp_t));
        }
 
        if (src->keys != NULL) {
-               dst->keys = isc_mem_get(mctx,
-                                       src->count * sizeof(dns_name_t *));
-               if (dst->keys == NULL) {
-                       result = ISC_R_NOMEMORY;
-                       goto cleanup_dscps;
-               }
-
                for (i = 0; i < src->count; i++) {
                        if (src->keys[i] != NULL) {
                                dst->keys[i] = isc_mem_get(mctx,
@@ -120,20 +124,121 @@ dns_ipkeylist_copy(isc_mem_t *mctx, const dns_ipkeylist_t *src,
                }
        }
 
+       if (src->labels != NULL) {
+               for (i = 0; i < src->count; i++) {
+                       if (src->labels[i] != NULL) {
+                               dst->labels[i] = isc_mem_get(mctx,
+                                                          sizeof(dns_name_t));
+                               if (dst->labels[i] == NULL) {
+                                       result = ISC_R_NOMEMORY;
+                                       goto cleanup_labels;
+                               }
+                               dns_name_init(dst->labels[i], NULL);
+                               result = dns_name_dup(src->labels[i], mctx,
+                                                     dst->labels[i]);
+                               if (result != ISC_R_SUCCESS)
+                                       goto cleanup_labels;
+                       } else {
+                               dst->labels[i] = NULL;
+                       }
+               }
+       }
+       dst->count = src->count;
        return (ISC_R_SUCCESS);
 
+  cleanup_labels:
+       do {
+               if (dst->labels[i] != NULL) {
+                       if (dns_name_dynamic(dst->labels[i]))
+                               dns_name_free(dst->labels[i], mctx);
+                       isc_mem_put(mctx, dst->labels[i], sizeof(dns_name_t));
+                       dst->labels[i] = NULL;
+               }
+       } while (i-- > 0);
+
   cleanup_keys:
        do {
                if (dst->keys[i] != NULL) {
                        if (dns_name_dynamic(dst->keys[i]))
                                dns_name_free(dst->keys[i], mctx);
                        isc_mem_put(mctx, dst->keys[i], sizeof(dns_name_t));
+                       dst->keys[i] = NULL;
                }
        } while (i-- > 0);
-       isc_mem_put(mctx, dst->keys, src->count * sizeof(dns_name_t *));
-  cleanup_dscps:
-       isc_mem_put(mctx, dst->dscps, src->count * sizeof(isc_dscp_t));
-  cleanup_addrs:
-       isc_mem_put(mctx, dst->addrs, src->count * sizeof(isc_sockaddr_t));
+
        return (result);
 }
+
+isc_result_t
+dns_ipkeylist_resize(isc_mem_t *mctx, dns_ipkeylist_t *ipkl, unsigned int n) {
+       isc_sockaddr_t *addrs = NULL;
+       isc_dscp_t *dscps = NULL;
+       dns_name_t **keys = NULL;
+       dns_name_t **labels = NULL;
+
+       REQUIRE(ipkl != NULL);
+       REQUIRE(n > ipkl->count);
+
+       if (n <= ipkl->allocated)
+               return (ISC_R_SUCCESS);
+
+       addrs = isc_mem_get(mctx, n * sizeof(isc_sockaddr_t));
+       if (addrs == NULL)
+               goto nomemory;
+       dscps = isc_mem_get(mctx, n * sizeof(isc_dscp_t));
+       if (dscps == NULL)
+               goto nomemory;
+       keys = isc_mem_get(mctx, n * sizeof(dns_name_t *));
+       if (keys == NULL)
+               goto nomemory;
+       labels = isc_mem_get(mctx, n * sizeof(dns_name_t *));
+       if (labels == NULL)
+               goto nomemory;
+
+       memmove(addrs, ipkl->addrs, ipkl->allocated * sizeof(isc_sockaddr_t));
+       if (ipkl->addrs != NULL)
+               isc_mem_put(mctx, ipkl->addrs,
+                           ipkl->allocated * sizeof(isc_sockaddr_t));
+       ipkl->addrs = addrs;
+       memset(&ipkl->addrs[ipkl->allocated], 0,
+              (n - ipkl->allocated) * sizeof(isc_sockaddr_t));
+
+       memmove(dscps, ipkl->dscps, ipkl->allocated * sizeof(isc_dscp_t));
+       if (ipkl->dscps != NULL)
+               isc_mem_put(mctx, ipkl->dscps,
+                           ipkl->allocated * sizeof(isc_dscp_t));
+       ipkl->dscps = dscps;
+       memset(&ipkl->dscps[ipkl->allocated], 0,
+              (n - ipkl->allocated) * sizeof(isc_dscp_t));
+
+       memmove(keys, ipkl->keys, ipkl->allocated * sizeof(dns_name_t *));
+       if (ipkl->keys)
+               isc_mem_put(mctx, ipkl->keys,
+                           ipkl->allocated * sizeof(dns_name_t *));
+       ipkl->keys = keys;
+       memset(&ipkl->keys[ipkl->allocated], 0,
+              (n - ipkl->allocated) * sizeof(dns_name_t *));
+
+       memmove(labels, ipkl->labels, ipkl->allocated * sizeof(dns_name_t *));
+       if (ipkl->labels)
+               isc_mem_put(mctx, ipkl->labels,
+                           ipkl->allocated * sizeof(dns_name_t *));
+       ipkl->labels = labels;
+       memset(&ipkl->labels[ipkl->allocated], 0,
+              (n - ipkl->allocated) * sizeof(dns_name_t *));
+
+       ipkl->allocated = n;
+       return (ISC_R_SUCCESS);
+
+nomemory:
+       if (addrs != NULL)
+               isc_mem_put(mctx, addrs, n * sizeof(isc_sockaddr_t));
+       if (dscps != NULL)
+               isc_mem_put(mctx, dscps, n * sizeof(isc_dscp_t));
+       if (keys != NULL)
+               isc_mem_put(mctx, keys, n * sizeof(dns_name_t *));
+       if (labels != NULL)
+               isc_mem_put(mctx, labels, n * sizeof(dns_name_t *));
+
+       return (ISC_R_NOMEMORY);
+}
index 85345bcb0731377f1629fbc82e563e1a22d5ee9b..2d4a247f35b16ffcb9a651540631fc464bcbe556 100644 (file)
@@ -2176,7 +2176,7 @@ dns_name_split(dns_name_t *name, unsigned int suffixlabels,
 
        REQUIRE(VALID_NAME(name));
        REQUIRE(suffixlabels > 0);
-       REQUIRE(suffixlabels < name->labels);
+       REQUIRE(suffixlabels <= name->labels);
        REQUIRE(prefix != NULL || suffix != NULL);
        REQUIRE(prefix == NULL ||
                (VALID_NAME(prefix) &&
index 74ae734f8e2a81ae706aad1f885bd434a33ceb31..4b7a45e57fa0d484232264124594ebbe15302e89 100644 (file)
@@ -384,7 +384,12 @@ struct dns_zone {
        /*%
         * catalog zone data
         */
-       dns_catz_zones_t                *catzs;
+       dns_catz_zones_t        *catzs;
+
+       /*%
+        * parent catalog zone
+        */
+       dns_catz_zone_t         *parentcatz;
 
        /*%
         * Serial number update method.
@@ -1044,6 +1049,7 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) {
        zone->rpz_num = DNS_RPZ_INVALID_NUM;
 
        zone->catzs = NULL;
+       zone->parentcatz = NULL;
 
        ISC_LIST_INIT(zone->forwards);
        zone->raw = NULL;
@@ -1787,6 +1793,25 @@ dns_zone_catz_enable_db(dns_zone_t *zone, dns_db_t *db) {
        }
 }
 
+/*
+ * Set catalog zone ownership of the zone
+ */
+void
+dns_zone_set_parentcatz(dns_zone_t *zone, dns_catz_zone_t *catz) {
+       REQUIRE(DNS_ZONE_VALID(zone));
+       REQUIRE(catz != NULL);
+       LOCK_ZONE(zone);
+       INSIST(zone->parentcatz == NULL || zone->parentcatz == catz);
+       zone->parentcatz = catz;
+       UNLOCK_ZONE(zone);
+}
+
+dns_catz_zone_t *
+dns_zone_get_parentcatz(const dns_zone_t *zone) {
+       REQUIRE(DNS_ZONE_VALID(zone));
+       return (zone->parentcatz);
+}
+
 
 static isc_boolean_t
 zone_touched(dns_zone_t *zone) {