]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Introduce the concept of broken catalog zones
authorAram Sargsyan <aram@isc.org>
Thu, 17 Mar 2022 14:43:18 +0000 (14:43 +0000)
committerAram Sargsyan <aram@isc.org>
Thu, 28 Apr 2022 12:36:58 +0000 (12:36 +0000)
The DNS catalog zones draft version 5 document describes various
situations when a catalog zones must be considered as "broken" and
not be processed.

Implement those checks in catz.c and add corresponding system tests.

bin/tests/system/catz/clean.sh
bin/tests/system/catz/ns1/catalog-bad1.example.db [new file with mode: 0644]
bin/tests/system/catz/ns1/catalog-bad2.example.db [new file with mode: 0644]
bin/tests/system/catz/ns1/catalog-bad3.example.db [new file with mode: 0644]
bin/tests/system/catz/ns1/named.conf.in
bin/tests/system/catz/ns2/named1.conf.in
bin/tests/system/catz/ns2/named2.conf.in
bin/tests/system/catz/tests.sh
lib/dns/catz.c

index 783ed4e763b21abafdc3754dbf6aabcc06ca9fca..e293e7dc0cad25c1660feb17bbf407d92594753c 100644 (file)
@@ -19,6 +19,7 @@ rm -f ns*/named.run
 rm -f ns*/named.run.prev
 rm -f ns1/*dom*example.db
 rm -f ns2/__catz__*db
+rm -f ns2/catalog-bad*.db
 rm -f ns2/named.conf.tmp
 rm -f ns3/dom2.example.db ns3/dom13.example.db ns3/dom14.example.db ns3/dom17.example.db ns3/dom18.example.db
 rm -f nsupdate.out.*
diff --git a/bin/tests/system/catz/ns1/catalog-bad1.example.db b/bin/tests/system/catz/ns1/catalog-bad1.example.db
new file mode 100644 (file)
index 0000000..b8402de
--- /dev/null
@@ -0,0 +1,13 @@
+; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+;
+; SPDX-License-Identifier: MPL-2.0
+;
+; This Source Code Form is subject to the terms of the Mozilla Public
+; License, v. 2.0.  If a copy of the MPL was not distributed with this
+; file, you can obtain one at https://mozilla.org/MPL/2.0/.
+;
+; See the COPYRIGHT file distributed with this work for additional
+; information regarding copyright ownership.
+
+@ 3600 SOA . . 1 86400 3600 86400 3600
+@ 3600 IN NS invalid.
diff --git a/bin/tests/system/catz/ns1/catalog-bad2.example.db b/bin/tests/system/catz/ns1/catalog-bad2.example.db
new file mode 100644 (file)
index 0000000..06b9121
--- /dev/null
@@ -0,0 +1,14 @@
+; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+;
+; SPDX-License-Identifier: MPL-2.0
+;
+; This Source Code Form is subject to the terms of the Mozilla Public
+; License, v. 2.0.  If a copy of the MPL was not distributed with this
+; file, you can obtain one at https://mozilla.org/MPL/2.0/.
+;
+; See the COPYRIGHT file distributed with this work for additional
+; information regarding copyright ownership.
+
+@ 3600 SOA . . 1 86400 3600 86400 3600
+@ 3600 IN NS invalid.
+version IN TXT "99"
diff --git a/bin/tests/system/catz/ns1/catalog-bad3.example.db b/bin/tests/system/catz/ns1/catalog-bad3.example.db
new file mode 100644 (file)
index 0000000..0116697
--- /dev/null
@@ -0,0 +1,15 @@
+; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+;
+; SPDX-License-Identifier: MPL-2.0
+;
+; This Source Code Form is subject to the terms of the Mozilla Public
+; License, v. 2.0.  If a copy of the MPL was not distributed with this
+; file, you can obtain one at https://mozilla.org/MPL/2.0/.
+;
+; See the COPYRIGHT file distributed with this work for additional
+; information regarding copyright ownership.
+
+@ 3600 SOA . . 1 86400 3600 86400 3600
+@ 3600 IN NS invalid.
+version IN TXT "1"
+version IN TXT "2"
index 5a46d3984f810213e1fde126a1a95ddbb1b3f481..5cf799f1c69feeceb146d63ff2f9f71698ad9159 100644 (file)
@@ -61,6 +61,36 @@ zone "catalog4.example" {
 
 /* catalog5 is missing on purpose */
 
+# No "version" property
+zone "catalog-bad1.example" {
+       type primary;
+       file "catalog-bad1.example.db";
+       allow-transfer { any; };
+       allow-update { any; };
+       also-notify { 10.53.0.2; };
+       notify explicit;
+};
+
+# Unsupported "version" property
+zone "catalog-bad2.example" {
+       type primary;
+       file "catalog-bad2.example.db";
+       allow-transfer { any; };
+       allow-update { any; };
+       also-notify { 10.53.0.2; };
+       notify explicit;
+};
+
+# Two RRs in TXT RRset for the "version" property
+zone "catalog-bad3.example" {
+       type primary;
+       file "catalog-bad3.example.db";
+       allow-transfer { any; };
+       allow-update { any; };
+       also-notify { 10.53.0.2; };
+       notify explicit;
+};
+
 key tsig_key. {
        secret "LSAnCU+Z";
        algorithm hmac-md5;
index ed7f87943bace8d25aa41cf7b639d22022409747..8ff6f687a06bd9fabd7b03c5711098725417c599 100644 (file)
@@ -43,6 +43,15 @@ options {
 #T1                    default-masters { 10.53.0.1; };
 #T2            zone "catalog5.example"
 #T2                    default-primaries { 10.53.0.1; };
+               zone "catalog-bad1.example"
+                       default-masters { 10.53.0.1; }
+                       in-memory yes;
+               zone "catalog-bad2.example"
+                       default-masters { 10.53.0.1; }
+                       in-memory yes;
+               zone "catalog-bad3.example"
+                       default-masters { 10.53.0.1; }
+                       in-memory yes;
        };
 };
 
@@ -83,6 +92,24 @@ zone "catalog4.example" {
        primaries { 10.53.0.1; };
 };
 
+zone "catalog-bad1.example" {
+       type secondary;
+       file "catalog-bad1.example.db";
+       primaries { 10.53.0.1; };
+};
+
+zone "catalog-bad2.example" {
+       type secondary;
+       file "catalog-bad2.example.db";
+       primaries { 10.53.0.1; };
+};
+
+zone "catalog-bad3.example" {
+       type secondary;
+       file "catalog-bad3.example.db";
+       primaries { 10.53.0.1; };
+};
+
 key tsig_key. {
        secret "LSAnCU+Z";
        algorithm hmac-md5;
index 12884c6797237c198addf7270b7c752f263f139f..787d62b510728ae9db12d4884e8d1fbb6ddd56ff 100644 (file)
@@ -61,6 +61,24 @@ zone "catalog4.example" {
        primaries { 10.53.0.1; };
 };
 
+zone "catalog-bad1.example" {
+    type secondary;
+    file "catalog-bad1.example.db";
+    primaries { 10.53.0.1; };
+};
+
+zone "catalog-bad2.example" {
+    type secondary;
+    file "catalog-bad2.example.db";
+    primaries { 10.53.0.1; };
+};
+
+zone "catalog-bad3.example" {
+    type secondary;
+    file "catalog-bad3.example.db";
+    primaries { 10.53.0.1; };
+};
+
 key tsig_key. {
        secret "LSAnCU+Z";
        algorithm hmac-md5;
index 6785dd84e94ef74e4d220206665c634c06ab5003..807fb14bc00a18e0065923fb6ac79f5a8211f686 100644 (file)
@@ -80,6 +80,36 @@ wait_for_no_zonefile() (
 
 status=0
 n=0
+
+##########################################################################
+n=$((n+1))
+echo_i "checking that catalog-bad1.example (with no version) has failed to load ($n)"
+ret=0
+wait_for_message ns2/named.run "catz: zone 'catalog-bad1.example' has no 'version' record" &&
+wait_for_message ns2/named.run "catz: new catalog zone 'catalog-bad1.example' is broken and will not be processed" || ret=1
+if [ $ret -ne 0 ]; then echo_i "failed"; fi
+status=$((status+ret))
+
+n=$((n+1))
+echo_i "checking that catalog-bad2.example (with unsupported version) has failed to load ($n)"
+ret=0
+wait_for_message ns2/named.run "catz: zone 'catalog-bad2.example' unsupported version '99'" &&
+wait_for_message ns2/named.run "catz: new catalog zone 'catalog-bad2.example' is broken and will not be processed" || ret=1
+if [ $ret -ne 0 ]; then echo_i "failed"; fi
+status=$((status+ret))
+
+n=$((n+1))
+echo_i "checking that catalog-bad3.example (with two supported version records) has failed to load ($n)"
+ret=0
+wait_for_message ns2/named.run "catz: 'version' property TXT RRset contains more than one record, which is invalid" &&
+wait_for_message ns2/named.run "catz: invalid record in catalog zone - version.catalog-bad3.example IN TXT (failure) - ignoring" &&
+wait_for_message ns2/named.run "catz: zone 'catalog-bad3.example' version is not set" &&
+wait_for_message ns2/named.run "catz: new catalog zone 'catalog-bad3.example' is broken and will not be processed" || ret=1
+if [ $ret -ne 0 ]; then echo_i "failed"; fi
+status=$((status+ret))
+
+nextpart ns2/named.run >/dev/null
+
 ##########################################################################
 echo_i "Testing adding/removing of domain in catalog zone"
 n=$((n+1))
@@ -145,6 +175,8 @@ wait_for_zonefile "ns2/zonedir/__catz___default_catalog1.example_dom1.example.db
 if [ $ret -ne 0 ]; then echo_i "failed"; fi
 status=$((status+ret))
 
+nextpart ns2/named.run >/dev/null
+
 n=$((n+1))
 echo_i "update dom1.example. ($n)"
 ret=0
@@ -176,6 +208,8 @@ test -f ns2/zonedir/__catz___default_catalog1.example_dom1.example.db.jnl || ret
 if [ $ret -ne 0 ]; then echo_i "failed"; fi
 status=$((status+ret))
 
+nextpart ns2/named.run >/dev/null
+
 n=$((n+1))
 echo_i "update catalog zone serial ($n)"
 ret=0
@@ -202,6 +236,8 @@ retry_quiet 10 wait_for_soa_equal_20 || ret=1
 if [ $ret -ne 0 ]; then echo_i "failed"; fi
 status=$((status+ret))
 
+nextpart ns2/named.run >/dev/null
+
 n=$((n+1))
 echo_i "update dom1.example. again ($n)"
 ret=0
@@ -259,6 +295,8 @@ wait_for_no_zonefile "ns2/zonedir/__catz___default_catalog1.example_dom1.example
 if [ $ret -ne 0 ]; then echo_i "failed"; fi
 status=$((status+ret))
 
+nextpart ns2/named.run >/dev/null
+
 ##########################################################################
 echo_i "Testing various simple operations on domains, including using multiple catalog zones and garbage in zone"
 n=$((n+1))
@@ -296,6 +334,7 @@ ret=0
 $NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
     server 10.53.0.1 ${PORT}
     update add 636722929740e507aaf27c502812fc395d30fb17.zones.catalog1.example. 3600 IN PTR dom2.example.
+    update add coo.636722929740e507aaf27c502812fc395d30fb17.zones.catalog1.example. 3600 IN TXT "catalog2.example."
     update add b901f492f3ebf6c1e5b597e51766f02f0479eb03.zones.catalog1.example. 3600 IN PTR dom3.example.
     update add e721433b6160b450260d4f54b3ec8bab30cb3b83.zones.catalog1.example. 3600 IN NS foo.bar.
     update add trash.catalog1.example. 3600 IN A 1.2.3.4
@@ -322,10 +361,11 @@ END
 if [ $ret -ne 0 ]; then echo_i "failed"; fi
 status=$((status+ret))
 
-
 n=$((n+1))
 echo_i "waiting for secondary to sync up ($n)"
 ret=0
+wait_for_message ns2/named.run "catz: adding zone 'dom2.example' from catalog 'catalog1.example'" &&
+wait_for_message ns2/named.run "catz: adding zone 'dom3.example' from catalog 'catalog1.example'" &&
 wait_for_message ns2/named.run "catz: adding zone 'dom4.example' from catalog 'catalog2.example'" &&
 wait_for_message ns2/named.run "transfer of 'dom4.example/IN' from 10.53.0.1#${EXTRAPORT1}: Transfer status: success" || ret=1
 if [ $ret -ne 0 ]; then echo_i "failed"; fi
@@ -338,7 +378,6 @@ wait_for_soa @10.53.0.2 dom4.example. dig.out.test$n || ret=1
 if [ $ret -ne 0 ]; then echo_i "failed"; fi
 status=$((status+ret))
 
-
 n=$((n+1))
 echo_i "checking that dom3.example. is not served by primary ($n)"
 ret=0
@@ -365,8 +404,6 @@ status=$((status+ret))
 n=$((n+1))
 echo_i "waiting for secondary to sync up ($n)"
 ret=0
-wait_for_message ns2/named.run "catz: adding zone 'dom2.example' from catalog 'catalog1.example'" &&
-wait_for_message ns2/named.run "catz: adding zone 'dom3.example' from catalog 'catalog1.example'" &&
 wait_for_message ns2/named.run  "transfer of 'dom2.example/IN' from 10.53.0.1#${PORT}: Transfer status: success" &&
 wait_for_message ns2/named.run  "transfer of 'dom3.example/IN' from 10.53.0.1#${PORT}: Transfer status: success" || ret=1
 if [ $ret -ne 0 ]; then echo_i "failed"; fi
@@ -381,6 +418,43 @@ status=$((status+ret))
 
 nextpart ns2/named.run >/dev/null
 
+# The member zone's PTR RRset must have only one record in it.
+# Check that adding a second record to the RRset is caught and such a
+# catalog zone is not processed.
+n=$((n+1))
+echo_i "adding domain dom4-reused-label.example. to catalog2 zone, reusing a label ($n)"
+ret=0
+$NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
+    server 10.53.0.3 ${PORT}
+    update add de26b88d855397a03f77ff1162fd055d8b419584.zones.catalog2.example. 3600 IN PTR dom4-reused-label.example.
+    send
+END
+if [ $ret -ne 0 ]; then echo_i "failed"; fi
+status=$((status+ret))
+
+n=$((n+1))
+echo_i "waiting for secondary to sync up, and checking that the reused label has been caught ($n)"
+ret=0
+wait_for_message ns2/named.run "de26b88d855397a03f77ff1162fd055d8b419584.zones.catalog2.example IN PTR (failure)" &&
+wait_for_message ns2/named.run "catz: new catalog zone 'catalog2.example' is broken and will not be processed" || ret=1
+if [ $ret -ne 0 ]; then echo_i "failed"; fi
+status=$((status+ret))
+
+nextpart ns2/named.run >/dev/null
+
+n=$((n+1))
+echo_i "deleting domain dom4-reused-label.example. from catalog2 zone ($n)"
+ret=0
+$NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
+    server 10.53.0.3 ${PORT}
+    update delete de26b88d855397a03f77ff1162fd055d8b419584.zones.catalog2.example. 3600 IN PTR dom4-reused-label.example.
+    send
+END
+if [ $ret -ne 0 ]; then echo_i "failed"; fi
+status=$((status+ret))
+
+nextpart ns2/named.run >/dev/null
+
 n=$((n+1))
 echo_i "adding domain dom2.example. to catalog2 zone to test change of ownership ($n)"
 ret=0
@@ -615,6 +689,7 @@ ret=0
 $NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
     server 10.53.0.1 ${PORT}
     update delete 636722929740e507aaf27c502812fc395d30fb17.zones.catalog1.example. 3600 IN PTR dom2.example.
+    update delete coo.636722929740e507aaf27c502812fc395d30fb17.zones.catalog1.example. 3600 IN TXT "catalog2.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
@@ -2083,7 +2158,7 @@ status=$((status+ret))
 n=$((n+1))
 echo_i "waiting for secondary to sync up ($n)"
 ret=0
-wait_for_message ns2/named.run "catz: unknown record in catalog zone - primaries.dom17.zones.catalog1.example IN A(failure) - ignoring" &&
+wait_for_message ns2/named.run "catz: invalid record in catalog zone - primaries.dom17.zones.catalog1.example IN A (failure) - ignoring" &&
 wait_for_message ns2/named.run "catz: adding zone 'dom17.example' from catalog 'catalog1.example'" &&
 wait_for_message ns2/named.run "catz: adding zone 'dom18.example' from catalog 'catalog1.example'" &&
 wait_for_message ns2/named.run "transfer of 'dom17.example/IN' from 10.53.0.1#${PORT}: Transfer status: success" &&
@@ -2174,7 +2249,7 @@ status=$((status+ret))
 n=$((n+1))
 echo_i "waiting for secondary to sync up ($n)"
 ret=0
-wait_for_message ns2/named.run "catz: unknown record in catalog zone - primaries.ext.dom18.zones.catalog2.example IN A(failure) - ignoring" &&
+wait_for_message ns2/named.run "catz: invalid record in catalog zone - primaries.ext.dom18.zones.catalog2.example IN A (failure) - ignoring" &&
 wait_for_message ns2/named.run "catz: adding zone 'dom17.example' from catalog 'catalog2.example'" &&
 wait_for_message ns2/named.run "catz: adding zone 'dom18.example' from catalog 'catalog2.example'" &&
 wait_for_message ns2/named.run "transfer of 'dom17.example/IN' from 10.53.0.3#${PORT}: Transfer status: success" &&
index f3dbbc39896cc0d2592c4cd1bd8ddb029d97f8d7..3c9e60d060e9d01a47ca939be987ad77303fcf13 100644 (file)
@@ -94,6 +94,7 @@ struct dns_catz_zone {
 
        bool active;
        bool db_registered;
+       bool broken;
 
        isc_refcount_t refs;
 };
@@ -1065,6 +1066,15 @@ catz_process_coo(dns_catz_zone_t *zone, dns_label_t *mhash,
                return (ISC_R_FAILURE);
        }
 
+       if (dns_rdataset_count(value) != 1) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                             DNS_LOGMODULE_MASTER, ISC_LOG_WARNING,
+                             "catz: 'coo' property PTR RRset contains "
+                             "more than one record, which is invalid");
+               zone->broken = true;
+               return (ISC_R_FAILURE);
+       }
+
        result = dns_rdataset_first(value);
        if (result != ISC_R_SUCCESS) {
                return (result);
@@ -1123,24 +1133,32 @@ catz_process_zones_entry(dns_catz_zone_t *zone, dns_rdataset_t *value,
        dns_rdata_ptr_t ptr;
        dns_catz_entry_t *entry = NULL;
 
-       /*
-        * We only take -first- value, as mhash must be
-        * different.
-        */
-       if (value->type != dns_rdatatype_ptr) {
+       if (value->rdclass != dns_rdataclass_in ||
+           value->type != dns_rdatatype_ptr) {
+               return (ISC_R_FAILURE);
+       }
+
+       if (dns_rdataset_count(value) != 1) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                             DNS_LOGMODULE_MASTER, ISC_LOG_WARNING,
+                             "catz: member zone PTR RRset contains "
+                             "more than one record, which is invalid");
+               zone->broken = true;
                return (ISC_R_FAILURE);
        }
 
        result = dns_rdataset_first(value);
        if (result != ISC_R_SUCCESS) {
-               return (ISC_R_FAILURE);
+               return (result);
        }
 
        dns_rdata_init(&rdata);
        dns_rdataset_current(value, &rdata);
 
        result = dns_rdata_tostruct(&rdata, &ptr, NULL);
-       RUNTIME_CHECK(result == ISC_R_SUCCESS);
+       if (result != ISC_R_SUCCESS) {
+               return (result);
+       }
 
        result = isc_ht_find(zone->entries, mhash->base, mhash->length,
                             (void **)&entry);
@@ -1186,6 +1204,15 @@ catz_process_version(dns_catz_zone_t *zone, dns_rdataset_t *value) {
                return (ISC_R_FAILURE);
        }
 
+       if (dns_rdataset_count(value) != 1) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                             DNS_LOGMODULE_MASTER, ISC_LOG_WARNING,
+                             "catz: 'version' property TXT RRset contains "
+                             "more than one record, which is invalid");
+               zone->broken = true;
+               return (ISC_R_FAILURE);
+       }
+
        result = dns_rdataset_first(value);
        if (result != ISC_R_SUCCESS) {
                return (result);
@@ -1195,7 +1222,9 @@ catz_process_version(dns_catz_zone_t *zone, dns_rdataset_t *value) {
        dns_rdataset_current(value, &rdata);
 
        result = dns_rdata_tostruct(&rdata, &rdatatxt, NULL);
-       RUNTIME_CHECK(result == ISC_R_SUCCESS);
+       if (result != ISC_R_SUCCESS) {
+               return (result);
+       }
 
        result = dns_rdata_txt_first(&rdatatxt);
        if (result != ISC_R_SUCCESS) {
@@ -1227,6 +1256,13 @@ catz_process_version(dns_catz_zone_t *zone, dns_rdataset_t *value) {
 
 cleanup:
        dns_rdata_freestruct(&rdatatxt);
+       if (result != ISC_R_SUCCESS) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                             DNS_LOGMODULE_MASTER, ISC_LOG_WARNING,
+                             "catz: invalid record for the catalog "
+                             "zone version property");
+               zone->broken = true;
+       }
        return (result);
 }
 
@@ -1670,6 +1706,14 @@ dns_catz_update_process(dns_catz_zones_t *catzs, dns_catz_zone_t *zone,
        REQUIRE(DNS_CATZ_ZONE_VALID(zone));
        REQUIRE(ISC_MAGIC_VALID(src_name, DNS_NAME_MAGIC));
 
+       if (rdataset->rdclass != dns_rdataclass_in) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                             DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
+                             "catz: RR found which has a non-IN class");
+               zone->broken = true;
+               return (ISC_R_FAILURE);
+       }
+
        nrres = dns_name_fullcompare(src_name, &zone->name, &order, &nlabels);
        if (nrres == dns_namereln_equal) {
                if (rdataset->type == dns_rdatatype_soa) {
@@ -2047,8 +2091,10 @@ dns_catz_update_from_db(dns_db_t *db, dns_catz_zones_t *catzs) {
        dns_rdatasetiter_t *rdsiter = NULL;
        dns_rdataset_t rdataset;
        char bname[DNS_NAME_FORMATSIZE];
+       char cname[DNS_NAME_FORMATSIZE];
        bool is_vers_processed = false;
        uint32_t vers;
+       uint32_t catz_vers;
 
        REQUIRE(DNS_DB_VALID(db));
        REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
@@ -2125,13 +2171,13 @@ dns_catz_update_from_db(dns_db_t *db, dns_catz_zones_t *catzs) {
        result = dns_dbiterator_seek(it, name);
        if (result != ISC_R_SUCCESS) {
                dns_dbiterator_destroy(&it);
-               dns_catz_zone_detach(&newzone);
                dns_db_closeversion(db, &oldzone->dbversion, false);
                isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
                              DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
                              "catz: zone '%s' has no 'version' record (%s)",
                              bname, isc_result_totext(result));
-               return;
+               newzone->broken = true;
+               goto final;
        }
 
        name = dns_fixedname_initname(&fixname);
@@ -2177,7 +2223,6 @@ dns_catz_update_from_db(dns_db_t *db, dns_catz_zones_t *catzs) {
                        result = dns_catz_update_process(catzs, newzone, name,
                                                         &rdataset);
                        if (result != ISC_R_SUCCESS) {
-                               char cname[DNS_NAME_FORMATSIZE];
                                char typebuf[DNS_RDATATYPE_FORMATSIZE];
                                char classbuf[DNS_RDATACLASS_FORMATSIZE];
 
@@ -2191,8 +2236,8 @@ dns_catz_update_from_db(dns_db_t *db, dns_catz_zones_t *catzs) {
                                isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
                                              DNS_LOGMODULE_MASTER,
                                              ISC_LOG_WARNING,
-                                             "catz: unknown record in catalog "
-                                             "zone - %s %s %s(%s) - ignoring",
+                                             "catz: invalid record in catalog "
+                                             "zone - %s %s %s (%s) - ignoring",
                                              cname, classbuf, typebuf,
                                              isc_result_totext(result));
                        }
@@ -2218,6 +2263,40 @@ dns_catz_update_from_db(dns_db_t *db, dns_catz_zones_t *catzs) {
                      ISC_LOG_DEBUG(3),
                      "catz: update_from_db: iteration finished");
 
+       /*
+        * Check catalog zone version compatibilites.
+        */
+       catz_vers = (newzone->version == DNS_CATZ_VERSION_UNDEFINED)
+                           ? oldzone->version
+                           : newzone->version;
+       if (catz_vers == DNS_CATZ_VERSION_UNDEFINED) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                             DNS_LOGMODULE_MASTER, ISC_LOG_WARNING,
+                             "catz: zone '%s' version is not set", bname);
+               newzone->broken = true;
+       } else if (catz_vers != 1 && catz_vers != 2) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                             DNS_LOGMODULE_MASTER, ISC_LOG_WARNING,
+                             "catz: zone '%s' unsupported version "
+                             "'%" PRIu32 "'",
+                             bname, catz_vers);
+               newzone->broken = true;
+       } else {
+               oldzone->version = catz_vers;
+       }
+
+final:
+       if (newzone->broken) {
+               dns_name_format(name, cname, DNS_NAME_FORMATSIZE);
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+                             DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
+                             "catz: new catalog zone '%s' is broken and "
+                             "will not be processed",
+                             bname);
+               dns_catz_zone_detach(&newzone);
+               return;
+       }
+
        /*
         * Finally merge new zone into old zone.
         */