From f75c39811d3956544bb2f785c4c16d18426de80e Mon Sep 17 00:00:00 2001 From: Aram Sargsyan Date: Wed, 16 Mar 2022 20:11:17 +0000 Subject: [PATCH 1/4] Implement catalog zones options new syntax based on custom properties According to DNS catalog zones draft version 5 document, catalog zone custom properties must be placed under the "ext" label. Make necessary changes to support the new custom properties syntax in catalog zones with version "2" of the schema. Change the default catalog zones schema version from "1" to "2" in ARM to prepare for the new features and changes which come starting from this commit in order to support the latest DNS catalog zones draft document. Make some restructuring in ARM and rename the term catalog zone "option" to "custom property" to better reflect the terms used in the draft. Change the version of 'catalog1.zone.' catalog zone in the "catz" system test to "2", and leave the version of 'catalog2.zone.' catalog zone at version "1" to test both versions. Add tests to check that the new syntax works only with the new schema version, and that the old syntax works only with the legacy schema version catalog zones. (cherry picked from commit cedfebc64a6d0f488c3c7649cbfac89e99ef5d28) --- bin/tests/system/catz/.gitignore | 2 + bin/tests/system/catz/clean.sh | 2 +- .../system/catz/ns1/catalog.example.db.in | 2 +- .../system/catz/ns3/catalog.example.db.in | 14 + bin/tests/system/catz/setup.sh | 2 +- bin/tests/system/catz/tests.sh | 313 ++++++++++++++++-- doc/arm/catz.rst | 98 +++--- lib/dns/catz.c | 51 ++- 8 files changed, 414 insertions(+), 70 deletions(-) create mode 100644 bin/tests/system/catz/ns3/catalog.example.db.in diff --git a/bin/tests/system/catz/.gitignore b/bin/tests/system/catz/.gitignore index 85e2e07384..b5ba95a12a 100644 --- a/bin/tests/system/catz/.gitignore +++ b/bin/tests/system/catz/.gitignore @@ -12,6 +12,8 @@ /ns1/*dom*.example.db /ns3/dom13.example.db /ns3/dom14.example.db +/ns3/dom17.example.db +/ns3/dom18.example.db /ns2/zonedir diff --git a/bin/tests/system/catz/clean.sh b/bin/tests/system/catz/clean.sh index c3600d305b..375587cc00 100644 --- a/bin/tests/system/catz/clean.sh +++ b/bin/tests/system/catz/clean.sh @@ -20,7 +20,7 @@ rm -f ns*/named.run.prev rm -f ns1/*dom*example.db rm -f ns2/__catz__*db rm -f ns2/named.conf.tmp -rm -f ns3/dom13.example.db ns3/dom14.example.db +rm -f ns3/dom13.example.db ns3/dom14.example.db ns3/dom17.example.db ns3/dom18.example.db rm -f nsupdate.out.* rm -f ns[123]/catalog[1234].example.db rm -rf ns2/zonedir diff --git a/bin/tests/system/catz/ns1/catalog.example.db.in b/bin/tests/system/catz/ns1/catalog.example.db.in index a0bab0dfe2..6b52947922 100644 --- a/bin/tests/system/catz/ns1/catalog.example.db.in +++ b/bin/tests/system/catz/ns1/catalog.example.db.in @@ -11,4 +11,4 @@ @ 3600 SOA . . 1 86400 3600 86400 3600 @ 3600 IN NS invalid. -version IN TXT "1" +version IN TXT "2" diff --git a/bin/tests/system/catz/ns3/catalog.example.db.in b/bin/tests/system/catz/ns3/catalog.example.db.in new file mode 100644 index 0000000000..a0bab0dfe2 --- /dev/null +++ b/bin/tests/system/catz/ns3/catalog.example.db.in @@ -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 "1" diff --git a/bin/tests/system/catz/setup.sh b/bin/tests/system/catz/setup.sh index 5e99898880..9f92499228 100644 --- a/bin/tests/system/catz/setup.sh +++ b/bin/tests/system/catz/setup.sh @@ -20,7 +20,7 @@ copy_setports ns2/named1.conf.in ns2/named.conf copy_setports ns3/named.conf.in ns3/named.conf cp -f ns1/catalog.example.db.in ns1/catalog1.example.db -cp -f ns1/catalog.example.db.in ns3/catalog2.example.db +cp -f ns3/catalog.example.db.in ns3/catalog2.example.db cp -f ns1/catalog.example.db.in ns1/catalog3.example.db cp -f ns1/catalog.example.db.in ns1/catalog4.example.db diff --git a/bin/tests/system/catz/tests.sh b/bin/tests/system/catz/tests.sh index 469acdcdb9..ae17b72e42 100644 --- a/bin/tests/system/catz/tests.sh +++ b/bin/tests/system/catz/tests.sh @@ -434,7 +434,7 @@ ret=0 $NSUPDATE -d <> nsupdate.out.test$n 2>&1 || ret=1 server 10.53.0.1 ${PORT} update add somerandomlabel.zones.catalog1.example. 3600 IN PTR dom5.example. - update add primaries.somerandomlabel.zones.catalog1.example. 3600 IN A 10.53.0.3 + update add primaries.ext.somerandomlabel.zones.catalog1.example. 3600 IN A 10.53.0.3 send END if [ $ret -ne 0 ]; then echo_i "failed"; fi @@ -489,8 +489,8 @@ echo_i "adding dom6.example. and a valid global primaries option (IP without TSI ret=0 $NSUPDATE -d <> nsupdate.out.test$n 2>&1 || ret=1 server 10.53.0.1 ${PORT} - update add primaries.catalog1.example. 3600 IN A 10.53.0.3 - update add primaries.catalog1.example. 3600 IN AAAA fd92:7065:b8e:ffff::3 + update add primaries.ext.catalog1.example. 3600 IN A 10.53.0.3 + update add primaries.ext.catalog1.example. 3600 IN AAAA fd92:7065:b8e:ffff::3 update add 4346f565b4d63ddb99e5d2497ff22d04e878e8f8.zones.catalog1.example. 3600 IN PTR dom6.example. send END @@ -517,8 +517,8 @@ echo_i "removing dom6.example. ($n)" ret=0 $NSUPDATE -d <> nsupdate.out.test$n 2>&1 || ret=1 server 10.53.0.1 ${PORT} - update delete primaries.catalog1.example. 3600 IN A 10.53.0.3 - update delete primaries.catalog1.example. 3600 IN AAAA fd92:7065:b8e:ffff::3 + update delete primaries.ext.catalog1.example. 3600 IN A 10.53.0.3 + update delete primaries.ext.catalog1.example. 3600 IN AAAA fd92:7065:b8e:ffff::3 update delete 4346f565b4d63ddb99e5d2497ff22d04e878e8f8.zones.catalog1.example. 3600 IN PTR dom6.example. send END @@ -546,7 +546,7 @@ echo_i "adding dom6.example. and an invalid global primaries option (TSIG withou ret=0 $NSUPDATE -d <> nsupdate.out.test$n 2>&1 || ret=1 server 10.53.0.1 ${PORT} - update add label1.primaries.catalog1.example. 3600 IN TXT "tsig_key" + update add label1.primaries.ext.catalog1.example. 3600 IN TXT "tsig_key" update add 4346f565b4d63ddb99e5d2497ff22d04e878e8f8.zones.catalog1.example. 3600 IN PTR dom6.example. send END @@ -566,7 +566,7 @@ echo_i "removing dom6.example. ($n)" ret=0 $NSUPDATE -d <> nsupdate.out.test$n 2>&1 || ret=1 server 10.53.0.1 ${PORT} - update delete label1.primaries.catalog1.example. 3600 IN TXT "tsig_key" + update delete label1.primaries.ext.catalog1.example. 3600 IN TXT "tsig_key" update delete 4346f565b4d63ddb99e5d2497ff22d04e878e8f8.zones.catalog1.example. 3600 IN PTR dom6.example. send END @@ -619,7 +619,7 @@ ret=0 $NSUPDATE -d <> nsupdate.out.test$n 2>&1 || ret=1 server 10.53.0.1 ${PORT} 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 + update add allow-query.ext.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 -ne 0 ]; then echo_i "failed"; fi @@ -661,8 +661,8 @@ ret=0 $NSUPDATE -d <> nsupdate.out.test$n 2>&1 || ret=1 server 10.53.0.1 ${PORT} 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 + update add allow-query.ext.catalog1.example. 3600 IN APL 1:10.53.0.1/32 + update add allow-transfer.ext.catalog1.example. 3600 IN APL 1:10.53.0.2/32 send END if [ $ret -ne 0 ]; then echo_i "failed"; fi @@ -712,8 +712,8 @@ echo_i "deleting global allow-query and allow-domain ACLs ($n)" ret=0 $NSUPDATE -d <> nsupdate.out.test$n 2>&1 || ret=1 server 10.53.0.1 ${PORT} - update delete allow-query.catalog1.example. 3600 IN APL 1:10.53.0.1/32 - update delete allow-transfer.catalog1.example. 3600 IN APL 1:10.53.0.2/32 + update delete allow-query.ext.catalog1.example. 3600 IN APL 1:10.53.0.1/32 + update delete allow-transfer.ext.catalog1.example. 3600 IN APL 1:10.53.0.2/32 send END if [ $ret -ne 0 ]; then echo_i "failed"; fi @@ -780,8 +780,8 @@ ret=0 $NSUPDATE -d <> nsupdate.out.test$n 2>&1 || ret=1 server 10.53.0.1 ${PORT} update add f0f989bc71c5c8ca3a1eb9c9ab5246521907e3af.zones.catalog1.example. 3600 IN PTR dom9.example. - update add label1.primaries.f0f989bc71c5c8ca3a1eb9c9ab5246521907e3af.zones.catalog1.example. 3600 IN A 10.53.0.1 - update add label1.primaries.f0f989bc71c5c8ca3a1eb9c9ab5246521907e3af.zones.catalog1.example. 3600 IN TXT "tsig_key" + update add label1.primaries.ext.f0f989bc71c5c8ca3a1eb9c9ab5246521907e3af.zones.catalog1.example. 3600 IN A 10.53.0.1 + update add label1.primaries.ext.f0f989bc71c5c8ca3a1eb9c9ab5246521907e3af.zones.catalog1.example. 3600 IN TXT "tsig_key" send END if [ $ret -ne 0 ]; then echo_i "failed"; fi @@ -808,8 +808,8 @@ ret=0 $NSUPDATE -d <> nsupdate.out.test$n 2>&1 || ret=1 server 10.53.0.1 ${PORT} update delete f0f989bc71c5c8ca3a1eb9c9ab5246521907e3af.zones.catalog1.example. 3600 IN PTR dom9.example. - update delete label1.primaries.f0f989bc71c5c8ca3a1eb9c9ab5246521907e3af.zones.catalog1.example. 3600 IN A 10.53.0.1 - update delete label1.primaries.f0f989bc71c5c8ca3a1eb9c9ab5246521907e3af.zones.catalog1.example. 3600 IN TXT "tsig_key" + update delete label1.primaries.ext.f0f989bc71c5c8ca3a1eb9c9ab5246521907e3af.zones.catalog1.example. 3600 IN A 10.53.0.1 + update delete label1.primaries.ext.f0f989bc71c5c8ca3a1eb9c9ab5246521907e3af.zones.catalog1.example. 3600 IN TXT "tsig_key" send END if [ $ret -ne 0 ]; then echo_i "failed"; fi @@ -837,7 +837,7 @@ ret=0 $NSUPDATE -d <> nsupdate.out.test$n 2>&1 || ret=1 server 10.53.0.1 ${PORT} update add f0f989bc71c5c8ca3a1eb9c9ab5246521907e3af.zones.catalog1.example. 3600 IN PTR dom9.example. - update add label1.primaries.f0f989bc71c5c8ca3a1eb9c9ab5246521907e3af.zones.catalog1.example. 3600 IN TXT "tsig_key" + update add label1.primaries.ext.f0f989bc71c5c8ca3a1eb9c9ab5246521907e3af.zones.catalog1.example. 3600 IN TXT "tsig_key" send END if [ $ret -ne 0 ]; then echo_i "failed"; fi @@ -857,7 +857,7 @@ ret=0 $NSUPDATE -d <> nsupdate.out.test$n 2>&1 || ret=1 server 10.53.0.1 ${PORT} update delete f0f989bc71c5c8ca3a1eb9c9ab5246521907e3af.zones.catalog1.example. 3600 IN PTR dom9.example. - update delete label1.primaries.f0f989bc71c5c8ca3a1eb9c9ab5246521907e3af.zones.catalog1.example. 3600 IN TXT "tsig_key" + update delete label1.primaries.ext.f0f989bc71c5c8ca3a1eb9c9ab5246521907e3af.zones.catalog1.example. 3600 IN TXT "tsig_key" send END if [ $ret -ne 0 ]; then echo_i "failed"; fi @@ -1319,7 +1319,7 @@ ret=0 $NSUPDATE -d <> nsupdate.out.test$n 2>&1 || ret=1 server 10.53.0.1 ${PORT} update add 8d7989c746b3f92b3bba2479e72afd977198363f.zones.catalog1.example. 3600 IN PTR dom13.example. - update add primaries.8d7989c746b3f92b3bba2479e72afd977198363f.zones.catalog1.example. 3600 IN A 10.53.0.1 + update add primaries.ext.8d7989c746b3f92b3bba2479e72afd977198363f.zones.catalog1.example. 3600 IN A 10.53.0.1 send END if [ $ret -ne 0 ]; then echo_i "failed"; fi @@ -1405,7 +1405,7 @@ ret=0 $NSUPDATE -d <> nsupdate.out.test$n 2>&1 || ret=1 server 10.53.0.1 ${PORT} update delete 8d7989c746b3f92b3bba2479e72afd977198363f.zones.catalog1.example. 3600 IN PTR dom13.example. - update delete primaries.8d7989c746b3f92b3bba2479e72afd977198363f.zones.catalog1.example. 3600 IN A 10.53.0.2 + update delete primaries.ext.8d7989c746b3f92b3bba2479e72afd977198363f.zones.catalog1.example. 3600 IN A 10.53.0.2 send END if [ $ret -ne 0 ]; then echo_i "failed"; fi @@ -1755,6 +1755,279 @@ wait_for_no_soa @10.53.0.2 dom16.example. dig.out.test$n || ret=1 if [ $ret -ne 0 ]; then echo_i "failed"; fi status=$((status+ret)) +########################################################################## +echo_i "Testing custom properties version '1' and version '2' syntaxes" +n=$((n+1)) +echo_i "checking that dom17.example. is not served by primary ($n)" +ret=0 +wait_for_no_soa @10.53.0.1 dom17.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 dom18.example. is not served by primary ($n)" +ret=0 +wait_for_no_soa @10.53.0.1 dom18.example. dig.out.test$n || ret=1 +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +n=$((n+1)) +echo_i "adding domains dom17.example. and dom18.example. to primary ns1 via RNDC ($n)" +ret=0 +echo "@ 3600 IN SOA . . 1 3600 3600 3600 3600" > ns1/dom17.example.db +echo "@ IN NS invalid." >> ns1/dom17.example.db +echo "@ IN A 192.0.2.1" >> ns1/dom17.example.db +rndccmd 10.53.0.1 addzone dom17.example. '{type primary; file "dom17.example.db";};' || ret=1 +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status+ret)) +echo "@ 3600 IN SOA . . 1 3600 3600 3600 3600" > ns1/dom18.example.db +echo "@ IN NS invalid." >> ns1/dom18.example.db +echo "@ IN A 192.0.2.1" >> ns1/dom18.example.db +rndccmd 10.53.0.1 addzone dom18.example. '{type primary; file "dom18.example.db";};' || ret=1 +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +n=$((n+1)) +echo_i "checking that dom17.example. is now served by primary ns1 ($n)" +ret=0 +wait_for_soa @10.53.0.1 dom17.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 dom18.example. is now served by primary ns1 ($n)" +ret=0 +wait_for_soa @10.53.0.1 dom18.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 dom17.example. is not served by primary ns3 ($n)" +ret=0 +wait_for_no_soa @10.53.0.3 dom17.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 dom18.example. is not served by primary ns3 ($n)" +ret=0 +wait_for_no_soa @10.53.0.3 dom18.example. dig.out.test$n || ret=1 +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +n=$((n+1)) +echo_i "adding domains dom17.example. and dom18.example. to primary ns3 via RNDC ($n)" +ret=0 +echo "@ 3600 IN SOA . . 1 3600 3600 3600 3600" > ns3/dom17.example.db +echo "@ IN NS invalid." >> ns3/dom17.example.db +echo "@ IN A 192.0.2.2" >> ns3/dom17.example.db +rndccmd 10.53.0.3 addzone dom17.example. '{type primary; file "dom17.example.db";};' || ret=1 +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status+ret)) +echo "@ 3600 IN SOA . . 1 3600 3600 3600 3600" > ns3/dom18.example.db +echo "@ IN NS invalid." >> ns3/dom18.example.db +echo "@ IN A 192.0.2.2" >> ns3/dom18.example.db +rndccmd 10.53.0.3 addzone dom18.example. '{type primary; file "dom18.example.db";};' || ret=1 +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +n=$((n+1)) +echo_i "checking that dom17.example. is now served by primary ns3 ($n)" +ret=0 +wait_for_soa @10.53.0.3 dom17.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 dom18.example. is now served by primary ns3 ($n)" +ret=0 +wait_for_soa @10.53.0.3 dom17.example. dig.out.test$n || 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 "adding domains dom17.example. and dom18.example. to catalog1 zone with ns3 as custom primary using different custom properties syntax ($n)" +ret=0 +$NSUPDATE -d <> nsupdate.out.test$n 2>&1 || ret=1 + server 10.53.0.1 ${PORT} + update add dom17.zones.catalog1.example. 3600 IN PTR dom17.example. + update add dom18.zones.catalog1.example. 3600 IN PTR dom18.example. + update add primaries.dom17.zones.catalog1.example. 3600 IN A 10.53.0.3 + update add primaries.ext.dom18.zones.catalog1.example. 3600 IN A 10.53.0.3 + 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 ($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: 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" && +wait_for_message ns2/named.run "transfer of 'dom18.example/IN' from 10.53.0.3#${PORT}: Transfer status: success" || ret=1 +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +# The "primaries" custom property for dom17.example. was added using the legacy +# syntax into a version 2 catalog1 zone, so we expect that it was ignored, no +# override of the default setting happened, and dom17.example. was transferred +# from the ns1 primary (the default). +n=$((n+1)) +echo_i "checking that dom17.example. is served by secondary and that it's the one from ns1 ($n)" +ret=0 +wait_for_a @10.53.0.2 dom17.example. dig.out.test$n || ret=1 +grep "192.0.2.1" dig.out.test$n > /dev/null || ret=1 +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +# The "primaries" custom property for dom18.example. was added using a supported +# syntax into a version 2 catalog1 zone, so we expect that it was processed, +# will override the default setting, and dom18.example. was transferred +# from the ns3 primary. +n=$((n+1)) +echo_i "checking that dom18.example. is served by secondary and that it's the one from ns3 ($n)" +ret=0 +wait_for_a @10.53.0.2 dom18.example. dig.out.test$n || ret=1 +grep "192.0.2.2" dig.out.test$n > /dev/null || 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 dom17.example. and dom18.example. from catalog1 ($n)" +ret=0 +$NSUPDATE -d <> nsupdate.out.test$n 2>&1 || ret=1 + server 10.53.0.1 ${PORT} + update delete dom17.zones.catalog1.example. 3600 IN PTR dom17.example. + update delete dom18.zones.catalog1.example. 3600 IN PTR dom18.example. + update delete primaries.dom17.zones.catalog1.example. 3600 IN A 10.53.0.3 + update delete primaries.ext.dom18.zones.catalog1.example. 3600 IN A 10.53.0.3 + 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 ($n)" +ret=0 +wait_for_message ns2/named.run "catz: deleting zone 'dom17.example' from catalog 'catalog1.example' - success" && +wait_for_message ns2/named.run "catz: deleting zone 'dom18.example' from catalog 'catalog1.example' - success" && +wait_for_message ns2/named.run "zone_shutdown: zone dom17.example/IN: shutting down" && +wait_for_message ns2/named.run "zone_shutdown: zone dom18.example/IN: shutting down" || ret=1 +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +n=$((n+1)) +echo_i "checking that dom17.example. is not served by secondary ($n)" +ret=0 +wait_for_no_soa @10.53.0.2 dom17.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 dom18.example. is not served by secondary ($n)" +ret=0 +wait_for_no_soa @10.53.0.2 dom18.example. dig.out.test$n || 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 "adding domains dom17.example. and dom18.example. to catalog2 zone with ns3 as custom primary using different custom properties syntax ($n)" +ret=0 +$NSUPDATE -d <> nsupdate.out.test$n 2>&1 || ret=1 + server 10.53.0.3 ${PORT} + update add dom17.zones.catalog2.example. 3600 IN PTR dom17.example. + update add dom18.zones.catalog2.example. 3600 IN PTR dom18.example. + update add primaries.dom17.zones.catalog2.example. 3600 IN A 10.53.0.3 + update add primaries.ext.dom18.zones.catalog2.example. 3600 IN A 10.53.0.3 + 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 ($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: 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" && +wait_for_message ns2/named.run "transfer of 'dom18.example/IN' from 10.53.0.1#${EXTRAPORT1}: Transfer status: success" || ret=1 +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +# The "primaries" custom property for dom17.example. was added using a supported +# syntax into a version 1 catalog1 zone, so we expect that it was processed, +# will override the default setting, and dom17.example. was transferred +# from the ns3 primary. +n=$((n+1)) +echo_i "checking that dom17.example. is served by secondary and that it's the one from ns3 ($n)" +ret=0 +wait_for_a @10.53.0.2 dom17.example. dig.out.test$n || ret=1 +grep "192.0.2.2" dig.out.test$n > /dev/null || ret=1 +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +# The "primaries" custom property for dom18.example. was added using the new +# syntax into a version 1 catalog1 zone, so we expect that it was ignored, no +# override of the default setting happened, and dom18.example. was transferred +# from the ns1 primary (the default). +n=$((n+1)) +echo_i "checking that dom18.example. is served by secondary and that it's the one from ns1 ($n)" +ret=0 +wait_for_a @10.53.0.2 dom18.example. dig.out.test$n || ret=1 +grep "192.0.2.1" dig.out.test$n > /dev/null || 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 dom17.example. and dom18.example. from catalog2 ($n)" +ret=0 +$NSUPDATE -d <> nsupdate.out.test$n 2>&1 || ret=1 + server 10.53.0.3 ${PORT} + update delete dom17.zones.catalog2.example. 3600 IN PTR dom17.example. + update delete dom18.zones.catalog2.example. 3600 IN PTR dom18.example. + update delete primaries.dom17.zones.catalog2.example. 3600 IN A 10.53.0.3 + update delete primaries.ext.dom18.zones.catalog2.example. 3600 IN A 10.53.0.3 + 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 ($n)" +ret=0 +wait_for_message ns2/named.run "catz: deleting zone 'dom17.example' from catalog 'catalog2.example' - success" && +wait_for_message ns2/named.run "catz: deleting zone 'dom18.example' from catalog 'catalog2.example' - success" && +wait_for_message ns2/named.run "zone_shutdown: zone dom17.example/IN: shutting down" && +wait_for_message ns2/named.run "zone_shutdown: zone dom18.example/IN: shutting down" || ret=1 +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +n=$((n+1)) +echo_i "checking that dom17.example. is not served by secondary ($n)" +ret=0 +wait_for_no_soa @10.53.0.2 dom17.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 dom18.example. is not served by secondary ($n)" +ret=0 +wait_for_no_soa @10.53.0.2 dom18.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 reconfig can delete and restore catalog zone configuration ($n)" ret=0 diff --git a/doc/arm/catz.rst b/doc/arm/catz.rst index 18af8cc6ea..70521d33eb 100644 --- a/doc/arm/catz.rst +++ b/doc/arm/catz.rst @@ -154,35 +154,60 @@ then a catalog zone may not be used by that server. :: catalog.example. IN SOA . . 2016022901 900 600 86400 1 - catalog.example. IN NS nsexample. - version.catalog.example. IN TXT "1" + catalog.example. IN NS invalid. + version.catalog.example. IN TXT "2" Note that this record must have the domain name ``version.catalog-zone-name``. The data stored in a catalog zone is indicated by the domain name label -immediately before the catalog zone domain. +immediately before the catalog zone domain. Currently BIND supports catalog zone +schema versions "1" and "2". -Catalog zone options can be set either globally for the whole catalog -zone or for a single member zone. Global options override the settings -in the configuration file, and member zone options override global -options. +Also note that the catalog zone must have an NS record in order to be a valid +DNS zone, and using the value "invalid." for NS is recommended. -Global options are set at the apex of the catalog zone, e.g.: +A member zone is added by including a ``PTR`` resource record in the +``zones`` sub-domain of the catalog zone. The record label can be any unique label. +The target of the PTR record is the member zone name. For example, to add member zones +``domain.example`` and ``domain2.example``: :: - primaries.catalog.example. IN AAAA 2001:db8::1 + 5960775ba382e7a4e09263fc06e7c00569b6a05c.zones.catalog.example. IN PTR domain.example. + uniquelabel.zones.catalog.example. IN PTR domain2.example. -BIND currently supports the following options: +The label is necessary to identify custom properties (see below) for a specific member zone. +Also, the zone state can be reset by changing its label. + +Catalog Zone Custom Properties +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +BIND uses catalog zones custom properties to define different properties which +can be set either globally for the whole catalog +zone or for a single member zone. Global custom properties override the settings +in the configuration file, and member zone custom properties override global +custom properties. + +For the version "1" of the schema custom properties must be placed without a special suffix. + +For the version "2" of the schema custom properties must be placed under the ".ext" suffix. + +Global custom properties are set at the apex of the catalog zone, e.g.: + +:: + + primaries.ext.catalog.example. IN AAAA 2001:db8::1 + +BIND currently supports the following custom properties: - A simple ``primaries`` definition: :: - primaries.catalog.example. IN A 192.0.2.1 + primaries.ext.catalog.example. IN A 192.0.2.1 - This option defines a primary server for the member zones, which can be + This custom property defines a primary server for the member zones, which can be either an A or AAAA record. If multiple primaries are set, the order in which they are used is random. @@ -192,11 +217,11 @@ BIND currently supports the following options: :: - label.primaries.catalog.example. IN A 192.0.2.2 - label.primaries.catalog.example. IN TXT "tsig_key_name" + label.primaries.ext.catalog.example. IN A 192.0.2.2 + label.primaries.ext.catalog.example. IN TXT "tsig_key_name" - This option defines a primary server for the member zone with a TSIG + This custom property defines a primary server for the member zone with a TSIG key set. The TSIG key must be configured in the configuration file. ``label`` can be any valid DNS label. @@ -206,43 +231,34 @@ BIND currently supports the following options: :: - allow-query.catalog.example. IN APL 1:10.0.0.1/24 - allow-transfer.catalog.example. IN APL !1:10.0.0.1/32 1:10.0.0.0/24 + allow-query.ext.catalog.example. IN APL 1:10.0.0.1/24 + allow-transfer.ext.catalog.example. IN APL !1:10.0.0.1/32 1:10.0.0.0/24 - These options are the equivalents of ``allow-query`` and - ``allow-transfer`` in a zone declaration in the :iscman:`named.conf` + These custom properties are the equivalents of ``allow-query`` and + ``allow-transfer`` options in a zone declaration in the :iscman:`named.conf` configuration file. The ACL is processed in order; if there is no match to any rule, the default policy is to deny access. For the syntax of the APL RR, see :rfc:`3123`. -A member zone is added by including a ``PTR`` resource record in the -``zones`` sub-domain of the catalog zone. The record label is a -``SHA-1`` hash of the member zone name in wire format. The target of the -PTR record is the member zone name. For example, to add the member zone -``domain.example``: +The member zone-specific custom properties are defined the same way as global +custom properties, but in the member zone subdomain: :: - 5960775ba382e7a4e09263fc06e7c00569b6a05c.zones.catalog.example. IN PTR domain.example. + primaries.ext.5960775ba382e7a4e09263fc06e7c00569b6a05c.zones.catalog.example. IN A 192.0.2.2 + label.primaries.ext.5960775ba382e7a4e09263fc06e7c00569b6a05c.zones.catalog.example. IN AAAA 2001:db8::2 + label.primaries.ext.5960775ba382e7a4e09263fc06e7c00569b6a05c.zones.catalog.example. IN TXT "tsig_key_name" + allow-query.ext.5960775ba382e7a4e09263fc06e7c00569b6a05c.zones.catalog.example. IN APL 1:10.0.0.0/24 + primaries.ext.uniquelabel.zones.catalog.example. IN A 192.0.2.3 -The hash is necessary to identify options for a specific member zone. -The member zone-specific options are defined the same way as global -options, but in the member zone subdomain: - -:: - - primaries.5960775ba382e7a4e09263fc06e7c00569b6a05c.zones.catalog.example. IN A 192.0.2.2 - label.primaries.5960775ba382e7a4e09263fc06e7c00569b6a05c.zones.catalog.example. IN AAAA 2001:db8::2 - label.primaries.5960775ba382e7a4e09263fc06e7c00569b6a05c.zones.catalog.example. IN TXT "tsig_key" - allow-query.5960775ba382e7a4e09263fc06e7c00569b6a05c.zones.catalog.example. IN APL 1:10.0.0.0/24 - -Options defined for a specific zone override the -global options defined in the catalog zone. These in turn override the +Custom properties defined for a specific zone override the +global custom properties defined in the catalog zone. These in turn override the global options defined in the ``catalog-zones`` statement in the configuration file. -Note that none of the global records for an option are inherited if any -records are defined for that option for the specific zone. For example, +Note that none of the global records for a custom property are inherited if any +records are defined for that custom property for the specific zone. For example, if the zone had a ``primaries`` record of type A but not AAAA, it -would *not* inherit the type AAAA record from the global option. +would *not* inherit the type AAAA record from the global custom property +or from global the option in the configuration file. diff --git a/lib/dns/catz.c b/lib/dns/catz.c index 7bee5d8949..28fc33be0e 100644 --- a/lib/dns/catz.c +++ b/lib/dns/catz.c @@ -40,6 +40,8 @@ #define DNS_CATZ_ZONES_VALID(catzs) ISC_MAGIC_VALID(catzs, DNS_CATZ_ZONES_MAGIC) #define DNS_CATZ_ENTRY_VALID(entry) ISC_MAGIC_VALID(entry, DNS_CATZ_ENTRY_MAGIC) +#define DNS_CATZ_VERSION_UNDEFINED ((uint32_t)(-1)) + /*% * Single member zone in a catalog */ @@ -651,7 +653,7 @@ dns_catz_new_zone(dns_catz_zones_t *catzs, dns_catz_zone_t **zonep, dns_catz_options_init(&new_zone->zoneoptions); new_zone->active = true; new_zone->db_registered = false; - new_zone->version = (uint32_t)(-1); + new_zone->version = DNS_CATZ_VERSION_UNDEFINED; isc_refcount_init(&new_zone->refs, 1); new_zone->magic = DNS_CATZ_ZONE_MAGIC; @@ -836,10 +838,12 @@ dns_catz_catzs_detach(dns_catz_zones_t **catzsp) { typedef enum { CATZ_OPT_NONE, CATZ_OPT_ZONES, + CATZ_OPT_VERSION, + CATZ_OPT_CUSTOM_START, /* CATZ custom properties must go below this */ + CATZ_OPT_EXT, CATZ_OPT_MASTERS, CATZ_OPT_ALLOW_QUERY, CATZ_OPT_ALLOW_TRANSFER, - CATZ_OPT_VERSION, } catz_opt_t; static bool @@ -856,7 +860,9 @@ catz_opt_cmp(const dns_label_t *option, const char *opt) { static catz_opt_t catz_get_option(const dns_label_t *option) { - if (catz_opt_cmp(option, "zones")) { + if (catz_opt_cmp(option, "ext")) { + return (CATZ_OPT_EXT); + } else if (catz_opt_cmp(option, "zones")) { return (CATZ_OPT_ZONES); } else if (catz_opt_cmp(option, "masters") || catz_opt_cmp(option, "primaries")) { @@ -1291,18 +1297,32 @@ catz_process_zones_suboption(dns_catz_zone_t *zone, dns_rdataset_t *value, dns_label_t option; dns_name_t prefix; catz_opt_t opt; + unsigned int suffix_labels = 1; REQUIRE(DNS_CATZ_ZONE_VALID(zone)); REQUIRE(mhash != NULL); REQUIRE(DNS_RDATASET_VALID(value)); REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC)); - if (name->labels == 0) { + if (name->labels < 1) { return (ISC_R_FAILURE); } dns_name_getlabel(name, name->labels - 1, &option); opt = catz_get_option(&option); + /* + * The custom properties in version 2 schema must be placed under the + * "ext" label. + */ + if (zone->version >= 2 && opt >= CATZ_OPT_CUSTOM_START) { + if (opt != CATZ_OPT_EXT || name->labels < 2) { + return (ISC_R_FAILURE); + } + suffix_labels++; + dns_name_getlabel(name, name->labels - 2, &option); + opt = catz_get_option(&option); + } + /* * We're adding this entry now, in case the option is invalid we'll get * rid of it in verification phase. @@ -1320,7 +1340,7 @@ catz_process_zones_suboption(dns_catz_zone_t *zone, dns_rdataset_t *value, } dns_name_init(&prefix, NULL); - dns_name_split(name, 1, &prefix, NULL); + dns_name_split(name, suffix_labels, &prefix, NULL); switch (opt) { case CATZ_OPT_MASTERS: return (catz_process_primaries(zone, &entry->opts.masters, @@ -1370,15 +1390,34 @@ catz_process_value(dns_catz_zone_t *zone, dns_name_t *name, dns_label_t option; dns_name_t prefix; catz_opt_t opt; + unsigned int suffix_labels = 1; REQUIRE(DNS_CATZ_ZONE_VALID(zone)); REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC)); REQUIRE(DNS_RDATASET_VALID(rdataset)); + if (name->labels < 1) { + return (ISC_R_FAILURE); + } dns_name_getlabel(name, name->labels - 1, &option); opt = catz_get_option(&option); + + /* + * The custom properties in version 2 schema must be placed under the + * "ext" label. + */ + if (zone->version >= 2 && opt >= CATZ_OPT_CUSTOM_START) { + if (opt != CATZ_OPT_EXT || name->labels < 2) { + return (ISC_R_FAILURE); + } + suffix_labels++; + dns_name_getlabel(name, name->labels - 2, &option); + opt = catz_get_option(&option); + } + dns_name_init(&prefix, NULL); - dns_name_split(name, 1, &prefix, NULL); + dns_name_split(name, suffix_labels, &prefix, NULL); + switch (opt) { case CATZ_OPT_ZONES: return (catz_process_zones(zone, rdataset, &prefix)); From 3e07e505b0f4428669189aa4b31f071a2ec9c6e7 Mon Sep 17 00:00:00 2001 From: Aram Sargsyan Date: Wed, 23 Mar 2022 10:58:39 +0000 Subject: [PATCH 2/4] Add CHANGES note for [GL #3222] (cherry picked from commit 321c93c05d4f8b993fabfea05e4984cc594e7dc7) --- CHANGES | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES b/CHANGES index a2e4b39679..bca73b2440 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,8 @@ +5860. [func] Implement support for catalog zones options new syntax + based on catalog zones custom properties with "ext" + suffix described in the DNS catalog zones draft version + 5 document. [GL #3222] + 5859. [bug] Fix an assertion failure when using dig with +nssearch and +tcp options by starting the next query in the send_done() callback (like in the UDP mode) instead From d8e1f51a04d85283c2ea2a244651fa23d74d617e Mon Sep 17 00:00:00 2001 From: Aram Sargsyan Date: Thu, 24 Mar 2022 20:24:00 +0000 Subject: [PATCH 3/4] Process the 'version' record of the catalog zone first When processing a new or updated catalog zone, the record datasets from the database are being processed in order. This creates a problem because we need to know the version of the catalog zone schema to process some of the records differently, but we do not know the version until the 'version' record gets processed. Find the 'version' record and process it first, only then iterate over the database to process the rest, making sure not to process the 'version' record twice. (cherry picked from commit 6035980bb1af8a84a40efbd9d7ff98d20beab26b) --- lib/dns/catz.c | 48 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/lib/dns/catz.c b/lib/dns/catz.c index 28fc33be0e..0ec243312c 100644 --- a/lib/dns/catz.c +++ b/lib/dns/catz.c @@ -1831,12 +1831,14 @@ dns_catz_update_from_db(dns_db_t *db, dns_catz_zones_t *catzs) { isc_result_t result; isc_region_t r; dns_dbnode_t *node = NULL; + const dns_dbnode_t *vers_node = NULL; dns_dbiterator_t *it = NULL; dns_fixedname_t fixname; dns_name_t *name; dns_rdatasetiter_t *rdsiter = NULL; dns_rdataset_t rdataset; char bname[DNS_NAME_FORMATSIZE]; + bool is_vers_processed = false; uint32_t vers; REQUIRE(DNS_DB_VALID(db)); @@ -1896,16 +1898,38 @@ dns_catz_update_from_db(dns_db_t *db, dns_catz_zones_t *catzs) { name = dns_fixedname_initname(&fixname); /* - * Iterate over database to fill the new zone. + * Take the version record to process first, because the other + * records might be processed differently depending on the version of + * the catalog zone's schema. */ - result = dns_dbiterator_first(it); + result = dns_name_fromstring2(name, "version", &db->origin, 0, NULL); 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: failed to get db iterator - %s", + "catz: failed to create name from string - %s", isc_result_totext(result)); + return; + } + 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; } + name = dns_fixedname_initname(&fixname); + + /* + * Iterate over database to fill the new zone. + */ while (result == ISC_R_SUCCESS) { result = dns_dbiterator_current(it, &node, name); if (result != ISC_R_SUCCESS) { @@ -1916,6 +1940,16 @@ dns_catz_update_from_db(dns_db_t *db, dns_catz_zones_t *catzs) { break; } + if (!is_vers_processed) { + /* Keep the version node to skip it later in the loop */ + vers_node = node; + } else if (node == vers_node) { + /* Skip the already processed version node */ + dns_db_detachnode(db, &node); + result = dns_dbiterator_next(it); + continue; + } + result = dns_db_allrdatasets(db, node, oldzone->dbversion, 0, &rdsiter); if (result != ISC_R_SUCCESS) { @@ -1963,7 +1997,13 @@ dns_catz_update_from_db(dns_db_t *db, dns_catz_zones_t *catzs) { dns_rdatasetiter_destroy(&rdsiter); dns_db_detachnode(db, &node); - result = dns_dbiterator_next(it); + + if (!is_vers_processed) { + is_vers_processed = true; + result = dns_dbiterator_first(it); + } else { + result = dns_dbiterator_next(it); + } } dns_dbiterator_destroy(&it); From 581d7bece0ef79639b52269f1db6c5c2f8e41bee Mon Sep 17 00:00:00 2001 From: Aram Sargsyan Date: Thu, 24 Mar 2022 21:38:08 +0000 Subject: [PATCH 4/4] Do not cancel processing record datasets in catalog zone after an error When there are multiple record datasets in a database node of a catalog zone, and BIND encounters a soft error during processing of a dataset, it breaks from the loop and doesn't process the other datasets in the node. There are cases when this is not desired. For example, the catalog zones draft version 5 states that there must be a TXT RRset named `version.$CATZ` with exactly one RR, but it doesn't set a limitation on possible non-TXT RRsets named `version.$CATZ` existing alongside with the TXT one. In case when one exists, we will get a processing error and will not continue the loop to process the TXT RRset coming next. Remove the "break" statement to continue processing all record datasets. (cherry picked from commit 0b2d5490cd8b17a01852fcd9e0a0e0c4b9c93ab6) --- lib/dns/catz.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/dns/catz.c b/lib/dns/catz.c index 0ec243312c..fa17fd7f60 100644 --- a/lib/dns/catz.c +++ b/lib/dns/catz.c @@ -1988,9 +1988,6 @@ dns_catz_update_from_db(dns_db_t *db, dns_catz_zones_t *catzs) { isc_result_totext(result)); } dns_rdataset_disassociate(&rdataset); - if (result != ISC_R_SUCCESS) { - break; - } result = dns_rdatasetiter_next(rdsiter); }