]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
add max-delegation-servers tests for out domain NS
authorColin Vidal <colin@isc.org>
Wed, 1 Apr 2026 20:31:50 +0000 (22:31 +0200)
committerMichał Kępień <michal@isc.org>
Thu, 7 May 2026 11:32:15 +0000 (13:32 +0200)
Add a new system test which ensures that the `max-delegation-servers`
limit is correctly respected also in the case a domain has only NS names
(and no glues). In particular, this test when there are multiple NS
names and multiples IPs per names.

If the number of IP (even from the first picked NS name) reaches
`max-delegation-servers`, and the resolution is not a success, the
resolver won't attempt another NS name, as it already used all its
"credit".

12 files changed:
bin/tests/system/nslimit_outdomain/ns1/named.conf.j2 [new file with mode: 0644]
bin/tests/system/nslimit_outdomain/ns1/root.db [new file with mode: 0644]
bin/tests/system/nslimit_outdomain/ns2/dnshoster.tld.db [new file with mode: 0644]
bin/tests/system/nslimit_outdomain/ns2/named.conf.j2 [new file with mode: 0644]
bin/tests/system/nslimit_outdomain/ns2/tld.db [new file with mode: 0644]
bin/tests/system/nslimit_outdomain/ns3/example.tld.db [new file with mode: 0644]
bin/tests/system/nslimit_outdomain/ns3/example4.tld.db [new file with mode: 0644]
bin/tests/system/nslimit_outdomain/ns3/named.conf.j2 [new file with mode: 0644]
bin/tests/system/nslimit_outdomain/ns4/named.args.j2 [new file with mode: 0644]
bin/tests/system/nslimit_outdomain/ns4/named.conf.j2 [new file with mode: 0644]
bin/tests/system/nslimit_outdomain/ns4/root.hint [new file with mode: 0644]
bin/tests/system/nslimit_outdomain/tests_nslimit_outdomain.py [new file with mode: 0644]

diff --git a/bin/tests/system/nslimit_outdomain/ns1/named.conf.j2 b/bin/tests/system/nslimit_outdomain/ns1/named.conf.j2
new file mode 100644 (file)
index 0000000..fd83fc3
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+options {
+       query-source address 10.53.0.1;
+       notify-source 10.53.0.1;
+       transfer-source 10.53.0.1;
+       port @PORT@;
+       pid-file "named.pid";
+       listen-on { 10.53.0.1; };
+       recursion no;
+       dnssec-validation no;
+};
+
+zone "." {
+       type primary;
+       file "root.db";
+};
diff --git a/bin/tests/system/nslimit_outdomain/ns1/root.db b/bin/tests/system/nslimit_outdomain/ns1/root.db
new file mode 100644 (file)
index 0000000..bfbf049
--- /dev/null
@@ -0,0 +1,24 @@
+; 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.
+
+$TTL 300
+.                      IN SOA  owner.root-servers.nil. a.root.servers.nil. (
+                               2010    ; serial
+                               600             ; refresh
+                               600             ; retry
+                               1200            ; expire
+                               600             ; minimum
+                               )
+.                      NS      a.root-servers.nil.
+a.root-servers.nil.    A       10.53.0.1
+
+tld.                   NS      ns.tld.
+ns.tld.                A       10.53.0.2
diff --git a/bin/tests/system/nslimit_outdomain/ns2/dnshoster.tld.db b/bin/tests/system/nslimit_outdomain/ns2/dnshoster.tld.db
new file mode 100644 (file)
index 0000000..9540da4
--- /dev/null
@@ -0,0 +1,33 @@
+; 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.
+
+$TTL 300
+dnshoster.tld.         IN SOA  owner.tld. ns.tld. (
+                               2010    ; serial
+                               600             ; refresh
+                               600             ; retry
+                               1200            ; expire
+                               600             ; minimum
+                               )
+
+dnshoster.tld.         NS      ns1.dnshoster.tld.
+ns1.dnshoster.tld.     A       10.53.0.5
+ns1.dnshoster.tld.     A       10.53.0.6
+
+dnshoster.tld.         NS      ns2.dnshoster.tld.
+ns2.dnshoster.tld.     A       10.53.1.1
+ns2.dnshoster.tld.     A       10.53.1.2
+
+dnshoster.tld.         NS      ns3.dnshoster.tld.
+ns3.dnshoster.tld.     A       10.53.2.1
+ns3.dnshoster.tld.     A       10.53.2.2
+
+
diff --git a/bin/tests/system/nslimit_outdomain/ns2/named.conf.j2 b/bin/tests/system/nslimit_outdomain/ns2/named.conf.j2
new file mode 100644 (file)
index 0000000..037ac60
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+options {
+       query-source address 10.53.0.2;
+       notify-source 10.53.0.2;
+       transfer-source 10.53.0.2;
+       port @PORT@;
+       pid-file "named.pid";
+       listen-on {
+               10.53.0.2;
+               10.53.0.5;
+               10.53.0.6;
+               10.53.0.7;
+               10.53.1.1;
+               10.53.1.2;
+               10.53.2.2;
+       };
+       recursion no;
+       dnssec-validation no;
+};
+
+zone "tld." {
+       type primary;
+       file "tld.db";
+};
+
+zone "dnshoster.tld." {
+       type primary;
+       file "dnshoster.tld.db";
+};
diff --git a/bin/tests/system/nslimit_outdomain/ns2/tld.db b/bin/tests/system/nslimit_outdomain/ns2/tld.db
new file mode 100644 (file)
index 0000000..e29bf91
--- /dev/null
@@ -0,0 +1,36 @@
+; 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.
+
+$TTL 300
+tld.                   IN SOA  owner.tld. ns.tld. (
+                               2010    ; serial
+                               600             ; refresh
+                               600             ; retry
+                               1200            ; expire
+                               600             ; minimum
+                               )
+tld.                   NS      ns.tld.
+ns.tld.                A       10.53.0.2
+
+example4.tld.          NS      ns.example4.tld.
+ns.example4.tld.       A       10.53.0.3
+
+dnshoster.tld.         NS      ns1.dnshoster.tld.
+ns1.dnshoster.tld.     A       10.53.0.5
+ns1.dnshoster.tld.     A       10.53.0.6
+
+dnshoster.tld.         NS      ns2.dnshoster.tld.
+ns2.dnshoster.tld.     A       10.53.1.1
+ns2.dnshoster.tld.     A       10.53.1.2
+
+dnshoster.tld.         NS      ns3.dnshoster.tld.
+ns3.dnshoster.tld.     A       10.53.2.1
+ns3.dnshoster.tld.     A       10.53.2.2
diff --git a/bin/tests/system/nslimit_outdomain/ns3/example.tld.db b/bin/tests/system/nslimit_outdomain/ns3/example.tld.db
new file mode 100644 (file)
index 0000000..2a599ee
--- /dev/null
@@ -0,0 +1,184 @@
+; 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.
+
+$TTL 300
+example.tld.           IN SOA  owner.dnshoster.tld. ns.dnshoster.tld. (
+                               2010    ; serial
+                               600             ; refresh
+                               600             ; retry
+                               1200            ; expire
+                               600             ; minimum
+                               )
+
+example.tld.                   NS      ns.example.tld.
+ns.example.tld.                        A       10.53.0.3
+
+sub.example.tld.               NS      ns01.sub.example.tld.
+sub.example.tld.               NS      ns02.sub.example.tld.
+sub.example.tld.               NS      ns03.sub.example.tld.
+sub.example.tld.               NS      ns04.sub.example.tld.
+sub.example.tld.               NS      ns05.sub.example.tld.
+sub.example.tld.               NS      ns06.sub.example.tld.
+sub.example.tld.               NS      ns07.sub.example.tld.
+sub.example.tld.               NS      ns08.sub.example.tld.
+sub.example.tld.               NS      ns09.sub.example.tld.
+sub.example.tld.               NS      ns10.sub.example.tld.
+
+ns01.sub.example.tld.          A       10.53.0.5
+ns01.sub.example.tld.          A       10.53.0.6
+ns01.sub.example.tld.          A       10.53.0.7
+ns01.sub.example.tld.          A       10.53.0.8
+ns01.sub.example.tld.          A       10.53.0.9
+ns01.sub.example.tld.          A       10.53.0.10
+ns01.sub.example.tld.          A       10.53.1.1
+ns01.sub.example.tld.          A       10.53.1.2
+ns01.sub.example.tld.          A       10.53.2.1
+ns01.sub.example.tld.          A       10.53.0.3
+ns01.sub.example.tld.          A       127.0.0.1
+ns01.sub.example.tld.          A       127.0.0.2
+; Those addresses won't be used (exceed the max-delegation-servers).
+ns01.sub.example.tld.          A       127.0.0.3
+ns01.sub.example.tld.          A       127.0.0.4
+
+ns02.sub.example.tld.          A       10.53.0.5
+ns02.sub.example.tld.          A       10.53.0.6
+ns02.sub.example.tld.          A       10.53.0.7
+ns02.sub.example.tld.          A       10.53.0.8
+ns02.sub.example.tld.          A       10.53.0.9
+ns02.sub.example.tld.          A       10.53.0.10
+ns02.sub.example.tld.          A       10.53.1.1
+ns02.sub.example.tld.          A       10.53.1.2
+ns02.sub.example.tld.          A       10.53.2.1
+ns02.sub.example.tld.          A       10.53.0.3
+ns02.sub.example.tld.          A       127.0.0.1
+ns02.sub.example.tld.          A       127.0.0.2
+ns02.sub.example.tld.          A       127.0.0.3
+ns02.sub.example.tld.          A       127.0.0.4
+
+ns03.sub.example.tld.          A       10.53.0.5
+ns03.sub.example.tld.          A       10.53.0.6
+ns03.sub.example.tld.          A       10.53.0.7
+ns03.sub.example.tld.          A       10.53.0.8
+ns03.sub.example.tld.          A       10.53.0.9
+ns03.sub.example.tld.          A       10.53.0.10
+ns03.sub.example.tld.          A       10.53.1.1
+ns03.sub.example.tld.          A       10.53.1.2
+ns03.sub.example.tld.          A       10.53.2.1
+ns03.sub.example.tld.          A       10.53.0.3
+ns03.sub.example.tld.          A       127.0.0.1
+ns03.sub.example.tld.          A       127.0.0.2
+ns03.sub.example.tld.          A       127.0.0.3
+ns03.sub.example.tld.          A       127.0.0.4
+
+ns04.sub.example.tld.          A       10.53.0.5
+ns04.sub.example.tld.          A       10.53.0.6
+ns04.sub.example.tld.          A       10.53.0.7
+ns04.sub.example.tld.          A       10.53.0.8
+ns04.sub.example.tld.          A       10.53.0.9
+ns04.sub.example.tld.          A       10.53.0.10
+ns04.sub.example.tld.          A       10.53.1.1
+ns04.sub.example.tld.          A       10.53.1.2
+ns04.sub.example.tld.          A       10.53.2.1
+ns04.sub.example.tld.          A       10.53.0.3
+ns04.sub.example.tld.          A       127.0.0.1
+ns04.sub.example.tld.          A       127.0.0.2
+ns04.sub.example.tld.          A       127.0.0.3
+ns04.sub.example.tld.          A       127.0.0.4
+
+ns05.sub.example.tld.          A       10.53.0.5
+ns05.sub.example.tld.          A       10.53.0.6
+ns05.sub.example.tld.          A       10.53.0.7
+ns05.sub.example.tld.          A       10.53.0.8
+ns05.sub.example.tld.          A       10.53.0.9
+ns05.sub.example.tld.          A       10.53.0.10
+ns05.sub.example.tld.          A       10.53.1.1
+ns05.sub.example.tld.          A       10.53.1.2
+ns05.sub.example.tld.          A       10.53.2.1
+ns05.sub.example.tld.          A       10.53.0.3
+ns05.sub.example.tld.          A       127.0.0.1
+ns05.sub.example.tld.          A       127.0.0.2
+ns05.sub.example.tld.          A       127.0.0.3
+ns05.sub.example.tld.          A       127.0.0.4
+
+ns06.sub.example.tld.          A       10.53.0.5
+ns06.sub.example.tld.          A       10.53.0.6
+ns06.sub.example.tld.          A       10.53.0.7
+ns06.sub.example.tld.          A       10.53.0.8
+ns06.sub.example.tld.          A       10.53.0.9
+ns06.sub.example.tld.          A       10.53.0.10
+ns06.sub.example.tld.          A       10.53.1.1
+ns06.sub.example.tld.          A       10.53.1.2
+ns06.sub.example.tld.          A       10.53.2.1
+ns06.sub.example.tld.          A       10.53.0.3
+ns06.sub.example.tld.          A       127.0.0.1
+ns06.sub.example.tld.          A       127.0.0.2
+ns06.sub.example.tld.          A       127.0.0.3
+ns06.sub.example.tld.          A       127.0.0.4
+
+ns07.sub.example.tld.          A       10.53.0.5
+ns07.sub.example.tld.          A       10.53.0.6
+ns07.sub.example.tld.          A       10.53.0.7
+ns07.sub.example.tld.          A       10.53.0.8
+ns07.sub.example.tld.          A       10.53.0.9
+ns07.sub.example.tld.          A       10.53.0.10
+ns07.sub.example.tld.          A       10.53.1.1
+ns07.sub.example.tld.          A       10.53.1.2
+ns07.sub.example.tld.          A       10.53.2.1
+ns07.sub.example.tld.          A       10.53.0.3
+ns07.sub.example.tld.          A       127.0.0.1
+ns07.sub.example.tld.          A       127.0.0.2
+ns07.sub.example.tld.          A       127.0.0.3
+ns07.sub.example.tld.          A       127.0.0.4
+
+ns08.sub.example.tld.          A       10.53.0.5
+ns08.sub.example.tld.          A       10.53.0.6
+ns08.sub.example.tld.          A       10.53.0.7
+ns08.sub.example.tld.          A       10.53.0.8
+ns08.sub.example.tld.          A       10.53.0.9
+ns08.sub.example.tld.          A       10.53.0.10
+ns08.sub.example.tld.          A       10.53.1.1
+ns08.sub.example.tld.          A       10.53.1.2
+ns08.sub.example.tld.          A       10.53.2.1
+ns08.sub.example.tld.          A       10.53.0.3
+ns08.sub.example.tld.          A       127.0.0.1
+ns08.sub.example.tld.          A       127.0.0.2
+ns08.sub.example.tld.          A       127.0.0.3
+ns08.sub.example.tld.          A       127.0.0.4
+
+ns09.sub.example.tld.          A       10.53.0.5
+ns09.sub.example.tld.          A       10.53.0.6
+ns09.sub.example.tld.          A       10.53.0.7
+ns09.sub.example.tld.          A       10.53.0.8
+ns09.sub.example.tld.          A       10.53.0.9
+ns09.sub.example.tld.          A       10.53.0.10
+ns09.sub.example.tld.          A       10.53.1.1
+ns09.sub.example.tld.          A       10.53.1.2
+ns09.sub.example.tld.          A       10.53.2.1
+ns09.sub.example.tld.          A       10.53.0.3
+ns09.sub.example.tld.          A       127.0.0.1
+ns09.sub.example.tld.          A       127.0.0.2
+ns09.sub.example.tld.          A       127.0.0.3
+ns09.sub.example.tld.          A       127.0.0.4
+
+ns10.sub.example.tld.          A       10.53.0.5
+ns10.sub.example.tld.          A       10.53.0.6
+ns10.sub.example.tld.          A       10.53.0.7
+ns10.sub.example.tld.          A       10.53.0.8
+ns10.sub.example.tld.          A       10.53.0.9
+ns10.sub.example.tld.          A       10.53.0.10
+ns10.sub.example.tld.          A       10.53.1.1
+ns10.sub.example.tld.          A       10.53.1.2
+ns10.sub.example.tld.          A       10.53.2.1
+ns10.sub.example.tld.          A       10.53.0.3
+ns10.sub.example.tld.          A       127.0.0.1
+ns10.sub.example.tld.          A       127.0.0.2
+ns10.sub.example.tld.          A       127.0.0.3
+ns10.sub.example.tld.          A       127.0.0.4
diff --git a/bin/tests/system/nslimit_outdomain/ns3/example4.tld.db b/bin/tests/system/nslimit_outdomain/ns3/example4.tld.db
new file mode 100644 (file)
index 0000000..f1c64be
--- /dev/null
@@ -0,0 +1,24 @@
+; 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.
+
+$TTL 300
+example4.tld.          IN SOA  owner.dnshoster.tld. ns.dnshoster.tld. (
+                               2010    ; serial
+                               600             ; refresh
+                               600             ; retry
+                               1200            ; expire
+                               600             ; minimum
+                               )
+
+example4.tld.          NS      ns.example4.tld.
+ns.example4.tld.       A       10.53.0.3
+sub.example4.tld.      NS      ns1.dnshoster.tld.
+sub.example4.tld.      NS      ns2.dnshoster.tld.
diff --git a/bin/tests/system/nslimit_outdomain/ns3/named.conf.j2 b/bin/tests/system/nslimit_outdomain/ns3/named.conf.j2
new file mode 100644 (file)
index 0000000..f7e03a8
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+options {
+       query-source address 10.53.0.3;
+       notify-source 10.53.0.3;
+       transfer-source 10.53.0.3;
+       port @PORT@;
+       pid-file "named.pid";
+       listen-on {
+               10.53.0.3;
+       };
+       recursion no;
+       dnssec-validation no;
+};
+
+zone "example4.tld." {
+       type primary;
+       file "example4.tld.db";
+};
diff --git a/bin/tests/system/nslimit_outdomain/ns4/named.args.j2 b/bin/tests/system/nslimit_outdomain/ns4/named.args.j2
new file mode 100644 (file)
index 0000000..68f1511
--- /dev/null
@@ -0,0 +1 @@
+-D selfpointedglue-ns4 -m record -c named.conf -d 99 -g -T maxcachesize=2097152 -4
diff --git a/bin/tests/system/nslimit_outdomain/ns4/named.conf.j2 b/bin/tests/system/nslimit_outdomain/ns4/named.conf.j2
new file mode 100644 (file)
index 0000000..09fbdd4
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+{% set maxdelegationservers = maxdelegationservers | default(None) %}
+
+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; };
+       recursion yes;
+       dnssec-validation no;
+       dnstap { resolver query; };
+       dnstap-output file "dnstap.out";
+       {% if maxdelegationservers %}
+        @maxdelegationservers@
+       {% endif %}
+};
+
+/*
+ * Forcing TCP ensures that ADDITIONAL won't be truncated (responses won't have
+ * the TC flag, hence the resolver won't retry using TCP by itself, see
+ * https://datatracker.ietf.org/doc/html/rfc2181#section-9)
+ */
+server 10.53.0.3 { tcp-only true; };
+server 10.53.0.5 { tcp-only true; };
+server 10.53.0.6 { tcp-only true; };
+server 10.53.0.7 { tcp-only true; };
+server 10.53.0.8 { tcp-only true; };
+server 10.53.0.9 { tcp-only true; };
+server 10.53.0.10 { tcp-only true; };
+server 10.53.1.1 { tcp-only true; };
+server 10.53.1.2 { tcp-only true; };
+server 10.53.2.1 { tcp-only true; };
+
+zone "." {
+       type hint;
+       file "root.hint";
+};
+
+key rndc_key {
+       secret "1234abcd8765";
+       algorithm @DEFAULT_HMAC@;
+};
+
+controls {
+       inet 10.53.0.4 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
+};
diff --git a/bin/tests/system/nslimit_outdomain/ns4/root.hint b/bin/tests/system/nslimit_outdomain/ns4/root.hint
new file mode 100644 (file)
index 0000000..d7d0e1f
--- /dev/null
@@ -0,0 +1,14 @@
+; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+;
+; SPDX-License-Identifier: MPL-2.0
+;
+; This Source Code Form is subject to the terms of the Mozilla Public
+; License, v. 2.0.  If a copy of the MPL was not distributed with this
+; file, you can obtain one at https://mozilla.org/MPL/2.0/.
+;
+; See the COPYRIGHT file distributed with this work for additional
+; information regarding copyright ownership.
+
+$TTL 999999
+.                       IN NS          a.root-servers.nil.
+a.root-servers.nil.     IN A           10.53.0.1
diff --git a/bin/tests/system/nslimit_outdomain/tests_nslimit_outdomain.py b/bin/tests/system/nslimit_outdomain/tests_nslimit_outdomain.py
new file mode 100644 (file)
index 0000000..228fb07
--- /dev/null
@@ -0,0 +1,120 @@
+# 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 isctest
+import isctest.mark
+
+pytestmark = [isctest.mark.with_dnstap]
+
+
+def line_to_ips_and_queries(line):
+    # dnstap-read output line example
+    # 05-Feb-2026 11:00:57.853 RQ 10.53.0.4:38507 -> 10.53.0.3:22047 TCP 56b sub.example.tld/IN/NS
+    _, _, _, _, _, dst, _, _, query = line.split(" ", 9)
+    ip, _ = dst.split(":", 1)
+    return (ip, query)
+
+
+def extract_dnstap(ns, expectedlen):
+    ns.rndc("dnstap -roll 1")
+    path = os.path.join(ns.identifier, "dnstap.out.0")
+    dnstapread = isctest.run.cmd(
+        [isctest.vars.ALL["DNSTAPREAD"], path],
+    )
+
+    lines = dnstapread.out.splitlines()
+    assert expectedlen == len(lines)
+    return list(map(line_to_ips_and_queries, lines))
+
+
+# Because DNSTAP doesn't have ordering guarantee, the order doesn't matter here.
+def has_ip_and_query(expected_ips_and_queries, ips_and_queries):
+    found_count = 0
+    for expected_ip, expected_query in expected_ips_and_queries:
+        for ip, query in ips_and_queries:
+            if ip == expected_ip and query == expected_query:
+                found_count += 1
+                break
+    return found_count == len(expected_ips_and_queries)
+
+
+# Test the max-delegation-servers limit on flow where ADB attempt
+# a lookup from an NS name rather than directly with the NS addresses.
+def test_nslimit_outdomain(ns4, templates):
+    templates.render(
+        "ns4/named.conf", {"maxdelegationservers": "max-delegation-servers 2;"}
+    )
+    with ns4.watch_log_from_here() as watcher:
+        ns4.rndc("flush")
+        ns4.rndc("reload")
+        watcher.wait_for_line("running")
+
+    msg = isctest.query.create("sub.example4.tld.", "A")
+    res = isctest.query.tcp(msg, ns4.ip)
+    isctest.check.servfail(res)
+
+    ips_and_queries = extract_dnstap(ns4, 9)
+
+    # The resolver first resolve example4.tld. and gets the NS for sub.example.tld.
+    # which is out-domain. So it resolves it.
+    assert has_ip_and_query(
+        [
+            ("10.53.0.1", "./IN/NS"),
+            ("10.53.0.1", "tld/IN/NS"),
+            ("10.53.0.2", "example4.tld/IN/NS"),
+            ("10.53.0.3", "sub.example4.tld/IN/A"),
+            ("10.53.0.2", "dnshoster.tld/IN/NS"),
+        ],
+        ips_and_queries,
+    )
+
+    # Then, because max-delegation-servers is 2, the resolver will try to use either
+    # the NS ns1.dnshoster.tld or the NS ns2.dnshoster.tld. or the NS ns3.dnshoster.tld.
+    #
+    # What is important here, is that the NS of sub.example4.tld are _names_, so
+    # this is going through the dns_adb_createfind() flow, and it does stop after 2
+    # queries (on the two IPs of one of the NS server above) and _won't_ try another
+    # NS name (becuse max-delegation-servers will be reached).
+    #
+    # Note that the sum of all the queries checked here is 8 and not 9. This is because
+    # when dnshoster.tld has been resolved, the resolver resolved 2 names. But the IPs
+    # of only one of the two names has been used. (This is checked below).
+
+    used_ns1 = has_ip_and_query(
+        [
+            ("10.53.0.2", "ns1.dnshoster.tld/IN/A"),
+            ("10.53.0.5", "sub.example4.tld/IN/A"),
+            ("10.53.0.6", "sub.example4.tld/IN/A"),
+        ],
+        ips_and_queries,
+    )
+
+    used_ns2 = has_ip_and_query(
+        [
+            ("10.53.0.2", "ns2.dnshoster.tld/IN/A"),
+            ("10.53.1.1", "sub.example4.tld/IN/A"),
+            ("10.53.1.2", "sub.example4.tld/IN/A"),
+        ],
+        ips_and_queries,
+    )
+
+    used_ns3 = has_ip_and_query(
+        [
+            ("10.53.0.2", "ns3.dnshoster.tld/IN/A"),
+            ("10.53.2.1", "sub.example4.tld/IN/A"),
+            ("10.53.2.2", "sub.example4.tld/IN/A"),
+        ],
+        ips_and_queries,
+    )
+
+    assert used_ns1 or used_ns2 or used_ns3