Add system test for self-pointed glue deduplication

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 ADB address limit knob (-T adbaddrslimit=).

(cherry picked from commit c21fc6cb95d77312d6fb891f17ce9df41a25af6d)
This commit is contained in:
Colin Vidal 2026-02-05 11:20:11 +01:00 committed by Michał Kępień
parent e25eaf9e6e
commit d2a67ba222
No known key found for this signature in database
12 changed files with 510 additions and 0 deletions

View file

@ -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";
};

View file

@ -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

View file

@ -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";
};

View file

@ -0,0 +1,27 @@
; 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

View file

@ -0,0 +1,155 @@
; 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
; Those RR (same below) pointing to 127.0.0.1 won't ever be used as they
; exceeded the ADB limit.
ns01.sub.example.tld. A 127.0.0.1
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
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
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
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
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
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
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
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
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

View file

@ -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

View file

@ -0,0 +1,44 @@
/*
* 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";
};

View file

@ -0,0 +1,3 @@
{% set adblimit = adblimit | default("") %}
-D selfpointedglue-ns4 -m record -c named.conf -d 99 -g -T maxcachesize=2097152 -4 @adblimit@

View file

@ -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; };
};

View file

@ -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

View file

@ -0,0 +1,20 @@
#!/bin/sh
# 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.
. ../conf.sh
$FEATURETEST --enable-dnstap || {
echo_i "This test requires dnstap support." >&2
exit 255
}
exit 0

View file

@ -0,0 +1,75 @@
# 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
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, nsid, expectedlen):
ns.rndc("dnstap -roll 1")
path = os.path.join(nsid, "dnstap.out.0")
dnstapread = isctest.run.cmd(
[os.getenv("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 test_selfpointedglue(ns4):
msg = isctest.query.create("a.sub.example.tld.", "A")
res = isctest.query.tcp(msg, ns4.ip)
isctest.check.servfail(res)
ips_and_queries = extract_dnstap(ns4, "ns4", 10)
# Thanks to the de-duplication, only the first 6 NS IPs are
# queried (once sub.example.tld. NS is found) instead of 60
# (60 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"),
],
ips_and_queries,
)