]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
convert dnssec validation tests to python
authorEvan Hunt <each@isc.org>
Fri, 27 Jun 2025 03:29:24 +0000 (20:29 -0700)
committerEvan Hunt <each@isc.org>
Thu, 31 Jul 2025 19:55:40 +0000 (12:55 -0700)
begin converting DNSSEC validation tests from shell to python,
and simplify the name servers used in the test.

ns4, the name server used for validation tests, is now configured
using jinja2 templates.

ns8, which was previously used for testing unsupported, disabled
and revoked keys and trust anchors, has been removed.  we now
use a jinja2 configuration in ns5 for this purpose.

the configurations in ns7 and ns6 didn't conflict with one another,
so the two servers have been merged into one.

33 files changed:
bin/tests/system/dnssec/README
bin/tests/system/dnssec/ns1/sign.sh
bin/tests/system/dnssec/ns2/example.db.in
bin/tests/system/dnssec/ns3/auto-nsec.example.db.in
bin/tests/system/dnssec/ns3/auto-nsec3.example.db.in
bin/tests/system/dnssec/ns3/expired.example.db.in
bin/tests/system/dnssec/ns3/expiring.example.db.in
bin/tests/system/dnssec/ns3/secure.example.db.in
bin/tests/system/dnssec/ns3/sign.sh
bin/tests/system/dnssec/ns3/update-nsec3.example.db.in
bin/tests/system/dnssec/ns4/named.conf.j2 [new file with mode: 0644]
bin/tests/system/dnssec/ns4/named1.conf.in [deleted file]
bin/tests/system/dnssec/ns4/named2.conf.in [deleted file]
bin/tests/system/dnssec/ns4/named3.conf.in [deleted file]
bin/tests/system/dnssec/ns4/named4.conf.in [deleted file]
bin/tests/system/dnssec/ns5/named.conf.j2
bin/tests/system/dnssec/ns6/named.args [deleted file]
bin/tests/system/dnssec/ns6/named.conf.j2
bin/tests/system/dnssec/ns6/named.nonearest [new file with mode: 0644]
bin/tests/system/dnssec/ns6/named.nosoa [moved from bin/tests/system/dnssec/ns7/named.nosoa with 100% similarity]
bin/tests/system/dnssec/ns6/named.tat=1 [new file with mode: 0644]
bin/tests/system/dnssec/ns6/nosoa.secure.example.db [moved from bin/tests/system/dnssec/ns7/nosoa.secure.example.db with 97% similarity]
bin/tests/system/dnssec/ns6/sign.sh
bin/tests/system/dnssec/ns6/split-rrsig.db.in [moved from bin/tests/system/dnssec/ns7/split-rrsig.db.in with 100% similarity]
bin/tests/system/dnssec/ns7/named.conf.j2 [deleted file]
bin/tests/system/dnssec/ns7/sign.sh [deleted file]
bin/tests/system/dnssec/ns8/named.conf.j2 [deleted file]
bin/tests/system/dnssec/setup.sh
bin/tests/system/dnssec/tests.sh
bin/tests/system/dnssec/tests_sh_dnssec.py
bin/tests/system/dnssec/tests_validation.py [new file with mode: 0644]
bin/tests/system/isctest/mark.py
bin/tests/system/start.pl

index fcaa3b6c80884af1b18e84165e85b4abdd541dc4..c72442c61be6437271798f505dd956e05eca7dea 100644 (file)
@@ -19,14 +19,11 @@ ns4 is a caching-only server, configured with the correct trusted key
 for the root.
 
 ns5 is a caching-only server, configured with the an incorrect trusted
-key for the root.  It is used for testing failure cases.
+key for the root, or with unsupported and disabled algorithms.  It is used
+for testing failure cases.
 
-ns6 is an caching and authoritative server used for testing unusual
-server behaviors such as disabled DNSSEC algorithms.
-
-ns7 is used for checking non-cacheable answers.
-
-ns8 is a caching-only server, configured with unsupported and disabled
-algorithms.  It is used for testing failure cases.
+ns6 is a caching and authoritative server used for testing unusual
+server behaviors such as disabled DNSSEC algorithms and non-cacheable
+responses. It runs with -T nonearest, -T nosoa, and -T tat=3.
 
 ns9 is a forwarding-only server.
index bd434aeb80963b6d3c4904db4f7b962d11688eeb..02c9e18320c2bbb7c5479ee9c9be130f7c23d662 100644 (file)
@@ -22,7 +22,6 @@ zonefile=root.db
 
 (cd ../ns2 && $SHELL sign.sh)
 (cd ../ns6 && $SHELL sign.sh)
-(cd ../ns7 && $SHELL sign.sh)
 
 echo_i "ns1/sign.sh"
 
@@ -51,7 +50,6 @@ cp trusted.conf ../ns2/trusted.conf
 cp trusted.conf ../ns3/trusted.conf
 cp trusted.conf ../ns4/trusted.conf
 cp trusted.conf ../ns6/trusted.conf
-cp trusted.conf ../ns7/trusted.conf
 cp trusted.conf ../ns9/trusted.conf
 
 keyfile_to_static_keys "$ksk" >trusted.keys
index 1efb1755b40fb6ed06b6b9b4204f1a11eb9b6bf2..c72513298818f7420416117274f69284308f7874 100644 (file)
@@ -9,7 +9,7 @@
 ; See the COPYRIGHT file distributed with this work for additional
 ; information regarding copyright ownership.
 
-$TTL 300       ; 5 minutes
+$TTL 3600      ; 1 hour
 @                      IN SOA  mname1. . (
                                2000042407 ; serial
                                20         ; refresh (20 seconds)
@@ -106,6 +106,9 @@ ns.dnskey-unknown   A       10.53.0.3
 dnskey-unsupported     NS      ns.dnskey-unsupported
 ns.dnskey-unsupported  A       10.53.0.3
 
+dnskey-unsupported-2   NS      ns.dnskey-unsupported
+ns.dnskey-unsupported-2        A       10.53.0.3
+
 ds-unsupported NS      ns.ds-unsupported
 ns.ds-unsupported      A       10.53.0.3
 
index a7792fd3b8733eaf2712ead734bf6db26b4cccb9..34006877b35516b0349f5fc3e5df58d774194bdb 100644 (file)
@@ -34,7 +34,7 @@ insecure              NS      ns.insecure
 ns.insecure            A       10.53.0.2
 
 nosoa                  NS      ns.nosoa
-ns.nosoa               A       10.53.0.7
+ns.nosoa               A       10.53.0.6
 
 normalthenrrsig                A       10.0.0.28
 rrsigonly              A       10.0.0.29
index a7792fd3b8733eaf2712ead734bf6db26b4cccb9..34006877b35516b0349f5fc3e5df58d774194bdb 100644 (file)
@@ -34,7 +34,7 @@ insecure              NS      ns.insecure
 ns.insecure            A       10.53.0.2
 
 nosoa                  NS      ns.nosoa
-ns.nosoa               A       10.53.0.7
+ns.nosoa               A       10.53.0.6
 
 normalthenrrsig                A       10.0.0.28
 rrsigonly              A       10.0.0.29
index b7706d3c9f1e74f29a8e75c26239e69f225f4d5f..eaee74b765cef48947f605810bad040d81ce9af7 100644 (file)
@@ -9,7 +9,7 @@
 ; See the COPYRIGHT file distributed with this work for additional
 ; information regarding copyright ownership.
 
-$TTL 300       ; 5 minutes
+$TTL 3600      ; 1 hour
 @                      IN SOA  mname1. . (
                                2000042407 ; serial
                                20         ; refresh (20 seconds)
@@ -36,7 +36,7 @@ insecure              NS      ns.insecure
 ns.insecure            A       10.53.0.2
 
 nosoa                  NS      ns.nosoa
-ns.nosoa               A       10.53.0.7
+ns.nosoa               A       10.53.0.6
 
 normalthenrrsig                A       10.0.0.28
 rrsigonly              A       10.0.0.29
index 8acf7b121ec3710b611b3c30af78a62ae3c151ec..be3e6803544aee05a9b76fc04a1cca08069372ea 100644 (file)
@@ -9,7 +9,7 @@
 ; See the COPYRIGHT file distributed with this work for additional
 ; information regarding copyright ownership.
 
-$TTL 300       ; 5 minutes
+$TTL 3600      ; 1 hour
 @                      IN SOA  mname1. . (
                                2000042407 ; serial
                                20         ; refresh (20 seconds)
index 9aebd98007889154775b79305ba4ba9cf7abb78f..5859ebcdc5e27be2b44264f53dab897e19f71101 100644 (file)
@@ -39,7 +39,7 @@ insecure              NS      ns2.insecure
 ns2.insecure           A       10.53.0.2
 
 nosoa                  NS      ns.nosoa
-ns.nosoa               A       10.53.0.7
+ns.nosoa               A       10.53.0.6
 
 normalthenrrsig                A       10.0.0.28
 rrsigonly              A       10.0.0.29
index 8cf46a2f06f658ac8bcae8ec4ca19b937fc1d573..58da7378f035d1f81726802d43ed222fda16512a 100644 (file)
@@ -53,7 +53,7 @@ for tld in managed trusted; do
   "$SIGNER" -z -3 - -o "$zone" -O full -f ${zonefile}.tmp "$zonefile" >/dev/null
   awk '$4 == "DNSKEY" { $7 = 255 } $4 == "RRSIG" { $6 = 255 } { print }' ${zonefile}.tmp >${zonefile}.signed
 
-  # Make trusted-keys and managed keys conf sections for ns8.
+  # Make trusted-keys and managed keys conf sections for ns5/many_anchors.
   mv ${keyname4}.key ${keyname4}.tmp
   awk '$1 == "unsupported.'"${tld}"'." { $6 = 255 } { print }' ${keyname4}.tmp >${keyname4}.key
 
@@ -67,10 +67,10 @@ for tld in managed trusted; do
 
   case $tld in
     "managed")
-      keyfile_to_initial_keys $keyname1 $keyname2 $keyname3 $keyname4 $keyname5 >../ns8/managed.conf
+      keyfile_to_initial_keys $keyname1 $keyname2 $keyname3 $keyname4 $keyname5 >../ns5/many-managed.conf
       ;;
     "trusted")
-      keyfile_to_static_keys $keyname1 $keyname2 $keyname3 $keyname4 $keyname5 >../ns8/trusted.conf
+      keyfile_to_static_keys $keyname1 $keyname2 $keyname3 $keyname4 $keyname5 >../ns5/many-trusted.conf
       ;;
   esac
 done
@@ -324,7 +324,7 @@ $DSFROMKEY -2 -A -f ${zonefile}.signed "$zone" | tail -1 >>"$DSFILE"
 
 #
 # A zone which is fine by itself (supported algorithm) but that is used
-# to mimic unsupported DS digest (see ns8).
+# to mimic unsupported DS digest (see ns5/many_anchors).
 #
 zone=ds-unsupported.example.
 infile=ds-unsupported.example.db.in
index a7792fd3b8733eaf2712ead734bf6db26b4cccb9..34006877b35516b0349f5fc3e5df58d774194bdb 100644 (file)
@@ -34,7 +34,7 @@ insecure              NS      ns.insecure
 ns.insecure            A       10.53.0.2
 
 nosoa                  NS      ns.nosoa
-ns.nosoa               A       10.53.0.7
+ns.nosoa               A       10.53.0.6
 
 normalthenrrsig                A       10.0.0.28
 rrsigonly              A       10.0.0.29
diff --git a/bin/tests/system/dnssec/ns4/named.conf.j2 b/bin/tests/system/dnssec/ns4/named.conf.j2
new file mode 100644 (file)
index 0000000..9a67908
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+// NS11
+
+
+{% set managed_key = managed_key | default(False) %}
+{% set accept_expired = accept_expired | default(False) %}
+{% set multi_view = multi_view | default(False) %}
+
+options {
+       query-source address 10.53.0.4;
+       notify-source 10.53.0.4;
+       transfer-source 10.53.0.4;
+       port @PORT@;
+       pid-file "named.pid";
+       listen-on { 10.53.0.4; };
+       listen-on-v6 { none; };
+       recursion yes;
+       minimal-responses no;
+
+       {% if accept_expired %}
+               dnssec-accept-expired yes;
+       {% endif %}
+
+       {% if managed_key %}
+               dnssec-validation auto;
+               bindkeys-file "managed.conf";
+       {% else %}
+               # Note: We only reference the bind.keys file here to
+               # confirm that it is *not* being used.  It contains the
+               # real root key, and we're using a local toy root zone for
+               # the tests, so it wouldn't work.  But dnssec-validation
+               # is set to "yes" not "auto", so that won't matter.
+               dnssec-validation yes;
+               bindkeys-file "../../../../../bind.keys";
+       {% endif %}
+
+       disable-algorithms "digest-alg-unsupported.example." { ECDSAP384SHA384; };
+       disable-ds-digests "digest-alg-unsupported.example." { "SHA384"; "SHA-384"; };
+       disable-ds-digests "ds-unsupported.example." { "SHA256"; "SHA-256"; "SHA384"; "SHA-384"; };
+       disable-algorithms "badalg.secure.example." { ECDSAP256SHA256; };
+};
+
+{% if not managed_key %}
+include "trusted.conf";
+{% endif %}
+
+key rndc_key {
+       secret "1234abcd8765";
+       algorithm @DEFAULT_HMAC@;
+};
+
+controls {
+       inet 10.53.0.4 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
+};
+
+{% if multi_view %}
+       view rec {
+               match-recursive-only yes;
+               recursion yes;
+               dnssec-accept-expired yes;
+               minimal-responses no;
+               dnssec-validation yes;
+
+               include "trusted.conf";
+
+               zone "." {
+                       type hint;
+                       file "../../_common/root.hint";
+               };
+
+               zone secure.example {
+                       type static-stub;
+                       server-addresses { 10.53.0.4; };
+               };
+
+               zone insecure.secure.example {
+                       type static-stub;
+                       server-addresses { 10.53.0.4; };
+               };
+       };
+
+       view auth {
+               recursion no;
+               allow-recursion { none; };
+               dnssec-validation no;
+
+               zone "." {
+                       type hint;
+                       file "../../_common/root.hint";
+               };
+
+               zone secure.example {
+                       type secondary;
+                       primaries { 10.53.0.3; };
+               };
+
+               zone insecure.secure.example {
+                       type secondary;
+                       primaries { 10.53.0.2; };
+               };
+       };
+{% else %}
+       zone "." {
+               type hint;
+               file "../../_common/root.hint";
+       };
+{% endif %}
diff --git a/bin/tests/system/dnssec/ns4/named1.conf.in b/bin/tests/system/dnssec/ns4/named1.conf.in
deleted file mode 100644 (file)
index 66975a3..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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.
- */
-
-// NS4
-
-options {
-       query-source address 10.53.0.4;
-       notify-source 10.53.0.4;
-       transfer-source 10.53.0.4;
-       port @PORT@;
-       pid-file "named.pid";
-       listen-on { 10.53.0.4; };
-       listen-on-v6 { none; };
-       recursion yes;
-       minimal-responses no;
-
-       # Note: We only reference the bind.keys file here to confirm that it
-       # is *not* being used.  It contains the real root key, and we're
-       # using a local toy root zone for the tests, so it wouldn't work.
-       # But since dnssec-validation is set to "yes" not "auto", that
-       # won't matter.
-       dnssec-validation yes;
-       bindkeys-file "../../../../../bind.keys";
-};
-
-include "trusted.conf";
-
-key rndc_key {
-       secret "1234abcd8765";
-       algorithm @DEFAULT_HMAC@;
-};
-
-controls {
-       inet 10.53.0.4 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
-};
-
-zone "." {
-       type hint;
-       file "../../_common/root.hint";
-};
diff --git a/bin/tests/system/dnssec/ns4/named2.conf.in b/bin/tests/system/dnssec/ns4/named2.conf.in
deleted file mode 100644 (file)
index c869983..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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.
- */
-
-// NS4
-
-options {
-       query-source address 10.53.0.4;
-       notify-source 10.53.0.4;
-       transfer-source 10.53.0.4;
-       port @PORT@;
-       pid-file "named.pid";
-       listen-on { 10.53.0.4; };
-       listen-on-v6 { none; };
-       recursion yes;
-       minimal-responses no;
-
-       dnssec-validation auto;
-       bindkeys-file "managed.conf";
-};
-
-key rndc_key {
-       secret "1234abcd8765";
-       algorithm @DEFAULT_HMAC@;
-};
-
-controls {
-       inet 10.53.0.4 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
-};
-
-zone "." {
-       type hint;
-       file "../../_common/root.hint";
-};
diff --git a/bin/tests/system/dnssec/ns4/named3.conf.in b/bin/tests/system/dnssec/ns4/named3.conf.in
deleted file mode 100644 (file)
index 24012b3..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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.
- */
-
-// NS4
-
-options {
-       query-source address 10.53.0.4;
-       notify-source 10.53.0.4;
-       transfer-source 10.53.0.4;
-       port @PORT@;
-       pid-file "named.pid";
-       listen-on { 10.53.0.4; };
-       listen-on-v6 { none; };
-       recursion yes;
-       minimal-responses no;
-
-       dnssec-accept-expired yes;
-       servfail-ttl 0;
-
-       dnssec-validation auto;
-       bindkeys-file "managed.conf";
-};
-
-key rndc_key {
-       secret "1234abcd8765";
-       algorithm @DEFAULT_HMAC@;
-};
-
-controls {
-       inet 10.53.0.4 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
-};
-
-zone "." {
-       type hint;
-       file "../../_common/root.hint";
-};
diff --git a/bin/tests/system/dnssec/ns4/named4.conf.in b/bin/tests/system/dnssec/ns4/named4.conf.in
deleted file mode 100644 (file)
index f5b387d..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * 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.
- */
-
-// NS4
-
-options {
-       query-source address 10.53.0.4;
-       notify-source 10.53.0.4;
-       transfer-source 10.53.0.4;
-       port @PORT@;
-       pid-file "named.pid";
-       listen-on { 10.53.0.4; };
-       listen-on-v6 { none; };
-       minimal-responses no;
-
-       disable-algorithms "digest-alg-unsupported.example." { ECDSAP384SHA384; };
-       disable-ds-digests "digest-alg-unsupported.example." { "SHA384"; "SHA-384"; };
-       disable-ds-digests "ds-unsupported.example." { "SHA256"; "SHA-256"; "SHA384"; "SHA-384"; };
-       disable-algorithms "badalg.secure.example." { ECDSAP256SHA256; };
-};
-
-key rndc_key {
-       secret "1234abcd8765";
-       algorithm @DEFAULT_HMAC@;
-};
-
-controls {
-       inet 10.53.0.4 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
-};
-
-key auth {
-       secret "1234abcd8765";
-       algorithm @DEFAULT_HMAC@;
-};
-
-view rec {
-       match-recursive-only yes;
-       recursion yes;
-       dnssec-accept-expired yes;
-       minimal-responses no;
-       dnssec-validation yes;
-
-       include "trusted.conf";
-
-       zone "." {
-               type hint;
-               file "../../_common/root.hint";
-       };
-
-       zone secure.example {
-               type static-stub;
-               server-addresses { 10.53.0.4; };
-       };
-
-       zone insecure.secure.example {
-               type static-stub;
-               server-addresses { 10.53.0.4; };
-       };
-};
-
-view auth {
-       recursion no;
-       allow-recursion { none; };
-       dnssec-validation no;
-
-       zone "." {
-               type hint;
-               file "../../_common/root.hint";
-       };
-
-       zone secure.example {
-               type secondary;
-               primaries { 10.53.0.3; };
-       };
-
-       zone insecure.secure.example {
-               type secondary;
-               primaries { 10.53.0.2; };
-       };
-};
index bdbd1f3b30f04216cfff71d9d77ea4710f5b18cd..a0332d9d6fff0c34171487994cbce6d10bdc042c 100644 (file)
@@ -13,6 +13,9 @@
 
 // NS5
 
+{% set revoked_key = revoked_key | default(False) %}
+{% set broken_key = broken_key | default(False) %}
+{% set many_anchors = many_anchors | default(False) %}
 options {
        query-source address 10.53.0.5;
        notify-source 10.53.0.5;
@@ -22,6 +25,14 @@ options {
        listen-on { 10.53.0.5; 127.0.0.1; };
        listen-on-v6 { none; };
        recursion yes;
+       minimal-responses no;
+       servfail-ttl 0;
+
+{% if many_anchors %}
+       dnssec-validation yes;
+       disable-algorithms "disabled.managed." { @DISABLED_ALGORITHM@; };
+       disable-algorithms "disabled.trusted." { @DISABLED_ALGORITHM@; };
+{% endif %}
 };
 
 key rndc_key {
@@ -33,8 +44,6 @@ controls {
        inet 10.53.0.5 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
 };
 
-{% set revoked_key = revoked_key | default(False) %}
-{% set broken_key = broken_key | default(False) %}
 {% if revoked_key %}
        view root {
                match-destinations { 127.0.0.1; };
@@ -62,6 +71,14 @@ controls {
        };
 
        include "broken.conf";
+{% elif many_anchors %}
+       zone "." {
+               type hint;
+               file "../../_common/root.hint";
+       };
+
+       include "many-managed.conf";
+       include "many-trusted.conf";
 {% else %}
        zone "." {
                type hint;
diff --git a/bin/tests/system/dnssec/ns6/named.args b/bin/tests/system/dnssec/ns6/named.args
deleted file mode 100644 (file)
index 04d5d1b..0000000
+++ /dev/null
@@ -1 +0,0 @@
--m record -c named.conf -d 99 -D dnssec-ns6 -g -T maxcachesize=2097152 -T nonearest -T tat=1
index 2818b7958cbb47cabf46b75d53f40f4fe8138013..c3c0da6e44e1bcc9368483daee9f403a023b69a5 100644 (file)
@@ -37,4 +37,15 @@ zone "optout-tld" {
        file "optout-tld.db.signed";
 };
 
+zone "nosoa.secure.example" {
+       type primary;
+       file "nosoa.secure.example.db";
+};
+
+zone "split-rrsig" {
+       type primary;
+       file "split-rrsig.db.signed";
+       allow-update { any; };
+};
+
 include "trusted.conf";
diff --git a/bin/tests/system/dnssec/ns6/named.nonearest b/bin/tests/system/dnssec/ns6/named.nonearest
new file mode 100644 (file)
index 0000000..ba4c4eb
--- /dev/null
@@ -0,0 +1,12 @@
+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.
+
+Add -T nonearest.
diff --git a/bin/tests/system/dnssec/ns6/named.tat=1 b/bin/tests/system/dnssec/ns6/named.tat=1
new file mode 100644 (file)
index 0000000..c5c93e9
--- /dev/null
@@ -0,0 +1,12 @@
+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.
+
+Add -T tat=1.
similarity index 97%
rename from bin/tests/system/dnssec/ns7/nosoa.secure.example.db
rename to bin/tests/system/dnssec/ns6/nosoa.secure.example.db
index d3c98783ead577d7e0a1f5a5de30db0867a15e7e..6b7af1255340f3fc3ed94ab3a6001e2c007a2ebd 100644 (file)
@@ -18,5 +18,5 @@ $TTL 300        ; 5 minutes
                                 3600       ; minimum (1 hour)
                                 )
 @                       IN NS   ns
-ns                     IN A    10.53.0.7
+ns                     IN A    10.53.0.6
 a                      IN A    1.2.3.4
index 103baf257eb15c3b76a127b34754f664f8734280..74ae836f7180927d9c464a8389c1a6d9a89f57c7 100644 (file)
@@ -27,3 +27,28 @@ keyname=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" "$zone")
 cat "$infile" "$keyname.key" >"$zonefile"
 
 "$SIGNER" -z -3 - -A -o "$zone" "$zonefile" >/dev/null 2>&1
+
+zone=split-rrsig
+infile=split-rrsig.db.in
+zonefile=split-rrsig.db
+
+k1=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" "$zone")
+k2=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" "$zone")
+
+cat "$infile" "$k1.key" "$k2.key" >"$zonefile"
+
+# The awk script below achieves two goals:
+#
+# - it puts one of the two RRSIG(SOA) records at the end of the zone file, so
+#   that these two records (forming a single RRset) are not placed immediately
+#   next to each other; the test then checks if RRSIG RRsets split this way are
+#   correctly added to resigning heaps,
+#
+# - it places a copy of one of the RRSIG(SOA) records somewhere else than at the
+#   zone apex; the test then checks whether such signatures are automatically
+#   removed from the zone after it is loaded.
+"$SIGNER" -P -3 - -A -o "$zone" -O full -f "$zonefile.unsplit" -e now-3600 -s now-7200 "$zonefile" >/dev/null 2>&1
+awk 'BEGIN { r = ""; }
+     $4 == "RRSIG" && $5 == "SOA" && r == "" { r = $0; next; }
+     { print }
+     END { print r; print "not-at-zone-apex." r; }' "$zonefile.unsplit" >"$zonefile.signed"
diff --git a/bin/tests/system/dnssec/ns7/named.conf.j2 b/bin/tests/system/dnssec/ns7/named.conf.j2
deleted file mode 100644 (file)
index b352846..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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.
- */
-
-// NS3
-
-options {
-       query-source address 10.53.0.7;
-       notify-source 10.53.0.7;
-       transfer-source 10.53.0.7;
-       port @PORT@;
-       pid-file "named.pid";
-       listen-on { 10.53.0.7; };
-       listen-on-v6 { none; };
-       recursion no;
-       notify yes;
-       dnssec-validation yes;
-       minimal-responses yes;
-};
-
-zone "." {
-       type hint;
-       file "../../_common/root.hint";
-};
-
-zone "nsec3.example" {
-       type secondary;
-       primaries { 10.53.0.3; };
-       file "nsec3.example.bk";
-};
-
-zone "optout.example" {
-       type secondary;
-       primaries { 10.53.0.3; };
-       file "optout.example.bk";
-};
-
-zone "nsec3-unknown.example" {
-       type secondary;
-       primaries { 10.53.0.3; };
-       file "nsec3-unknown.example.bk";
-};
-
-zone "optout-unknown.example" {
-       type secondary;
-       primaries { 10.53.0.3; };
-       file "optout-unknown.example.bk";
-};
-
-zone "multiple.example" {
-       type secondary;
-       primaries { 10.53.0.3; };
-       file "multiple.example.bk";
-};
-
-zone "nosoa.secure.example" {
-       type primary;
-       file "nosoa.secure.example.db";
-};
-
-zone "split-rrsig" {
-       type primary;
-       file "split-rrsig.db.signed";
-       allow-update { any; };
-};
-
-include "trusted.conf";
diff --git a/bin/tests/system/dnssec/ns7/sign.sh b/bin/tests/system/dnssec/ns7/sign.sh
deleted file mode 100644 (file)
index 7aacead..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/bin/sh -e
-
-# 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.
-
-# shellcheck source=conf.sh
-. ../../conf.sh
-
-set -e
-
-echo_i "ns7/sign.sh"
-
-zone=split-rrsig
-infile=split-rrsig.db.in
-zonefile=split-rrsig.db
-
-k1=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" "$zone")
-k2=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" "$zone")
-
-cat "$infile" "$k1.key" "$k2.key" >"$zonefile"
-
-# The awk script below achieves two goals:
-#
-# - it puts one of the two RRSIG(SOA) records at the end of the zone file, so
-#   that these two records (forming a single RRset) are not placed immediately
-#   next to each other; the test then checks if RRSIG RRsets split this way are
-#   correctly added to resigning heaps,
-#
-# - it places a copy of one of the RRSIG(SOA) records somewhere else than at the
-#   zone apex; the test then checks whether such signatures are automatically
-#   removed from the zone after it is loaded.
-"$SIGNER" -P -3 - -A -o "$zone" -O full -f "$zonefile.unsplit" -e now-3600 -s now-7200 "$zonefile" >/dev/null 2>&1
-awk 'BEGIN { r = ""; }
-     $4 == "RRSIG" && $5 == "SOA" && r == "" { r = $0; next; }
-     { print }
-     END { print r; print "not-at-zone-apex." r; }' "$zonefile.unsplit" >"$zonefile.signed"
diff --git a/bin/tests/system/dnssec/ns8/named.conf.j2 b/bin/tests/system/dnssec/ns8/named.conf.j2
deleted file mode 100644 (file)
index 2590de1..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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.
- */
-
-// NS8
-
-options {
-       query-source address 10.53.0.8;
-       notify-source 10.53.0.8;
-       transfer-source 10.53.0.8;
-       port @PORT@;
-       pid-file "named.pid";
-       listen-on { 10.53.0.8; };
-       listen-on-v6 { none; };
-       recursion yes;
-       dnssec-validation yes;
-       minimal-responses no;
-       disable-algorithms "disabled.managed." { @DISABLED_ALGORITHM@; };
-       disable-algorithms "disabled.trusted." { @DISABLED_ALGORITHM@; };
-};
-
-key rndc_key {
-       secret "1234abcd8765";
-       algorithm @DEFAULT_HMAC@;
-};
-
-controls {
-       inet 10.53.0.8 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
-};
-
-zone "." {
-       type hint;
-       file "../../_common/root.hint";
-};
-
-include "managed.conf";
-include "trusted.conf";
-
index 14970719b945d1723d87a65b9eeceedac2078cc9..7c895667fc401d80b2034a20d8904a6e54139107 100644 (file)
@@ -16,8 +16,6 @@
 
 set -e
 
-copy_setports ns4/named1.conf.in ns4/named.conf
-
 (
   cd ns1
   $SHELL sign.sh
index b0033f61277e6ea0856fbe9bf97f896057079011..2b4f9c864a8a7716b8b8a54193dca786bf29dde8 100644 (file)
@@ -75,165 +75,12 @@ checkprivate() {
   return 1
 }
 
-#
-# Ensure there is not multiple consecutive blank lines.
-# Ensure there is a blank line before "Start view" and
-# "Negative trust anchors:".
-# Ensure there is not a blank line before "Secure roots:".
-#
-check_secroots_layout() {
-  awk '$0 == "" { if (empty) exit(1); empty=1; next }
-            /Start view/ { if (!empty) exit(1) }
-            /Secure roots:/ { if (empty) exit(1) }
-            /Negative trust anchors:/ { if (!empty) exit(1) }
-            { empty=0 }' $1 || return $?
-}
-
-# Check that for a query against a validating resolver where the
-# authoritative zone is unsigned (insecure delegation), glue is returned
-# in the additional section
-echo_i "checking that additional glue is returned for unsigned delegation ($n)"
-ret=0
-$DIG +tcp +dnssec -p "$PORT" a.insecure.example. @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-grep "ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 2" dig.out.ns4.test$n >/dev/null || ret=1
-grep "ns\\.insecure\\.example\\..*A.10\\.53\\.0\\.3" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-if [ "$ret" -ne 0 ]; then echo_i "failed"; fi
-status=$((status + ret))
-
-# Check the example. domain
-
-echo_i "checking that zone transfer worked ($n)"
-for i in 1 2 3 4 5 6 7 8 9; do
-  ret=0
-  dig_with_opts a.example. @10.53.0.2 a >dig.out.ns2.test$n || ret=1
-  dig_with_opts a.example. @10.53.0.3 a >dig.out.ns3.test$n || ret=1
-  $PERL ../digcomp.pl dig.out.ns2.test$n dig.out.ns3.test$n >/dev/null || ret=1
-  [ "$ret" -eq 0 ] && break
-  sleep 1
-done
-digcomp dig.out.ns2.test$n dig.out.ns3.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-# test AD bit:
-#  - dig +adflag asks for authentication (ad in response)
-echo_i "checking AD bit asking for validation ($n)"
-ret=0
-dig_with_opts +noauth +noadd +nodnssec +adflag a.example. @10.53.0.2 a >dig.out.ns2.test$n || ret=1
-dig_with_opts +noauth +noadd +nodnssec +adflag a.example. @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns2.test$n dig.out.ns4.test$n || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-# test AD bit:
-#  - dig +noadflag
-echo_i "checking that AD is not set without +adflag or +dnssec ($n)"
-ret=0
-dig_with_opts +noauth +noadd +nodnssec +noadflag a.example. @10.53.0.2 a >dig.out.ns2.test$n || ret=1
-dig_with_opts +noauth +noadd +nodnssec +noadflag a.example. @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns2.test$n dig.out.ns4.test$n || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null && ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking for AD in authoritative answer ($n)"
-ret=0
-dig_with_opts a.example. @10.53.0.2 a >dig.out.ns2.test$n || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns2.test$n >/dev/null && ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking positive validation NSEC ($n)"
-ret=0
-dig_with_opts +noauth a.example. @10.53.0.2 a >dig.out.ns2.test$n || ret=1
-dig_with_opts +noauth a.example. @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns2.test$n dig.out.ns4.test$n || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking recovery from spoofed server addresses ($n)"
-ret=0
-# prime cache with spoofed address records
-dig_with_opts +cd target.peer-ns-spoof @10.53.0.4 a >dig.out.prime.ns4.test$n || ret=1
-grep "status: SERVFAIL" dig.out.prime.ns4.test$n >/dev/null || ret=1
-rndccmd 10.53.0.4 dumpdb | sed 's/^/ns4 /' | cat_i
-mv ns4/named_dump.db ns4/named_dump.db.test$n >/dev/null || ret=1
-grep "10.53.0.100" ns4/named_dump.db.test$n || ret=1
-# reload server with properly signed zone
-cp ns2/peer.peer-ns-spoof.db.next ns2/peer.peer-ns-spoof.db.signed
-nextpart ns2/named.run >/dev/null
-rndccmd 10.53.0.2 reload peer.peer-ns-spoof | sed 's/^/ns2 /' | cat_i
-wait_for_log 5 "zone peer.peer-ns-spoof/IN: loaded serial 2000042408" ns2/named.run || ret=1
-dig_with_opts +noauth test.target.peer-ns-spoof @10.53.0.4 txt >dig.out.ns4.test$n || ret=1
-grep "status: NXDOMAIN" dig.out.ns4.test$n >/dev/null || ret=1
-grep "flags: qr rd ra ad;" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking recovery from stripped DNSKEY RRSIG ($n)"
-ret=0
-# prime cache with DNSKEY without RRSIGs
-dig_with_opts +noauth +cd dnskey-rrsigs-stripped. @10.53.0.4 dnskey >dig.out.prime.ns4.test$n || ret=1
-grep ";; flags: qr rd ra cd; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1" dig.out.prime.ns4.test$n >/dev/null || ret=1
-grep "status: NOERROR" dig.out.prime.ns4.test$n >/dev/null || ret=1
-grep "RRSIG.DNSKEY" dig.out.prime.ns4.test$n >/dev/null && ret=1
-# reload server with properly signed zone
-cp ns2/dnskey-rrsigs-stripped.db.next ns2/dnskey-rrsigs-stripped.db.signed
-nextpart ns2/named.run >/dev/null
-rndccmd 10.53.0.2 reload dnskey-rrsigs-stripped | sed 's/^/ns2 /' | cat_i
-wait_for_log 5 "zone dnskey-rrsigs-stripped/IN: loaded serial 2000042408" ns2/named.run || ret=1
-dig_with_opts +noauth b.dnskey-rrsigs-stripped. @10.53.0.2 a >dig.out.ns2.test$n || ret=1
-dig_with_opts +noauth b.dnskey-rrsigs-stripped. @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns2.test$n dig.out.ns4.test$n || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking recovery from stripped DS RRSIG ($n)"
-ret=0
-# prime cache with DS without RRSIGs
-dig_with_opts +noauth +cd child.ds-rrsigs-stripped. @10.53.0.4 ds >dig.out.prime.ns4.test$n || ret=1
-grep ";; flags: qr rd ra cd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1" dig.out.prime.ns4.test$n >/dev/null || ret=1
-grep "status: NOERROR" dig.out.prime.ns4.test$n >/dev/null || ret=1
-grep "RRSIG.DS" dig.out.prime.ns4.test$n >/dev/null && ret=1
-# reload server with properly signed zone
-cp ns2/ds-rrsigs-stripped.db.next ns2/ds-rrsigs-stripped.db.signed
-nextpart ns2/named.run >/dev/null
-rndccmd 10.53.0.2 reload ds-rrsigs-stripped | sed 's/^/ns2 /' | cat_i
-wait_for_log 5 "zone ds-rrsigs-stripped/IN: loaded serial 2000042408" ns2/named.run || ret=1
-dig_with_opts +noauth b.child.ds-rrsigs-stripped. @10.53.0.2 a >dig.out.ns2.test$n || ret=1
-dig_with_opts +noauth b.child.ds-rrsigs-stripped. @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns2.test$n dig.out.ns4.test$n || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking that 'example/DS' from the referral was used in previous validation ($n)"
-ret=0
-grep "query 'example/DS/IN' approved" ns1/named.run >/dev/null && ret=1
-grep "fetch: example/DS" ns4/named.run >/dev/null && ret=1
-grep "validating example/DS: starting" ns4/named.run >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
 if [ -x "${DELV}" ]; then
   ret=0
   echo_i "checking positive validation NSEC using dns_client ($n)"
   delv_with_opts @10.53.0.4 a a.example >delv.out$n || ret=1
   grep "a.example..*10.0.0.1" delv.out$n >/dev/null || ret=1
-  grep "a.example..*.RRSIG.A [0-9][0-9]* 2 300 .*" delv.out$n >/dev/null || ret=1
+  grep "a.example..*.RRSIG.A [0-9][0-9]* 2 3600 .*" delv.out$n >/dev/null || ret=1
   n=$((n + 1))
   test "$ret" -eq 0 || echo_i "failed"
   status=$((status + ret))
@@ -242,50 +89,22 @@ if [ -x "${DELV}" ]; then
   echo_i "checking positive validation NSEC using dns_client (trusted-keys) ($n)"
   "$DELV" -a ns1/trusted.keys -p "$PORT" @10.53.0.4 a a.example >delv.out$n || ret=1
   grep "a.example..*10.0.0.1" delv.out$n >/dev/null || ret=1
-  grep "a.example..*.RRSIG.A [0-9][0-9]* 2 300 .*" delv.out$n >/dev/null || ret=1
+  grep "a.example..*.RRSIG.A [0-9][0-9]* 2 3600 .*" delv.out$n >/dev/null || ret=1
   n=$((n + 1))
   test "$ret" -eq 0 || echo_i "failed"
   status=$((status + ret))
-fi
-
-echo_i "checking positive validation NSEC3 ($n)"
-ret=0
-dig_with_opts +noauth a.nsec3.example. \
-  @10.53.0.3 a >dig.out.ns3.test$n || ret=1
-dig_with_opts +noauth a.nsec3.example. \
-  @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
 
-if [ -x "${DELV}" ]; then
   ret=0
   echo_i "checking positive validation NSEC3 using dns_client ($n)"
   delv_with_opts @10.53.0.4 a a.nsec3.example >delv.out$n || ret=1
   grep "a.nsec3.example..*10.0.0.1" delv.out$n >/dev/null || ret=1
-  grep "a.nsec3.example..*RRSIG.A [0-9][0-9]* 3 300.*" delv.out$n >/dev/null || ret=1
+  grep "a.nsec3.example..*RRSIG.A [0-9][0-9]* 3 300 .*" delv.out$n >/dev/null || ret=1
   n=$((n + 1))
   test "$ret" -eq 0 || echo_i "failed"
   status=$((status + ret))
-fi
-
-echo_i "checking positive validation OPTOUT ($n)"
-ret=0
-dig_with_opts +noauth a.optout.example. \
-  @10.53.0.3 a >dig.out.ns3.test$n || ret=1
-dig_with_opts +noauth a.optout.example. \
-  @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
 
-SP="[[:space:]]+"
+  SP="[[:space:]]+"
 
-if [ -x "${DELV}" ]; then
   ret=0
   echo_i "checking positive validation OPTOUT using dns_client ($n)"
   delv_with_opts @10.53.0.4 a a.optout.example >delv.out$n || ret=1
@@ -294,66 +113,16 @@ if [ -x "${DELV}" ]; then
   n=$((n + 1))
   test "$ret" -eq 0 || echo_i "failed"
   status=$((status + ret))
-fi
 
-echo_i "checking positive wildcard validation NSEC ($n)"
-ret=0
-dig_with_opts a.wild.example. @10.53.0.3 a >dig.out.ns3.test$n || ret=1
-dig_with_opts a.wild.example. @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-stripns dig.out.ns3.test$n >dig.out.ns3.stripped.test$n
-stripns dig.out.ns4.test$n >dig.out.ns4.stripped.test$n
-digcomp dig.out.ns3.stripped.test$n dig.out.ns4.stripped.test$n || ret=1
-grep "\\*\\.wild\\.example\\..*RRSIG   NSEC" dig.out.ns4.test$n >/dev/null || ret=1
-grep "\\*\\.wild\\.example\\..*NSEC    z\\.example" dig.out.ns4.test$n >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-grep "status: NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-if [ -x "${DELV}" ]; then
   ret=0
   echo_i "checking positive wildcard validation NSEC using dns_client ($n)"
   delv_with_opts @10.53.0.4 a a.wild.example >delv.out$n || ret=1
   grep "a.wild.example..*10.0.0.27" delv.out$n >/dev/null || ret=1
-  grep -E "a.wild.example..*RRSIG.A [0-9]+ 2 300.*" delv.out$n >/dev/null || ret=1
+  grep -E "a.wild.example..*RRSIG.A [0-9]+ 2 3600 .*" delv.out$n >/dev/null || ret=1
   n=$((n + 1))
   test "$ret" -eq 0 || echo_i "failed"
   status=$((status + ret))
-fi
-
-echo_i "checking positive wildcard answer NSEC3 ($n)"
-ret=0
-dig_with_opts a.wild.nsec3.example. @10.53.0.3 a >dig.out.ns3.test$n || ret=1
-grep "AUTHORITY: 4," dig.out.ns3.test$n >/dev/null || ret=1
-grep "status: NOERROR" dig.out.ns3.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking positive wildcard answer NSEC3 ($n)"
-ret=0
-dig_with_opts a.wild.nsec3.example. @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-grep "AUTHORITY: 4," dig.out.ns4.test$n >/dev/null || ret=1
-grep "status: NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking positive wildcard validation NSEC3 ($n)"
-ret=0
-dig_with_opts a.wild.nsec3.example. @10.53.0.3 a >dig.out.ns3.test$n || ret=1
-dig_with_opts a.wild.nsec3.example. @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-stripns dig.out.ns3.test$n >dig.out.ns3.stripped.test$n
-stripns dig.out.ns4.test$n >dig.out.ns4.stripped.test$n
-digcomp dig.out.ns3.stripped.test$n dig.out.ns4.stripped.test$n || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-grep "status: NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
 
-if [ -x "${DELV}" ]; then
   ret=0
   echo_i "checking positive wildcard validation NSEC3 using dns_client ($n)"
   delv_with_opts @10.53.0.4 a a.wild.nsec3.example >delv.out$n || ret=1
@@ -362,24 +131,7 @@ if [ -x "${DELV}" ]; then
   n=$((n + 1))
   test "$ret" -eq 0 || echo_i "failed"
   status=$((status + ret))
-fi
 
-echo_i "checking positive wildcard validation OPTOUT ($n)"
-ret=0
-dig_with_opts a.wild.optout.example. \
-  @10.53.0.3 a >dig.out.ns3.test$n || ret=1
-dig_with_opts a.wild.optout.example. \
-  @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-stripns dig.out.ns3.test$n >dig.out.ns3.stripped.test$n
-stripns dig.out.ns4.test$n >dig.out.ns4.stripped.test$n
-digcomp dig.out.ns3.stripped.test$n dig.out.ns4.stripped.test$n || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null && ret=1
-grep "status: NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-if [ -x "${DELV}" ]; then
   ret=0
   echo_i "checking positive wildcard validation OPTOUT using dns_client ($n)"
   delv_with_opts @10.53.0.4 a a.wild.optout.example >delv.out$n || ret=1
@@ -388,20 +140,7 @@ if [ -x "${DELV}" ]; then
   n=$((n + 1))
   test "$ret" -eq 0 || echo_i "failed"
   status=$((status + ret))
-fi
-
-echo_i "checking negative validation NXDOMAIN NSEC ($n)"
-ret=0
-dig_with_opts +noauth q.example. @10.53.0.2 a >dig.out.ns2.test$n || ret=1
-dig_with_opts +noauth q.example. @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns2.test$n dig.out.ns4.test$n || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-grep "status: NXDOMAIN" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
 
-if [ -x "${DELV}" ]; then
   ret=0
   echo_i "checking negative validation NXDOMAIN NSEC using dns_client ($n)"
   delv_with_opts @10.53.0.4 a q.example >delv.out$n 2>&1 || ret=1
@@ -409,30 +148,7 @@ if [ -x "${DELV}" ]; then
   n=$((n + 1))
   test "$ret" -eq 0 || echo_i "failed"
   status=$((status + ret))
-fi
-
-echo_i "checking RRSIG covered type in negative cache entry ($n)"
-ret=0
-rndc_dumpdb ns4
-grep -F '; example. RRSIG NSEC ...' ns4/named_dump.db.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking negative validation NXDOMAIN NSEC3 ($n)"
-ret=0
-dig_with_opts +noauth q.nsec3.example. \
-  @10.53.0.3 a >dig.out.ns3.test$n || ret=1
-dig_with_opts +noauth q.nsec3.example. \
-  @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-grep "status: NXDOMAIN" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
 
-if [ -x "${DELV}" ]; then
   ret=0
   echo_i "checking negative validation NXDOMAIN NSEC3 using dns_client ($n)"
   delv_with_opts @10.53.0.4 a q.nsec3.example >delv.out$n 2>&1 || ret=1
@@ -440,23 +156,7 @@ if [ -x "${DELV}" ]; then
   n=$((n + 1))
   test "$ret" -eq 0 || echo_i "failed"
   status=$((status + ret))
-fi
-
-echo_i "checking negative validation NXDOMAIN OPTOUT ($n)"
-ret=0
-dig_with_opts +noauth q.optout.example. \
-  @10.53.0.3 a >dig.out.ns3.test$n || ret=1
-dig_with_opts +noauth q.optout.example. \
-  @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "status: NXDOMAIN" dig.out.ns4.test$n >/dev/null || ret=1
-# Note - this is looking for failure, hence the &&
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null && ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
 
-if [ -x "${DELV}" ]; then
   ret=0
   echo_i "checking negative validation NXDOMAIN OPTOUT using dns_client ($n)"
   delv_with_opts @10.53.0.4 a q.optout.example >delv.out$n 2>&1 || ret=1
@@ -464,21 +164,7 @@ if [ -x "${DELV}" ]; then
   n=$((n + 1))
   test "$ret" -eq 0 || echo_i "failed"
   status=$((status + ret))
-fi
-
-echo_i "checking negative validation NODATA NSEC ($n)"
-ret=0
-dig_with_opts +noauth a.example. @10.53.0.2 txt >dig.out.ns2.test$n || ret=1
-dig_with_opts +noauth a.example. @10.53.0.4 txt >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns2.test$n dig.out.ns4.test$n || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-grep "status: NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-grep "ANSWER: 0" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
 
-if [ -x "${DELV}" ]; then
   ret=0
   echo_i "checking negative validation NODATA OPTOUT using dns_client ($n)"
   delv_with_opts @10.53.0.4 txt a.example >delv.out$n 2>&1 || ret=1
@@ -486,23 +172,7 @@ if [ -x "${DELV}" ]; then
   n=$((n + 1))
   test "$ret" -eq 0 || echo_i "failed"
   status=$((status + ret))
-fi
 
-echo_i "checking negative validation NODATA NSEC3 ($n)"
-ret=0
-dig_with_opts +noauth a.nsec3.example. \
-  @10.53.0.3 txt >dig.out.ns3.test$n || ret=1
-dig_with_opts +noauth a.nsec3.example. \
-  @10.53.0.4 txt >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-grep "status: NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-grep "ANSWER: 0" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-if [ -x "${DELV}" ]; then
   ret=0
   echo_i "checking negative validation NODATA NSEC3 using dns_client ($n)"
   delv_with_opts @10.53.0.4 txt a.nsec3.example >delv.out$n 2>&1 || ret=1
@@ -510,23 +180,7 @@ if [ -x "${DELV}" ]; then
   n=$((n + 1))
   test "$ret" -eq 0 || echo_i "failed"
   status=$((status + ret))
-fi
-
-echo_i "checking negative validation NODATA OPTOUT ($n)"
-ret=0
-dig_with_opts +noauth a.optout.example. \
-  @10.53.0.3 txt >dig.out.ns3.test$n || ret=1
-dig_with_opts +noauth a.optout.example. \
-  @10.53.0.4 txt >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-grep "status: NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-grep "ANSWER: 0" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
 
-if [ -x "${DELV}" ]; then
   ret=0
   echo_i "checking negative validation NODATA OPTOUT using dns_client ($n)"
   delv_with_opts @10.53.0.4 txt a.optout.example >delv.out$n 2>&1 || ret=1
@@ -534,20 +188,7 @@ if [ -x "${DELV}" ]; then
   n=$((n + 1))
   test "$ret" -eq 0 || echo_i "failed"
   status=$((status + ret))
-fi
-
-echo_i "checking negative wildcard validation NSEC ($n)"
-ret=0
-dig_with_opts b.wild.example. @10.53.0.2 txt >dig.out.ns2.test$n || ret=1
-dig_with_opts b.wild.example. @10.53.0.4 txt >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns2.test$n dig.out.ns4.test$n || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-grep "status: NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
 
-if [ -x "${DELV}" ]; then
   ret=0
   echo_i "checking negative wildcard validation NSEC using dns_client ($n)"
   delv_with_opts @10.53.0.4 txt b.wild.example >delv.out$n 2>&1 || ret=1
@@ -555,19 +196,7 @@ if [ -x "${DELV}" ]; then
   n=$((n + 1))
   test "$ret" -eq 0 || echo_i "failed"
   status=$((status + ret))
-fi
-
-echo_i "checking negative wildcard validation NSEC3 ($n)"
-ret=0
-dig_with_opts b.wild.nsec3.example. @10.53.0.3 txt >dig.out.ns3.test$n || ret=1
-dig_with_opts b.wild.nsec3.example. @10.53.0.4 txt >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
 
-if [ -x "${DELV}" ]; then
   ret=0
   echo_i "checking negative wildcard validation NSEC3 using dns_client ($n)"
   delv_with_opts @10.53.0.4 txt b.wild.nsec3.example >delv.out$n 2>&1 || ret=1
@@ -575,23 +204,7 @@ if [ -x "${DELV}" ]; then
   n=$((n + 1))
   test "$ret" -eq 0 || echo_i "failed"
   status=$((status + ret))
-fi
-
-echo_i "checking negative wildcard validation OPTOUT ($n)"
-ret=0
-dig_with_opts b.wild.optout.example. \
-  @10.53.0.3 txt >dig.out.ns3.test$n || ret=1
-dig_with_opts b.wild.optout.example. \
-  @10.53.0.4 txt >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "status: NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-# Note - this is looking for failure, hence the &&
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null && ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
 
-if [ -x "${DELV}" ]; then
   ret=0
   echo_i "checking negative wildcard validation OPTOUT using dns_client ($n)"
   delv_with_opts @10.53.0.4 txt b.optout.nsec3.example >delv.out$n 2>&1 || ret=1
@@ -599,23 +212,7 @@ if [ -x "${DELV}" ]; then
   n=$((n + 1))
   test "$ret" -eq 0 || echo_i "failed"
   status=$((status + ret))
-fi
-
-# Check the insecure.example domain
-
-echo_i "checking 1-server insecurity proof NSEC ($n)"
-ret=0
-dig_with_opts +noauth a.insecure.example. @10.53.0.3 a >dig.out.ns3.test$n || ret=1
-dig_with_opts +noauth a.insecure.example. @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "status: NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-# Note - this is looking for failure, hence the &&
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null && ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
 
-if [ -x "${DELV}" ]; then
   ret=0
   echo_i "checking 1-server insecurity proof NSEC using dns_client ($n)"
   delv_with_opts @10.53.0.4 a a.insecure.example >delv.out$n || ret=1
@@ -623,21 +220,7 @@ if [ -x "${DELV}" ]; then
   n=$((n + 1))
   test "$ret" -eq 0 || echo_i "failed"
   status=$((status + ret))
-fi
-
-echo_i "checking 1-server insecurity proof NSEC3 ($n)"
-ret=0
-dig_with_opts +noauth a.insecure.nsec3.example. @10.53.0.3 a >dig.out.ns3.test$n || ret=1
-dig_with_opts +noauth a.insecure.nsec3.example. @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "status: NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-# Note - this is looking for failure, hence the &&
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null && ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
 
-if [ -x "${DELV}" ]; then
   ret=0
   echo_i "checking 1-server insecurity proof NSEC3 using dns_client ($n)"
   delv_with_opts @10.53.0.4 a a.insecure.nsec3.example >delv.out$n || ret=1
@@ -645,21 +228,7 @@ if [ -x "${DELV}" ]; then
   n=$((n + 1))
   test "$ret" -eq 0 || echo_i "failed"
   status=$((status + ret))
-fi
-
-echo_i "checking 1-server insecurity proof OPTOUT ($n)"
-ret=0
-dig_with_opts +noauth a.insecure.optout.example. @10.53.0.3 a >dig.out.ns3.test$n || ret=1
-dig_with_opts +noauth a.insecure.optout.example. @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "status: NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-# Note - this is looking for failure, hence the &&
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null && ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
 
-if [ -x "${DELV}" ]; then
   ret=0
   echo_i "checking 1-server insecurity proof OPTOUT using dns_client ($n)"
   delv_with_opts @10.53.0.4 a a.insecure.optout.example >delv.out$n || ret=1
@@ -667,23 +236,7 @@ if [ -x "${DELV}" ]; then
   n=$((n + 1))
   test "$ret" -eq 0 || echo_i "failed"
   status=$((status + ret))
-fi
-
-echo_i "checking 1-server negative insecurity proof NSEC ($n)"
-ret=0
-dig_with_opts q.insecure.example. a @10.53.0.3 \
-  >dig.out.ns3.test$n || ret=1
-dig_with_opts q.insecure.example. a @10.53.0.4 \
-  >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "status: NXDOMAIN" dig.out.ns4.test$n >/dev/null || ret=1
-# Note - this is looking for failure, hence the &&
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null && ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
 
-if [ -x "${DELV}" ]; then
   ret=0
   echo_i "checking 1-server negative insecurity proof NSEC using dns_client ($n)"
   delv_with_opts @10.53.0.4 a q.insecure.example >delv.out$n 2>&1 || ret=1
@@ -691,23 +244,7 @@ if [ -x "${DELV}" ]; then
   n=$((n + 1))
   test "$ret" -eq 0 || echo_i "failed"
   status=$((status + ret))
-fi
 
-echo_i "checking 1-server negative insecurity proof NSEC3 ($n)"
-ret=0
-dig_with_opts q.insecure.nsec3.example. a @10.53.0.3 \
-  >dig.out.ns3.test$n || ret=1
-dig_with_opts q.insecure.nsec3.example. a @10.53.0.4 \
-  >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "status: NXDOMAIN" dig.out.ns4.test$n >/dev/null || ret=1
-# Note - this is looking for failure, hence the &&
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null && ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-if [ -x "${DELV}" ]; then
   ret=0
   echo_i "checking 1-server negative insecurity proof NSEC3 using dns_client ($n)"
   delv_with_opts @10.53.0.4 a q.insecure.nsec3.example >delv.out$n 2>&1 || ret=1
@@ -715,23 +252,7 @@ if [ -x "${DELV}" ]; then
   n=$((n + 1))
   test "$ret" -eq 0 || echo_i "failed"
   status=$((status + ret))
-fi
-
-echo_i "checking 1-server negative insecurity proof OPTOUT ($n)"
-ret=0
-dig_with_opts q.insecure.optout.example. a @10.53.0.3 \
-  >dig.out.ns3.test$n || ret=1
-dig_with_opts q.insecure.optout.example. a @10.53.0.4 \
-  >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "status: NXDOMAIN" dig.out.ns4.test$n >/dev/null || ret=1
-# Note - this is looking for failure, hence the &&
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null && ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
 
-if [ -x "${DELV}" ]; then
   ret=0
   echo_i "checking 1-server negative insecurity proof OPTOUT using dns_client ($n)"
   delv_with_opts @10.53.0.4 a q.insecure.optout.example >delv.out$n 2>&1 || ret=1
@@ -739,718 +260,34 @@ if [ -x "${DELV}" ]; then
   n=$((n + 1))
   test "$ret" -eq 0 || echo_i "failed"
   status=$((status + ret))
-fi
-
-echo_i "checking 1-server negative insecurity proof with SOA hack NSEC ($n)"
-ret=0
-dig_with_opts r.insecure.example. soa @10.53.0.3 \
-  >dig.out.ns3.test$n || ret=1
-dig_with_opts r.insecure.example. soa @10.53.0.4 \
-  >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "status: NXDOMAIN" dig.out.ns4.test$n >/dev/null || ret=1
-grep "0        IN      SOA" dig.out.ns4.test$n >/dev/null || ret=1
-# Note - this is looking for failure, hence the &&
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null && ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
 
-echo_i "checking 1-server negative insecurity proof with SOA hack NSEC3 ($n)"
-ret=0
-dig_with_opts r.insecure.nsec3.example. soa @10.53.0.3 \
-  >dig.out.ns3.test$n || ret=1
-dig_with_opts r.insecure.nsec3.example. soa @10.53.0.4 \
-  >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "status: NXDOMAIN" dig.out.ns4.test$n >/dev/null || ret=1
-grep "0        IN      SOA" dig.out.ns4.test$n >/dev/null || ret=1
-# Note - this is looking for failure, hence the &&
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null && ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
+  ret=0
+  echo_i "checking failed validation using dns_client ($n)"
+  delv_with_opts +cd @10.53.0.4 a a.bogus.example >delv.out$n 2>&1 || ret=1
+  grep "resolution failed: RRSIG failed to verify" delv.out$n >/dev/null || ret=1
+  n=$((n + 1))
+  test "$ret" -eq 0 || echo_i "failed"
+  status=$((status + ret))
 
-echo_i "checking 1-server negative insecurity proof with SOA hack OPTOUT ($n)"
-ret=0
-dig_with_opts r.insecure.optout.example. soa @10.53.0.3 \
-  >dig.out.ns3.test$n || ret=1
-dig_with_opts r.insecure.optout.example. soa @10.53.0.4 \
-  >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "status: NXDOMAIN" dig.out.ns4.test$n >/dev/null || ret=1
-grep "0        IN      SOA" dig.out.ns4.test$n >/dev/null || ret=1
-# Note - this is looking for failure, hence the &&
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null && ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
+  ret=0
+  echo_i "checking that validation fails when key record is missing using dns_client ($n)"
+  delv_with_opts +cd @10.53.0.4 a a.b.keyless.example >delv.out$n 2>&1 || ret=1
+  grep "resolution failed: insecurity proof failed" delv.out$n >/dev/null || ret=1
+  n=$((n + 1))
+  test "$ret" -eq 0 || echo_i "failed"
+  status=$((status + ret))
 
-# Check the secure.example domain
+  ret=0
+  echo_i "checking that validation succeeds when a revoked key is encountered using dns_client ($n)"
+  delv_with_opts +cd @10.53.0.4 soa revkey.example >delv.out$n 2>&1 || ret=1
+  grep "fully validated" delv.out$n >/dev/null || ret=1
+  n=$((n + 1))
+  test "$ret" -eq 0 || echo_i "failed"
+  status=$((status + ret))
+fi
 
-echo_i "checking multi-stage positive validation NSEC/NSEC ($n)"
-ret=0
-dig_with_opts +noauth a.secure.example. \
-  @10.53.0.3 a >dig.out.ns3.test$n || ret=1
-dig_with_opts +noauth a.secure.example. \
-  @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "status: NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking mixed-case positive validation ($n)"
-ret=0
-for type in a txt aaaa loc; do
-  dig_with_opts +noauth mixedcase.secure.example. \
-    @10.53.0.3 $type >dig.out.$type.ns3.test$n || ret=1
-  dig_with_opts +noauth mixedcase.secure.example. \
-    @10.53.0.4 $type >dig.out.$type.ns4.test$n || ret=1
-  digcomp --lc dig.out.$type.ns3.test$n dig.out.$type.ns4.test$n || ret=1
-  grep "status: NOERROR" dig.out.$type.ns4.test$n >/dev/null || ret=1
-  grep "flags:.*ad.*QUERY" dig.out.$type.ns4.test$n >/dev/null || ret=1
-done
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking multi-stage positive validation NSEC/NSEC3 ($n)"
-ret=0
-dig_with_opts +noauth a.nsec3.example. \
-  @10.53.0.3 a >dig.out.ns3.test$n || ret=1
-dig_with_opts +noauth a.nsec3.example. \
-  @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "status: NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking multi-stage positive validation NSEC/OPTOUT ($n)"
-ret=0
-dig_with_opts +noauth a.optout.example. \
-  @10.53.0.3 a >dig.out.ns3.test$n || ret=1
-dig_with_opts +noauth a.optout.example. \
-  @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "status: NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking multi-stage positive validation NSEC3/NSEC ($n)"
-ret=0
-dig_with_opts +noauth a.secure.nsec3.example. \
-  @10.53.0.3 a >dig.out.ns3.test$n || ret=1
-dig_with_opts +noauth a.secure.nsec3.example. \
-  @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "status: NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking multi-stage positive validation NSEC3/NSEC3 ($n)"
-ret=0
-dig_with_opts +noauth a.nsec3.nsec3.example. \
-  @10.53.0.3 a >dig.out.ns3.test$n || ret=1
-dig_with_opts +noauth a.nsec3.nsec3.example. \
-  @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "status: NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking multi-stage positive validation NSEC3/OPTOUT ($n)"
-ret=0
-dig_with_opts +noauth a.optout.nsec3.example. \
-  @10.53.0.3 a >dig.out.ns3.test$n || ret=1
-dig_with_opts +noauth a.optout.nsec3.example. \
-  @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "status: NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking multi-stage positive validation OPTOUT/NSEC ($n)"
-ret=0
-dig_with_opts +noauth a.secure.optout.example. \
-  @10.53.0.3 a >dig.out.ns3.test$n || ret=1
-dig_with_opts +noauth a.secure.optout.example. \
-  @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "status: NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking multi-stage positive validation OPTOUT/NSEC3 ($n)"
-ret=0
-dig_with_opts +noauth a.nsec3.optout.example. \
-  @10.53.0.3 a >dig.out.ns3.test$n || ret=1
-dig_with_opts +noauth a.nsec3.optout.example. \
-  @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "status: NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking multi-stage positive validation OPTOUT/OPTOUT ($n)"
-ret=0
-dig_with_opts +noauth a.optout.optout.example. \
-  @10.53.0.3 a >dig.out.ns3.test$n || ret=1
-dig_with_opts +noauth a.optout.optout.example. \
-  @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "status: NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking empty NODATA OPTOUT ($n)"
-ret=0
-dig_with_opts +noauth empty.optout.example. \
-  @10.53.0.3 a >dig.out.ns3.test$n || ret=1
-dig_with_opts +noauth empty.optout.example. \
-  @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "status: NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-#grep "flags:.*ad.*QUERY" dig.out.ns4.test$n > /dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking positive validation with private algorithm works ($n)"
-ret=0
-dig_with_opts +noauth a.rsasha256oid.example. \
-  @10.53.0.3 a >dig.out.ns3.test$n || ret=1
-dig_with_opts +noauth a.rsasha256oid.example. \
-  @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-if [ -x "${DELV}" ]; then
-  ret=0
-  echo_i "checking positive validation NSEC3 using dns_client ($n)"
-  delv_with_opts @10.53.0.4 a a.nsec3.example >delv.out$n || ret=1
-  grep "a.nsec3.example..*10.0.0.1" delv.out$n >/dev/null || ret=1
-  grep "a.nsec3.example..*RRSIG.A [0-9][0-9]* 3 300.*" delv.out$n >/dev/null || ret=1
-  n=$((n + 1))
-  test "$ret" -eq 0 || echo_i "failed"
-  status=$((status + ret))
-fi
-
-echo_i "checking positive validation with unknown private algorithm works ($n)"
-ret=0
-dig_with_opts +noauth a.unknownoid.example. \
-  @10.53.0.3 a >dig.out.ns3.test$n || ret=1
-dig_with_opts +noauth a.unknownoid.example. \
-  @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null && ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking positive validation with extra ds for private algorithm ($n)"
-ret=0
-dig_with_opts +noauth a.extradsoid.example. \
-  @10.53.0.3 a >dig.out.ns3.test$n || ret=1
-dig_with_opts +noauth a.extradsoid.example. \
-  @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking positive validation with extra ds for unknown private algorithm fails ($n)"
-ret=0
-dig_with_opts +noauth a.extradsunknownoid.example. \
-  @10.53.0.3 a >dig.out.ns3.test$n || ret=1
-dig_with_opts +noauth a.extradsunknownoid.example. \
-  @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-grep "status: NOERROR" dig.out.ns3.test$n >/dev/null || ret=1
-grep "status: SERVFAIL" dig.out.ns4.test$n >/dev/null || ret=1
-grep 'No DNSKEY for extradsunknownoid.example/DS with PRIVATEOID algorithm, tag [1-9][0-9]*$' ns4/named.run >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-if $FEATURETEST --extended-ds-digest; then
-  echo_i "checking positive validation with extra ds using extended digest type for unknown private algorithm succeeds ($n)"
-  ret=0
-  dig_with_opts +noauth a.extended-ds-unknown-oid.example. \
-    @10.53.0.3 a >dig.out.ns3.test$n || ret=1
-  dig_with_opts +noauth a.extended-ds-unknown-oid.example. \
-    @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-  digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-  grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null && ret=1
-  n=$((n + 1))
-  test "$ret" -eq 0 || echo_i "failed"
-  status=$((status + ret))
-fi
-
-# Check the bogus domain
-
-echo_i "checking failed validation ($n)"
-ret=0
-dig_with_opts a.bogus.example. @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-grep "SERVFAIL" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-if [ -x "${DELV}" ]; then
-  ret=0
-  echo_i "checking failed validation using dns_client ($n)"
-  delv_with_opts +cd @10.53.0.4 a a.bogus.example >delv.out$n 2>&1 || ret=1
-  grep "resolution failed: RRSIG failed to verify" delv.out$n >/dev/null || ret=1
-  n=$((n + 1))
-  test "$ret" -eq 0 || echo_i "failed"
-  status=$((status + ret))
-fi
-
-echo_i "checking that validation fails when key record is missing ($n)"
-ret=0
-dig_with_opts a.b.keyless.example. a @10.53.0.4 >dig.out.ns4.test$n || ret=1
-grep "SERVFAIL" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-if [ -x "${DELV}" ]; then
-  ret=0
-  echo_i "checking that validation fails when key record is missing using dns_client ($n)"
-  delv_with_opts +cd @10.53.0.4 a a.b.keyless.example >delv.out$n 2>&1 || ret=1
-  grep "resolution failed: insecurity proof failed" delv.out$n >/dev/null || ret=1
-  n=$((n + 1))
-  test "$ret" -eq 0 || echo_i "failed"
-  status=$((status + ret))
-fi
-
-echo_i "checking that validation succeeds when a revoked key is encountered ($n)"
-ret=0
-dig_with_opts revkey.example soa @10.53.0.4 >dig.out.ns4.test$n || ret=1
-grep "NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-grep "flags: .* ad" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-if [ -x "${DELV}" ]; then
-  ret=0
-  echo_i "checking that validation succeeds when a revoked key is encountered using dns_client ($n)"
-  delv_with_opts +cd @10.53.0.4 soa revkey.example >delv.out$n 2>&1 || ret=1
-  grep "fully validated" delv.out$n >/dev/null || ret=1
-  n=$((n + 1))
-  test "$ret" -eq 0 || echo_i "failed"
-  status=$((status + ret))
-fi
-
-echo_i "Checking that a bad CNAME signature is caught after a +CD query ($n)"
-ret=0
-#prime
-dig_with_opts +cd bad-cname.example. @10.53.0.4 >dig.out.ns4.prime$n || ret=1
-#check: requery with +CD.  pending data should be returned even if it's bogus
-expect="a.example.
-10.0.0.1"
-ans=$(dig_with_opts +cd +nodnssec +short bad-cname.example. @10.53.0.4) || ret=1
-test "$ans" = "$expect" || ret=1
-test "$ret" -eq 0 || echo_i "failed, got '$ans', expected '$expect'"
-#check: requery without +CD.  bogus cached data should be rejected.
-dig_with_opts +nodnssec bad-cname.example. @10.53.0.4 >dig.out.ns4.test$n || ret=1
-grep "SERVFAIL" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "Checking that a bad DNAME signature is caught after a +CD query ($n)"
-ret=0
-#prime
-dig_with_opts +cd a.bad-dname.example. @10.53.0.4 >dig.out.ns4.prime$n || ret=1
-#check: requery with +CD.  pending data should be returned even if it's bogus
-expect="example.
-a.example.
-10.0.0.1"
-ans=$(dig_with_opts +cd +nodnssec +short a.bad-dname.example. @10.53.0.4) || ret=1
-test "$ans" = "$expect" || ret=1
-test "$ret" -eq 0 || echo_i "failed, got '$ans', expected '$expect'"
-#check: requery without +CD.  bogus cached data should be rejected.
-dig_with_opts +nodnssec a.bad-dname.example. @10.53.0.4 >dig.out.ns4.test$n || ret=1
-grep "SERVFAIL" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-# Check the insecure.secure.example domain (insecurity proof)
-
-echo_i "checking 2-server insecurity proof ($n)"
-ret=0
-dig_with_opts +noauth a.insecure.secure.example. @10.53.0.2 a \
-  >dig.out.ns2.test$n || ret=1
-dig_with_opts +noauth a.insecure.secure.example. @10.53.0.4 a \
-  >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns2.test$n dig.out.ns4.test$n || ret=1
-grep "status: NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-# Note - this is looking for failure, hence the &&
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null && ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-# Check a negative response in insecure.secure.example
-
-echo_i "checking 2-server insecurity proof with a negative answer ($n)"
-ret=0
-dig_with_opts q.insecure.secure.example. @10.53.0.2 a >dig.out.ns2.test$n \
-  || ret=1
-dig_with_opts q.insecure.secure.example. @10.53.0.4 a >dig.out.ns4.test$n \
-  || ret=1
-digcomp dig.out.ns2.test$n dig.out.ns4.test$n || ret=1
-grep "status: NXDOMAIN" dig.out.ns4.test$n >/dev/null || ret=1
-# Note - this is looking for failure, hence the &&
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null && ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking 2-server insecurity proof with a negative answer and SOA hack ($n)"
-ret=0
-dig_with_opts r.insecure.secure.example. @10.53.0.2 soa >dig.out.ns2.test$n \
-  || ret=1
-dig_with_opts r.insecure.secure.example. @10.53.0.4 soa >dig.out.ns4.test$n \
-  || ret=1
-digcomp dig.out.ns2.test$n dig.out.ns4.test$n || ret=1
-grep "status: NXDOMAIN" dig.out.ns4.test$n >/dev/null || ret=1
-# Note - this is looking for failure, hence the &&
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null && ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-# Check that the query for a security root is successful and has ad set
-
-echo_i "checking security root query ($n)"
-ret=0
-dig_with_opts . @10.53.0.4 key >dig.out.ns4.test$n || ret=1
-grep "NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking insecurity proof works using negative cache ($n)"
-ret=0
-rndccmd 10.53.0.4 flush 2>&1 | sed 's/^/ns4 /' | cat_i
-dig_with_opts +cd @10.53.0.4 insecure.example. ds >dig.out.ns4.test$n.1 || ret=1
-for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18; do
-  dig_with_opts @10.53.0.4 nonexistent.insecure.example. >dig.out.ns4.test$n.2 || ret=1
-  if grep "status: NXDOMAIN" dig.out.ns4.test$n.2 >/dev/null; then
-    break
-  fi
-  sleep 1
-done
-grep "status: NXDOMAIN" dig.out.ns4.test$n.2 >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-# Should work with FIPS mode as we are only validating
-echo_i "checking positive validation RSASHA1 NSEC ($n)"
-ret=0
-if $FEATURETEST --rsasha1; then
-  dig_with_opts +noauth a.rsasha1.example. @10.53.0.3 a >dig.out.ns3.test$n || ret=1
-  dig_with_opts +noauth a.rsasha1.example. @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-  digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-  grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-else
-  echo_i "skip: RSASHA1 not supported by OS"
-fi
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-# Should work with FIPS mode as we are only validating
-echo_i "checking positive validation RSASHA1 (1024 bits) NSEC ($n)"
-ret=0
-if $FEATURETEST --rsasha1; then
-  dig_with_opts +noauth a.rsasha1-1024.example. @10.53.0.3 a >dig.out.ns3.test$n || ret=1
-  dig_with_opts +noauth a.rsasha1-1024.example. @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-  digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-  grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-else
-  echo_i "skip: RSASHA1 not supported by OS"
-fi
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking positive validation RSASHA256 NSEC ($n)"
-ret=0
-dig_with_opts +noauth a.rsasha256.example. @10.53.0.3 a >dig.out.ns3.test$n || ret=1
-dig_with_opts +noauth a.rsasha256.example. @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking positive validation RSASHA512 NSEC ($n)"
-ret=0
-dig_with_opts +noauth a.rsasha512.example. @10.53.0.3 a >dig.out.ns3.test$n || ret=1
-dig_with_opts +noauth a.rsasha512.example. @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking positive validation with KSK-only DNSKEY signature ($n)"
-ret=0
-dig_with_opts +noauth a.kskonly.example. @10.53.0.3 a >dig.out.ns3.test$n || ret=1
-dig_with_opts +noauth a.kskonly.example. @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking that validation of an ANY query works ($n)"
-ret=0
-dig_with_opts +noauth foo.example. any @10.53.0.2 >dig.out.ns2.test$n || ret=1
-dig_with_opts +noauth foo.example. any @10.53.0.4 >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns2.test$n dig.out.ns4.test$n || ret=1
-grep "NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-# 2 records in the zone, 1 NXT, 3 SIGs
-grep "ANSWER: 6" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking that validation of a query returning a CNAME works ($n)"
-ret=0
-dig_with_opts +noauth cname1.example. txt @10.53.0.2 \
-  >dig.out.ns2.test$n || ret=1
-dig_with_opts +noauth cname1.example. txt @10.53.0.4 \
-  >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns2.test$n dig.out.ns4.test$n || ret=1
-grep "NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-# the CNAME & its sig, the TXT and its SIG
-grep "ANSWER: 4" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking that validation of a query returning a DNAME works ($n)"
-ret=0
-dig_with_opts +noauth foo.dname1.example. txt @10.53.0.2 \
-  >dig.out.ns2.test$n || ret=1
-dig_with_opts +noauth foo.dname1.example. txt @10.53.0.4 \
-  >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns2.test$n dig.out.ns4.test$n || ret=1
-grep "NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-# The DNAME & its sig, the TXT and its SIG, and the synthesized CNAME.
-# It would be nice to test that the CNAME is being synthesized by the
-# recursive server and not cached, but I don't know how.
-grep "ANSWER: 5" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking that validation of an ANY query returning a CNAME works ($n)"
-ret=0
-dig_with_opts +noauth cname2.example. any @10.53.0.2 \
-  >dig.out.ns2.test$n || ret=1
-dig_with_opts +noauth cname2.example. any @10.53.0.4 \
-  >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns2.test$n dig.out.ns4.test$n || ret=1
-grep "NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-# The CNAME, NXT, and their SIGs
-grep "ANSWER: 4" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking that validation of an ANY query returning a DNAME works ($n)"
-ret=0
-dig_with_opts +noauth foo.dname2.example. any @10.53.0.2 \
-  >dig.out.ns2.test$n || ret=1
-dig_with_opts +noauth foo.dname2.example. any @10.53.0.4 \
-  >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns2.test$n dig.out.ns4.test$n || ret=1
-grep "NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking that lookups succeed after disabling an algorithm ($n)"
-ret=0
-dig_with_opts +noauth example. SOA @10.53.0.2 \
-  >dig.out.ns2.test$n || ret=1
-dig_with_opts +noauth example. SOA @10.53.0.6 \
-  >dig.out.ns6.test$n || ret=1
-digcomp dig.out.ns2.test$n dig.out.ns6.test$n || ret=1
-# Note - this is looking for failure, hence the &&
-grep "flags:.*ad.*QUERY" dig.out.ns6.test$n >/dev/null && ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking a non-cachable NODATA works ($n)"
-ret=0
-dig_with_opts +noauth a.nosoa.secure.example. txt @10.53.0.7 \
-  >dig.out.ns7.test$n || ret=1
-grep "AUTHORITY: 0" dig.out.ns7.test$n >/dev/null || ret=1
-dig_with_opts +noauth a.nosoa.secure.example. txt @10.53.0.4 \
-  >dig.out.ns4.test$n || ret=1
-grep "status: NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking a non-cachable NXDOMAIN works ($n)"
-ret=0
-dig_with_opts +noauth b.nosoa.secure.example. txt @10.53.0.7 \
-  >dig.out.ns7.test$n || ret=1
-grep "AUTHORITY: 0" dig.out.ns7.test$n >/dev/null || ret=1
-dig_with_opts +noauth b.nosoa.secure.example. txt @10.53.0.4 \
-  >dig.out.ns4.test$n || ret=1
-grep "status: NXDOMAIN" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking that we can load a rfc2535 signed zone ($n)"
-ret=0
-dig_with_opts rfc2535.example. SOA @10.53.0.2 \
-  >dig.out.ns2.test$n || ret=1
-grep "status: NOERROR" dig.out.ns2.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking that we can transfer a rfc2535 signed zone ($n)"
-ret=0
-dig_with_opts rfc2535.example. SOA @10.53.0.3 \
-  >dig.out.ns3.test$n || ret=1
-grep "status: NOERROR" dig.out.ns3.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-# Test that "rndc secroots" is able to dump trusted keys
-echo_i "checking rndc secroots ($n)"
-ret=0
-keyid=$(cat ns1/managed.key.id)
-rndccmd 10.53.0.4 secroots 2>&1 | sed 's/^/ns4 /' | cat_i
-cp ns4/named.secroots named.secroots.test$n
-check_secroots_layout named.secroots.test$n || ret=1
-linecount=$(grep -c "./$DEFAULT_ALGORITHM/$keyid ; static" named.secroots.test$n || true)
-[ "$linecount" -eq 1 ] || ret=1
-linecount=$(wc <named.secroots.test$n -l)
-[ "$linecount" -eq 9 ] || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-# Check direct query for RRSIG.  If we first ask for normal (non RRSIG)
-# record, the corresponding RRSIG should be cached and subsequent query
-# for RRSIG will be returned with the cached record.
-echo_i "checking RRSIG query from cache ($n)"
-ret=0
-dig_with_opts normalthenrrsig.secure.example. @10.53.0.4 a >/dev/null || ret=1
-ans=$(dig_with_opts +short normalthenrrsig.secure.example. @10.53.0.4 rrsig) || ret=1
-expect=$(dig_with_opts +short normalthenrrsig.secure.example. @10.53.0.3 rrsig | grep -E '^(A|NSEC)') || ret=1
-test "$ans" = "$expect" || ret=1
-# also check that RA is set
-dig_with_opts normalthenrrsig.secure.example. @10.53.0.4 rrsig >dig.out.ns4.test$n || ret=1
-grep "flags:.*ra.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-# Check direct query for RRSIG: If it's not cached with other records,
-# it should result in an empty response.
-echo_i "checking RRSIG query not in cache ($n)"
-ret=0
-ans=$(dig_with_opts +short rrsigonly.secure.example. @10.53.0.4 rrsig) || ret=1
-test -z "$ans" || ret=1
-# also check that RA is cleared
-dig_with_opts rrsigonly.secure.example. @10.53.0.4 rrsig >dig.out.ns4.test$n || ret=1
-grep "flags:.*ra.*QUERY" dig.out.ns4.test$n >/dev/null && ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-#
-# RT21868 regression test.
-#
-echo_i "checking NSEC3 zone with mismatched NSEC3PARAM / NSEC parameters ($n)"
-ret=0
-dig_with_opts non-exist.badparam. @10.53.0.2 a >dig.out.ns2.test$n || ret=1
-grep "status: NXDOMAIN" dig.out.ns2.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-#
-# RT22007 regression test.
-#
-echo_i "checking optout NSEC3 referral with only insecure delegations ($n)"
-ret=0
-dig_with_opts +norec delegation.single-nsec3. @10.53.0.2 a >dig.out.ns2.test$n || ret=1
-grep "status: NOERROR" dig.out.ns2.test$n >/dev/null || ret=1
-grep "3KL3NK1HKQ4IUEEHBEF12VGFKUETNBAN.*NSEC3 1 1 1 - 3KL3NK1HKQ4IUEEHBEF12VGFKUETNBAN" dig.out.ns2.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking optout NSEC3 NXDOMAIN with only insecure delegations ($n)"
-ret=0
-dig_with_opts +norec nonexist.single-nsec3. @10.53.0.2 a >dig.out.ns2.test$n || ret=1
-grep "status: NXDOMAIN" dig.out.ns2.test$n >/dev/null || ret=1
-grep "3KL3NK1HKQ4IUEEHBEF12VGFKUETNBAN.*NSEC3 1 1 1 - 3KL3NK1HKQ4IUEEHBEF12VGFKUETNBAN" dig.out.ns2.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-
-status=$((status + ret))
-echo_i "checking optout NSEC3 nodata with only insecure delegations ($n)"
-ret=0
-dig_with_opts +norec single-nsec3. @10.53.0.2 a >dig.out.ns2.test$n || ret=1
-grep "status: NOERROR" dig.out.ns2.test$n >/dev/null || ret=1
-grep "3KL3NK1HKQ4IUEEHBEF12VGFKUETNBAN.*NSEC3 1 1 1 - 3KL3NK1HKQ4IUEEHBEF12VGFKUETNBAN" dig.out.ns2.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking that a zone finishing the transition from $ALTERNATIVE_ALGORITHM to $DEFAULT_ALGORITHM validates secure ($n)"
-ret=0
-dig_with_opts ns algroll. @10.53.0.4 >dig.out.ns4.test$n || ret=1
-grep "NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-grep "flags:[^;]* ad[^;]*;" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-# Run a minimal update test if possible.  This is really just
-# a regression test for RT #2399; more tests should be added.
+# Run a minimal update test if possible.  This is really just
+# a regression test for RT #2399; more tests should be added.
 
 if $PERL -e 'use Net::DNS;' 2>/dev/null; then
   echo_i "running DNSSEC update test"
@@ -1466,128 +303,6 @@ else
   echo_i "The DNSSEC update test requires the Net::DNS library." >&2
 fi
 
-n=$((n + 1))
-echo_i "checking managed key maintenance has not started yet ($n)"
-ret=0
-[ -f "ns4/managed-keys.bind.jnl" ] && ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-# Reconfigure caching server to use "dnssec-validation auto", and repeat
-# some of the DNSSEC validation tests to ensure that it works correctly.
-# Also setup a placeholder managed-keys zone to check if named can process it
-# correctly.
-echo_i "switching to automatic root key configuration"
-cp ns4/managed-keys.bind.in ns4/managed-keys.bind
-copy_setports ns4/named2.conf.in ns4/named.conf
-rndccmd 10.53.0.4 reconfig 2>&1 | sed 's/^/ns4 /' | cat_i
-sleep 5
-
-echo_i "checking managed key maintenance timer has now started ($n)"
-ret=0
-[ -f "ns4/managed-keys.bind.jnl" ] || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking positive validation NSEC ($n)"
-ret=0
-dig_with_opts +noauth a.example. @10.53.0.2 a >dig.out.ns2.test$n || ret=1
-dig_with_opts +noauth a.example. @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns2.test$n dig.out.ns4.test$n || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking positive validation NSEC3 ($n)"
-ret=0
-dig_with_opts +noauth a.nsec3.example. \
-  @10.53.0.3 a >dig.out.ns3.test$n || ret=1
-dig_with_opts +noauth a.nsec3.example. \
-  @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking positive validation OPTOUT ($n)"
-ret=0
-dig_with_opts +noauth a.optout.example. \
-  @10.53.0.3 a >dig.out.ns3.test$n || ret=1
-dig_with_opts +noauth a.optout.example. \
-  @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking negative validation ($n)"
-ret=0
-dig_with_opts +noauth q.example. @10.53.0.2 a >dig.out.ns2.test$n || ret=1
-dig_with_opts +noauth q.example. @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns2.test$n dig.out.ns4.test$n || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-grep "status: NXDOMAIN" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking that root DS queries validate ($n)"
-ret=0
-dig_with_opts +noauth . @10.53.0.1 ds >dig.out.ns1.test$n || ret=1
-dig_with_opts +noauth . @10.53.0.4 ds >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns1.test$n dig.out.ns4.test$n || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-grep "status: NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking that DS at a RFC 1918 empty zone lookup succeeds ($n)"
-ret=0
-dig_with_opts +noauth 10.in-addr.arpa ds @10.53.0.2 >dig.out.ns2.test$n || ret=1
-dig_with_opts +noauth 10.in-addr.arpa ds @10.53.0.4 >dig.out.ns6.test$n || ret=1
-digcomp dig.out.ns2.test$n dig.out.ns6.test$n || ret=1
-grep "status: NOERROR" dig.out.ns6.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking expired signatures remain with "'"allow-update { none; };"'" and no keys available ($n)"
-ret=0
-dig_with_opts +noauth expired.example. +dnssec @10.53.0.3 soa >dig.out.ns3.test$n || ret=1
-grep "RRSIG.SOA" dig.out.ns3.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-
-status=$((status + ret))
-echo_i "checking expired signatures do not validate ($n)"
-ret=0
-dig_with_opts +noauth expired.example. +dnssec @10.53.0.4 soa >dig.out.ns4.test$n || ret=1
-grep "SERVFAIL" dig.out.ns4.test$n >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null && ret=1
-grep "expired.example/.*: RRSIG has expired" ns4/named.run >/dev/null || ret=1
-grep "; EDE: 7 (Signature Expired): (expired.example/DNSKEY)" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-status=$((status + ret))
-echo_i "checking signatures in the future do not validate ($n)"
-ret=0
-dig_with_opts +noauth future.example. +dnssec @10.53.0.4 soa >dig.out.ns4.test$n || ret=1
-grep "SERVFAIL" dig.out.ns4.test$n >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null && ret=1
-grep "future.example/.*: RRSIG validity period has not begun" ns4/named.run >/dev/null || ret=1
-grep "; EDE: 8 (Signature Not Yet Valid): (future.example/DNSKEY)" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
 echo_i "checking that the NSEC3 record for the apex is properly signed when a DNSKEY is added via UPDATE ($n)"
 ret=0
 (
@@ -1602,447 +317,97 @@ ret=0
 dig_with_opts +dnssec a update-nsec3.example. @10.53.0.4 >dig.out.ns4.test$n || ret=1
 grep "NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
 grep "flags:.* ad[ ;]" dig.out.ns4.test$n >/dev/null || ret=1
-grep "NSEC3 1 0 0 - .*" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking that signing records have been marked as complete ($n)"
-ret=0
-checkprivate dynamic.example 10.53.0.3 || ret=1
-checkprivate auto-nsec3.example 10.53.0.3 || ret=1
-checkprivate expiring.example 10.53.0.3 || ret=1
-checkprivate auto-nsec.example 10.53.0.3 || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "check that 'rndc signing' without arguments is handled ($n)"
-ret=0
-rndccmd 10.53.0.3 signing >/dev/null 2>&1 && ret=1
-rndccmd 10.53.0.3 status >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "check that 'rndc signing -list' without zone is handled ($n)"
-ret=0
-rndccmd 10.53.0.3 signing -list >/dev/null 2>&1 && ret=1
-rndccmd 10.53.0.3 status >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "check that 'rndc signing -clear' without additional arguments is handled ($n)"
-ret=0
-rndccmd 10.53.0.3 signing -clear >/dev/null 2>&1 && ret=1
-rndccmd 10.53.0.3 status >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "check that 'rndc signing -clear all' without zone is handled ($n)"
-ret=0
-rndccmd 10.53.0.3 signing -clear all >/dev/null 2>&1 && ret=1
-rndccmd 10.53.0.3 status >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "check rndc signing -list output ($n)"
-ret=0
-{ rndccmd 10.53.0.3 signing -list dynamic.example >signing.out.dynamic.example; } 2>&1
-grep -q "No signing records found" signing.out.dynamic.example || {
-  ret=1
-  sed 's/^/ns3 /' signing.out.dynamic.example | cat_i
-}
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking that a insecure zone beneath a cname resolves ($n)"
-ret=0
-dig_with_opts soa insecure.below-cname.example. @10.53.0.4 >dig.out.ns4.test$n || ret=1
-grep "NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-grep "ANSWER: 1," dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking that a secure zone beneath a cname resolves ($n)"
-ret=0
-dig_with_opts soa secure.below-cname.example. @10.53.0.4 >dig.out.ns4.test$n || ret=1
-grep "NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-grep "ANSWER: 2," dig.out.ns4.test$n >/dev/null || ret=1
-grep "flags:.* ad[ ;]" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-my_dig() {
-  "$DIG" +noadd +nosea +nostat +noquest +nocomm +nocmd -p "$PORT" @10.53.0.4 "$@"
-}
-
-echo_i "checking DNSKEY query with no data still gets put in cache ($n)"
-ret=0
-firstVal=$(my_dig insecure.example. dnskey | awk '$1 != ";;" { print $2 }')
-sleep 1
-secondVal=$(my_dig insecure.example. dnskey | awk '$1 != ";;" { print $2 }')
-if [ "${firstVal:-0}" -eq "${secondVal:-0}" ]; then
-  sleep 1
-  thirdVal=$(my_dig insecure.example. dnskey | awk '$1 != ";;" { print $2 }')
-  if [ "${firstVal:-0}" -eq "${thirdVal:-0}" ]; then
-    echo_i "cannot confirm query answer still in cache"
-    ret=1
-  fi
-fi
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "check that a split dnssec dnssec-signzone work ($n)"
-ret=0
-dig_with_opts soa split-dnssec.example. @10.53.0.4 >dig.out.ns4.test$n || ret=1
-grep "NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-grep "ANSWER: 2," dig.out.ns4.test$n >/dev/null || ret=1
-grep "flags:.* ad[ ;]" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "check that a smart split dnssec dnssec-signzone work ($n)"
-ret=0
-dig_with_opts soa split-smart.example. @10.53.0.4 >dig.out.ns4.test$n || ret=1
-grep "NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-grep "ANSWER: 2," dig.out.ns4.test$n >/dev/null || ret=1
-grep "flags:.* ad[ ;]" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "check dnssec-dsfromkey from stdin ($n)"
-ret=0
-dig_with_opts dnskey algroll. @10.53.0.2 \
-  | $DSFROMKEY -f - algroll. >dig.out.ns2.test$n || ret=1
-NF=$(awk '{print NF}' dig.out.ns2.test$n | sort -u)
-[ "${NF}" = 7 ] || ret=1
-# make canonical
-awk '{
-       for (i=1;i<7;i++) printf("%s ", $i);
-       for (i=7;i<=NF;i++) printf("%s", $i);
-       printf("\n");
-}' <dig.out.ns2.test$n >canonical1.$n || ret=1
-awk '{
-       for (i=1;i<7;i++) printf("%s ", $i);
-       for (i=7;i<=NF;i++) printf("%s", $i);
-       printf("\n");
-}' <ns1/dsset-algroll. >canonical2.$n || ret=1
-diff -b canonical1.$n canonical2.$n >/dev/null 2>&1 || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-# Intentionally strip ".key" from keyfile name to ensure the error message
-# includes it anyway to avoid confusion (RT #21731)
-echo_i "check dnssec-dsfromkey error message when keyfile is not found ($n)"
-ret=0
-key=$($KEYGEN -a $DEFAULT_ALGORITHM -q example.) || ret=1
-mv "$key.key" "$key"
-$DSFROMKEY "$key" >dsfromkey.out.$n 2>&1 && ret=1
-grep "$key.key: file not found" dsfromkey.out.$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "check dnssec-dsfromkey with revoked key ($n)"
-ret=0
-dig_with_opts revkey.example dnskey @10.53.0.4 >dig.out.ns4.test$n || ret=1
-grep "DNSKEY.256 3 13" dig.out.ns4.test$n >/dev/null || ret=1 # ZSK
-grep "DNSKEY.385 3 13" dig.out.ns4.test$n >/dev/null || ret=1 # revoked KSK
-grep "DNSKEY.257 3 13" dig.out.ns4.test$n >/dev/null || ret=1 # KSK
-test $(awk '$4 == "DNSKEY" { print }' dig.out.ns4.test$n | wc -l) -eq 3 || ret=1
-$DSFROMKEY -f dig.out.ns4.test$n revkey.example. >dsfromkey.out.test$n || ret=1
-test $(wc -l <dsfromkey.out.test$n) -eq 1 || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-
-echo_i "testing soon-to-expire RRSIGs without a replacement private key ($n)"
-ret=0
-dig_with_answeropts +nottlid expiring.example ns @10.53.0.3 | grep RRSIG >dig.out.ns3.test$n 2>&1
-# there must be a signature here
-[ -s dig.out.ns3.test$n ] || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "testing legacy upper case signer name validation ($n)"
-ret=0
-$DIG +tcp +noadd +noauth +dnssec -p "$PORT" soa upper.example @10.53.0.4 \
-  >dig.out.ns4.test$n 2>&1 || ret=1
-grep "flags:.* ad;" dig.out.ns4.test$n >/dev/null || ret=1
-grep "RRSIG.*SOA.* UPPER\\.EXAMPLE\\. " dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "testing that we lower case signer name ($n)"
-ret=0
-$DIG +tcp +noadd +noauth +dnssec -p "$PORT" soa LOWER.EXAMPLE @10.53.0.4 \
-  >dig.out.ns4.test$n 2>&1 || ret=1
-grep "flags:.* ad;" dig.out.ns4.test$n >/dev/null || ret=1
-grep "RRSIG.*SOA.* lower\\.example\\. " dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "testing TTL is capped at RRSIG expiry time ($n)"
-ret=0
-rndccmd 10.53.0.3 freeze expiring.example 2>&1 | sed 's/^/ns3 /' | cat_i
-(
-  cd ns3 || exit 1
-  for file in K*.moved; do
-    mv "$file" "$(basename "$file" .moved)"
-  done
-  $SIGNER -S -N increment -e now+1mi -o expiring.example expiring.example.db >/dev/null
-) || ret=1
-rndc_reload ns3 10.53.0.3 expiring.example
-
-rndccmd 10.53.0.4 flush 2>&1 | sed 's/^/ns4 /' | cat_i
-dig_with_answeropts +cd expiring.example soa @10.53.0.4 >dig.out.ns4.1.$n
-dig_with_answeropts expiring.example soa @10.53.0.4 >dig.out.ns4.2.$n
-ttls=$(awk '$1 != ";;" {print $2}' dig.out.ns4.1.$n)
-ttls2=$(awk '$1 != ";;" {print $2}' dig.out.ns4.2.$n)
-for ttl in ${ttls:-0}; do
-  [ "${ttl}" -eq 300 ] || ret=1
-done
-for ttl in ${ttls2:-0}; do
-  [ "${ttl}" -le 60 ] || ret=1
-done
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "testing TTL is capped at RRSIG expiry time for records in the additional section (NS) ($n)"
-ret=0
-rndccmd 10.53.0.4 flush 2>&1 | sed 's/^/ns4 /' | cat_i
-sleep 1
-dig_with_additionalopts +cd expiring.example ns @10.53.0.4 >dig.out.ns4.1.$n
-dig_with_additionalopts expiring.example ns @10.53.0.4 >dig.out.ns4.2.$n
-ttls=$(awk '$1 != ";;" {print $2}' dig.out.ns4.1.$n)
-ttls2=$(awk '$1 != ";;" {print $2}' dig.out.ns4.2.$n)
-for ttl in ${ttls:-300}; do
-  [ "$ttl" -le 300 ] && [ "$ttl" -gt 240 ] || ret=1
-done
-for ttl in ${ttls2:-0}; do
-  [ "$ttl" -le 60 ] || ret=1
-done
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "testing TTL is capped at RRSIG expiry time for records in the additional section (MX) ($n)"
-ret=0
-rndccmd 10.53.0.4 flush 2>&1 | sed 's/^/ns4 /' | cat_i
-sleep 1
-dig_with_additionalopts +cd expiring.example mx @10.53.0.4 >dig.out.ns4.1.$n
-dig_with_additionalopts expiring.example mx @10.53.0.4 >dig.out.ns4.2.$n
-ttls=$(awk '$1 != ";;" {print $2}' dig.out.ns4.1.$n)
-ttls2=$(awk '$1 != ";;" {print $2}' dig.out.ns4.2.$n)
-for ttl in ${ttls:-300}; do
-  [ "$ttl" -le 300 ] && [ "$ttl" -gt 240 ] || ret=1
-done
-for ttl in ${ttls2:-0}; do
-  [ "$ttl" -le 60 ] || ret=1
-done
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-copy_setports ns4/named3.conf.in ns4/named.conf
-rndccmd 10.53.0.4 reconfig 2>&1 | sed 's/^/ns4 /' | cat_i
-sleep 3
-
-echo_i "testing TTL of about to expire RRsets with dnssec-accept-expired yes; ($n)"
-ret=0
-rndccmd 10.53.0.4 flush 2>&1 | sed 's/^/ns4 /' | cat_i
-dig_with_answeropts +cd expiring.example soa @10.53.0.4 >dig.out.ns4.1.$n
-dig_with_answeropts expiring.example soa @10.53.0.4 >dig.out.ns4.2.$n
-ttls=$(awk '$1 != ";;" {print $2}' dig.out.ns4.1.$n)
-ttls2=$(awk '$1 != ";;" {print $2}' dig.out.ns4.2.$n)
-for ttl in ${ttls:-0}; do
-  [ "$ttl" -eq 300 ] || ret=1
-done
-for ttl in ${ttls2:-0}; do
-  [ "$ttl" -eq 120 ] || ret=1
-done
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "testing TTL of expired RRsets with dnssec-accept-expired yes; ($n)"
-ret=0
-dig_with_answeropts +cd expired.example soa @10.53.0.4 >dig.out.ns4.1.$n
-dig_with_answeropts expired.example soa @10.53.0.4 >dig.out.ns4.2.$n
-ttls=$(awk '$1 != ";;" {print $2}' dig.out.ns4.1.$n)
-ttls2=$(awk '$1 != ";;" {print $2}' dig.out.ns4.2.$n)
-for ttl in ${ttls:-0}; do
-  [ "$ttl" -eq 300 ] || ret=1
-done
-for ttl in ${ttls2:-0}; do
-  [ "$ttl" -eq 120 ] || ret=1
-done
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "testing TTL is capped at RRSIG expiry time for records in the additional section with dnssec-accept-expired yes; ($n)"
-ret=0
-rndccmd 10.53.0.4 flush 2>&1 | sed 's/^/ns4 /' | cat_i
-dig_with_additionalopts +cd expiring.example mx @10.53.0.4 >dig.out.ns4.1.$n
-dig_with_additionalopts expiring.example mx @10.53.0.4 >dig.out.ns4.2.$n
-ttls=$(awk '$1 != ";;" {print $2}' dig.out.ns4.1.$n)
-ttls2=$(awk '$1 != ";;" {print $2}' dig.out.ns4.2.$n)
-for ttl in ${ttls:-300}; do
-  [ "$ttl" -le 300 ] && [ "$ttl" -gt 240 ] || ret=1
-done
-for ttl in ${ttls2:-0}; do
-  [ "$ttl" -le 120 ] && [ "$ttl" -gt 60 ] || ret=1
-done
+grep "NSEC3 1 0 0 - .*" dig.out.ns4.test$n >/dev/null || ret=1
 n=$((n + 1))
 test "$ret" -eq 0 || echo_i "failed"
 status=$((status + ret))
 
-echo_i "testing DNSKEY lookup via CNAME ($n)"
+echo_i "checking that signing records have been marked as complete ($n)"
 ret=0
-dig_with_opts +noauth cnameandkey.secure.example. \
-  @10.53.0.3 dnskey >dig.out.ns3.test$n || ret=1
-dig_with_opts +noauth cnameandkey.secure.example. \
-  @10.53.0.4 dnskey >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-grep "CNAME" dig.out.ns4.test$n >/dev/null || ret=1
+checkprivate dynamic.example 10.53.0.3 || ret=1
+checkprivate auto-nsec3.example 10.53.0.3 || ret=1
+checkprivate expiring.example 10.53.0.3 || ret=1
+checkprivate auto-nsec.example 10.53.0.3 || ret=1
 n=$((n + 1))
 test "$ret" -eq 0 || echo_i "failed"
 status=$((status + ret))
 
-echo_i "testing KEY lookup at CNAME (present) ($n)"
+echo_i "check that 'rndc signing' without arguments is handled ($n)"
 ret=0
-dig_with_opts +noauth cnameandkey.secure.example. \
-  @10.53.0.3 key >dig.out.ns3.test$n || ret=1
-dig_with_opts +noauth cnameandkey.secure.example. \
-  @10.53.0.4 key >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-grep "CNAME" dig.out.ns4.test$n >/dev/null && ret=1
+rndccmd 10.53.0.3 signing >/dev/null 2>&1 && ret=1
+rndccmd 10.53.0.3 status >/dev/null || ret=1
 n=$((n + 1))
 test "$ret" -eq 0 || echo_i "failed"
 status=$((status + ret))
 
-echo_i "testing KEY lookup at CNAME (not present) ($n)"
+echo_i "check that 'rndc signing -list' without zone is handled ($n)"
 ret=0
-dig_with_opts +noauth cnamenokey.secure.example. \
-  @10.53.0.3 key >dig.out.ns3.test$n || ret=1
-dig_with_opts +noauth cnamenokey.secure.example. \
-  @10.53.0.4 key >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-grep "CNAME" dig.out.ns4.test$n >/dev/null && ret=1
+rndccmd 10.53.0.3 signing -list >/dev/null 2>&1 && ret=1
+rndccmd 10.53.0.3 status >/dev/null || ret=1
 n=$((n + 1))
 test "$ret" -eq 0 || echo_i "failed"
 status=$((status + ret))
 
-echo_i "testing DNSKEY lookup via DNAME ($n)"
+echo_i "check that 'rndc signing -clear' without additional arguments is handled ($n)"
 ret=0
-dig_with_opts a.dnameandkey.secure.example. \
-  @10.53.0.3 dnskey >dig.out.ns3.test$n || ret=1
-dig_with_opts a.dnameandkey.secure.example. \
-  @10.53.0.4 dnskey >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-grep "CNAME" dig.out.ns4.test$n >/dev/null || ret=1
-grep "DNAME" dig.out.ns4.test$n >/dev/null || ret=1
+rndccmd 10.53.0.3 signing -clear >/dev/null 2>&1 && ret=1
+rndccmd 10.53.0.3 status >/dev/null || ret=1
 n=$((n + 1))
 test "$ret" -eq 0 || echo_i "failed"
 status=$((status + ret))
 
-echo_i "testing KEY lookup via DNAME ($n)"
+echo_i "check that 'rndc signing -clear all' without zone is handled ($n)"
 ret=0
-dig_with_opts b.dnameandkey.secure.example. \
-  @10.53.0.3 key >dig.out.ns3.test$n || ret=1
-dig_with_opts b.dnameandkey.secure.example. \
-  @10.53.0.4 key >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-grep "DNAME" dig.out.ns4.test$n >/dev/null || ret=1
+rndccmd 10.53.0.3 signing -clear all >/dev/null 2>&1 && ret=1
+rndccmd 10.53.0.3 status >/dev/null || ret=1
 n=$((n + 1))
 test "$ret" -eq 0 || echo_i "failed"
 status=$((status + ret))
 
-echo_i "check that named doesn't loop when all private keys are not available ($n)"
+echo_i "check rndc signing -list output ($n)"
 ret=0
-lines=$(grep -c "reading private key file expiring.example" ns3/named.run || true)
-test "${lines:-1000}" -lt 15 || ret=1
+{ rndccmd 10.53.0.3 signing -list dynamic.example >signing.out.dynamic.example; } 2>&1
+grep -q "No signing records found" signing.out.dynamic.example || {
+  ret=1
+  sed 's/^/ns3 /' signing.out.dynamic.example | cat_i
+}
 n=$((n + 1))
 test "$ret" -eq 0 || echo_i "failed"
 status=$((status + ret))
 
-echo_i "check against against missing nearest provable proof ($n)"
-dig_with_opts +norec b.c.d.optout-tld. \
-  @10.53.0.6 ds >dig.out.ds.ns6.test$n || ret=1
-nsec3=$(grep -c "IN.NSEC3" dig.out.ds.ns6.test$n || true)
-[ "$nsec3" -eq 2 ] || ret=1
-dig_with_opts +norec b.c.d.optout-tld. \
-  @10.53.0.6 A >dig.out.ns6.test$n || ret=1
-nsec3=$(grep -c "IN.NSEC3" dig.out.ns6.test$n || true)
-[ "$nsec3" -eq 1 ] || ret=1
-dig_with_opts optout-tld. \
-  @10.53.0.4 SOA >dig.out.soa.ns4.test$n || ret=1
-grep "flags:.*ad.*QUERY" dig.out.soa.ns4.test$n >/dev/null || ret=1
-dig_with_opts b.c.d.optout-tld. \
-  @10.53.0.4 A >dig.out.ns4.test$n || ret=1
-grep "status: NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null && ret=1
+echo_i "check that a split dnssec dnssec-signzone work ($n)"
+ret=0
+dig_with_opts soa split-dnssec.example. @10.53.0.4 >dig.out.ns4.test$n || ret=1
+grep "NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
+grep "ANSWER: 2," dig.out.ns4.test$n >/dev/null || ret=1
+grep "flags:.* ad[ ;]" dig.out.ns4.test$n >/dev/null || ret=1
 n=$((n + 1))
 test "$ret" -eq 0 || echo_i "failed"
 status=$((status + ret))
 
-echo_i "check that key id are logged when dumping the cache ($n)"
+echo_i "check that a smart split dnssec dnssec-signzone work ($n)"
 ret=0
-rndc_dumpdb ns4
-grep "; key id = " ns4/named_dump.db.test$n >/dev/null || ret=1
+dig_with_opts soa split-smart.example. @10.53.0.4 >dig.out.ns4.test$n || ret=1
+grep "NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
+grep "ANSWER: 2," dig.out.ns4.test$n >/dev/null || ret=1
+grep "flags:.* ad[ ;]" dig.out.ns4.test$n >/dev/null || ret=1
 n=$((n + 1))
 test "$ret" -eq 0 || echo_i "failed"
 status=$((status + ret))
 
-echo_i "check KEYDATA records are printed in human readable form in key zone ($n)"
-# force the managed-keys zone to be written out
-rndccmd 10.53.0.4 managed-keys sync 2>&1 | sed 's/^/ns4 /' | cat_i
-for i in 1 2 3 4 5 6 7 8 9; do
-  ret=0
-  if test -f ns4/managed-keys.bind; then
-    grep KEYDATA ns4/managed-keys.bind >/dev/null \
-      && grep "next refresh:" ns4/managed-keys.bind >/dev/null \
-      && break
-  fi
-  ret=1
-  sleep 1
-done
+echo_i "testing soon-to-expire RRSIGs without a replacement private key ($n)"
+ret=0
+dig_with_answeropts +nottlid expiring.example ns @10.53.0.3 | grep RRSIG >dig.out.ns3.test$n 2>&1
+# there must be a signature here
+[ -s dig.out.ns3.test$n ] || ret=1
 n=$((n + 1))
 test "$ret" -eq 0 || echo_i "failed"
 status=$((status + ret))
 
-copy_setports ns4/named4.conf.in ns4/named.conf
-rndccmd 10.53.0.4 reconfig 2>&1 | sed 's/^/ns4 /' | cat_i
-sleep 3
-
-echo_i "check insecure delegation between static-stub zones ($n)"
+echo_i "check that named doesn't loop when all private keys are not available ($n)"
 ret=0
-dig_with_opts ns insecure.secure.example \
-  @10.53.0.4 >dig.out.ns4.1.test$n || ret=1
-grep "SERVFAIL" dig.out.ns4.1.test$n >/dev/null && ret=1
-dig_with_opts ns secure.example \
-  @10.53.0.4 >dig.out.ns4.2.test$n || ret=1
-grep "SERVFAIL" dig.out.ns4.2.test$n >/dev/null && ret=1
+lines=$(grep -c "reading private key file expiring.example" ns3/named.run || true)
+test "${lines:-1000}" -lt 15 || ret=1
 n=$((n + 1))
 test "$ret" -eq 0 || echo_i "failed"
 status=$((status + ret))
@@ -2068,22 +433,6 @@ n=$((n + 1))
 test "$ret" -eq 0 || echo_i "failed"
 status=$((status + ret))
 
-echo_i "check that split rrsigs are handled ($n)"
-ret=0
-dig_with_opts split-rrsig soa @10.53.0.7 >dig.out.test$n || ret=1
-awk 'BEGIN { ok=0; } $4 == "SOA" { if ($7 > 1) ok=1; } END { if (!ok) exit(1); }' dig.out.test$n || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "check that not-at-zone-apex RRSIG(SOA) RRsets are removed from the zone after load ($n)"
-ret=0
-dig_with_opts split-rrsig AXFR @10.53.0.7 >dig.out.test$n || ret=1
-grep -q "not-at-zone-apex.*RRSIG.*SOA" dig.out.test$n && ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
 echo_i "check that CDS records are signed using KSK by dnssec-signzone ($n)"
 ret=0
 dig_with_opts +noall +answer @10.53.0.2 cds cds.secure >dig.out.test$n
@@ -2102,18 +451,6 @@ n=$((n + 1))
 test "$ret" -eq 0 || echo_i "failed"
 status=$((status + ret))
 
-echo_i "checking that positive unknown NSEC3 hash algorithm does validate ($n)"
-ret=0
-dig_with_opts +noauth +noadd +nodnssec +adflag @10.53.0.3 nsec3-unknown.example SOA >dig.out.ns3.test$n
-dig_with_opts +noauth +noadd +nodnssec +adflag @10.53.0.4 nsec3-unknown.example SOA >dig.out.ns4.test$n
-grep "status: NOERROR," dig.out.ns3.test$n >/dev/null || ret=1
-grep "status: NOERROR," dig.out.ns4.test$n >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-grep "ANSWER: 1," dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
 echo_i "check that CDS records are signed using KSK by with dnssec-policy ($n)"
 ret=0
 dig_with_opts +noall +answer @10.53.0.2 cds cds-auto.secure >dig.out.test$n
@@ -2189,18 +526,6 @@ n=$((n + 1))
 test "$ret" -eq 0 || echo_i "failed"
 status=$((status + ret))
 
-echo_i "checking that positive unknown NSEC3 hash algorithm with OPTOUT does validate ($n)"
-ret=0
-dig_with_opts +noauth +noadd +nodnssec +adflag @10.53.0.3 optout-unknown.example SOA >dig.out.ns3.test$n
-dig_with_opts +noauth +noadd +nodnssec +adflag @10.53.0.4 optout-unknown.example SOA >dig.out.ns4.test$n
-grep "status: NOERROR," dig.out.ns3.test$n >/dev/null || ret=1
-grep "status: NOERROR," dig.out.ns4.test$n >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-grep "ANSWER: 1," dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
 echo_i "check that a non matching CDS record is accepted with a matching CDS record ($n)"
 ret=0
 (
@@ -2227,16 +552,6 @@ n=$((n + 1))
 test "$ret" -eq 0 || echo_i "failed"
 status=$((status + ret))
 
-echo_i "checking that negative unknown NSEC3 hash algorithm does not validate ($n)"
-ret=0
-dig_with_opts +noauth +noadd +nodnssec +adflag @10.53.0.3 nsec3-unknown.example A >dig.out.ns3.test$n
-dig_with_opts +noauth +noadd +nodnssec +adflag @10.53.0.4 nsec3-unknown.example A >dig.out.ns4.test$n
-grep "status: NOERROR," dig.out.ns3.test$n >/dev/null || ret=1
-grep "status: SERVFAIL," dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
 echo_i "check that CDNSKEY records are signed using KSK by dnssec-signzone ($n)"
 ret=0
 dig_with_opts +noall +answer @10.53.0.2 cdnskey cdnskey.secure >dig.out.test$n
@@ -2255,16 +570,6 @@ n=$((n + 1))
 test "$ret" -eq 0 || echo_i "failed"
 status=$((status + ret))
 
-echo_i "checking that negative unknown NSEC3 hash algorithm with OPTOUT does not validate ($n)"
-ret=0
-dig_with_opts +noauth +noadd +nodnssec +adflag @10.53.0.3 optout-unknown.example A >dig.out.ns3.test$n
-dig_with_opts +noauth +noadd +nodnssec +adflag @10.53.0.4 optout-unknown.example A >dig.out.ns4.test$n
-grep "status: NOERROR," dig.out.ns3.test$n >/dev/null || ret=1
-grep "status: SERVFAIL," dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
 echo_i "check that CDNSKEY records are signed using KSK by with dnssec-auto ($n)"
 ret=0
 dig_with_opts +noall +answer @10.53.0.2 cdnskey cdnskey-auto.secure >dig.out.test$n
@@ -2274,78 +579,6 @@ n=$((n + 1))
 test "$ret" -eq 0 || echo_i "failed"
 status=$((status + ret))
 
-echo_i "checking that unknown DNSKEY algorithm validates as insecure ($n)"
-ret=0
-dig_with_opts +noauth +noadd +nodnssec +adflag @10.53.0.3 dnskey-unknown.example A >dig.out.ns3.test$n
-dig_with_opts +noauth +noadd +nodnssec +adflag @10.53.0.4 dnskey-unknown.example A >dig.out.ns4.test$n
-grep "status: NOERROR," dig.out.ns3.test$n >/dev/null || ret=1
-grep "status: NOERROR," dig.out.ns4.test$n >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null && ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking that unsupported DNSKEY algorithm validates as insecure ($n)"
-ret=0
-dig_with_opts +noauth +noadd +nodnssec +adflag @10.53.0.3 dnskey-unsupported.example A >dig.out.ns3.test$n
-dig_with_opts +noauth +noadd +nodnssec +adflag @10.53.0.4 dnskey-unsupported.example A >dig.out.ns4.test$n
-grep "status: NOERROR," dig.out.ns3.test$n >/dev/null || ret=1
-grep "status: NOERROR," dig.out.ns4.test$n >/dev/null || ret=1
-grep "; EDE: 1 (Unsupported DNSKEY Algorithm): (255 dnskey-unsupported.example/SOA)" dig.out.ns4.test$n >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null && ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking EDE code 2 for unsupported DS digest ($n)"
-ret=0
-dig_with_opts @10.53.0.4 a.ds-unsupported.example >dig.out.ns4.test$n || ret=1
-grep "; EDE: 2 (Unsupported DS Digest Type): (SHA-256 ds-unsupported.example/DNSKEY)" dig.out.ns4.test$n >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null && ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking EDE code 1 for bad alg mnemonic ($n)"
-ret=0
-dig_with_opts @10.53.0.4 badalg.secure.example >dig.out.ns4.test$n || ret=1
-grep "; EDE: 1 (Unsupported DNSKEY Algorithm): (ECDSAP256SHA256 badalg.secure.example/NSEC)" dig.out.ns4.test$n >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null && ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking both EDE code 1 and 2 for unsupported digest on one DNSKEY and alg on the other ($n)"
-ret=0
-dig_with_opts @10.53.0.4 a.digest-alg-unsupported.example >dig.out.ns4.test$n || ret=1
-grep "; EDE: 1 (Unsupported DNSKEY Algorithm): (ECDSAP384SHA384 digest-alg-unsupported.example/DNSKEY)" dig.out.ns4.test$n >/dev/null || ret=1
-grep "; EDE: 2 (Unsupported DS Digest Type): (SHA-384 digest-alg-unsupported.example/DNSKEY)" dig.out.ns4.test$n >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null && ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking that unsupported DNSKEY algorithm is in DNSKEY RRset ($n)"
-ret=0
-dig_with_opts +noauth +noadd +nodnssec +adflag @10.53.0.3 dnskey-unsupported-2.example DNSKEY >dig.out.test$n
-grep "status: NOERROR," dig.out.test$n >/dev/null || ret=1
-grep "dnskey-unsupported-2\.example\..*IN.*DNSKEY.*257 3 255" dig.out.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-# Check that a query for a domain that has a KSK that is not actively signing
-# the DNSKEY RRset. This should not result in a broken trust chain if there is
-# another KSK that is signing the DNSKEY RRset.
-echo_i "checking that a secure chain with one active and one inactive KSK validates as secure ($n)"
-ret=0
-dig_with_opts @10.53.0.4 a.lazy-ksk A >dig.out.ns4.test$n
-grep "status: NOERROR," dig.out.ns4.test$n >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
 # TODO: test case for GL #1689.
 # If we allow the dnssec tools to use deprecated algorithms (such as RSAMD5)
 # we could write a test that signs a zone with supported and unsupported
@@ -2371,17 +604,6 @@ n=$((n + 1))
 test "$ret" -eq 0 || echo_i "failed"
 status=$((status + ret))
 
-echo_i "checking that unknown DNSKEY algorithm + unknown NSEC3 has algorithm validates as insecure ($n)"
-ret=0
-dig_with_opts +noauth +noadd +nodnssec +adflag @10.53.0.3 dnskey-nsec3-unknown.example A >dig.out.ns3.test$n
-dig_with_opts +noauth +noadd +nodnssec +adflag @10.53.0.4 dnskey-nsec3-unknown.example A >dig.out.ns4.test$n
-grep "status: NOERROR," dig.out.ns3.test$n >/dev/null || ret=1
-grep "status: NOERROR," dig.out.ns4.test$n >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null && ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
 echo_i "check that CDNSKEY records are signed using KSK only when added by nsupdate ($n)"
 ret=0
 keyid=$(cat ns2/cdnskey-update.secure.id)
@@ -2425,22 +647,6 @@ n=$((n + 1))
 test "$ret" -eq 0 || echo_i "failed"
 status=$((status + ret))
 
-echo_i "check that a named managed zone that was signed 'in-the-future' is re-signed when loaded ($n)"
-ret=0
-dig_with_opts managed-future.example. @10.53.0.4 a >dig.out.ns4.test$n || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
-grep "status: NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "check that the view is logged in messages from the validator when using views ($n)"
-ret=0
-grep "view rec: *validat" ns4/named.run >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
 echo_i "check that DNAME at apex with NSEC3 is correctly signed (dnssec-signzone) ($n)"
 ret=0
 dig_with_opts txt dname-at-apex-nsec3.example @10.53.0.3 >dig.out.ns3.test$n || ret=1
@@ -2472,164 +678,6 @@ n=$((n + 1))
 test "$ret" -eq 0 || echo_i "failed"
 status=$((status + ret))
 
-#
-# DNSSEC tests related to unsupported, disabled and revoked trust anchors.
-#
-
-# This nameserver (ns8) is loaded with a bunch of trust anchors.  Some of
-# them are good (enabled.managed, enabled.trusted, secure.managed,
-# secure.trusted), and some of them are bad (disabled.managed,
-# revoked.managed, unsupported.managed, disabled.trusted, revoked.trusted,
-# unsupported.trusted).  Make sure that the bad trust anchors are ignored.
-# This is tested by looking for the corresponding lines in the logfile.
-echo_i "checking that keys with unsupported algorithms and disabled algorithms are ignored ($n)"
-ret=0
-grep -q "ignoring static-key for 'disabled\.trusted\.': algorithm is disabled" ns8/named.run || ret=1
-grep -q "ignoring static-key for 'unsupported\.trusted\.': algorithm is unsupported" ns8/named.run || ret=1
-grep -q "ignoring static-key for 'revoked\.trusted\.': bad key type" ns8/named.run || ret=1
-grep -q "ignoring initial-key for 'disabled\.managed\.': algorithm is disabled" ns8/named.run || ret=1
-grep -q "ignoring initial-key for 'unsupported\.managed\.': algorithm is unsupported" ns8/named.run || ret=1
-grep -q "ignoring initial-key for 'revoked\.managed\.': bad key type" ns8/named.run || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-# The next two tests are fairly normal DNSSEC queries to signed zones with a
-# default algorithm.  First, a query is made against the server that is
-# authoritative for the given zone (ns3).  Second, a query is made against a
-# resolver with trust anchors for the given zone (ns8).  Both are expected to
-# return an authentic data positive response.
-echo_i "checking that a trusted key using a supported algorithm validates as secure ($n)"
-ret=0
-dig_with_opts @10.53.0.3 a.secure.trusted A >dig.out.ns3.test$n
-dig_with_opts @10.53.0.8 a.secure.trusted A >dig.out.ns8.test$n
-grep "status: NOERROR," dig.out.ns3.test$n >/dev/null || ret=1
-grep "status: NOERROR," dig.out.ns8.test$n >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns8.test$n >/dev/null || ret=1
-grep "; EDE: " dig.out.ns8.test$n >/dev/null && ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking that a managed key using a supported algorithm validates as secure ($n)"
-ret=0
-dig_with_opts @10.53.0.3 a.secure.managed A >dig.out.ns3.test$n
-dig_with_opts @10.53.0.8 a.secure.managed A >dig.out.ns8.test$n
-grep "status: NOERROR," dig.out.ns3.test$n >/dev/null || ret=1
-grep "status: NOERROR," dig.out.ns8.test$n >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns8.test$n >/dev/null || ret=1
-grep "; EDE: " dig.out.ns8.test$n >/dev/null && ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-# The next two queries ensure that a zone signed with a DNSKEY with an unsupported
-# algorithm will yield insecure positive responses.  These trust anchors in ns8 are
-# ignored and so this domain is treated as insecure.  The AD bit should not be set
-# in the response.
-echo_i "checking that a trusted key using an unsupported algorithm validates as insecure ($n)"
-ret=0
-dig_with_opts @10.53.0.3 a.unsupported.trusted A >dig.out.ns3.test$n
-dig_with_opts @10.53.0.8 a.unsupported.trusted A >dig.out.ns8.test$n
-grep "status: NOERROR," dig.out.ns3.test$n >/dev/null || ret=1
-grep "status: NOERROR," dig.out.ns8.test$n >/dev/null || ret=1
-grep "; EDE: 1 (Unsupported DNSKEY Algorithm): (255 ns3.unsupported.trusted (cached))" dig.out.ns8.test$n >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns8.test$n >/dev/null && ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking that a managed key using an unsupported algorithm validates as insecure ($n)"
-ret=0
-dig_with_opts @10.53.0.3 a.unsupported.managed A >dig.out.ns3.test$n
-dig_with_opts @10.53.0.8 a.unsupported.managed A >dig.out.ns8.test$n
-grep "status: NOERROR," dig.out.ns3.test$n >/dev/null || ret=1
-grep "status: NOERROR," dig.out.ns8.test$n >/dev/null || ret=1
-grep "; EDE: 1 (Unsupported DNSKEY Algorithm): (255 ns3.unsupported.managed (cached))" dig.out.ns8.test$n >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns8.test$n >/dev/null && ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-# The next two queries ensure that a zone signed with a DNSKEY that the nameserver
-# has a disabled algorithm match for will yield insecure positive responses.
-# These trust anchors in ns8 are ignored and so this domain is treated as insecure.
-# The AD bit should not be set in the response.
-echo_i "checking that a trusted key using a disabled algorithm validates as insecure ($n)"
-ret=0
-dig_with_opts @10.53.0.3 a.disabled.trusted A >dig.out.ns3.test$n
-dig_with_opts @10.53.0.8 a.disabled.trusted A >dig.out.ns8.test$n
-grep "status: NOERROR," dig.out.ns3.test$n >/dev/null || ret=1
-grep "status: NOERROR," dig.out.ns8.test$n >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns8.test$n >/dev/null && ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking that a managed key using a disabled algorithm validates as insecure ($n)"
-ret=0
-dig_with_opts @10.53.0.3 a.disabled.managed A >dig.out.ns3.test$n
-dig_with_opts @10.53.0.8 a.disabled.managed A >dig.out.ns8.test$n
-grep "status: NOERROR," dig.out.ns3.test$n >/dev/null || ret=1
-grep "status: NOERROR," dig.out.ns8.test$n >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns8.test$n >/dev/null && ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-# The next two queries ensure that a zone signed with a DNSKEY that the
-# nameserver has a disabled algorithm for, but for a different domain, will
-# yield secure positive responses.  Since "enabled.trusted." and
-# "enabled.managed." do not match the "disable-algorithms" option, no
-# special rules apply and these zones should validate as secure, with the AD
-# bit set.
-echo_i "checking that a trusted key using an algorithm disabled for another domain validates as secure ($n)"
-ret=0
-dig_with_opts @10.53.0.3 a.enabled.trusted A >dig.out.ns3.test$n
-dig_with_opts @10.53.0.8 a.enabled.trusted A >dig.out.ns8.test$n
-grep "status: NOERROR," dig.out.ns3.test$n >/dev/null || ret=1
-grep "status: NOERROR," dig.out.ns8.test$n >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns8.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking that a managed key using an algorithm disabled for another domain validates as secure ($n)"
-ret=0
-dig_with_opts @10.53.0.3 a.enabled.managed A >dig.out.ns3.test$n
-dig_with_opts @10.53.0.8 a.enabled.managed A >dig.out.ns8.test$n
-grep "status: NOERROR," dig.out.ns3.test$n >/dev/null || ret=1
-grep "status: NOERROR," dig.out.ns8.test$n >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns8.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-# A configured revoked trust anchor is ignored and thus the two queries below
-# should result in insecure responses, since no trust points for the
-# "revoked.trusted." and "revoked.managed." zones are created.
-echo_i "checking that a trusted key that is revoked validates as insecure ($n)"
-ret=0
-dig_with_opts @10.53.0.3 a.revoked.trusted A >dig.out.ns3.test$n
-dig_with_opts @10.53.0.8 a.revoked.trusted A >dig.out.ns8.test$n
-grep "status: NOERROR," dig.out.ns3.test$n >/dev/null || ret=1
-grep "status: NOERROR," dig.out.ns8.test$n >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns8.test$n >/dev/null && ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking that a managed key that is revoked validates as insecure ($n)"
-ret=0
-dig_with_opts @10.53.0.3 a.revoked.managed A >dig.out.ns3.test$n
-dig_with_opts @10.53.0.8 a.revoked.managed A >dig.out.ns8.test$n
-grep "status: NOERROR," dig.out.ns3.test$n >/dev/null || ret=1
-grep "status: NOERROR," dig.out.ns8.test$n >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns8.test$n >/dev/null && ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
 ###
 ### Additional checks for when the KSK is offline.
 ###
@@ -2924,216 +972,5 @@ for qtype in "SOA" "TXT"; do
   status=$((status + ret))
 done
 
-echo_i "checking secroots output with multiple views ($n)"
-ret=0
-rndccmd 10.53.0.4 secroots 2>&1 | sed 's/^/ns4 /' | cat_i
-cp ns4/named.secroots named.secroots.test$n
-check_secroots_layout named.secroots.test$n || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking validation succeeds during transition to signed ($n)"
-ret=0
-dig_with_opts @10.53.0.4 inprogress A >dig.out.ns4.test$n || ret=1
-grep "flags: qr rd ra;" dig.out.ns4.test$n >/dev/null || ret=1
-grep "status: NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-grep 'A.10\.53\.0\.10' dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking excessive NSEC3 iteration warnings in named.run ($n)"
-ret=0
-grep "zone too-many-iterations/IN: excessive NSEC3PARAM iterations [0-9]* > 50" ns2/named.run >/dev/null 2>&1 || ret=1
-grep "zone too-many-iterations/IN: excessive NSEC3PARAM iterations [0-9]* > 50" ns3/named.run >/dev/null 2>&1 || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-# Check that the validating resolver will fallback to insecure if the answer
-# contains NSEC3 records with high iteration count.
-echo_i "checking fallback to insecure when NSEC3 iterations is too high (nxdomain) ($n)"
-ret=0
-dig_with_opts @10.53.0.2 does-not-exist.too-many-iterations >dig.out.ns2.test$n || ret=1
-dig_with_opts @10.53.0.4 does-not-exist.too-many-iterations >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns2.test$n dig.out.ns4.test$n || ret=1
-grep "flags: qr rd ra;" dig.out.ns4.test$n >/dev/null || ret=1
-grep "status: NXDOMAIN" dig.out.ns4.test$n >/dev/null || ret=1
-grep "ANSWER: 0, AUTHORITY: 8" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking fallback to insecure when NSEC3 iterations is too high (nodata) ($n)"
-ret=0
-dig_with_opts @10.53.0.2 a.too-many-iterations txt >dig.out.ns2.test$n || ret=1
-dig_with_opts @10.53.0.4 a.too-many-iterations txt >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns2.test$n dig.out.ns4.test$n || ret=1
-grep "flags: qr rd ra;" dig.out.ns4.test$n >/dev/null || ret=1
-grep "status: NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-grep "ANSWER: 0, AUTHORITY: 4" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking fallback to insecure when NSEC3 iterations is too high (wildcard) ($n)"
-ret=0
-dig_with_opts @10.53.0.2 wild.a.too-many-iterations >dig.out.ns2.test$n || ret=1
-dig_with_opts @10.53.0.4 wild.a.too-many-iterations >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns2.test$n dig.out.ns4.test$n || ret=1
-grep "flags: qr rd ra;" dig.out.ns4.test$n >/dev/null || ret=1
-grep "status: NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-grep 'wild\.a\.too-many-iterations\..*A.10\.0\.0\.3' dig.out.ns4.test$n >/dev/null || ret=1
-grep "ANSWER: 2, AUTHORITY: 4" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-echo_i "checking fallback to insecure when NSEC3 iterations is too high (wildcard nodata) ($n)"
-ret=0
-dig_with_opts @10.53.0.2 type100 wild.a.too-many-iterations >dig.out.ns2.test$n || ret=1
-dig_with_opts @10.53.0.4 type100 wild.a.too-many-iterations >dig.out.ns4.test$n || ret=1
-digcomp dig.out.ns2.test$n dig.out.ns4.test$n || ret=1
-grep "flags: qr rd ra;" dig.out.ns4.test$n >/dev/null || ret=1
-grep "status: NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-grep "ANSWER: 0, AUTHORITY: 8" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-# Check that a query against a validating resolver succeeds when there is
-# a negative cache entry with trust level "pending" for the DS.  Prime
-# with a +cd DS query to produce the negative cache entry, then send a
-# query that uses that entry as part of the validation process. [GL #3279]
-echo_i "check that pending negative DS cache entry validates ($n)"
-ret=0
-dig_with_opts @10.53.0.4 +cd insecure2.example. ds >dig.out.prime.ns4.test$n || ret=1
-grep "flags: qr rd ra cd;" dig.out.prime.ns4.test$n >/dev/null || ret=1
-grep "status: NOERROR" dig.out.prime.ns4.test$n >/dev/null || ret=1
-grep "ANSWER: 0, AUTHORITY: 4, " dig.out.prime.ns4.test$n >/dev/null || ret=1
-dig_with_opts @10.53.0.4 a.insecure2.example. a >dig.out.ns4.test$n || ret=1
-grep "ANSWER: 1, AUTHORITY: 1, " dig.out.ns4.test$n >/dev/null || ret=1
-grep "flags: qr rd ra;" dig.out.ns4.test$n >/dev/null || ret=1
-grep "status: NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-if [ "$ret" -ne 0 ]; then echo_i "failed"; fi
-status=$((status + ret))
-
-echo_i "checking NSEC3 nxdomain response closest encloser with 0 ENT ($n)"
-ret=0
-dig_with_opts @10.53.0.4 b.b.b.b.b.a.nsec3.example. >dig.out.ns4.test$n
-grep "status: NXDOMAIN" dig.out.ns4.test$n >/dev/null || ret=1
-# closest encloser (a.nsec3.example)
-pat1="^6OVDUHTN094ML2PV8AN90U0DPU823GH2\.nsec3\.example\..*NSEC3 1 0 0 - 7AT0S0RIDCJRFF2M5H5AAV22CSFJBUL4 A RRSIG\$"
-grep "$pat1" dig.out.ns4.test$n >/dev/null || ret=1
-# no QNAME proof (b.a.nsec3.example / DSPF4R9UKOEPJ9O34E1H4539LSOTL14E)
-pat2="^CG2DVCNE20EKU1PDRLMI2L4DGC2FO1H3\.nsec3\.example\..*NSEC3 1 0 0 - EF2S05SGK1IR2K5SKMFIRERGQCLMR18M A RRSIG\$"
-grep "$pat2" dig.out.ns4.test$n >/dev/null || ret=1
-# no WILDCARD proof (*.a.nsec3.example / TFGQ60S97BS31IT1EBEDO63ETM0T5JFA)
-pat3="^R8EVDMNIGNOKME4LH2H90OSP2PRSNJ1Q\.nsec3\.example\..*NSEC3 1 0 0 - VH656EQUD4J02OFVSO4GKOK5D02MS1TL NS DS RRSIG\$"
-grep "$pat3" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-if [ "$ret" -ne 0 ]; then echo_i "failed"; fi
-status=$((status + ret))
-
-echo_i "checking NSEC3 nxdomain response closest encloser with 1 ENTs ($n)"
-ret=0
-dig_with_opts @10.53.0.4 b.b.b.b.b.a.a.nsec3.example. >dig.out.ns4.test$n
-grep "status: NXDOMAIN" dig.out.ns4.test$n >/dev/null || ret=1
-# closest encloser (a.a.nsec3.example)
-pat1="^NGCJFSOLJUUE27PFNQNJIME4TQ0OU2DH\.nsec3\.example\..*NSEC3 1 0 0 - R8EVDMNIGNOKME4LH2H90OSP2PRSNJ1Q\$"
-grep "$pat1" dig.out.ns4.test$n >/dev/null || ret=1
-# no QNAME proof (b.a.a.nsec3.example / V8I8SAIIVC3HOVMOVENSDRA6ATDCEMJI)
-pat2="^R8EVDMNIGNOKME4LH2H90OSP2PRSNJ1Q\.nsec3\.example\..*NSEC3 1 0 0 - VH656EQUD4J02OFVSO4GKOK5D02MS1TL NS DS RRSIG\$"
-grep "$pat2" dig.out.ns4.test$n >/dev/null || ret=1
-# no WILDCARD proof (*.a.a.nsec3.example / V7JNNDJ4NLRIU195FRB7DLUCSLU4LLFM)
-pat3="^R8EVDMNIGNOKME4LH2H90OSP2PRSNJ1Q\.nsec3\.example\..*NSEC3 1 0 0 - VH656EQUD4J02OFVSO4GKOK5D02MS1TL NS DS RRSIG\$"
-grep "$pat3" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-if [ "$ret" -ne 0 ]; then echo_i "failed"; fi
-status=$((status + ret))
-
-echo_i "checking NSEC3 nxdomain response closest encloser with 2 ENTs ($n)"
-ret=0
-dig_with_opts @10.53.0.4 b.b.b.b.b.a.a.a.nsec3.example. >dig.out.ns4.test$n
-grep "status: NXDOMAIN" dig.out.ns4.test$n >/dev/null || ret=1
-# closest encloser (a.a.a.nsec3.example)
-pat1="^H7RHPDCHSVVRAND332F878C8AB6IBJQV\.nsec3\.example\..*NSEC3 1 0 0 - K8IG76R2UPQ13IKFO49L7IB9JRVB6QJI\$"
-grep "$pat1" dig.out.ns4.test$n >/dev/null || ret=1
-# no QNAME proof (b.a.a.a.nsec3.example / 18Q8D89RM8GGRSSOPFRB05QS6VEGB1P4)
-pat2="^VH656EQUD4J02OFVSO4GKOK5D02MS1TL\.nsec3\.example\..*NSEC3 1 0 0 - 1HARMGSKJH0EBU2EI2OJIKTDPIQA6KBI NS DS RRSIG\$"
-grep "$pat2" dig.out.ns4.test$n >/dev/null || ret=1
-# no WILDCARD proof (*.a.a.a.nsec3.example / 8113LDMSEFPUAG4VGFF1C8KLOUT4Q6PH)
-pat3="^7AT0S0RIDCJRFF2M5H5AAV22CSFJBUL4\.nsec3\.example\..*NSEC3 1 0 0 - BEJ5GMQA872JF4DAGQ0R3O5Q7A2O5S9L A RRSIG\$"
-grep "$pat3" dig.out.ns4.test$n >/dev/null || ret=1
-n=$((n + 1))
-if [ "$ret" -ne 0 ]; then echo_i "failed"; fi
-status=$((status + ret))
-
-echo_i "checking validator behavior with mismatching NS ($n)"
-ret=0
-rndccmd 10.53.0.4 flush 2>&1 | sed 's/^/ns4 /' | cat_i
-$DIG +tcp +cd -p "$PORT" -t ns inconsistent @10.53.0.4 >dig.out.ns4.test$n.1 || ret=1
-grep "ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 2" dig.out.ns4.test$n.1 >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n.1 >/dev/null && ret=1
-$DIG +tcp +cd +dnssec -p "$PORT" -t ns inconsistent @10.53.0.4 >dig.out.ns4.test$n.2 || ret=1
-grep "ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 2" dig.out.ns4.test$n.2 >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n.2 >/dev/null && ret=1
-$DIG +tcp +dnssec -p "$PORT" -t ns inconsistent @10.53.0.4 >dig.out.ns4.test$n.3 || ret=1
-grep "ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1" dig.out.ns4.test$n.3 >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns4.test$n.3 >/dev/null || ret=1
-n=$((n + 1))
-if [ "$ret" -ne 0 ]; then echo_i "failed"; fi
-status=$((status + ret))
-
-echo_i "checking that a insecure negative response where there is a NSEC without a RRSIG succeeds ($n)"
-ret=0
-# check server preconditions
-dig_with_opts +notcp @10.53.0.10 nsec-rrsigs-stripped. TXT +dnssec >dig.out.ns10.test$n
-grep "status: NOERROR" dig.out.ns10.test$n >/dev/null || ret=1
-grep "QUERY: 1, ANSWER: 0, AUTHORITY: 2, ADDITIONAL: 1" dig.out.ns10.test$n >/dev/null || ret=1
-grep "IN.RRSIG.NSEC" dig.out.ns10.test$n >/dev/null && ret=1
-# check resolver succeeds
-dig_with_opts @10.53.0.4 nsec-rrsigs-stripped. TXT +dnssec >dig.out.ns4.test$n
-grep "status: NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
-grep "QUERY: 1, ANSWER: 0, AUTHORITY: 2, ADDITIONAL: 1" dig.out.ns4.test$n >/dev/null || ret=1
-grep "IN.RRSIG.NSEC" dig.out.ns4.test$n >/dev/null && ret=1
-n=$((n + 1))
-if [ "$ret" -ne 0 ]; then echo_i "failed"; fi
-status=$((status + ret))
-
-echo_i "checking validating forwarder behavior with mismatching NS ($n)"
-ret=0
-rndccmd 10.53.0.4 flush 2>&1 | sed 's/^/ns4 /' | cat_i
-$DIG +tcp +cd -p "$PORT" -t ns inconsistent @10.53.0.9 >dig.out.ns9.test$n.1 || ret=1
-grep "ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1" dig.out.ns9.test$n.1 >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns9.test$n.1 >/dev/null && ret=1
-$DIG +tcp +cd +dnssec -p "$PORT" -t ns inconsistent @10.53.0.9 >dig.out.ns9.test$n.2 || ret=1
-grep "ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1" dig.out.ns9.test$n.2 >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns9.test$n.2 >/dev/null && ret=1
-$DIG +tcp +dnssec -p "$PORT" -t ns inconsistent @10.53.0.9 >dig.out.ns9.test$n.3 || ret=1
-grep "ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1" dig.out.ns9.test$n.3 >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns9.test$n.3 >/dev/null || ret=1
-n=$((n + 1))
-if [ "$ret" -ne 0 ]; then echo_i "failed"; fi
-status=$((status + ret))
-
-echo_i "checking forwarder CD behavior (DS mismatch and local trust anchor) ($n)"
-ret=0
-rndccmd 10.53.0.4 flush 2>&1 | sed 's/^/ns4 /' | cat_i
-# confirm invalid DS produces SERVFAIL in resolver
-$DIG +tcp +dnssec -p "$PORT" @10.53.0.4 localkey.example soa >dig.out.ns4.test$n || ret=1
-grep "status: SERVFAIL" dig.out.ns4.test$n >/dev/null || ret=1
-# check that lookup using forwarder succeeds and that SERVFAIL was received
-nextpart ns9/named.run >/dev/null
-$DIG +tcp +dnssec -p "$PORT" @10.53.0.9 localkey.example soa >dig.out.ns9.test$n || ret=1
-grep "status: NOERROR" dig.out.ns9.test$n >/dev/null || ret=1
-grep "flags:.*ad.*QUERY" dig.out.ns9.test$n >/dev/null || ret=1
-nextpart ns9/named.run | grep 'status: SERVFAIL' >/dev/null || ret=1
-n=$((n + 1))
-if [ "$ret" -ne 0 ]; then echo_i "failed"; fi
-status=$((status + ret))
-
 echo_i "exit status: $status"
 [ $status -eq 0 ] || exit 1
index 854c15424d39bfe180fdc8436ad5785e7f4bbd78..9f9cc76bf68a9faf4923307430b1c27f6951b5a8 100644 (file)
@@ -165,9 +165,11 @@ pytestmark = pytest.mark.extra_artifacts(
         "ns4/named_dump.db.*",
         "ns5/broken.conf",
         "ns5/revoked.conf",
+        "ns5/many-trusted.conf",
+        "ns5/many-managed.conf",
         "ns6/optout-tld.db",
-        "ns7/split-rrsig.db",
-        "ns7/split-rrsig.db.unsplit",
+        "ns6/split-rrsig.db",
+        "ns6/split-rrsig.db.unsplit",
         "ns9/trusted-localkey.conf",
     ]
 )
diff --git a/bin/tests/system/dnssec/tests_validation.py b/bin/tests/system/dnssec/tests_validation.py
new file mode 100644 (file)
index 0000000..f0d132b
--- /dev/null
@@ -0,0 +1,1606 @@
+# 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.
+
+import os
+import re
+import shutil
+import time
+
+from dns import edns, flags, name, rcode, rdataclass, rdatatype
+
+import pytest
+
+pytest.importorskip("dns", minversion="2.0.0")
+import isctest
+import isctest.mark
+from isctest.util import param
+
+
+# helper functions
+def grep_q(regex, filename):
+    with open(filename, "r", encoding="utf-8") as f:
+        blob = f.read().splitlines()
+    results = [x for x in blob if re.search(regex, x)]
+    return len(results) != 0
+
+
+def getfrom(file):
+    with open(file, encoding="utf-8") as f:
+        return f.read().strip()
+
+
+@pytest.mark.requires_zones_loaded("ns2", "ns3")
+@pytest.mark.parametrize(
+    "qname, qtype",
+    [
+        param("a.example.", "A"),
+        param("rfc2535.example.", "SOA"),
+    ],
+)
+def test_load_transfer(qname, qtype):
+    # check that we can load and transfer zone
+    msg = isctest.query.create(qname, qtype)
+    res1 = isctest.query.tcp(msg, "10.53.0.2")
+    res2 = isctest.query.tcp(msg, "10.53.0.3")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.noerror(res1)
+
+
+def test_insecure_glue():
+    # check that for a query against a validating resolver where the
+    # authoritative zone is unsigned (insecure delegation), glue is returned
+    # in the additional section
+    msg = isctest.query.create("a.insecure.example", "A")
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.noerror(res)
+    isctest.check.rr_count_eq(res.answer, 1)
+    isctest.check.rr_count_eq(res.authority, 1)
+    isctest.check.rr_count_eq(res.additional, 1)
+    assert str(res.additional[0].name) == "ns.insecure.example."
+    addrs = [str(a) for a in res.additional[0]]
+    assert "10.53.0.3" in addrs
+
+
+def test_adflag():
+    # compare auth and recursive answers
+    msg = isctest.query.create("a.example", "A", dnssec=False)
+    res1 = isctest.query.tcp(msg, "10.53.0.2")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+
+    # check no AD flag in authoritative response
+    isctest.check.noadflag(res1)
+
+    # check validating resolver sends AD=1 if the client sent AD=1
+    isctest.check.adflag(res2)
+
+    # check that AD=0 unless the client sent AD=1
+    msg = isctest.query.create("a.example", "A", dnssec=False, ad=False)
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.noadflag(res2)
+
+
+def test_secure_root(servers):
+    # check that a query for a secure root validates
+    msg = isctest.query.create(".", "KEY")
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.noerror(res)
+    isctest.check.adflag(res)
+
+    # check that "rndc secroots" dumps the trusted keys
+    ns4 = servers["ns4"]
+    key = int(getfrom("ns1/managed.key.id"))
+    alg = os.environ["DEFAULT_ALGORITHM"]
+    expected = f"./{alg}/{key} ; static"
+    response = ns4.rndc("secroots -", log=False).splitlines()
+    assert expected in response
+    assert len(response) == 10
+
+
+def test_positive_validation_nsec():
+    # positive answer
+    msg = isctest.query.create("a.example", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.2")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.adflag(res2)
+
+    # wildcard
+    msg = isctest.query.create("a.wild.example", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.noerror(res2)
+    isctest.check.adflag(res2)
+
+    assert str(res2.authority[0].name) == "*.wild.example."
+    assert res2.authority[0].rdtype == rdatatype.NSEC
+    nsecs = [str(a).split(" ", maxsplit=1)[0] for a in res2.authority[0]]
+    assert "z.example." in nsecs
+    assert res2.authority[1].rdtype == rdatatype.RRSIG
+    assert res2.authority[1].covers == rdatatype.NSEC
+
+    # mixed case
+    for rrtype in ["a", "txt", "aaaa", "loc"]:
+        msg = isctest.query.create("mixedcase.secure.example", rrtype)
+        res1 = isctest.query.tcp(msg, "10.53.0.3")
+        res2 = isctest.query.tcp(msg, "10.53.0.4")
+        isctest.check.same_answer(res1, res2)
+        isctest.check.noerror(res2)
+        isctest.check.adflag(res2)
+
+
+def test_positive_validation_nsec3():
+    # positive answer
+    msg = isctest.query.create("a.nsec3.example", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.noerror(res2)
+    isctest.check.adflag(res2)
+
+    # wildcard
+    msg = isctest.query.create("a.wild.nsec3.example", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.noerror(res2)
+    isctest.check.adflag(res2)
+    isctest.check.rr_count_eq(res2.authority, 4)
+
+    # unknown NSEC3 hash algorithm
+    msg = isctest.query.create("nsec3-unknown.example", "SOA", dnssec=False)
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.noerror(res1)
+    isctest.check.noerror(res2)
+    isctest.check.adflag(res2)
+    isctest.check.rr_count_eq(res2.answer, 1)
+
+
+def test_positive_validation_optout():
+    # positive answer
+    msg = isctest.query.create("a.optout.example", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.adflag(res2)
+
+    # wildcard
+    msg = isctest.query.create("a.wild.optout.example", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.noerror(res2)
+    isctest.check.noadflag(res2)
+
+    # unknown NSEC3 hash algorithm
+    msg = isctest.query.create("optout-unknown.example", "SOA", dnssec=False)
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.noerror(res1)
+    isctest.check.noerror(res2)
+    isctest.check.adflag(res2)
+    isctest.check.rr_count_eq(res2.answer, 1)
+
+
+def answer_has(r, rdtype):
+    return bool([r for r in r.answer if r.rdtype == rdtype])
+
+
+def test_chain_validation():
+    # check validation of ANY response
+    msg = isctest.query.create("foo.example", "ANY")
+    res1 = isctest.query.tcp(msg, "10.53.0.2")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.noerror(res2)
+    isctest.check.rr_count_eq(res2.answer, 6)  # 2 records, 1 NSEC, 3 RRSIGs
+
+    # check validation of CNAME response
+    msg = isctest.query.create("cname1.example", "TXT")
+    res1 = isctest.query.tcp(msg, "10.53.0.2")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.noerror(res2)
+    isctest.check.rr_count_eq(res2.answer, 4)  # CNAME, TXT, 2 RRSIGs
+
+    # check validation of DNAME response
+    msg = isctest.query.create("foo.dname1.example", "TXT")
+    res1 = isctest.query.tcp(msg, "10.53.0.2")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.noerror(res2)
+    isctest.check.rr_count_eq(res2.answer, 5)  # DNAME, TXT, 2 RRSIGs, synth CNAME
+
+    # check validation of CNAME response to ANY query
+    msg = isctest.query.create("cname2.example", "ANY")
+    res1 = isctest.query.tcp(msg, "10.53.0.2")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.noerror(res2)
+    isctest.check.rr_count_eq(res2.answer, 4)  # CNAME, NSEC, 2 RRSIGs
+
+    # check validation of DNAME response to ANY query
+    msg = isctest.query.create("foo.dname2.example", "ANY")
+    res1 = isctest.query.tcp(msg, "10.53.0.2")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.noerror(res2)
+    isctest.check.rr_count_eq(res2.answer, 3)  # DNAME, RSRIG, synth CNAME
+
+    # check bad CNAME signature is caught after +CD query
+    msg = isctest.query.create("bad-cname.example", "A", dnssec=False, cd=True)
+    # query once with CD to prime the cache
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    # query again with CD, bogus pending data should be returned
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.noerror(res)
+    assert "a.example." in str(res.answer[0])
+    assert "10.0.0.1" in str(res.answer[1])
+    # query again without CD, bogus data should be rejected
+    msg = isctest.query.create("bad-cname.example", "A", dnssec=False)
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.servfail(res)
+
+    # check bad DNAME signature is caught after +CD query
+    msg = isctest.query.create("a.bad-dname.example", "A", dnssec=False, cd=True)
+    # query once with CD to prime the cache
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    # query again with CD, bogus pending data should be returned
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.noerror(res)
+    assert "example." in str(res.answer[0])
+    assert "a.example." in str(res.answer[1])
+    assert "10.0.0.1" in str(res.answer[2])
+    # query again without CD, bogus data should be rejected
+    msg = isctest.query.create("a.bad-dname.example", "A", dnssec=False)
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.servfail(res)
+
+    # check DNSKEY lookup via CNAME
+    msg = isctest.query.create("cnameandkey.secure.example", "DNSKEY")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.adflag(res2)
+    assert answer_has(res2, rdatatype.CNAME)
+
+    # check KEY lookup via CNAME
+    msg = isctest.query.create("cnameandkey.secure.example", "KEY")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.adflag(res2)
+    assert not answer_has(res2, rdatatype.CNAME)
+
+    # check KEY lookup via CNAME (not present)
+    msg = isctest.query.create("cnamenokey.secure.example", "KEY")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.adflag(res2)
+    assert not answer_has(res2, rdatatype.CNAME)
+
+    # check DNSKEY lookup via DNAME
+    msg = isctest.query.create("a.dnameandkey.secure.example", "DNSKEY")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.adflag(res2)
+    assert answer_has(res2, rdatatype.DNAME)
+
+    # check KEY lookup via DNAME
+    msg = isctest.query.create("a.dnameandkey.secure.example", "KEY")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.adflag(res2)
+    assert answer_has(res2, rdatatype.DNAME)
+
+
+@isctest.mark.rsasha1
+def test_signing_algorithms_rsasha1():
+    # rsasha1 (should work with FIPS mode we're as only validating)
+    msg = isctest.query.create("a.rsasha1.example", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.adflag(res2)
+
+    # rsasha1 (1024 bits) NSEC
+    msg = isctest.query.create("a.rsasha1-1024.example", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.adflag(res2)
+
+
+def test_signing_algorithms():
+    # rsasha256
+    msg = isctest.query.create("a.rsasha256.example", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.adflag(res2)
+
+    # rsasha512
+    msg = isctest.query.create("a.rsasha512.example", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.adflag(res2)
+
+    # KSK-only DNSKEY
+    msg = isctest.query.create("a.kskonly.example", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.adflag(res2)
+
+
+def test_private_algorithms(servers):
+    # positive answer, private algorithm
+    msg = isctest.query.create("a.rsasha256oid.example", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.adflag(res2)
+
+    # positive answer, unknown private algorithm
+    msg = isctest.query.create("a.unknownoid.example", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.noadflag(res2)
+
+    # positive answer, extra ds for private algorithm
+    msg = isctest.query.create("a.extradsoid.example", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.adflag(res2)
+
+    # positive anwer, extra ds for unknown private algorithm
+    ns4 = servers["ns4"]
+    with ns4.watch_log_from_here() as watcher:
+        msg = isctest.query.create("a.extradsunknownoid.example", "A")
+        res1 = isctest.query.tcp(msg, "10.53.0.3")
+        res2 = isctest.query.tcp(msg, "10.53.0.4")
+        isctest.check.noerror(res1)
+        isctest.check.servfail(res2)
+        watcher.wait_for_line(
+            "No DNSKEY for extradsunknownoid.example/DS with PRIVATEOID"
+        )
+
+
+@isctest.mark.extended_ds_digest
+def test_private_algorithms_extended_ds():
+    # check positive validation with extra ds using extended digest
+    # type for unknown private algorithm
+    msg = isctest.query.create("a.extended-ds-unknown-oid.example", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.adflag(res2)
+
+
+def test_negative_validation_nsec():
+    # nxdomain
+    msg = isctest.query.create("q.example", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.2")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.nxdomain(res2)
+    isctest.check.adflag(res2)
+
+    # nodata
+    msg = isctest.query.create("a.example", "TXT")
+    res1 = isctest.query.tcp(msg, "10.53.0.2")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.noerror(res2)
+    isctest.check.empty_answer(res2)
+    isctest.check.adflag(res2)
+
+    # negative wildcard
+    msg = isctest.query.create("b.wild.example", "TXT")
+    res1 = isctest.query.tcp(msg, "10.53.0.2")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.noerror(res2)
+    isctest.check.adflag(res2)
+
+
+def test_negative_validation_nsec3():
+    # nxdomain
+    msg = isctest.query.create("q.nsec3.example", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.nxdomain(res2)
+    isctest.check.adflag(res2)
+
+    # nodata
+    msg = isctest.query.create("a.nsec3.example", "TXT")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.noerror(res2)
+    isctest.check.empty_answer(res2)
+    isctest.check.adflag(res2)
+
+    # negative wildcard
+    msg = isctest.query.create("b.wild.nsec3.example", "TXT")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.noerror(res2)
+    isctest.check.adflag(res2)
+
+    # check NSEC3 zone with mismatched NSEC3PARAM / NSEC parameters
+    msg = isctest.query.create("non-exist.badparam", "A")
+    res = isctest.query.tcp(msg, "10.53.0.2")
+    isctest.check.nxdomain(res)
+
+    # check negative unknown NSEC3 hash algorithm does not validate
+    msg = isctest.query.create("nsec3-unknown.example", "A", dnssec=False)
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.noerror(res1)
+    isctest.check.servfail(res2)
+
+
+def test_excessive_nsec3_iterations():
+    assert grep_q(
+        "zone too-many-iterations/IN: excessive NSEC3PARAM iterations", "ns2/named.run"
+    )
+    assert grep_q(
+        "zone too-many-iterations/IN: excessive NSEC3PARAM iterations", "ns3/named.run"
+    )
+
+    # check fallback to insecure with NSEC3 iterations is too high
+    msg = isctest.query.create("does-not-exist.too-many-iterations", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.2")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_data(res1, res2)
+    isctest.check.noadflag(res2)
+    isctest.check.rr_count_eq(res2.answer, 0)
+    isctest.check.rr_count_eq(res2.authority, 8)
+
+    # check fallback to insecure with NSEC3 iterations is too high (nodata)
+    msg = isctest.query.create("a.too-many-iterations", "TXT")
+    res1 = isctest.query.tcp(msg, "10.53.0.2")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_data(res1, res2)
+    isctest.check.noadflag(res2)
+    isctest.check.rr_count_eq(res2.answer, 0)
+    isctest.check.rr_count_eq(res2.authority, 4)
+
+    # check fallback to insecure with NSEC3 iterations is too high (wildcard)
+    msg = isctest.query.create("wild.a.too-many-iterations", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.2")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_data(res1, res2)
+    isctest.check.noadflag(res2)
+    isctest.check.rr_count_eq(res2.answer, 2)
+    isctest.check.rr_count_eq(res2.authority, 4)
+    a, _ = res2.answer
+    assert str(a.name) == "wild.a.too-many-iterations."
+    assert str(a[0]) == "10.0.0.3"
+
+    # check fallback to insecure with high NSEC3 iterations (wildcard nodata)
+    msg = isctest.query.create("wild.a.too-many-iterations", 100)
+    res1 = isctest.query.tcp(msg, "10.53.0.2")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_data(res1, res2)
+    isctest.check.noadflag(res2)
+    isctest.check.rr_count_eq(res2.authority, 8)
+
+
+def test_auth_nsec3():
+    # nxdomain response, closest encloser with 0 empty non-terminals
+    msg = isctest.query.create("b.b.b.b.b.a.nsec3.example", "A")
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.nxdomain(res)
+    # closest encloser (a.nsec3.example):
+    rrset = res.get_rrset(
+        res.authority,
+        name.from_text("6OVDUHTN094ML2PV8AN90U0DPU823GH2.nsec3.example."),
+        rdataclass.IN,
+        rdatatype.NSEC3,
+    )
+    assert rrset, "NSEC3 missing from AUTHORITY: " + str(res)
+    assert "7AT0S0RIDCJRFF2M5H5AAV22CSFJBUL4" in str(rrset[0]).upper()
+    # no QNAME (b.a.nsec3.example/DSPF4R9UKOEPJ9O34E1H4539LSOTL14E)
+    rrset = res.get_rrset(
+        res.authority,
+        name.from_text("BEJ5GMQA872JF4DAGQ0R3O5Q7A2O5S9L.nsec3.example."),
+        rdataclass.IN,
+        rdatatype.NSEC3,
+    )
+    assert rrset, "expected NSEC3 missing from AUTHORITY: " + str(res)
+    assert "EF2S05SGK1IR2K5SKMFIRERGQCLMR18M" in str(rrset[0]).upper()
+    # no WILDCARD (*.a.nsec3.example/TFGQ60S97BS31IT1EBEDO63ETM0T5JFA)
+    rrset = res.get_rrset(
+        res.authority,
+        name.from_text("R8EVDMNIGNOKME4LH2H90OSP2PRSNJ1Q.nsec3.example."),
+        rdataclass.IN,
+        rdatatype.NSEC3,
+    )
+    assert rrset, "expected NSEC3 missing from AUTHORITY: " + str(res)
+    assert "VH656EQUD4J02OFVSO4GKOK5D02MS1TL" in str(rrset[0]).upper()
+
+    # nxdomain response, closest encloser with 1 ENT
+    msg = isctest.query.create("b.b.b.b.b.a.a.nsec3.example", "A")
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.nxdomain(res)
+    # closest encloser (a.a.nsec3.example):
+    rrset = res.get_rrset(
+        res.authority,
+        name.from_text("NGCJFSOLJUUE27PFNQNJIME4TQ0OU2DH.nsec3.example."),
+        rdataclass.IN,
+        rdatatype.NSEC3,
+    )
+    assert rrset, "expected NSEC3 missing from AUTHORITY: " + str(res)
+    assert "R8EVDMNIGNOKME4LH2H90OSP2PRSNJ1Q" in str(rrset[0]).upper()
+    # noqname (b.a.a.nsec3.example):
+    rrset = res.get_rrset(
+        res.authority,
+        name.from_text("R8EVDMNIGNOKME4LH2H90OSP2PRSNJ1Q.nsec3.example."),
+        rdataclass.IN,
+        rdatatype.NSEC3,
+    )
+    assert rrset, "expected NSEC3 missing from AUTHORITY: " + str(res)
+    assert "VH656EQUD4J02OFVSO4GKOK5D02MS1TL" in str(rrset[0]).upper()
+    # no wildcard (*.a.a.nsec3.example/V7JNNDJ4NLRIU195FRB7DLUCSLU4LLFM)
+    # is covered by the noqname proof in this case
+
+    # nxdomain response, closest encloser with 2 ENTs
+    msg = isctest.query.create("b.b.b.b.b.a.a.a.nsec3.example", "A")
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.nxdomain(res)
+    # closest encloser (a.a.a.nsec3.example):
+    rrset = res.get_rrset(
+        res.authority,
+        name.from_text("H7RHPDCHSVVRAND332F878C8AB6IBJQV.nsec3.example."),
+        rdataclass.IN,
+        rdatatype.NSEC3,
+    )
+    assert rrset, "expected NSEC3 missing from AUTHORITY: " + str(res)
+    assert "K8IG76R2UPQ13IKFO49L7IB9JRVB6QJI" in str(rrset[0]).upper()
+    # noqname (b.a.a.a.nsec3.example/18Q8D89RM8GGRSSOPFRB05QS6VEGB1P4)
+    rrset = res.get_rrset(
+        res.authority,
+        name.from_text("0T7VH688AEK0612T69V8692OCMJD50M4.nsec3.example."),
+        rdataclass.IN,
+        rdatatype.NSEC3,
+    )
+    assert rrset, "expected NSEC3 missing from AUTHORITY: " + str(res)
+    assert "1HARMGSKJH0EBU2EI2OJIKTDPIQA6KBI" in str(rrset[0]).upper()
+    # no WILDCARD (*.a.a.a.nsec3.example/8113LDMSEFPUAG4VGFF1C8KLOUT4Q6PH)
+    rrset = res.get_rrset(
+        res.authority,
+        name.from_text("7AT0S0RIDCJRFF2M5H5AAV22CSFJBUL4.nsec3.example."),
+        rdataclass.IN,
+        rdatatype.NSEC3,
+    )
+    assert rrset, "expected NSEC3 missing from AUTHORITY: " + str(res)
+    assert "BEJ5GMQA872JF4DAGQ0R3O5Q7A2O5S9L" in str(rrset[0]).upper()
+
+
+def test_negative_validation_optout():
+    # nxdomain
+    msg = isctest.query.create("q.optout.example", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.nxdomain(res2)
+    isctest.check.noadflag(res2)
+
+    # nodata
+    msg = isctest.query.create("a.optout.example", "TXT")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.noerror(res2)
+    isctest.check.empty_answer(res2)
+    isctest.check.adflag(res2)
+
+    # negative wildcard
+    msg = isctest.query.create("b.wild.optout.example", "TXT")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.noerror(res2)
+    isctest.check.noadflag(res2)
+
+    # empty NODATA
+    msg = isctest.query.create("empty.optout.example", "TXT")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.noerror(res2)
+    isctest.check.noadflag(res2)
+
+    # (rt22007 regression tests:)
+    # check optout NSEC3 referral with only insecure delegatons
+    msg = isctest.query.create("delegation.single-nsec3", "A")
+    res = isctest.query.tcp(msg, "10.53.0.2")
+    isctest.check.noerror(res)
+    for rrset in res.authority:
+        if (
+            rrset.rdtype != rdatatype.NSEC3
+            or "3KL3NK1HKQ4IUEEHBEF12VGFKUETNBAN" not in rrset.name
+        ):
+            continue
+        assert "1 1 1 - 3KL3NK1HKQ4IUEEHBEF12VGFKUETNBAN" in str(rrset[0])
+
+    # check optout NSEC3 NXDOMAIN with only insecure delegatons
+    msg = isctest.query.create("nonexist.single-nsec3", "A")
+    res = isctest.query.tcp(msg, "10.53.0.2")
+    isctest.check.nxdomain(res)
+    for rrset in res.authority:
+        if (
+            rrset.rdtype != rdatatype.NSEC3
+            or "3KL3NK1HKQ4IUEEHBEF12VGFKUETNBAN" not in rrset.name
+        ):
+            continue
+        assert "1 1 1 - 3KL3NK1HKQ4IUEEHBEF12VGFKUETNBAN" in str(rrset[0])
+
+    # check optout NSEC3 NODATA with only insecure delegatons
+    msg = isctest.query.create("single-nsec3", "A")
+    res = isctest.query.tcp(msg, "10.53.0.2")
+    isctest.check.noerror(res)
+    for rrset in res.authority:
+        if (
+            rrset.rdtype != rdatatype.NSEC3
+            or "3KL3NK1HKQ4IUEEHBEF12VGFKUETNBAN" not in rrset.name
+        ):
+            continue
+        assert "1 1 1 - 3KL3NK1HKQ4IUEEHBEF12VGFKUETNBAN" in str(rrset[0])
+
+    # check negative unknown NSEC3-OPTOUT hash algorithm does not validate
+    msg = isctest.query.create("optout-unknown.example", "A", dnssec=False)
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.noerror(res1)
+    isctest.check.servfail(res2)
+
+
+def test_cache(servers):
+    ns4 = servers["ns4"]
+
+    # check that key id's are logged when dumping the cache
+    ns4.rndc("dumpdb -cache", log=False)
+    assert grep_q("; key id = ", "ns4/named_dump.db")
+
+    # check for RRSIG covered type in negative cache
+    assert grep_q("; example. RRSIG NSEC ", "ns4/named_dump.db")
+
+    # check validated data are not cached longer than originalttl
+    msg = isctest.query.create("a.ttlpatch.example", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.rr_count_eq(res1.answer, 2)
+    isctest.check.rr_count_eq(res2.answer, 2)
+    for rrset in res1.answer:
+        assert rrset.ttl <= 3600
+    for rrset in res2.answer:
+        assert rrset.ttl <= 300
+
+    # query for a record, then follow it with a query for the
+    # corresponding RRSIG, check that it's answered from the cache
+    msg = isctest.query.create("normalthenrrsig.secure.example", "A")
+    isctest.query.tcp(msg, "10.53.0.4")
+
+    msg = isctest.query.create("normalthenrrsig.secure.example", "RRSIG")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.noerror(res2)
+    isctest.check.raflag(res2)
+
+    # check direct query for RRSIG: if it's not cached with other records,
+    # it should result in an empty response.
+    msg = isctest.query.create("rrsigonly.secure.example", "RRSIG")
+    res1 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.empty_answer(res1)
+    isctest.check.noraflag(res1)
+
+    # check that a DNSKEY query with no data still gets cached
+    msg = isctest.query.create("insecure.example", "DNSKEY")
+    res1 = isctest.query.tcp(msg, "10.53.0.4")
+    time.sleep(1)  # give the TTL time to change
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    if res1.authority[0].ttl == res2.authority[0].ttl:
+        time.sleep(1)
+        res2 = isctest.query.tcp(msg, "10.53.0.4")
+        assert res1.authority[0].ttl != res2.authority[0].ttl
+
+
+def test_insecure_proof_nsec(servers):
+    # 1-server positive
+    msg = isctest.query.create("a.insecure.example", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.noerror(res2)
+    isctest.check.noadflag(res2)
+
+    # 1-server negative
+    msg = isctest.query.create("q.insecure.example", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.nxdomain(res2)
+    isctest.check.noadflag(res2)
+
+    # 1-server negative with SOA hack
+    msg = isctest.query.create("r.insecure.example", "SOA")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.nxdomain(res2)
+    isctest.check.noadflag(res2)
+    assert res2.authority[0].rdtype == rdatatype.SOA
+    assert res2.authority[0].ttl == 0
+
+    # 2-server positive
+    msg = isctest.query.create("a.insecure.secure.example", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.2")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.noerror(res2)
+    isctest.check.noadflag(res2)
+
+    # 2-server negative
+    msg = isctest.query.create("q.insecure.secure.example", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.2")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.nxdomain(res2)
+    isctest.check.noadflag(res2)
+
+    # 2-server negative with SOA hack
+    msg = isctest.query.create("r.insecure.secure.example", "SOA")
+    res1 = isctest.query.tcp(msg, "10.53.0.2")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.nxdomain(res2)
+    isctest.check.noadflag(res2)
+
+    # insecurity proof using negative cache
+    ns4 = servers["ns4"]
+    ns4.rndc("flush", log=False)
+    msg = isctest.query.create("insecure.example", "DS", cd=True)
+    isctest.query.tcp(msg, "10.53.0.4")
+
+    def query_and_check_nxdomain():
+        msg = isctest.query.create("nonexistent.insecure.example", "A")
+        res = isctest.query.tcp(msg, "10.53.0.4")
+        isctest.check.nxdomain(res)
+        return True
+
+    isctest.run.retry_with_timeout(query_and_check_nxdomain, 20)
+
+    # check insecure negative response with an unsigned NSEC
+    # first try the auth server...
+    msg = isctest.query.create("nsec-rrsigs-stripped", "TXT")
+    res1 = isctest.query.udp(msg, "10.53.0.10")
+    isctest.check.noerror(res1)
+    isctest.check.empty_answer(res1)
+    isctest.check.rr_count_eq(res1.authority, 2)
+    isctest.check.rr_count_eq(res1.additional, 0)
+    # make sure there's no RRSIG(NSEC)
+    for rrset in res1.authority:
+        assert rrset.rdtype != rdatatype.RRSIG or rrset.covers != rdatatype.NSEC
+    # now try the resolver
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_data(res1, res2)
+    isctest.check.noadflag(res2)
+
+
+def test_insecure_proof_nsec3():
+    # 1-server
+    msg = isctest.query.create("a.insecure.nsec3.example", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_data(res1, res2)
+    isctest.check.noerror(res2)
+    isctest.check.noadflag(res2)
+
+    # 1-server negative
+    msg = isctest.query.create("q.insecure.nsec3.example", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_data(res1, res2)
+    isctest.check.nxdomain(res2)
+    isctest.check.noadflag(res2)
+
+    # 1-server negative with SOA hack
+    msg = isctest.query.create("r.insecure.nsec3.example", "SOA")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_data(res1, res2)
+    isctest.check.nxdomain(res2)
+    isctest.check.noadflag(res2)
+    assert res2.authority[0].rdtype == rdatatype.SOA
+    assert res2.authority[0].ttl == 0
+
+
+def test_insecure_proof_optout():
+    # 1-server
+    msg = isctest.query.create("a.insecure.optout.example", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_data(res1, res2)
+    isctest.check.noerror(res2)
+    isctest.check.noadflag(res2)
+
+    # 1-server negative
+    msg = isctest.query.create("q.insecure.optout.example", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_data(res1, res2)
+    isctest.check.nxdomain(res2)
+    isctest.check.noadflag(res2)
+
+    # 1-server negative with SOA hack
+    msg = isctest.query.create("r.insecure.optout.example", "SOA")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_data(res1, res2)
+    isctest.check.nxdomain(res2)
+    isctest.check.noadflag(res2)
+    assert res2.authority[0].rdtype == rdatatype.SOA
+    assert res2.authority[0].ttl == 0
+
+
+def test_below_cname():
+    # check insecure zone below a cname resolves
+    msg = isctest.query.create("insecure.below-cname.example", "SOA")
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.noerror(res)
+    isctest.check.noadflag(res)
+    isctest.check.rr_count_eq(res.answer, 1)
+
+    # check secure zone below a cname resolves and validates
+    msg = isctest.query.create("secure.below-cname.example", "SOA")
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.noerror(res)
+    isctest.check.adflag(res)
+    isctest.check.rr_count_eq(res.answer, 2)
+
+
+@pytest.mark.parametrize(
+    "qname",
+    [
+        "a.secure.example",  # NSEC/NSEC
+        "a.nsec3.example",  # NSEC/NSEC3
+        "a.optout.example",  # NSEC/OPTOUT
+        "a.secure.nsec3.example",  # NSEC3/NSEC
+        "a.nsec3.nsec3.example",  # NSEC3/NSEC3
+        "a.optout.nsec3.example",  # NSEC3/OPTOUT
+        "a.secure.optout.example",  # OPTOUT/NSEC
+        "a.nsec3.optout.example",  # OPTOUT/NSEC3
+        "a.optout.optout.example",  # OPTOUT/OPTOUT
+    ],
+)
+def test_positive_validation_multistage(qname):
+    msg = isctest.query.create(qname, "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.noerror(res2)
+    isctest.check.adflag(res2)
+
+
+def test_validation_recovery(servers):
+    ns2 = servers["ns2"]
+    ns4 = servers["ns4"]
+
+    # check recovery from spoofed server address.
+    # prime cache with spoofed address records...
+    msg = isctest.query.create("target.peer-ns-spoof", "A", cd=True)
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.servfail(res)
+    ns4.rndc("dumpdb", log=False)
+    grep_q("10.53.0.100", "ns4/named_dump.db")
+
+    # then reload server with properly signed zone
+    shutil.copyfile(
+        "ns2/peer.peer-ns-spoof.db.next", "ns2/peer.peer-ns-spoof.db.signed"
+    )
+    with ns2.watch_log_from_here() as watcher:
+        ns2.rndc("reload peer.peer-ns-spoof", log=False)
+        watcher.wait_for_line("zone peer.peer-ns-spoof/IN: loaded serial 2000042408")
+
+    # and check we can resolve with the correct server address
+    msg = isctest.query.create("test.target.peer-ns-spoof", "TXT")
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.nxdomain(res)
+    isctest.check.adflag(res)
+
+    # check recovery from stripped DNSKEY RRSIG.
+    # prime cache with spoofed address records...
+    msg = isctest.query.create("dnskey-rrsigs-stripped", "DNSKEY", cd=True)
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.noerror(res)
+    isctest.check.rr_count_eq(res.answer, 2)
+
+    # then reload server with properly signed zone
+    shutil.copyfile(
+        "ns2/dnskey-rrsigs-stripped.db.next", "ns2/dnskey-rrsigs-stripped.db.signed"
+    )
+    with ns2.watch_log_from_here() as watcher:
+        ns2.rndc("reload dnskey-rrsigs-stripped", log=False)
+        watcher.wait_for_line(
+            "zone dnskey-rrsigs-stripped/IN: loaded serial 2000042408"
+        )
+
+    # and check we can now resolve with the correct server address
+    msg = isctest.query.create("b.dnskey-rrsigs-stripped", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.2")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.adflag(res2)
+
+    # check recovery from stripped DS RRSIG.
+    # prime cache with spoofed address records...
+    msg = isctest.query.create("child.ds-rrsigs-stripped", "DS", cd=True)
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.noerror(res)
+    isctest.check.rr_count_eq(res.answer, 1)
+
+    # then reload server with properly signed zone
+    shutil.copyfile(
+        "ns2/ds-rrsigs-stripped.db.next", "ns2/ds-rrsigs-stripped.db.signed"
+    )
+    with ns2.watch_log_from_here() as watcher:
+        ns2.rndc("reload ds-rrsigs-stripped", log=False)
+        watcher.wait_for_line("zone ds-rrsigs-stripped/IN: loaded serial 2000042408")
+
+    # and check we can now resolve with the correct server address
+    msg = isctest.query.create("b.child.ds-rrsigs-stripped", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.2")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.adflag(res2)
+
+    # check recovery with mismatching NS
+    ns4.rndc("flush", log=False)
+    msg = isctest.query.create("inconsistent", "NS", dnssec=False, cd=True)
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.noadflag(res)
+    isctest.check.rr_count_eq(res.answer, 1)
+    isctest.check.rr_count_eq(res.additional, 1)
+
+    msg = isctest.query.create("inconsistent", "NS", cd=True)
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.noadflag(res)
+    isctest.check.rr_count_eq(res.answer, 1)
+    isctest.check.rr_count_eq(res.additional, 1)
+
+    msg = isctest.query.create("inconsistent", "NS")
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.adflag(res)
+    isctest.check.rr_count_eq(res.answer, 3)
+    isctest.check.rr_count_eq(res.additional, 0)
+
+
+def test_failed_validation():
+    # bogus zone
+    msg = isctest.query.create("a.bogus.example", "A")
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.servfail(res)
+
+    # missing key record
+    msg = isctest.query.create("a.b.keyless.example", "A")
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.servfail(res)
+
+
+def test_revoked_key():
+    # validation should succeed if a revoked key is encountered
+    msg = isctest.query.create("revkey.example", "SOA")
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.noerror(res)
+    isctest.check.adflag(res)
+
+
+def test_standby_key():
+    # check that a secure chain with one active and one inactive KSK
+    # validates as secure
+    msg = isctest.query.create("a.lazy-ksk", "A")
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.noerror(res)
+    isctest.check.adflag(res)
+
+
+def test_transitions():
+    # check that a zone finishing transitioning from one algorithm
+    # to another validates secure
+    msg = isctest.query.create("algroll", "NS")
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.noerror(res)
+    isctest.check.adflag(res)
+
+    # check that validation yields insecure during transition to signed
+    msg = isctest.query.create("inprogress", "A")
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.noerror(res)
+    isctest.check.noadflag(res)
+    a, _ = res.answer
+    assert str(a[0]) == "10.53.0.10"
+
+
+def test_validating_forwarder(servers):
+    ns9 = servers["ns9"]
+    ns4 = servers["ns4"]
+
+    # check validating forwarder behavior with mismatching NS
+    ns4.rndc("flush", log=False)
+    msg = isctest.query.create("inconsistent", "NS", dnssec=False, cd=True)
+    res = isctest.query.tcp(msg, "10.53.0.9")
+    isctest.check.noerror(res)
+    isctest.check.rr_count_eq(res.answer, 1)
+    isctest.check.rr_count_eq(res.additional, 0)
+    isctest.check.noadflag(res)
+
+    msg = isctest.query.create("inconsistent", "NS", cd=True)
+    res = isctest.query.tcp(msg, "10.53.0.9")
+    isctest.check.rr_count_eq(res.additional, 0)
+    isctest.check.noadflag(res)
+    isctest.check.rr_count_eq(res.answer, 1)
+    isctest.check.rr_count_eq(res.authority, 0)
+    isctest.check.rr_count_eq(res.additional, 0)
+
+    msg.flags &= ~flags.CD
+    res = isctest.query.tcp(msg, "10.53.0.9")
+    isctest.check.rr_count_eq(res.answer, 3)
+    isctest.check.rr_count_eq(res.authority, 0)
+    isctest.check.rr_count_eq(res.additional, 0)
+    isctest.check.adflag(res)
+
+    # check validating forwarder sends CD to validate with a local trust anchor
+    ns4.rndc("flush", log=False)
+    msg = isctest.query.create("localkey.example", "SOA")
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.servfail(res)
+
+    with ns9.watch_log_from_here() as watcher:
+        res = isctest.query.tcp(msg, "10.53.0.9")
+        isctest.check.noerror(res)
+        isctest.check.adflag(res)
+        watcher.wait_for_line("status: SERVFAIL")
+
+
+def test_expired_signatures(servers):
+    # check expired signatures are still in place when updates are disabled
+    msg = isctest.query.create("expired.example", "SOA")
+    res = isctest.query.tcp(msg, "10.53.0.3")
+    soa, sigs = res.answer
+    assert sigs
+
+    # check expired signatures do not validate
+    msg = isctest.query.create("expired.example", "SOA")
+    res = isctest.query.tcp(msg, "10.53.0.3")
+    rrsig = res.get_rrset(
+        res.answer,
+        name.from_text("expired.example."),
+        rdataclass.IN,
+        rdatatype.RRSIG,
+        rdatatype.SOA,
+    )
+    assert rrsig, "expected RRSIG(SOA) missing from AUTHORITY: " + str(rrsig)
+    isctest.check.rr_count_eq(res.answer, 2)
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.servfail(res)
+    isctest.check.noadflag(res)
+    if hasattr(res, "extended_errors"):
+        assert res.extended_errors()[0].code == edns.EDECode.SIGNATURE_EXPIRED
+    assert grep_q("expired.example/.*: RRSIG has expired", "ns4/named.run")
+
+    # check future signatures do not validate
+    msg = isctest.query.create("future.example", "SOA")
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.servfail(res)
+    isctest.check.noadflag(res)
+    if hasattr(res, "extended_errors"):
+        assert res.extended_errors()[0].code == edns.EDECode.SIGNATURE_NOT_YET_VALID
+    assert grep_q(
+        "future.example/.*: RRSIG validity period has not begun", "ns4/named.run"
+    )
+
+    # check that a dynamic zone with future signatures is re-signed on load
+    msg = isctest.query.create("managed-future.example", "A")
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.adflag(res)
+    isctest.check.noerror(res)
+
+    ns4 = servers["ns4"]
+
+    # test TTL is capped at RRSIG expiry time
+    ns4.rndc("flush", log=False)
+    msg = isctest.query.create("expiring.example", "SOA", cd=True)
+    res1 = isctest.query.tcp(msg, "10.53.0.4")
+    msg = isctest.query.create("expiring.example", "SOA")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    for rrset in res1.answer:
+        assert rrset.ttl <= 3600
+    for rrset in res2.answer:
+        assert rrset.ttl <= 60
+
+    # test TTL is capped at RRSIG expiry time in the additional section (NS)
+    ns4.rndc("flush", log=False)
+    msg = isctest.query.create("expiring.example", "NS", cd=True)
+    res1 = isctest.query.tcp(msg, "10.53.0.4")
+    msg = isctest.query.create("expiring.example", "NS")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    for rrset in res1.additional:
+        assert rrset.ttl <= 3600
+    for rrset in res2.additional:
+        assert rrset.ttl <= 60
+
+    # test TTL is capped at RRSIG expiry time in the additional section (MX)
+    ns4.rndc("flush", log=False)
+    msg = isctest.query.create("expiring.example", "MX", cd=True)
+    res1 = isctest.query.tcp(msg, "10.53.0.4")
+    msg = isctest.query.create("expiring.example", "MX")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    for rrset in res1.additional:
+        assert rrset.ttl <= 3600
+    for rrset in res2.additional:
+        assert rrset.ttl <= 60
+
+
+def test_accept_expired(servers, templates):
+    ns4 = servers["ns4"]
+    templates.render("ns4/named.conf", {"accept_expired": True})
+    ns4.reconfigure(log=False)
+
+    # test TTL of about-to-expire rrsets with accept-expired
+    ns4.rndc("flush", log=False)
+    msg = isctest.query.create("expiring.example", "SOA")
+    msg.flags |= flags.CD
+    res1 = isctest.query.tcp(msg, "10.53.0.4")
+    msg = isctest.query.create("expiring.example", "SOA")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    for rrset in res1.answer:
+        assert rrset.ttl <= 3600
+    for rrset in res2.answer:
+        assert rrset.ttl <= 120
+
+    # test TTL is capped at RRSIG expiry time in the additional section
+    # with accept-expired
+    ns4.rndc("flush", log=False)
+    msg = isctest.query.create("expiring.example", "MX")
+    msg.flags |= flags.CD
+    res1 = isctest.query.tcp(msg, "10.53.0.4")
+    msg = isctest.query.create("expiring.example", "MX")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    for rrset in res1.additional:
+        assert rrset.ttl <= 3600
+    for rrset in res2.additional:
+        assert rrset.ttl <= 120
+
+    # test TTL of expired rrsets with accept-expired
+    ns4.rndc("flush", log=False)
+    msg = isctest.query.create("expired.example", "SOA")
+    msg.flags |= flags.CD
+    res1 = isctest.query.tcp(msg, "10.53.0.4")
+    msg = isctest.query.create("expired.example", "SOA")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    for rrset in res1.additional:
+        assert rrset.ttl <= 3600
+    for rrset in res2.additional:
+        assert rrset.ttl <= 120
+
+
+def test_casing():
+    # test legacy upper-case signer name validation
+    msg = isctest.query.create("upper.example", "SOA")
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.adflag(res)
+    _, sig = res.answer
+    assert sig.rdtype == rdatatype.RRSIG
+    assert sig.covers == rdatatype.SOA
+    assert "UPPER.EXAMPLE." in str(sig[0])
+
+    # test that we lower-case signer name
+    msg = isctest.query.create("LOWER.EXAMPLE", "SOA")
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    _, sig = res.answer
+    assert sig.rdtype == rdatatype.RRSIG
+    assert sig.covers == rdatatype.SOA
+    assert "lower.example." in str(sig[0])
+
+
+def test_broken_servers():
+    # check that a non-cacheable NODATA works
+    msg = isctest.query.create("a.nosoa.secure.example", "TXT")
+    res1 = isctest.query.tcp(msg, "10.53.0.6")
+    isctest.check.rr_count_eq(res1.authority, 0)
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.noerror(res2)
+
+    # check that a non-cacheable NXDOMAIN works
+    msg = isctest.query.create("b.nosoa.secure.example", "TXT")
+    res1 = isctest.query.tcp(msg, "10.53.0.6")
+    isctest.check.rr_count_eq(res1.authority, 0)
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.nxdomain(res2)
+
+    # check that split RRSIGs are handled
+    msg = isctest.query.create("split-rrsig", "SOA")
+    res = isctest.query.tcp(msg, "10.53.0.6")
+    soa, _ = res.answer
+    assert soa[0].serial > 1
+
+    # check that not-at-zone-apex RRSIG(SOA) rrsets are removed
+    msg = isctest.query.create("split-rrsig", "AXFR")
+    res = isctest.query.tcp(msg, "10.53.0.6")
+
+    nza = [
+        r
+        for r in res.answer
+        if str(r.name) == "not-at-zone-apex.split-rrsig."
+        and r.rdtype == rdatatype.RRSIG
+        and r.covers == rdatatype.SOA
+    ]
+    assert not nza
+
+    # check validation with missing nearest encloser proof
+    msg = isctest.query.create("b.c.d.optout-tld", "DS")
+    res = isctest.query.tcp(msg, "10.53.0.6")
+    nsec3s = [a for a in res.authority if a.rdtype == rdatatype.NSEC3]
+    assert len(nsec3s) == 2
+
+    msg = isctest.query.create("b.c.d.optout-tld", "A")
+    res = isctest.query.tcp(msg, "10.53.0.6")
+    nsec3s = [a for a in res.authority if a.rdtype == rdatatype.NSEC3]
+    assert len(nsec3s) == 1
+
+    res = isctest.query.tcp(msg, "10.53.0.6")
+    isctest.check.noerror(res)
+    isctest.check.noadflag(res)
+    msg = isctest.query.create("optout-tld", "SOA")
+    res = isctest.query.tcp(msg, "10.53.0.6")
+    isctest.check.noadflag(res)
+
+
+def test_pending_ds():
+    # check that a query against a validating resolver succeeds when there is
+    # a negative cache entry with trust level "pending" for the DS.  prime
+    # with a +cd DS query to produce the negative cache entry, then send a
+    # query that uses that entry as part of the validation process.
+    ns4.rndc("flush", log=False)
+    msg = isctest.query.create("insecure.example", "DS", cd=True)
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.noerror(res)
+    isctest.check.rr_count_eq(res.authority, 4)
+    msg = isctest.query.create("a.insecure.example", "A")
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.noerror(res)
+    isctest.check.rr_count_eq(res.answer, 1)
+    isctest.check.rr_count_eq(res.authority, 1)
+    isctest.check.noadflag(res)
+
+
+def test_trust_anchors(servers, templates):
+    # DNSSEC tests related to unsupported, disabled and revoked trust anchors.
+    #
+    # This nameserver is loaded with a bunch of trust anchors.
+    # Some of them are good (enabled.managed, enabled.trusted,
+    # secure.managed, secure.trusted), and some of them are bad
+    # (disabled.managed, revoked.managed, unsupported.managed,
+    # disabled.trusted, revoked.trusted, unsupported.trusted).  Make sure
+    # that the bad trust anchors are ignored.  This is tested by looking
+    # for the corresponding lines in the logfile.
+    ns5 = servers["ns5"]
+    templates.render("ns5/named.conf", {"many_anchors": True})
+    ns5.reconfigure(log=False)
+
+    # check that keys with unsupported/disabled algorithms are ignored
+    grep_q(
+        "ignoring static-key for 'disabled.trusted.': algorithm is disabled",
+        "ns5/named.run",
+    )
+    grep_q(
+        "ignoring static-key for 'disabled.managed.': algorithm is disabled",
+        "ns5/named.run",
+    )
+    grep_q(
+        "ignoring static-key for 'unsupported.trusted.': algorithm is unsupported",
+        "ns5/named.run",
+    )
+    grep_q(
+        "ignoring static-key for 'unsupported.managed.': algorithm is unsupported",
+        "ns5/named.run",
+    )
+    grep_q("ignoring static-key for 'revoked.trusted.': bad key type", "ns5/named.run")
+    grep_q("ignoring static-key for 'revoked.managed.': bad key type", "ns5/named.run")
+
+    # check that a key with supported algorithm validates as secure
+    msg = isctest.query.create("a.secure.trusted", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.5")
+    isctest.check.noerror(res1)
+    isctest.check.noerror(res2)
+    isctest.check.adflag(res2)
+    if hasattr(res2, "extended_errors"):
+        assert not res2.extended_errors()
+
+    msg = isctest.query.create("a.secure.managed", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.5")
+    isctest.check.noerror(res1)
+    isctest.check.noerror(res2)
+    isctest.check.adflag(res2)
+    if hasattr(res2, "extended_errors"):
+        assert not res2.extended_errors()
+
+    # check that an unsupported signing algorithm yields insecure
+    msg = isctest.query.create("a.unsupported.trusted", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.5")
+    isctest.check.noerror(res1)
+    if hasattr(res2, "extended_errors"):
+        assert (
+            res2.extended_errors()[0].code == edns.EDECode.UNSUPPORTED_DNSKEY_ALGORITHM
+        )
+    isctest.check.noerror(res2)
+    isctest.check.noadflag(res2)
+
+    msg = isctest.query.create("a.unsupported.managed", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.5")
+    isctest.check.noerror(res1)
+    if hasattr(res2, "extended_errors"):
+        assert (
+            res2.extended_errors()[0].code == edns.EDECode.UNSUPPORTED_DNSKEY_ALGORITHM
+        )
+    isctest.check.noerror(res2)
+    isctest.check.noadflag(res2)
+
+    # check that a disabled signing algorithm yields insecure
+    msg = isctest.query.create("a.disabled.trusted", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.5")
+    isctest.check.noerror(res1)
+    isctest.check.noerror(res2)
+    isctest.check.noadflag(res2)
+
+    msg = isctest.query.create("a.disabled.managed", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.5")
+    isctest.check.noerror(res1)
+    isctest.check.noerror(res2)
+    isctest.check.noadflag(res2)
+
+    # check that zone signed with an algorithm that's disabled for
+    # some other domain, but not for this one, validates as secure.
+    # "enabled.trusted." and "enabled.managed." do not match the
+    # "disable-algorithms" option, so no special rules apply. (static)
+    msg = isctest.query.create("a.enabled.trusted", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.5")
+    isctest.check.noerror(res1)
+    isctest.check.noerror(res2)
+    isctest.check.adflag(res2)
+
+    msg = isctest.query.create("a.enabled.managed", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.5")
+    isctest.check.noerror(res1)
+    isctest.check.noerror(res2)
+    isctest.check.adflag(res2)
+
+    # a revoked trust anchor is ignored when configured; check that
+    # this yields insecure.
+    msg = isctest.query.create("a.revoked.trusted", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.5")
+    isctest.check.noerror(res1)
+    isctest.check.noerror(res2)
+    isctest.check.noadflag(res2)
+
+    msg = isctest.query.create("a.revoked.managed", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.5")
+    isctest.check.noerror(res1)
+    isctest.check.noerror(res2)
+    isctest.check.noadflag(res2)
+
+
+def test_unknown_algorithms():
+    # check that unknown DNSKEY algorithm validates as insecure
+    msg = isctest.query.create("dnskey-unknown.example", "A", dnssec=False)
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.noerror(res1)
+    isctest.check.noerror(res2)
+    isctest.check.noadflag(res2)
+
+    # check that unsupported DNSKEY algorithms are in the DNSKEY RRsets
+    msg = isctest.query.create("dnskey-unsupported.example", "DNSKEY")
+    res = isctest.query.tcp(msg, "10.53.0.3")
+    isctest.check.noerror(res)
+
+    msg = isctest.query.create("dnskey-unsupported-2.example", "DNSKEY")
+    res = isctest.query.tcp(msg, "10.53.0.3")
+    isctest.check.noerror(res)
+    rrsets = [str(r) for r in res.answer]
+    assert any("257 3 255" in r for r in rrsets)
+
+    # check that unsupported DNSKEY algorithm validates as insecure
+    msg = isctest.query.create("dnskey-unsupported.example", "DNSKEY")
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.noerror(res)
+    isctest.check.noadflag(res)
+    if hasattr(res, "extended_errors"):
+        assert (
+            res.extended_errors()[0].code == edns.EDECode.UNSUPPORTED_DNSKEY_ALGORITHM
+        )
+
+    # check that DNSKEY with an unsupported reserve key validates
+    msg = isctest.query.create("dnskey-unsupported-2.example", "DNSKEY")
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.noerror(res)
+    isctest.check.adflag(res)
+
+    # check EDE code 2 for unsupported DS digest algorithm
+    msg = isctest.query.create("a.ds-unsupported.example", "A")
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    if hasattr(res, "extended_errors"):
+        assert res.extended_errors()[0].code == edns.EDECode.UNSUPPORTED_DS_DIGEST_TYPE
+
+    # check EDE code 1 for bad algorithm mnemonic
+    msg = isctest.query.create("badalg.secure.example", "A")
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.noadflag(res)
+    if hasattr(res, "extended_errors"):
+        assert (
+            res.extended_errors()[0].code == edns.EDECode.UNSUPPORTED_DNSKEY_ALGORITHM
+        )
+
+    # check both EDE code 1 and 2 for unsupported digest on one DNSKEY
+    # and unsupported algorithm on the other
+    msg = isctest.query.create("a.digest-alg-unsupported.example", "A")
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.noadflag(res)
+    if hasattr(res, "extended_errors"):
+        codes = {ede.code for ede in res.extended_errors()}
+        assert edns.EDECode.UNSUPPORTED_DNSKEY_ALGORITHM in codes
+        assert edns.EDECode.UNSUPPORTED_DS_DIGEST_TYPE in codes
+
+    # check that unknown DNSKEY algorithm + unknown NSEC3 hash algorithm
+    # validates as insecure
+    msg = isctest.query.create("dnskey-nsec3-unknown.example", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.noerror(res1)
+    isctest.check.noerror(res2)
+    isctest.check.noadflag(res2)
+
+
+###################################
+##### BEGIN MANAGED KEY TESTS #####
+###################################
+def test_switch_managed(servers, templates):
+    # switch to intializing trust anchor instead of static
+    ns4 = servers["ns4"]
+    assert os.path.exists("ns4/managed-keys.bind.jnl") is False
+    shutil.copyfile("ns4/managed-keys.bind.in", "ns4/managed-keys.bind")
+    templates.render("ns4/named.conf", {"managed_key": True})
+    ns4.reconfigure(log=False)
+
+
+def test_secure_root_managed(servers):
+    # check that a query for a secure root validates
+    msg = isctest.query.create(".", "KEY")
+    res = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.noerror(res)
+    isctest.check.adflag(res)
+
+    # check that "rndc secroots" dumps the trusted keys
+    ns4 = servers["ns4"]
+    key = int(getfrom("ns1/managed.key.id"))
+    alg = os.environ["DEFAULT_ALGORITHM"]
+    expected = f"./{alg}/{key} ; managed"
+    response = ns4.rndc("secroots -", log=False).splitlines()
+    assert expected in response
+    assert len(response) == 10
+
+
+def test_positive_validation_nsec_managed():
+    msg = isctest.query.create("a.example", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.2")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.adflag(res2)
+
+
+def test_positive_validation_nsec3_managed():
+    msg = isctest.query.create("a.nsec3.example", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.noerror(res2)
+    isctest.check.adflag(res2)
+
+
+def test_positive_validation_optout_managed():
+    msg = isctest.query.create("a.optout.example", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.3")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.adflag(res2)
+
+
+def test_negative_validation_nsec_managed():
+    # nxdomain
+    msg = isctest.query.create("q.example", "A")
+    res1 = isctest.query.tcp(msg, "10.53.0.2")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_answer(res1, res2)
+    isctest.check.nxdomain(res2)
+    isctest.check.adflag(res2)
+
+
+def test_ds_managed():
+    # check root DS queries validate
+    msg = isctest.query.create(".", "DS")
+    res1 = isctest.query.tcp(msg, "10.53.0.1")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_data(res1, res2)
+    isctest.check.adflag(res2)
+    isctest.check.noerror(res2)
+
+    # check DS queries succeed at RFC 1918 empty zone
+    msg = isctest.query.create("10.in-addr.arpa", "DS")
+    res1 = isctest.query.tcp(msg, "10.53.0.2")
+    res2 = isctest.query.tcp(msg, "10.53.0.4")
+    isctest.check.same_data(res1, res2)
+    isctest.check.noerror(res2)
+
+
+def test_keydata_storage(servers):
+    ns4 = servers["ns4"]
+    ns4.rndc("managed-keys sync", log=False)
+    with isctest.log.WatchLogFromStart("ns4/managed-keys.bind") as watcher:
+        watcher.wait_for_line(["KEYDATA", "next refresh:"])
+
+
+############################################
+##### BEGIN MULTIVIEW VALIDATION TESTS #####
+############################################
+def test_insecure_staticstub_delegation(servers, templates):
+    ns4 = servers["ns4"]
+    templates.render("ns4/named.conf", {"multi_view": True})
+    ns4.reconfigure(log=False)
+
+    # check insecure delegation between static-stub zones
+    msg = isctest.query.create("insecure.secure.example", "NS")
+    for _ in range(5):
+        res = isctest.query.tcp(msg, "10.53.0.4")
+        if res.rcode() == rcode.SERVFAIL:
+            time.sleep(1)
+            continue
+    isctest.check.noerror(res)
+
+    msg = isctest.query.create("secure.example", "NS")
+    for _ in range(5):
+        res = isctest.query.tcp(msg, "10.53.0.4")
+        if res.rcode() == rcode.SERVFAIL:
+            time.sleep(1)
+            continue
+    isctest.check.noerror(res)
index 0961d9bb141bc31dfec295f01555af97e3f00c47..97a03d217af9b1279ec7234762d6a8cae3f4e46f 100644 (file)
@@ -42,6 +42,15 @@ def feature_test(feature):
     return True
 
 
+rsasha1 = pytest.mark.skipif(not feature_test("--rsasha1"), reason="RSASHA1 disabled")
+
+
+extended_ds_digest = pytest.mark.skipif(
+    not feature_test("--extended-ds-digest"),
+    reason="extended DS digest algorithms disabled",
+)
+
+
 def is_host_freebsd_13(*_):
     return platform.system() == "FreeBSD" and platform.release().startswith("13")
 
index a82bc98f22d9c1006c5e9b87d7fcb996dcfb5a6b..79757fc0a7e34fde22982df8757690125b3447b9 100755 (executable)
@@ -264,8 +264,8 @@ sub construct_ns_command {
 
                foreach my $t_option(
                        "dropedns", "ednsformerr", "ednsnotimp", "ednsrefused",
-                       "cookiealwaysvalid", "noaa", "noedns", "nosoa",
-                       "maxudp512", "maxudp1460",
+                       "cookiealwaysvalid", "noaa", "noedns", "nonearest",
+                       "nosoa", "maxudp512", "maxudp1460", "tat=1", "tat=3"
                    ) {
                        if (-e "$testdir/$server/named.$t_option") {
                                $command .= "-T $t_option "