diff --git a/bin/tests/system/nsprocessinglimit/ns1/named.conf.j2 b/bin/tests/system/nsprocessinglimit/ns1/named.conf.j2 new file mode 100644 index 0000000000..5ad42a185a --- /dev/null +++ b/bin/tests/system/nsprocessinglimit/ns1/named.conf.j2 @@ -0,0 +1,39 @@ +/* + * 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; +}; + +view "default" { + zone "." { + type primary; + file "root.db"; + }; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm @DEFAULT_HMAC@; +}; + +controls { + inet 10.53.0.1 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; diff --git a/bin/tests/system/nsprocessinglimit/ns1/root.db b/bin/tests/system/nsprocessinglimit/ns1/root.db new file mode 100644 index 0000000000..41c97bf445 --- /dev/null +++ b/bin/tests/system/nsprocessinglimit/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 marka.isc.org. 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/nsprocessinglimit/ns2/named.conf.j2 b/bin/tests/system/nsprocessinglimit/ns2/named.conf.j2 new file mode 100644 index 0000000000..8851c3728d --- /dev/null +++ b/bin/tests/system/nsprocessinglimit/ns2/named.conf.j2 @@ -0,0 +1,37 @@ +/* + * 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"; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm @DEFAULT_HMAC@; +}; + +controls { + inet 10.53.0.2 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; diff --git a/bin/tests/system/nsprocessinglimit/ns2/tld.db b/bin/tests/system/nsprocessinglimit/ns2/tld.db new file mode 100644 index 0000000000..003fa089d2 --- /dev/null +++ b/bin/tests/system/nsprocessinglimit/ns2/tld.db @@ -0,0 +1,25 @@ +; 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 marka.isc.org. 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 + diff --git a/bin/tests/system/nsprocessinglimit/ns3/example.tld.db b/bin/tests/system/nsprocessinglimit/ns3/example.tld.db new file mode 100644 index 0000000000..48f6219693 --- /dev/null +++ b/bin/tests/system/nsprocessinglimit/ns3/example.tld.db @@ -0,0 +1,68 @@ +; 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 marka.isc.org. 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. +sub.example.tld. NS ns11.sub.example.tld. +sub.example.tld. NS ns12.sub.example.tld. +sub.example.tld. NS ns12.sub.example.tld. +sub.example.tld. NS ns12.sub.example.tld. +sub.example.tld. NS ns13.sub.example.tld. +sub.example.tld. NS ns14.sub.example.tld. +sub.example.tld. NS ns15.sub.example.tld. +sub.example.tld. NS ns16.sub.example.tld. +sub.example.tld. NS ns17.sub.example.tld. +sub.example.tld. NS ns18.sub.example.tld. +sub.example.tld. NS ns19.sub.example.tld. +sub.example.tld. NS ns20.sub.example.tld. +sub.example.tld. NS ns21.sub.example.tld. +sub.example.tld. NS ns22.sub.example.tld. +sub.example.tld. NS ns23.sub.example.tld. +ns01.sub.example.tld. A 127.0.0.1 +ns02.sub.example.tld. A 127.0.0.2 +ns03.sub.example.tld. A 127.0.0.3 +ns04.sub.example.tld. A 127.0.0.4 +ns05.sub.example.tld. A 127.0.0.5 +ns06.sub.example.tld. A 127.0.0.6 +ns07.sub.example.tld. A 127.0.0.7 +ns08.sub.example.tld. A 127.0.0.8 +ns09.sub.example.tld. A 127.0.0.9 +ns10.sub.example.tld. A 127.0.0.10 +ns11.sub.example.tld. A 127.0.0.11 +ns12.sub.example.tld. A 127.0.0.12 +ns13.sub.example.tld. A 127.0.0.13 +ns14.sub.example.tld. A 127.0.0.14 +ns15.sub.example.tld. A 127.0.0.15 +ns16.sub.example.tld. A 127.0.0.16 +ns17.sub.example.tld. A 127.0.0.17 +ns18.sub.example.tld. A 127.0.0.18 +ns19.sub.example.tld. A 127.0.0.19 +ns20.sub.example.tld. A 127.0.0.20 +ns21.sub.example.tld. A 127.0.0.21 +ns22.sub.example.tld. A 127.0.0.22 +ns23.sub.example.tld. A 127.0.0.23 diff --git a/bin/tests/system/nsprocessinglimit/ns3/named.conf.j2 b/bin/tests/system/nsprocessinglimit/ns3/named.conf.j2 new file mode 100644 index 0000000000..15b68ee434 --- /dev/null +++ b/bin/tests/system/nsprocessinglimit/ns3/named.conf.j2 @@ -0,0 +1,37 @@ +/* + * 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 "example.tld." { + type primary; + file "example.tld.db"; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm @DEFAULT_HMAC@; +}; + +controls { + inet 10.53.0.3 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; diff --git a/bin/tests/system/nsprocessinglimit/ns4/named.args b/bin/tests/system/nsprocessinglimit/ns4/named.args new file mode 100644 index 0000000000..71c23a43ce --- /dev/null +++ b/bin/tests/system/nsprocessinglimit/ns4/named.args @@ -0,0 +1 @@ +-D nsprocessinglimit-ns4 -m record -c named.conf -d 99 -g -T maxcachesize=2097152 -4 diff --git a/bin/tests/system/nsprocessinglimit/ns4/named.conf.j2 b/bin/tests/system/nsprocessinglimit/ns4/named.conf.j2 new file mode 100644 index 0000000000..8f1aea151b --- /dev/null +++ b/bin/tests/system/nsprocessinglimit/ns4/named.conf.j2 @@ -0,0 +1,39 @@ +/* + * 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.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"; +}; + +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/nsprocessinglimit/ns4/root.hint b/bin/tests/system/nsprocessinglimit/ns4/root.hint new file mode 100644 index 0000000000..d7d0e1faba --- /dev/null +++ b/bin/tests/system/nsprocessinglimit/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/nsprocessinglimit/tests_nsprocessinglimit.py b/bin/tests/system/nsprocessinglimit/tests_nsprocessinglimit.py new file mode 100644 index 0000000000..3577c89460 --- /dev/null +++ b/bin/tests/system/nsprocessinglimit/tests_nsprocessinglimit.py @@ -0,0 +1,74 @@ +# 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 map(line_to_ips_and_queries, lines) + + +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 expect_next_ip_and_query(expected_ips_and_queries, ips_and_queries): + for expected_ip, expected_query in expected_ips_and_queries: + ip, query = next(ips_and_queries) + assert ip == expected_ip + assert query == expected_query + + +def test_selfpointedglue_nslimit(ns4): + msg = isctest.query.create("a.sub.example.tld.", "A") + res = isctest.query.tcp(msg, ns4.ip) + isctest.check.servfail(res) + + # The 4 formers lines are request to find sub.example2.tld NSs. + # The latest 20 are queries to sub.example2.tld NSs. + ips_and_queries = extract_dnstap(ns4, 24) + + # Checking the begining of the resulution + expect_next_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"), + ], + ips_and_queries, + ) + expect_query("a.sub.example.tld/IN/A", 20, ips_and_queries) diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index 1557aef2e8..1a3bfb054f 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -3474,6 +3474,8 @@ fctx_getaddresses(fetchctx_t *fctx) { bool have_address = false; unsigned int ns_processed = 0; size_t fetches_allowed = 0; + dns_rdata_t nameservers_s[NS_PROCESSING_LIMIT]; + dns_rdata_t *nameservers[NS_PROCESSING_LIMIT]; FCTXTRACE5("getaddresses", "fctx->depth=", fctx->depth); @@ -3657,73 +3659,74 @@ normal_nses: break; } + for (result = dns_rdataset_first(&fctx->nameservers); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&fctx->nameservers)) + { + dns_rdata_t *rdata = nameservers[ns_processed] = + &nameservers_s[ns_processed]; + + dns_rdata_init(rdata); + + dns_rdataset_current(&fctx->nameservers, rdata); + + if (++ns_processed >= NS_PROCESSING_LIMIT) { + break; + } + } + + if (ns_processed > 1 && ns_processed > fetches_allowed) { + /* + * Skip the shuffle if: + * - there's nothing to shuffle (no or one nameserver) + * - there are less nameserver than allowed fetches as + * we are going to start fetches for all of them. + */ + for (size_t i = 0; i < ns_processed - 1; i++) { + size_t j = i + isc_random_uniform(ns_processed - i); + + ISC_SWAP(nameservers[i], nameservers[j]); + } + } + for (;;) { - size_t nscount = dns_rdataset_count(&fctx->nameservers); - size_t maxstartns = nscount > NS_PROCESSING_LIMIT - ? NS_PROCESSING_LIMIT - : nscount; - size_t startns = isc_random_uniform(maxstartns); + for (size_t i = 0; i < ns_processed; i++) { + bool overquota = false; + unsigned int static_stub = 0; + unsigned int no_fetch = 0; + dns_rdata_t *rdata = nameservers[i]; - for (size_t pass = 0; pass < 2; pass++) { - size_t curns = 0; - - for (result = dns_rdataset_first(&fctx->nameservers); - result == ISC_R_SUCCESS; - result = dns_rdataset_next(&fctx->nameservers)) - { - dns_rdata_t rdata = DNS_RDATA_INIT; - bool overquota = false; - unsigned int static_stub = 0; - unsigned int no_fetch = 0; - - if (pass == 0 && curns++ < startns) { - continue; - } - if (pass == 1 && curns++ >= startns) { - break; - } - - dns_rdataset_current(&fctx->nameservers, - &rdata); - /* - * Extract the name from the NS record. - */ - result = dns_rdata_tostruct(&rdata, &ns, NULL); - if (result != ISC_R_SUCCESS) { - continue; - } - - if (STATICSTUB(&fctx->nameservers) && - dns_name_equal(&ns.name, fctx->domain)) - { - static_stub = DNS_ADBFIND_STATICSTUB; - } - - /* - * Make sure we only launch a limited number of - * outgoing fetches. - */ - if (fctx->pending_running >= fetches_allowed) { - no_fetch = DNS_ADBFIND_NOFETCH; - } - - findname(fctx, &ns.name, 0, - stdoptions | static_stub | no_fetch, 0, - now, &overquota, &need_alternate, - &have_address); - - if (!overquota) { - all_spilled = false; - } - - dns_rdata_reset(&rdata); - dns_rdata_freestruct(&ns); - - if (++ns_processed >= NS_PROCESSING_LIMIT) { - result = ISC_R_NOMORE; - break; - } + /* + * Extract the name from the NS record. + */ + result = dns_rdata_tostruct(rdata, &ns, NULL); + if (result != ISC_R_SUCCESS) { + continue; } + + if (STATICSTUB(&fctx->nameservers) && + dns_name_equal(&ns.name, fctx->domain)) + { + static_stub = DNS_ADBFIND_STATICSTUB; + } + + /* + * Make sure we only launch a limited number of + * outgoing fetches. + */ + if (fctx->pending_running >= fetches_allowed) { + no_fetch = DNS_ADBFIND_NOFETCH; + } + + findname(fctx, &ns.name, 0, + stdoptions | static_stub | no_fetch, 0, now, + &overquota, &need_alternate, &have_address); + + if (!overquota) { + all_spilled = false; + } + + dns_rdata_freestruct(&ns); } /*