diff --git a/bin/named/main.c b/bin/named/main.c index f3b2ec80f5..b298d6e98d 100644 --- a/bin/named/main.c +++ b/bin/named/main.c @@ -123,6 +123,7 @@ extern unsigned int dns_zone_mkey_month; extern unsigned int dns_adb_entrywindow; extern unsigned int dns_adb_cachemin; extern size_t dns_dispatch_tcppipelining; +extern size_t dns_adb_addrslimit; static bool want_stats = false; static char program_name[NAME_MAX] = "named"; @@ -817,6 +818,13 @@ parse_T_opt(char *option) { "least 1"); } dns_dispatch_tcppipelining = pipelining; + } else if (!strncmp(option, "adbaddrslimit=", 14)) { + size_t adb_addrslimit = atoi(option + 14); + if (adb_addrslimit < 1) { + named_main_earlyfatal("adbaddrslimit must be at " + "least 1"); + } + dns_adb_addrslimit = adb_addrslimit; } else { fprintf(stderr, "unknown -T flag '%s'\n", option); } 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..5935fd841c --- /dev/null +++ b/bin/tests/system/selfpointedglue/ns2/tld.db @@ -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 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..83ea4d37ec --- /dev/null +++ b/bin/tests/system/selfpointedglue/ns3/example.tld.db @@ -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 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/named.conf.j2 b/bin/tests/system/selfpointedglue/ns3/named.conf.j2 new file mode 100644 index 0000000000..b5c8bfcf33 --- /dev/null +++ b/bin/tests/system/selfpointedglue/ns3/named.conf.j2 @@ -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"; +}; 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..071508fd70 --- /dev/null +++ b/bin/tests/system/selfpointedglue/ns4/named.args.j2 @@ -0,0 +1,3 @@ +{% set adblimit = adblimit | default("") %} + +-D selfpointedglue-ns4 -m record -c named.conf -d 99 -g -T maxcachesize=2097152 -4 @adblimit@ 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..0fb6c3e374 --- /dev/null +++ b/bin/tests/system/selfpointedglue/tests_selfpointedglue.py @@ -0,0 +1,122 @@ +# 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 subprocess + +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 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, 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, + ) + + +def test_selfpointedglue_adblimit(ns4, templates): + templates.render("ns4/named.args", {"adblimit": "-T adbaddrslimit=2"}) + with ns4.watch_log_from_here() as watcher: + # Server needs a full stop/restart to read the new command line options. + ns4.stop() + ns4.start(["--noclean", "--restart", "--port", os.environ["PORT"]]) + watcher.wait_for_line("running") + + 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, 6) + + 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"), + # Because of the ADB limit, only 2 IP are used instead + # of the 10 provided ones. + ("10.53.0.3", "a.sub.example.tld/IN/A"), + ("10.53.0.5", "a.sub.example.tld/IN/A"), + ], + ips_and_queries, + ) + + +def test_selfpointedglue_adblimitlower(ns4, templates): + templates.render("ns4/named.args", {"adblimit": "-T adbaddrslimit=0"}) + with ns4.watch_log_from_here() as watcher: + # Server needs a full stop/restart to read the new command line options. + ns4.stop() + failed_to_start = False + try: + ns4.start(["--noclean", "--restart", "--port", os.environ["PORT"]]) + except subprocess.CalledProcessError as _: + failed_to_start = True + assert failed_to_start is True + watcher.wait_for_line("adbaddrslimit must be at least 1") 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/lib/dns/adb.c b/lib/dns/adb.c index 50d4d83bf3..1514fb46b9 100644 --- a/lib/dns/adb.c +++ b/lib/dns/adb.c @@ -78,6 +78,15 @@ #define DNS_ADB_MINADBSIZE (1024U * 1024U) /*%< 1 Megabyte */ +/* + * Default and override for the per-find address limit, the sum of the number of + * A and AAAA RR from an ADB NS name resolution. When non-zero, this value is + * used instead of the default. Can be set via 'named -T adbaddrslimit=N' for + * testing. + */ +#define DEFAULT_ADDRSLIMIT 6 +size_t dns_adb_addrslimit = 0; + typedef ISC_LIST(dns_adbname_t) dns_adbnamelist_t; typedef struct dns_adbnamehook dns_adbnamehook_t; typedef ISC_LIST(dns_adbnamehook_t) dns_adbnamehooklist_t; @@ -1473,6 +1482,9 @@ static void copy_namehook_lists(dns_adb_t *adb, dns_adbfind_t *find, dns_adbname_t *name) { dns_adbnamehook_t *namehook = NULL; dns_adbentry_t *entry = NULL; + size_t count = 0; + size_t limit = dns_adb_addrslimit != 0 ? dns_adb_addrslimit + : DEFAULT_ADDRSLIMIT; if ((find->options & DNS_ADBFIND_INET) != 0) { namehook = ISC_LIST_HEAD(name->v4); @@ -1493,6 +1505,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); + + if (++count >= limit) { + DP(ISC_LOG_DEBUG(3), "skipping addresses"); + return; + } + nextv4: namehook = ISC_LIST_NEXT(namehook, name_link); } @@ -1517,6 +1535,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); + + if (++count >= limit) { + DP(ISC_LOG_DEBUG(3), "skipping addresses"); + return; + } + nextv6: namehook = ISC_LIST_NEXT(namehook, name_link); } diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index 6d6fb8eba3..8b76b7da23 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -374,7 +374,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 @@ -1314,7 +1323,7 @@ fctx_cleanup(fetchctx_t *fctx) { dns_adb_destroyfind(&find); fetchctx_unref(fctx); } - fctx->find = NULL; + fctx->foundaddrinfo = NULL; for (find = ISC_LIST_HEAD(fctx->altfinds); find != NULL; find = next_find) @@ -3164,89 +3173,6 @@ add_bad(fetchctx_t *fctx, dns_message_t *rmessage, dns_adbaddrinfo_t *addrinfo, isc_result_totext(reason), namebuf, typebuf, 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, *curr; - dns_adbfindlist_t sorted; - dns_adbaddrinfo_t *addrinfo, *bestaddrinfo; - - /* Sort each find's addrinfo list by SRTT. */ - for (curr = ISC_LIST_HEAD(*findlist); curr != NULL; - curr = ISC_LIST_NEXT(curr, publink)) - { - sort_adbfind(curr, bias); - } - - /* Lame N^2 bubble sort. */ - ISC_LIST_INIT(sorted); - while (!ISC_LIST_EMPTY(*findlist)) { - 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 @@ -3367,6 +3293,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 { @@ -3841,8 +3768,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); result = ISC_R_SUCCESS; } @@ -3914,6 +3839,80 @@ 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). + */ + for (dns_adbfind_t *find = ISC_LIST_HEAD(fctx->finds); find != NULL; + find = ISC_LIST_NEXT(find, publink)) + { + for (dns_adbaddrinfo_t *ai = ISC_LIST_HEAD(find->list); + ai != NULL; ai = ISC_LIST_NEXT(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, *start; @@ -3936,7 +3935,6 @@ fctx_nextaddress(fetchctx_t *fctx) { possibly_mark(fctx, addrinfo); if (UNMARKED(addrinfo)) { addrinfo->flags |= FCTX_ADDRINFO_MARK; - fctx->find = NULL; fctx->forwarding = true; /* @@ -3957,49 +3955,9 @@ 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. - */ - addrinfo = NULL; - if (find != NULL) { - start = find; - do { - for (addrinfo = ISC_LIST_HEAD(find->list); - addrinfo != NULL; - addrinfo = ISC_LIST_NEXT(addrinfo, publink)) - { - if (!UNMARKED(addrinfo)) { - continue; - } - possibly_mark(fctx, addrinfo); - if (UNMARKED(addrinfo)) { - addrinfo->flags |= FCTX_ADDRINFO_MARK; - break; - } - } - if (addrinfo != NULL) { - break; - } - find = ISC_LIST_NEXT(find, publink); - if (find == NULL) { - find = ISC_LIST_HEAD(fctx->finds); - } - } while (find != start); - } - - fctx->find = find; - if (addrinfo != NULL) { - return addrinfo; + faddrinfo = nextaddress(fctx); + if (faddrinfo != NULL) { + return faddrinfo; } /*