From ea2cb4e9df52d0eadcf87d0c49df258c51a02a71 Mon Sep 17 00:00:00 2001 From: Colin Vidal Date: Fri, 27 Mar 2026 16:00:25 +0100 Subject: [PATCH] test for auth+res server and glues in delegation When a resolver+auth server has a delegation on a local zone and has a glue, the glue can only be for in-domain NS. In this case, when the resolver is looking at the zonecut, `dns_view_bestzonecut()` synthesizes a delegset from an NS rdataset found in the local zone (the delegation inside auth zone), and ignores the glues if any. As a result, the delegset will contain a single delegation of type DNS_DELEGTYPE_NS_NAMES, which leads to an ADB fetch. But it's actually an in-memory fetch, because in this case, the fetch will immediately find the A/AAAA glues from the local zone. An alternative approach (not chosen here) would be to make `dns_view_bestzonecut()`, when converting an NS rdataset into a `dns_deleg_t`, check for glues for the delegation in the auth zone, and add those in the `dns_deleg_t`. The delegation would be of type DNS_DELEGTYPE_NS_GLUES which would avoid the ADB name lookup. However, that's extra code, extra logic and complexities, for a lookup that will be done in memory anyway, just a bit later. So for now, this is not implemented that way. The test is added, however, to confirm that there is no attempt from the resolver to get the NS fron the child zone. --- bin/tests/system/auth_res_deleg/README | 12 +++++ .../system/auth_res_deleg/ns1/named.conf.j2 | 30 ++++++++++++ bin/tests/system/auth_res_deleg/ns1/root.db | 24 ++++++++++ .../system/auth_res_deleg/ns2/example.db | 24 ++++++++++ .../system/auth_res_deleg/ns2/named.conf.j2 | 41 ++++++++++++++++ .../system/auth_res_deleg/ns3/named.conf.j2 | 30 ++++++++++++ .../system/auth_res_deleg/ns3/sub.example.db | 23 +++++++++ .../auth_res_deleg/tests_auth_res_deleg.py | 48 +++++++++++++++++++ lib/dns/view.c | 12 ++++- lib/ns/query.c | 2 +- 10 files changed, 244 insertions(+), 2 deletions(-) create mode 100644 bin/tests/system/auth_res_deleg/README create mode 100644 bin/tests/system/auth_res_deleg/ns1/named.conf.j2 create mode 100644 bin/tests/system/auth_res_deleg/ns1/root.db create mode 100644 bin/tests/system/auth_res_deleg/ns2/example.db create mode 100644 bin/tests/system/auth_res_deleg/ns2/named.conf.j2 create mode 100644 bin/tests/system/auth_res_deleg/ns3/named.conf.j2 create mode 100644 bin/tests/system/auth_res_deleg/ns3/sub.example.db create mode 100644 bin/tests/system/auth_res_deleg/tests_auth_res_deleg.py diff --git a/bin/tests/system/auth_res_deleg/README b/bin/tests/system/auth_res_deleg/README new file mode 100644 index 00000000000..cc051658e98 --- /dev/null +++ b/bin/tests/system/auth_res_deleg/README @@ -0,0 +1,12 @@ +ns1 simulates a root server + +ns2 is an auth server (over example.) which is also a resolver. + +ns3 is an auth server on `sub.example.`. + +The point of the test is to show that because ns2 knows the IP address of the +NS delegating sub.example., there won't be any queries to resolve +ns.sub.example. + +In order to prove it, ns2 also does a DNSTAP dump of its outgoing queries and +checks there is no NS queries to ns3 (but only an AAAA query). diff --git a/bin/tests/system/auth_res_deleg/ns1/named.conf.j2 b/bin/tests/system/auth_res_deleg/ns1/named.conf.j2 new file mode 100644 index 00000000000..41a46be0d07 --- /dev/null +++ b/bin/tests/system/auth_res_deleg/ns1/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.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; + dnssec-validation no; +}; + +zone "." { + type primary; + file "root.db"; +}; diff --git a/bin/tests/system/auth_res_deleg/ns1/root.db b/bin/tests/system/auth_res_deleg/ns1/root.db new file mode 100644 index 00000000000..4613a4e0504 --- /dev/null +++ b/bin/tests/system/auth_res_deleg/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 a.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 + +example. NS ns.example. +ns.example. A 10.53.0.2 diff --git a/bin/tests/system/auth_res_deleg/ns2/example.db b/bin/tests/system/auth_res_deleg/ns2/example.db new file mode 100644 index 00000000000..e7d388b2347 --- /dev/null +++ b/bin/tests/system/auth_res_deleg/ns2/example.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 example. example. ( + 2000042100 ; serial + 600 ; refresh + 600 ; retry + 1200 ; expire + 600 ; minimum + ) +@ NS ns.example. +ns.example. A 10.53.0.2 + +sub.example. NS ns.sub.example. +ns.sub.example. A 10.53.0.3 diff --git a/bin/tests/system/auth_res_deleg/ns2/named.conf.j2 b/bin/tests/system/auth_res_deleg/ns2/named.conf.j2 new file mode 100644 index 00000000000..dbd8a992e73 --- /dev/null +++ b/bin/tests/system/auth_res_deleg/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; }; + listen-on-v6 { none; }; + recursion yes; + notify yes; + dnssec-validation no; + dnstap { resolver query; }; + dnstap-output file "dnstap.out"; +}; + +zone "." { + type primary; + file "example.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/auth_res_deleg/ns3/named.conf.j2 b/bin/tests/system/auth_res_deleg/ns3/named.conf.j2 new file mode 100644 index 00000000000..503891d4758 --- /dev/null +++ b/bin/tests/system/auth_res_deleg/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; }; + listen-on-v6 { none; }; + recursion no; + notify yes; + dnssec-validation no; +}; + +zone "." { + type primary; + file "sub.example.db"; +}; diff --git a/bin/tests/system/auth_res_deleg/ns3/sub.example.db b/bin/tests/system/auth_res_deleg/ns3/sub.example.db new file mode 100644 index 00000000000..da53af62509 --- /dev/null +++ b/bin/tests/system/auth_res_deleg/ns3/sub.example.db @@ -0,0 +1,23 @@ +; 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 sub.example. sub.example. ( + 2000042100 ; serial + 600 ; refresh + 600 ; retry + 1200 ; expire + 600 ; minimum + ) +@ NS ns.sub.example. +ns.sub.example. A 10.53.0.3 + +aaaa.sub.example. AAAA ac::dc diff --git a/bin/tests/system/auth_res_deleg/tests_auth_res_deleg.py b/bin/tests/system/auth_res_deleg/tests_auth_res_deleg.py new file mode 100644 index 00000000000..3656dc34246 --- /dev/null +++ b/bin/tests/system/auth_res_deleg/tests_auth_res_deleg.py @@ -0,0 +1,48 @@ +# 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_query(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 + _, _, _, _, _, _, _, _, query = line.split(" ", 9) + return query + + +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 list(map(line_to_query, lines)) + + +def test_auth_res_deleg(ns2): + msg = isctest.query.create("aaaa.sub.example.", "AAAA") + res = isctest.query.udp(msg, ns2.ip) + isctest.check.noerror(res) + assert len(res.answer[0]) == 1 + res.answer[0].ttl = 300 + assert str(res.answer[0]) == "aaaa.sub.example. 300 IN AAAA ac::dc" + + queries = extract_dnstap(ns2) + assert len(queries) == 1 + assert queries[0] == "aaaa.sub.example/IN/AAAA" diff --git a/lib/dns/view.c b/lib/dns/view.c index 9f18357a244..2e4853d928b 100644 --- a/lib/dns/view.c +++ b/lib/dns/view.c @@ -1180,7 +1180,17 @@ dns_view_bestzonecut(dns_view_t *view, const dns_name_t *name, if (result != ISC_R_SUCCESS) { result = DNS_R_NXDOMAIN; } else { - dns_delegset_fromrdataset(rdataset, delegsetp); + /* + * The rdataset came either from a local zone or a hint. Either + * way, we only considering the NS rdataset here, so if there + * are glues, they'll be ignored. This is okay: the delegation + * type will be DNS_DELEGSET_NS_NAMES, so ADB will do a NS name + * lookup but immediately find the results locally (because this + * came from a local zone or hint). So the resolution will be + * the same, and this avoid adding extra code here to extract + * A/AAAA rdataset if any. + */ + dns_delegset_fromnsrdataset(rdataset, delegsetp); } dns_rdataset_cleanup(rdataset); diff --git a/lib/ns/query.c b/lib/ns/query.c index d59d1f6f73d..4deeda60742 100644 --- a/lib/ns/query.c +++ b/lib/ns/query.c @@ -8668,7 +8668,7 @@ query_delegation_recurse(query_ctx_t *qctx) { qctx->client->inner.now, 0, true, true, &delegset); if (tresult != ISC_R_SUCCESS) { - dns_delegset_fromrdataset(qctx->rdataset, &delegset); + dns_delegset_fromnsrdataset(qctx->rdataset, &delegset); fname = qctx->fname; } -- 2.47.3