]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Add system test for self-pointed glue deduplication
authorColin Vidal <colin@isc.org>
Thu, 5 Feb 2026 10:20:11 +0000 (11:20 +0100)
committerMichał Kępień <michal@isc.org>
Thu, 7 May 2026 11:32:15 +0000 (13:32 +0200)
Test the resolver's behavior with self-pointed glue where each NS
has the same set of addresses.  Verify that addresses are
deduplicated and each unique IP is only queried once.

Also test the NS processing limit (max-delegation-servers) and the
ADB address limit (adbaddrslimit), both individually and combined.

12 files changed:
bin/tests/system/selfpointedglue/ns1/named.conf.j2 [new file with mode: 0644]
bin/tests/system/selfpointedglue/ns1/root.db [new file with mode: 0644]
bin/tests/system/selfpointedglue/ns2/named.conf.j2 [new file with mode: 0644]
bin/tests/system/selfpointedglue/ns2/tld.db [new file with mode: 0644]
bin/tests/system/selfpointedglue/ns3/example.tld.db [new file with mode: 0644]
bin/tests/system/selfpointedglue/ns3/example2.tld.db [new file with mode: 0644]
bin/tests/system/selfpointedglue/ns3/example3.tld.db [new file with mode: 0644]
bin/tests/system/selfpointedglue/ns3/named.conf.j2 [new file with mode: 0644]
bin/tests/system/selfpointedglue/ns4/named.args.j2 [new file with mode: 0644]
bin/tests/system/selfpointedglue/ns4/named.conf.j2 [new file with mode: 0644]
bin/tests/system/selfpointedglue/ns4/root.hint [new file with mode: 0644]
bin/tests/system/selfpointedglue/tests_selfpointedglue.py [new file with mode: 0644]

diff --git a/bin/tests/system/selfpointedglue/ns1/named.conf.j2 b/bin/tests/system/selfpointedglue/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/selfpointedglue/ns1/root.db b/bin/tests/system/selfpointedglue/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/selfpointedglue/ns2/named.conf.j2 b/bin/tests/system/selfpointedglue/ns2/named.conf.j2
new file mode 100644 (file)
index 0000000..2993832
--- /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.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; };
+       recursion no;
+       dnssec-validation no;
+};
+
+zone "tld." {
+       type primary;
+       file "tld.db";
+};
diff --git a/bin/tests/system/selfpointedglue/ns2/tld.db b/bin/tests/system/selfpointedglue/ns2/tld.db
new file mode 100644 (file)
index 0000000..66f7925
--- /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.
+
+$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
+
+example.tld.           NS      ns.example.tld.
+ns.example.tld.                A       10.53.0.3
+
+example2.tld.          NS      ns.example2.tld.
+ns.example2.tld.       A       10.53.0.3
+
+example3.tld.          NS      ns.example3.tld.
+ns.example3.tld.       A       10.53.0.3
diff --git a/bin/tests/system/selfpointedglue/ns3/example.tld.db b/bin/tests/system/selfpointedglue/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/selfpointedglue/ns3/example2.tld.db b/bin/tests/system/selfpointedglue/ns3/example2.tld.db
new file mode 100644 (file)
index 0000000..bcab6e3
--- /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
+example2.tld.          IN SOA  owner.dnshoster.tld. ns.dnshoster.tld. (
+                               2010    ; serial
+                               600             ; refresh
+                               600             ; retry
+                               1200            ; expire
+                               600             ; minimum
+                               )
+
+example2.tld.                  NS      ns.example2.tld.
+ns.example2.tld.               A       10.53.0.3
+
+sub.example2.tld.              NS      ns01.sub.example2.tld.
+sub.example2.tld.              NS      ns02.sub.example2.tld.
+sub.example2.tld.              NS      ns03.sub.example2.tld.
+
+ns01.sub.example2.tld.         A       10.53.1.1
+ns01.sub.example2.tld.         A       10.53.0.5
+ns02.sub.example2.tld.         A       10.53.1.2
+ns02.sub.example2.tld.         A       10.53.0.6
+ns03.sub.example2.tld.         A       10.53.2.1
+ns03.sub.example2.tld.         A       10.53.0.7
diff --git a/bin/tests/system/selfpointedglue/ns3/example3.tld.db b/bin/tests/system/selfpointedglue/ns3/example3.tld.db
new file mode 100644 (file)
index 0000000..e2c522c
--- /dev/null
@@ -0,0 +1,63 @@
+; 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
+example3.tld.          IN SOA  owner.dnshoster.tld. ns.dnshoster.tld. (
+                               2010    ; serial
+                               600             ; refresh
+                               600             ; retry
+                               1200            ; expire
+                               600             ; minimum
+                               )
+
+example3.tld.                  NS      ns.example3.tld.
+ns.example3.tld.               A       10.53.0.3
+
+sub.example3.tld.              NS      ns01.sub.example3.tld.
+sub.example3.tld.              NS      ns02.sub.example3.tld.
+sub.example3.tld.              NS      ns03.sub.example3.tld.
+sub.example3.tld.              NS      ns04.sub.example3.tld.
+sub.example3.tld.              NS      ns05.sub.example3.tld.
+sub.example3.tld.              NS      ns06.sub.example3.tld.
+sub.example3.tld.              NS      ns07.sub.example3.tld.
+sub.example3.tld.              NS      ns08.sub.example3.tld.
+sub.example3.tld.              NS      ns09.sub.example3.tld.
+sub.example3.tld.              NS      ns10.sub.example3.tld.
+
+ns01.sub.example3.tld.         A       10.53.0.5
+ns01.sub.example3.tld.         A       10.53.0.6
+
+ns02.sub.example3.tld.         A       10.53.0.5
+ns02.sub.example3.tld.         A       10.53.0.6
+
+ns03.sub.example3.tld.         A       10.53.0.5
+ns03.sub.example3.tld.         A       10.53.0.6
+
+ns04.sub.example3.tld.         A       10.53.0.5
+ns04.sub.example3.tld.         A       10.53.0.6
+
+ns05.sub.example3.tld.         A       10.53.0.5
+ns05.sub.example3.tld.         A       10.53.0.6
+
+ns06.sub.example3.tld.         A       10.53.0.5
+ns06.sub.example3.tld.         A       10.53.0.6
+
+ns07.sub.example3.tld.         A       10.53.0.5
+ns07.sub.example3.tld.         A       10.53.0.6
+
+ns08.sub.example3.tld.         A       10.53.0.5
+ns08.sub.example3.tld.         A       10.53.0.6
+
+ns09.sub.example3.tld.         A       10.53.0.5
+ns09.sub.example3.tld.         A       10.53.0.6
+
+ns10.sub.example3.tld.         A       10.53.0.5
+ns10.sub.example3.tld.         A       10.53.0.6
diff --git a/bin/tests/system/selfpointedglue/ns3/named.conf.j2 b/bin/tests/system/selfpointedglue/ns3/named.conf.j2
new file mode 100644 (file)
index 0000000..0c50ca8
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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;
+               10.53.0.5;
+               10.53.0.6;
+               10.53.0.7;
+               10.53.0.8;
+               10.53.0.9;
+               10.53.0.10;
+               10.53.1.1;
+               10.53.1.2;
+               10.53.2.1;
+       };
+       recursion no;
+       dnssec-validation no;
+};
+
+zone "example.tld." {
+       type primary;
+       file "example.tld.db";
+};
+
+zone "example2.tld." {
+       type primary;
+       file "example2.tld.db";
+};
+
+zone "example3.tld." {
+       type primary;
+       file "example3.tld.db";
+};
diff --git a/bin/tests/system/selfpointedglue/ns4/named.args.j2 b/bin/tests/system/selfpointedglue/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/selfpointedglue/ns4/named.conf.j2 b/bin/tests/system/selfpointedglue/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/selfpointedglue/ns4/root.hint b/bin/tests/system/selfpointedglue/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/selfpointedglue/tests_selfpointedglue.py b/bin/tests/system/selfpointedglue/tests_selfpointedglue.py
new file mode 100644 (file)
index 0000000..d559b76
--- /dev/null
@@ -0,0 +1,157 @@
+# 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 expect_ip_and_query(expected_ips_and_queries, ips_and_queries):
+    found_count = 0
+    for expected_ip, expected_query in expected_ips_and_queries:
+        found = False
+        for ip, query in ips_and_queries:
+            if ip == expected_ip and query == expected_query:
+                found = True
+                found_count += 1
+                break
+        assert found
+    assert found_count == len(expected_ips_and_queries)
+
+
+def expect_query(expected_query, expected_query_count, ips_and_queries):
+    count = 0
+    for _, query in ips_and_queries:
+        if query == expected_query:
+            count += 1
+    assert count == expected_query_count
+
+
+def test_selfpointedglue1(ns4):
+    msg = isctest.query.create("a.sub.example.tld.", "A")
+    res = isctest.query.tcp(msg, ns4.ip)
+    isctest.check.servfail(res)
+
+    # 4 queries to get to the delegation.
+    # 13 queries to delegation NS servers.
+    ips_and_queries = extract_dnstap(ns4, 17)
+
+    # Thanks to the de-duplication, only the first 13 NS IPs are
+    # queried (once sub.example.tld. NS is found) instead of 13*10
+    # (13 per NS, with 10 NS).
+    expect_ip_and_query(
+        [
+            ("10.53.0.1", "./IN/NS"),
+            ("10.53.0.1", "tld/IN/NS"),
+            ("10.53.0.2", "example.tld/IN/NS"),
+            ("10.53.0.3", "sub.example.tld/IN/NS"),
+            ("10.53.0.3", "a.sub.example.tld/IN/A"),
+            ("10.53.0.5", "a.sub.example.tld/IN/A"),
+            ("10.53.0.6", "a.sub.example.tld/IN/A"),
+            ("10.53.0.7", "a.sub.example.tld/IN/A"),
+            ("10.53.0.8", "a.sub.example.tld/IN/A"),
+            ("10.53.0.9", "a.sub.example.tld/IN/A"),
+            ("10.53.0.10", "a.sub.example.tld/IN/A"),
+            ("10.53.1.1", "a.sub.example.tld/IN/A"),
+            ("10.53.1.2", "a.sub.example.tld/IN/A"),
+            ("10.53.2.1", "a.sub.example.tld/IN/A"),
+            ("10.53.0.3", "a.sub.example.tld/IN/A"),
+            ("127.0.0.1", "a.sub.example.tld/IN/A"),
+            ("127.0.0.2", "a.sub.example.tld/IN/A"),
+        ],
+        ips_and_queries,
+    )
+
+
+# This test is useful because the one above hits the max-delegation-servers
+# from the first NS name lookup. This one doesn't, because there is only 2
+# addresses per NS, but the deduplication avoid the explosion of duplicate
+# addresses.
+def test_selfpointedglue2(ns4):
+    with ns4.watch_log_from_here() as watcher:
+        ns4.rndc("flush")
+        ns4.rndc("reload")
+        watcher.wait_for_line("running")
+    msg = isctest.query.create("a.sub.example3.tld.", "A")
+    res = isctest.query.tcp(msg, ns4.ip)
+    isctest.check.servfail(res)
+
+    # 4 queries to get to the delegation.
+    # 2 queries to delegation NS servers.
+    ips_and_queries = extract_dnstap(ns4, 6)
+
+    # Thanks to the de-duplication, only the first 2 NS IPs are
+    # queried (once sub.example.tld. NS is found) instead of 2*10
+    # (2 per NS with 10 NS).
+    expect_ip_and_query(
+        [
+            ("10.53.0.1", "./IN/NS"),
+            ("10.53.0.1", "tld/IN/NS"),
+            ("10.53.0.2", "example3.tld/IN/NS"),
+            ("10.53.0.3", "sub.example3.tld/IN/NS"),
+            ("10.53.0.5", "a.sub.example3.tld/IN/A"),
+            ("10.53.0.6", "a.sub.example3.tld/IN/A"),
+        ],
+        ips_and_queries,
+    )
+
+
+def test_selfpointedglue_nslimit(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("a.sub.example2.tld.", "A")
+    res = isctest.query.tcp(msg, ns4.ip)
+    isctest.check.servfail(res)
+
+    ips_and_queries = extract_dnstap(ns4, 6)
+
+    # Checking the beginning of the resolution
+    expect_ip_and_query(
+        [
+            ("10.53.0.1", "./IN/NS"),
+            ("10.53.0.1", "tld/IN/NS"),
+            ("10.53.0.2", "example2.tld/IN/NS"),
+            ("10.53.0.3", "sub.example2.tld/IN/NS"),
+        ],
+        ips_and_queries,
+    )
+
+    expect_query("a.sub.example2.tld/IN/A", 2, ips_and_queries)