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 index 0000000000..fd83fc3c19 --- /dev/null +++ b/bin/tests/system/nslimit_outdomain/ns1/named.conf.j2 @@ -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 index 0000000000..bfbf049b80 --- /dev/null +++ b/bin/tests/system/nslimit_outdomain/ns1/root.db @@ -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 index 0000000000..9540da4743 --- /dev/null +++ b/bin/tests/system/nslimit_outdomain/ns2/dnshoster.tld.db @@ -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 index 0000000000..037ac60fe0 --- /dev/null +++ b/bin/tests/system/nslimit_outdomain/ns2/named.conf.j2 @@ -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 index 0000000000..e29bf91f7d --- /dev/null +++ b/bin/tests/system/nslimit_outdomain/ns2/tld.db @@ -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 index 0000000000..2a599ee876 --- /dev/null +++ b/bin/tests/system/nslimit_outdomain/ns3/example.tld.db @@ -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 index 0000000000..f1c64be066 --- /dev/null +++ b/bin/tests/system/nslimit_outdomain/ns3/example4.tld.db @@ -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 index 0000000000..f7e03a82e7 --- /dev/null +++ b/bin/tests/system/nslimit_outdomain/ns3/named.conf.j2 @@ -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 index 0000000000..68f1511c7c --- /dev/null +++ b/bin/tests/system/nslimit_outdomain/ns4/named.args.j2 @@ -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 index 0000000000..09fbdd4e70 --- /dev/null +++ b/bin/tests/system/nslimit_outdomain/ns4/named.conf.j2 @@ -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 index 0000000000..d7d0e1faba --- /dev/null +++ b/bin/tests/system/nslimit_outdomain/ns4/root.hint @@ -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 index 0000000000..228fb07918 --- /dev/null +++ b/bin/tests/system/nslimit_outdomain/tests_nslimit_outdomain.py @@ -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 diff --git a/bin/tests/system/selfpointedglue/ns1/named.conf.j2 b/bin/tests/system/selfpointedglue/ns1/named.conf.j2 new file mode 100644 index 0000000000..fd83fc3c19 --- /dev/null +++ b/bin/tests/system/selfpointedglue/ns1/named.conf.j2 @@ -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 index 0000000000..bfbf049b80 --- /dev/null +++ b/bin/tests/system/selfpointedglue/ns1/root.db @@ -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 index 0000000000..2993832da2 --- /dev/null +++ b/bin/tests/system/selfpointedglue/ns2/named.conf.j2 @@ -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 index 0000000000..66f7925d99 --- /dev/null +++ b/bin/tests/system/selfpointedglue/ns2/tld.db @@ -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 index 0000000000..2a599ee876 --- /dev/null +++ b/bin/tests/system/selfpointedglue/ns3/example.tld.db @@ -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 index 0000000000..bcab6e38c1 --- /dev/null +++ b/bin/tests/system/selfpointedglue/ns3/example2.tld.db @@ -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 index 0000000000..e2c522cc3e --- /dev/null +++ b/bin/tests/system/selfpointedglue/ns3/example3.tld.db @@ -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 index 0000000000..0c50ca8658 --- /dev/null +++ b/bin/tests/system/selfpointedglue/ns3/named.conf.j2 @@ -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 index 0000000000..68f1511c7c --- /dev/null +++ b/bin/tests/system/selfpointedglue/ns4/named.args.j2 @@ -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 index 0000000000..09fbdd4e70 --- /dev/null +++ b/bin/tests/system/selfpointedglue/ns4/named.conf.j2 @@ -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 index 0000000000..d7d0e1faba --- /dev/null +++ b/bin/tests/system/selfpointedglue/ns4/root.hint @@ -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 index 0000000000..d559b76ea8 --- /dev/null +++ b/bin/tests/system/selfpointedglue/tests_selfpointedglue.py @@ -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) diff --git a/bin/tests/system/srtt/README b/bin/tests/system/srtt/README new file mode 100644 index 0000000000..c86a697931 --- /dev/null +++ b/bin/tests/system/srtt/README @@ -0,0 +1,18 @@ +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. + +ns1 is root + +ans{2-5} simulates four NS servers making authority on the same domain +`example.`. ans2 is the quickest to answer, followed by ans3, then ans4, with +ans5 being the slowest. + +ns6 is a resolver diff --git a/bin/tests/system/srtt/ans2/ans.py b/bin/tests/system/srtt/ans2/ans.py new file mode 100644 index 0000000000..147a65f828 --- /dev/null +++ b/bin/tests/system/srtt/ans2/ans.py @@ -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. +""" + +import dns.rcode + +from isctest.asyncserver import AsyncDnsServer, IgnoreAllQueries + +from ..srtt_ans import DelayedQnameRangeHandler + + +class Foo1ToFoo99Handler(DelayedQnameRangeHandler): + max_qname = 99 + delay = 0.0 + + +def main() -> None: + server = AsyncDnsServer(default_aa=True, default_rcode=dns.rcode.NOERROR) + server.install_response_handlers( + Foo1ToFoo99Handler(), + IgnoreAllQueries(), + ) + server.run() + + +if __name__ == "__main__": + main() diff --git a/bin/tests/system/srtt/ans3/ans.py b/bin/tests/system/srtt/ans3/ans.py new file mode 100644 index 0000000000..ecd590afd0 --- /dev/null +++ b/bin/tests/system/srtt/ans3/ans.py @@ -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. +""" + +import dns.rcode + +from isctest.asyncserver import AsyncDnsServer, IgnoreAllQueries + +from ..srtt_ans import DelayedQnameRangeHandler + + +class Foo1ToFoo199Handler(DelayedQnameRangeHandler): + max_qname = 199 + delay = 0.03 + + +def main() -> None: + server = AsyncDnsServer(default_aa=True, default_rcode=dns.rcode.NOERROR) + server.install_response_handlers( + Foo1ToFoo199Handler(), + IgnoreAllQueries(), + ) + server.run() + + +if __name__ == "__main__": + main() diff --git a/bin/tests/system/srtt/ans4/ans.py b/bin/tests/system/srtt/ans4/ans.py new file mode 100644 index 0000000000..af337c27fb --- /dev/null +++ b/bin/tests/system/srtt/ans4/ans.py @@ -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. +""" + +import dns.rcode + +from isctest.asyncserver import AsyncDnsServer, IgnoreAllQueries + +from ..srtt_ans import DelayedQnameRangeHandler + + +class Foo1ToFoo299Handler(DelayedQnameRangeHandler): + max_qname = 299 + delay = 0.08 + + +def main() -> None: + server = AsyncDnsServer(default_aa=True, default_rcode=dns.rcode.NOERROR) + server.install_response_handlers( + Foo1ToFoo299Handler(), + IgnoreAllQueries(), + ) + server.run() + + +if __name__ == "__main__": + main() diff --git a/bin/tests/system/srtt/ans5/ans.py b/bin/tests/system/srtt/ans5/ans.py new file mode 100644 index 0000000000..8bac83a798 --- /dev/null +++ b/bin/tests/system/srtt/ans5/ans.py @@ -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. +""" + +import dns.rcode + +from isctest.asyncserver import AsyncDnsServer, IgnoreAllQueries + +from ..srtt_ans import DelayedQnameRangeHandler + + +class Foo1ToFoo399Handler(DelayedQnameRangeHandler): + max_qname = 399 + delay = 0.15 + + +def main() -> None: + server = AsyncDnsServer(default_aa=True, default_rcode=dns.rcode.NOERROR) + server.install_response_handlers( + Foo1ToFoo399Handler(), + IgnoreAllQueries(), + ) + server.run() + + +if __name__ == "__main__": + main() diff --git a/bin/tests/system/srtt/ns1/named.conf.j2 b/bin/tests/system/srtt/ns1/named.conf.j2 new file mode 100644 index 0000000000..eb079c95ab --- /dev/null +++ b/bin/tests/system/srtt/ns1/named.conf.j2 @@ -0,0 +1,29 @@ +/* + * 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; }; + listen-on-v6 { none; }; + recursion no; + notify yes; +}; + +zone "." { + type primary; + file "root.db"; +}; diff --git a/bin/tests/system/srtt/ns1/root.db b/bin/tests/system/srtt/ns1/root.db new file mode 100644 index 0000000000..29ecd1d89d --- /dev/null +++ b/bin/tests/system/srtt/ns1/root.db @@ -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 +. IN SOA owner.root-servers.nil. a.root-servers.nil. ( + 2000042100 ; serial + 600 ; refresh + 600 ; retry + 1200 ; expire + 600 ; minimum + ) +. NS a.root-servers.nil. +a.root-servers.nil. A 10.53.0.1 + +; The idea is that the resolver would do 2 ADB lookups, so there would be 2 +; find list, both with 2 IPs in it. ns1 (which is actually ans2 and ans5) would +; have both the slowest and fastest addresses. ns2 (which is actually ans3 and +; ans4) would have two addresses in the middle. + +example. NS ns1.example. +example. NS ns1.example. +example. NS ns2.example. +example. NS ns2.example. + +ns1.example. A 10.53.0.2 ; delay is 0 +ns1.example. A 10.53.0.5 ; delay is 0.15 +ns2.example. A 10.53.0.4 ; delay is 0.08 +ns2.example. A 10.53.0.3 ; delay is 0.03 diff --git a/bin/tests/system/srtt/ns6/named.args b/bin/tests/system/srtt/ns6/named.args new file mode 100644 index 0000000000..b5de5874ec --- /dev/null +++ b/bin/tests/system/srtt/ns6/named.args @@ -0,0 +1 @@ +-D srtt-ns6 -m record -c named.conf -d 99 -g -T maxcachesize=2097152 -4 diff --git a/bin/tests/system/srtt/ns6/named.conf.j2 b/bin/tests/system/srtt/ns6/named.conf.j2 new file mode 100644 index 0000000000..1d27505a8e --- /dev/null +++ b/bin/tests/system/srtt/ns6/named.conf.j2 @@ -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.6; + notify-source 10.53.0.6; + transfer-source 10.53.0.6; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.6; }; + listen-on-v6 { none; }; + recursion yes; + dnssec-validation no; + dnstap { resolver query; }; + dnstap-output file "dnstap.out"; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm @DEFAULT_HMAC@; +}; + +controls { + inet 10.53.0.6 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +zone "." { + type hint; + file "../../_common/root.hint"; +}; diff --git a/bin/tests/system/srtt/srtt_ans.py b/bin/tests/system/srtt/srtt_ans.py new file mode 100644 index 0000000000..9387486993 --- /dev/null +++ b/bin/tests/system/srtt/srtt_ans.py @@ -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. +""" + +from collections.abc import AsyncGenerator + +import abc + +import dns.rdataclass +import dns.rdatatype +import dns.rrset + +from isctest.asyncserver import DnsResponseSend, QnameQtypeHandler, QueryContext + + +class DelayedQnameRangeHandler(QnameQtypeHandler): + """ + Respond to queries for QNAMEs "foo1.example." through "foo.example." + with QTYPE=A, where must be defined by the subclass. Every response is + delayed by a fixed amount of time, which must also be defined (in seconds) + by the subclass. + """ + + @property + def qnames(self) -> list[str]: + return [f"foo{x}.example." for x in range(1, self.max_qname + 1)] + + qtypes = [dns.rdatatype.A] + + @property + @abc.abstractmethod + def max_qname(self) -> int: + raise NotImplementedError + + @property + @abc.abstractmethod + def delay(self) -> float: + raise NotImplementedError + + def __str__(self) -> str: + return f"{self.__class__.__name__}(foo[1-{self.max_qname}].example/A)" + + async def get_responses( + self, qctx: QueryContext + ) -> AsyncGenerator[DnsResponseSend, None]: + a_rrset = dns.rrset.from_text( + qctx.qname, 300, dns.rdataclass.IN, dns.rdatatype.A, "10.53.9.9" + ) + qctx.response.answer.append(a_rrset) + yield DnsResponseSend(qctx.response, delay=self.delay) diff --git a/bin/tests/system/srtt/tests_srtt.py b/bin/tests/system/srtt/tests_srtt.py new file mode 100644 index 0000000000..0ce18fcccf --- /dev/null +++ b/bin/tests/system/srtt/tests_srtt.py @@ -0,0 +1,89 @@ +# 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_dst_ips(line): + # dnstap-read output line example + # 05-Feb-2026 11:00:57.853 RQ 10.53.0.6:38507 -> 10.53.0.3:22047 TCP 56b fooXXX.example./IN/NS + _, _, _, _, _, dst, _, _, _ = line.split(" ", 9) + ip, _ = dst.split(":", 1) + return ip + + +def extract_dnstap(ns): + 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() + return map(line_to_dst_ips, lines) + + +def assert_used_auth(ns, authip): + ips = extract_dnstap(ns) + queries = 0 + matches = 0 + for ip in ips: + queries += 1 + if ip == authip: + matches += 1 + assert matches > 85 + assert queries <= 115 + + +def test_srtt(ns6): + for i in range(1, 100): + msg = isctest.query.create(f"foo{i}.example.", "A") + res = isctest.query.udp(msg, ns6.ip) + isctest.check.noerror(res) + assert len(res.answer[0]) == 1 + res.answer[0].ttl = 300 + assert str(res.answer[0]) == f"foo{i}.example. 300 IN A 10.53.9.9" + + assert_used_auth(ns6, "10.53.0.2") + + for i in range(100, 200): + msg = isctest.query.create(f"foo{i}.example.", "A") + res = isctest.query.udp(msg, ns6.ip) + isctest.check.noerror(res) + assert len(res.answer[0]) == 1 + res.answer[0].ttl = 300 + assert str(res.answer[0]) == f"foo{i}.example. 300 IN A 10.53.9.9" + + assert_used_auth(ns6, "10.53.0.3") + + for i in range(200, 300): + msg = isctest.query.create(f"foo{i}.example.", "A") + res = isctest.query.udp(msg, ns6.ip) + isctest.check.noerror(res) + assert len(res.answer[0]) == 1 + res.answer[0].ttl = 300 + assert str(res.answer[0]) == f"foo{i}.example. 300 IN A 10.53.9.9" + + assert_used_auth(ns6, "10.53.0.4") + + for i in range(300, 400): + msg = isctest.query.create(f"foo{i}.example.", "A") + res = isctest.query.udp(msg, ns6.ip) + isctest.check.noerror(res) + assert len(res.answer[0]) == 1 + res.answer[0].ttl = 300 + assert str(res.answer[0]) == f"foo{i}.example. 300 IN A 10.53.9.9" + assert_used_auth(ns6, "10.53.0.5") diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst index a42a08d326..10ba133ef1 100644 --- a/doc/arm/reference.rst +++ b/doc/arm/reference.rst @@ -4187,14 +4187,11 @@ Tuning .. namedconf:statement:: max-delegation-servers :tags: server - :short: Configure the maximum number of nameserver names considered for a delegation + :short: Configure the maximum number of nameservers considered for a delegation When looking up remote nameservers for a delegation, the list of nameserver names is sorted according to Canonical RR Ordering within an RRset (see - :rfc:`4034` Section 6.3), and the number of names for which :iscman:`named` - looks up IP addresses is capped at :any:`max-delegation-servers`. - - This capped list of nameserver names is then randomly shuffled every time + :rfc:`4034` Section 6.3). This list is then randomly shuffled every time :iscman:`named` needs additional remote addresses for those nameservers. This randomized selection works around situations where the first few nameserver names in the zone are unresponsive. @@ -4207,6 +4204,12 @@ Tuning outgoing DNS query is initiated only if the DNS resolver does not already have existing IP addresses for any of the nameserver names in the cache. + The known NS addresses for an NS name (cached from a previous resolution, or + the NS name has glues, or it is defined from a local zone or hints) are + counted as delegation servers. Thus, the maximum queries the resolver does + to resolve a name at a delegation point is capped at + :any:`max-delegation-servers`. + The default and recommended value is ``13``. This limit prevents excessive resource use while processing large or misconfigured delegations. The default value should only be increased in controlled environments where a remote diff --git a/lib/dns/adb.c b/lib/dns/adb.c index f189664f80..f51b2a8821 100644 --- a/lib/dns/adb.c +++ b/lib/dns/adb.c @@ -1370,8 +1370,10 @@ log_quota(dns_adbentry_t *entry, const char *fmt, ...) { } static void -copy_namehook_lists(dns_adb_t *adb, dns_adbfind_t *find, dns_adbname_t *name) { +copy_namehook_lists(dns_adb_t *adb, dns_adbfind_t *find, dns_adbname_t *name, + size_t maxfindlen, size_t *findlen) { dns_adbentry_t *entry = NULL; + size_t count = 0; if ((find->options & DNS_ADBFIND_INET) != 0) { ISC_LIST_FOREACH(name->v4, namehook, name_link) { @@ -1391,6 +1393,12 @@ copy_namehook_lists(dns_adb_t *adb, dns_adbfind_t *find, dns_adbname_t *name) { * Found a valid entry. Add it to the find's list. */ ISC_LIST_APPEND(find->list, addrinfo, publink); + + count++; + if (maxfindlen - count == 0) { + SET_IF_NOT_NULL(findlen, count); + return; + } } } @@ -1412,8 +1420,16 @@ copy_namehook_lists(dns_adb_t *adb, dns_adbfind_t *find, dns_adbname_t *name) { * Found a valid entry. Add it to the find's list. */ ISC_LIST_APPEND(find->list, addrinfo, publink); + + count++; + if (maxfindlen - count == 0) { + SET_IF_NOT_NULL(findlen, count); + return; + } } } + + SET_IF_NOT_NULL(findlen, count); } static bool @@ -1735,7 +1751,7 @@ out: void dns_adb_createaddrinfosfind(dns_adb_t *adb, isc_netaddrlist_t *addrs, in_port_t port, unsigned int options, - isc_stdtime_t now, size_t maxaddrs, + isc_stdtime_t now, size_t maxfindlen, dns_adbfind_t **findp, size_t *findlen) { dns_adbfind_t *find = NULL; isc_sockaddr_t sockaddr = {}; @@ -1743,7 +1759,7 @@ dns_adb_createaddrinfosfind(dns_adb_t *adb, isc_netaddrlist_t *addrs, REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(addrs != NULL); REQUIRE(findp != NULL && *findp == NULL); - REQUIRE(maxaddrs > 0); + REQUIRE(maxfindlen > 0); rcu_read_lock(); @@ -1794,7 +1810,7 @@ dns_adb_createaddrinfosfind(dns_adb_t *adb, isc_netaddrlist_t *addrs, ISC_LIST_APPEND(find->list, addrinfo, publink); (*findlen)++; - if (maxaddrs - *findlen == 0) { + if (maxfindlen - *findlen == 0) { break; } } @@ -1825,7 +1841,7 @@ dns_adb_createfind(dns_adb_t *adb, isc_loop_t *loop, isc_job_cb cb, void *cbarg, const dns_name_t *name, unsigned int options, isc_stdtime_t now, in_port_t port, unsigned int depth, isc_counter_t *qc, isc_counter_t *gqc, fetchctx_t *parent, - dns_adbfind_t **findp) { + size_t maxfindlen, dns_adbfind_t **findp, size_t *findlen) { isc_result_t result = ISC_R_UNEXPECTED; dns_adbfind_t *find = NULL; dns_adbname_t *adbname = NULL; @@ -1844,6 +1860,7 @@ dns_adb_createfind(dns_adb_t *adb, isc_loop_t *loop, isc_job_cb cb, void *cbarg, } REQUIRE(name != NULL); REQUIRE(findp != NULL && *findp == NULL); + REQUIRE(maxfindlen > 0); REQUIRE((options & DNS_ADBFIND_ADDRESSMASK) != 0); @@ -2056,7 +2073,7 @@ fetch: * Run through the name and copy out the bits we are * interested in. */ - copy_namehook_lists(adb, find, adbname); + copy_namehook_lists(adb, find, adbname, maxfindlen, findlen); post_copy: if (NAME_FETCH_A(adbname)) { diff --git a/lib/dns/include/dns/adb.h b/lib/dns/include/dns/adb.h index 7c1aef4cc5..82abbf98f5 100644 --- a/lib/dns/include/dns/adb.h +++ b/lib/dns/include/dns/adb.h @@ -285,7 +285,7 @@ dns_adb_createfind(dns_adb_t *adb, isc_loop_t *loop, isc_job_cb cb, void *cbarg, const dns_name_t *name, unsigned int options, isc_stdtime_t now, in_port_t port, unsigned int depth, isc_counter_t *qc, isc_counter_t *gqc, fetchctx_t *parent, - dns_adbfind_t **find); + size_t maxfindlen, dns_adbfind_t **find, size_t *findlen); /*%< * Main interface for clients. The adb will look up the name given in * "name" and will build up a list of found addresses, and perhaps start @@ -333,6 +333,8 @@ dns_adb_createfind(dns_adb_t *adb, isc_loop_t *loop, isc_job_cb cb, void *cbarg, * *\li find != NULL && *find == NULL. * + *\li findlen is optional, if not NULL, it will be set to the length of find. + * * Returns: * *\li #ISC_R_SUCCESS Addresses might have been returned, and events will be diff --git a/lib/dns/notify.c b/lib/dns/notify.c index 46766ea711..3b2ec98f5d 100644 --- a/lib/dns/notify.c +++ b/lib/dns/notify.c @@ -761,9 +761,10 @@ dns_notify_find_address(dns_notify_t *notify) { goto destroy; } - result = dns_adb_createfind(adb, loop, process_notify_adb_event, notify, - ¬ify->ns, options, 0, notify->port, 0, - NULL, NULL, NULL, ¬ify->find); + result = dns_adb_createfind( + adb, loop, process_notify_adb_event, notify, ¬ify->ns, + options, 0, notify->port, 0, NULL, NULL, NULL, + view->max_delegation_servers, ¬ify->find, NULL); dns_adb_detach(&adb); /* Something failed? */ diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index 742748d2f3..e66e602888 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -368,7 +368,16 @@ struct fetchctx { dns_message_t *qmessage; ISC_LIST(resquery_t) queries; dns_adbfindlist_t finds; - dns_adbfind_t *find; + /* + * This is a state to keep track of the latest upstream server which is + * being queried. See `nextaddress()`. + * + * `addrinfo` is basically a copy of `foundaddrinfo` but came from the + * response of the query, so fields like the SRTT/timing might have been + * altered. So it might be possible (?) to wrap those two in an union + * for clarity (and memory saving). + */ + dns_adbaddrinfo_t *foundaddrinfo; /* * altfinds are names and/or addresses of dual stack servers that * should be used when iterative resolution to a server is not @@ -1409,7 +1418,7 @@ fctx_cleanup(fetchctx_t *fctx) { dns_adb_destroyfind(&find); fetchctx_unref(fctx); } - fctx->find = NULL; + fctx->foundaddrinfo = NULL; ISC_LIST_FOREACH(fctx->altfinds, find, publink) { ISC_LIST_UNLINK(fctx->altfinds, find, publink); @@ -3251,89 +3260,6 @@ add_bad(fetchctx_t *fctx, dns_message_t *rmessage, dns_adbaddrinfo_t *addrinfo, classbuf, addrbuf); } -/* - * Sort addrinfo list by RTT. - */ -static void -sort_adbfind(dns_adbfind_t *find, unsigned int bias) { - dns_adbaddrinfo_t *best, *curr; - dns_adbaddrinfolist_t sorted; - - /* Lame N^2 bubble sort. */ - ISC_LIST_INIT(sorted); - while (!ISC_LIST_EMPTY(find->list)) { - unsigned int best_srtt; - best = ISC_LIST_HEAD(find->list); - best_srtt = best->srtt; - if (isc_sockaddr_pf(&best->sockaddr) != AF_INET6) { - best_srtt += bias; - } - curr = ISC_LIST_NEXT(best, publink); - while (curr != NULL) { - unsigned int curr_srtt = curr->srtt; - if (isc_sockaddr_pf(&curr->sockaddr) != AF_INET6) { - curr_srtt += bias; - } - if (curr_srtt < best_srtt) { - best = curr; - best_srtt = curr_srtt; - } - curr = ISC_LIST_NEXT(curr, publink); - } - ISC_LIST_UNLINK(find->list, best, publink); - ISC_LIST_APPEND(sorted, best, publink); - } - find->list = sorted; -} - -/* - * Sort a list of finds by server RTT. - */ -static void -sort_finds(dns_adbfindlist_t *findlist, unsigned int bias) { - dns_adbfind_t *best = NULL; - dns_adbfindlist_t sorted; - dns_adbaddrinfo_t *addrinfo, *bestaddrinfo; - - /* Sort each find's addrinfo list by SRTT. */ - ISC_LIST_FOREACH(*findlist, curr, publink) { - sort_adbfind(curr, bias); - } - - /* Lame N^2 bubble sort. */ - ISC_LIST_INIT(sorted); - while (!ISC_LIST_EMPTY(*findlist)) { - dns_adbfind_t *curr = NULL; - unsigned int best_srtt; - - best = ISC_LIST_HEAD(*findlist); - bestaddrinfo = ISC_LIST_HEAD(best->list); - INSIST(bestaddrinfo != NULL); - best_srtt = bestaddrinfo->srtt; - if (isc_sockaddr_pf(&bestaddrinfo->sockaddr) != AF_INET6) { - best_srtt += bias; - } - curr = ISC_LIST_NEXT(best, publink); - while (curr != NULL) { - unsigned int curr_srtt; - addrinfo = ISC_LIST_HEAD(curr->list); - INSIST(addrinfo != NULL); - curr_srtt = addrinfo->srtt; - if (isc_sockaddr_pf(&addrinfo->sockaddr) != AF_INET6) { - curr_srtt += bias; - } - if (curr_srtt < best_srtt) { - best = curr; - best_srtt = curr_srtt; - } - curr = ISC_LIST_NEXT(curr, publink); - } - ISC_LIST_UNLINK(*findlist, best, publink); - ISC_LIST_APPEND(sorted, best, publink); - } - *findlist = sorted; -} - /* * Return true iff the ADB find has an already pending fetch for 'type'. This * is used to find out whether we're in a loop, where a fetch is waiting for a @@ -3375,7 +3301,8 @@ already_waiting_for(dns_adbfind_t *find, dns_rdatatype_t type) { static void findname(fetchctx_t *fctx, const dns_name_t *name, in_port_t port, unsigned int options, unsigned int flags, isc_stdtime_t now, - bool *overquota, bool *need_alternate, bool *have_address) { + bool *overquota, bool *need_alternate, bool *have_address, + size_t maxfindlen, size_t *findlen) { dns_adbfind_t *find = NULL; dns_resolver_t *res = fctx->res; bool unshared = ((fctx->options & DNS_FETCHOPT_UNSHARED) != 0); @@ -3416,7 +3343,7 @@ findname(fetchctx_t *fctx, const dns_name_t *name, in_port_t port, result = dns_adb_createfind(fctx->adb, fctx->loop, fctx_finddone, fctx, name, options, now, res->view->dstport, fctx->depth + 1, fctx->qc, fctx->gqc, fctx, - &find); + maxfindlen, &find, findlen); isc_log_write(DNS_LOGCATEGORY_RESOLVER, DNS_LOGMODULE_RESOLVER, ISC_LOG_DEBUG(3), "fctx %p(%s): createfind for %s - %s", @@ -3458,6 +3385,7 @@ findname(fetchctx_t *fctx, const dns_name_t *name, in_port_t port, } } } + if ((flags & FCTX_ADDRINFO_DUALSTACK) != 0) { ISC_LIST_APPEND(fctx->altfinds, find, publink); } else { @@ -3670,7 +3598,7 @@ fctx_getaddresses_addresses(fetchctx_t *fctx, isc_stdtime_t now, ISC_LIST_FOREACH(fctx->delegset->delegs, deleg, link) { dns_adbfind_t *find = NULL; - size_t maxaddrs = max_delegation_servers - *ns_processed; + size_t maxfindlen = max_delegation_servers - *ns_processed; size_t findlen = 0; if (*ns_processed >= max_delegation_servers) { @@ -3690,7 +3618,7 @@ fctx_getaddresses_addresses(fetchctx_t *fctx, isc_stdtime_t now, fetchctx_ref(fctx); dns_adb_createaddrinfosfind(fctx->adb, &deleg->addresses, fctx->res->view->dstport, options, - now, maxaddrs, &find, &findlen); + now, maxfindlen, &find, &findlen); if (find == NULL) { fetchctx_unref(fctx); @@ -3778,6 +3706,12 @@ shufflens: unsigned int static_stub = 0; unsigned int no_fetch = 0; dns_name_t *ns = nameservers[i]; + size_t maxfindlen = max_delegation_servers - *ns_processed; + size_t findlen = 0; + + if (*ns_processed >= max_delegation_servers) { + break; + } if (fctx->delegset->staticstub && dns_name_equal(ns, fctx->domain)) @@ -3794,15 +3728,15 @@ shufflens: } findname(fctx, ns, 0, stdoptions | static_stub | no_fetch, 0, - now, &overquota, need_alternatep, &have_address); + now, &overquota, need_alternatep, &have_address, + maxfindlen, &findlen); if (!overquota) { *all_spilledp = false; } + *ns_processed += findlen; - if (++(*ns_processed) >= max_delegation_servers) { - break; - } + INSIST(*ns_processed <= max_delegation_servers); } if (fctx->pending_running == 0 && !have_address) { @@ -3828,7 +3762,8 @@ fctx_getaddresses_alternate(fetchctx_t *fctx, isc_stdtime_t now, if (!a->isaddress) { findname(fctx, &a->_u._n.name, a->_u._n.port, stdoptions, FCTX_ADDRINFO_DUALSTACK, now, NULL, - NULL, NULL); + NULL, NULL, + fctx->res->view->max_delegation_servers, NULL); continue; } if (isc_sockaddr_pf(&a->_u.addr) != family) { @@ -4019,8 +3954,6 @@ out: * We've found some addresses. We might still be * looking for more addresses. */ - sort_finds(&fctx->finds, res->view->v6bias); - sort_finds(&fctx->altfinds, 0); return ISC_R_SUCCESS; } @@ -4132,6 +4065,76 @@ possibly_mark(fetchctx_t *fctx, dns_adbaddrinfo_t *addr) { } } +static dns_adbaddrinfo_t * +nextaddress(fetchctx_t *fctx) { + dns_adbaddrinfo_t *prevai = fctx->foundaddrinfo, *lowestsrttai = NULL; + unsigned int v6bias = fctx->res->view->v6bias, lowestsrtt = 0; + + /* + * Let's walk through the list of dns_adbaddrinfo_t to find the best + * next server address to query. This is linear on the number of + * dns_adbaddrinfo_t which are grouped in find list (for each ADB find). + */ + ISC_LIST_FOREACH(fctx->finds, find, publink) { + ISC_LIST_FOREACH(find->list, ai, publink) { + /* + * This address has been marked already, skip it. + */ + if (!UNMARKED(ai)) { + continue; + } + + /* + * This address is the same as the previously used + * address, it's a duplicate, mark it and skip it! + */ + if (prevai != NULL) { + if (prevai->entry == ai->entry) { + ai->flags |= FCTX_ADDRINFO_MARK; + continue; + } + } + + /* + * Mark and skip this address if incompatible (i.e. IPv6 + * address on a v4 only server, or for ACL reason, etc.) + */ + possibly_mark(fctx, ai); + if (!UNMARKED(ai)) { + continue; + } + + /* + * This address hasn't been tried yet and is a + * good candidate. Let's keep track of it if it + * has the lowest SRTT so far (or if there is no + * address with lowest SRTT found yet). + */ + unsigned int aisrtt = ai->srtt; + + if (isc_sockaddr_pf(&ai->sockaddr) != AF_INET6) { + aisrtt += v6bias; + } + + if (lowestsrttai == NULL || aisrtt < lowestsrtt) { + lowestsrttai = ai; + lowestsrtt = aisrtt; + continue; + } + } + } + + /* + * This is the next address to query. If this is NULL, we're done. + */ + if (lowestsrttai != NULL) { + lowestsrttai->flags |= FCTX_ADDRINFO_MARK; + } + fctx->foundaddrinfo = lowestsrttai; + + return lowestsrttai; +} + static dns_adbaddrinfo_t * fctx_nextaddress(fetchctx_t *fctx) { dns_adbfind_t *find = NULL, *start = NULL; @@ -4151,7 +4154,6 @@ fctx_nextaddress(fetchctx_t *fctx) { possibly_mark(fctx, ai); if (UNMARKED(ai)) { ai->flags |= FCTX_ADDRINFO_MARK; - fctx->find = NULL; fctx->forwarding = true; /* @@ -4172,44 +4174,7 @@ fctx_nextaddress(fetchctx_t *fctx) { fctx->forwarding = false; FCTX_ATTR_SET(fctx, FCTX_ATTR_TRIEDFIND); - find = fctx->find; - if (find == NULL) { - find = ISC_LIST_HEAD(fctx->finds); - } else { - find = ISC_LIST_NEXT(find, publink); - if (find == NULL) { - find = ISC_LIST_HEAD(fctx->finds); - } - } - - /* - * Find the first unmarked addrinfo. - */ - if (find != NULL) { - start = find; - do { - ISC_LIST_FOREACH(find->list, ai, publink) { - if (!UNMARKED(ai)) { - continue; - } - possibly_mark(fctx, ai); - if (UNMARKED(ai)) { - ai->flags |= FCTX_ADDRINFO_MARK; - faddrinfo = ai; - break; - } - } - if (faddrinfo != NULL) { - break; - } - find = ISC_LIST_NEXT(find, publink); - if (find == NULL) { - find = ISC_LIST_HEAD(fctx->finds); - } - } while (find != start); - } - - fctx->find = find; + faddrinfo = nextaddress(fctx); if (faddrinfo != NULL) { return faddrinfo; } diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 3414ed8d35..4ab816c1f2 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -17278,9 +17278,11 @@ checkds_find_address(dns_checkds_t *checkds) { isc_result_t result; unsigned int options; dns_adb_t *adb = NULL; + dns_view_t *view = NULL; REQUIRE(DNS_CHECKDS_VALID(checkds)); + view = checkds->zone->view; options = DNS_ADBFIND_WANTEVENT; if (isc_net_probeipv4() != ISC_R_DISABLED) { options |= DNS_ADBFIND_INET; @@ -17289,7 +17291,7 @@ checkds_find_address(dns_checkds_t *checkds) { options |= DNS_ADBFIND_INET6; } - dns_view_getadb(checkds->zone->view, &adb); + dns_view_getadb(view, &adb); if (adb == NULL) { goto destroy; } @@ -17297,7 +17299,7 @@ checkds_find_address(dns_checkds_t *checkds) { result = dns_adb_createfind( adb, checkds->zone->loop, process_checkds_adb_event, checkds, &checkds->ns, options, 0, checkds->zone->view->dstport, 0, NULL, - NULL, NULL, &checkds->find); + NULL, NULL, view->max_delegation_servers, &checkds->find, NULL); dns_adb_detach(&adb); /* Something failed? */