From 63145fb1d328eb66f9c786d2273bc2e3f6a3ecf5 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Wed, 13 Oct 2021 17:06:48 +1100 Subject: [PATCH 1/2] Prevent existing catalog zone entries being incorrectly deleted After receiving a new version of a catalog zone it is required to merge it with the old version. The algorithm walks through the new version's hash table and applies the following logic: 1. If an entry from the new version does not exist in the old version, then it's a new entry, add the entry to the `toadd` hash table. 2. If the zone does not exist in the set of configured zones, because it was deleted via rndc delzone or it was removed from another catalog zone instance, then add into to the `toadd` hash table to be reinstantiated. 3. If an entry from the new version also exists in the old version, but is modified, then add the entry to the `tomod` hash table, then remove it from the old version's hash table. 4. If an entry from the new version also exists in the old version and is the same (unmodified) then just remove it from the old version's hash table. The algorithm then deletes all the remaining zones which still exist in the old version's hash table (because only the ones that don't exist in the new version should now remain there), then adds the ones that were added to the `toadd`, and modifies the ones that were added to the `tomod`, completing the merge. During a recent refactoring, the part when the entry should be removed from the old version's hash table on condition (4.) above was accidentally omitted, so the unmodified zones were remaining in the old version's hash table and consequently being deleted. --- lib/dns/catz.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/dns/catz.c b/lib/dns/catz.c index b0c4527ee2..c519244b49 100644 --- a/lib/dns/catz.c +++ b/lib/dns/catz.c @@ -494,6 +494,14 @@ dns_catz_zones_merge(dns_catz_zone_t *target, dns_catz_zone_t *newzone) { zname, czname); continue; } + + /* + * Delete the old entry so that it won't accidentally be + * removed as a non-existing entry below. + */ + dns_catz_entry_detach(target, &oentry); + result = isc_ht_delete(target->entries, key, (uint32_t)keysize); + RUNTIME_CHECK(result == ISC_R_SUCCESS); } RUNTIME_CHECK(result == ISC_R_NOMORE); isc_ht_iter_destroy(&iter1); From bf9c5698526f2d81cfcae0a4032e3bbd00bcb535 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Wed, 13 Oct 2021 17:39:00 +1100 Subject: [PATCH 2/2] Check that existing catalog zone entries are preserved Update the 'catz' system test by adding tests that update an catalog zone (catalog1.example) while preserving existing entries (increase SOA serial) then check that catalog zone has transferred and that the existing entries have not accidentally been removed as a consequence (can return updated zone content). --- bin/tests/system/catz/tests.sh | 50 ++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/bin/tests/system/catz/tests.sh b/bin/tests/system/catz/tests.sh index ffb1abd153..5197fbd94c 100644 --- a/bin/tests/system/catz/tests.sh +++ b/bin/tests/system/catz/tests.sh @@ -174,6 +174,56 @@ 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)) +n=$((n+1)) +echo_i "update catalog zone serial ($n)" +ret=0 +# default minimum update rate is once / 5 seconds +sleep 5 +$NSUPDATE -d <> nsupdate.out.test$n 2>&1 || ret=1 + server 10.53.0.1 ${PORT} + update add catalog1.example 3600 SOA . . 20 86400 3600 86400 3600 + send +END +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +n=$((n+1)) +echo_i "wait for catalog zone to transfer ($n)" +ret=0 +wait_for_soa_equal_20() { + dig_with_opts @10.53.0.2 SOA catalog1.example. > dig.out.test$n || return 1 + grep "ANSWER: 1," dig.out.test$n > /dev/null || return 1 + grep "status: NOERROR" dig.out.test$n > /dev/null || return 1 + grep 'IN.SOA.\. \. 20 ' dig.out.test$n > /dev/null || return 1 +} +retry_quiet 10 wait_for_soa_equal_20 || ret=1 +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +n=$((n+1)) +echo_i "update dom1.example. again ($n)" +ret=0 +$NSUPDATE -d <> nsupdate.out.test$n 2>&1 || ret=1 + server 10.53.0.1 ${PORT} + update add foo.dom1.example 0 IN TXT added record + send +END +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +n=$((n+1)) +echo_i "wait for secondary to be updated again ($n)" +ret=0 +wait_for_txt() { + dig_with_opts @10.53.0.2 TXT foo.dom1.example. > dig.out.test$n || return 1 + grep "ANSWER: 1," dig.out.test$n > /dev/null || return 1 + grep "status: NOERROR" dig.out.test$n > /dev/null || return 1 + grep "IN.TXT." dig.out.test$n > /dev/null || return 1 +} +retry_quiet 10 wait_for_txt || ret=1 +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + n=$((n+1)) echo_i "removing domain dom1.example. from catalog1 zone ($n)" ret=0