From: Ondřej Surý Date: Wed, 20 May 2026 16:28:15 +0000 (+0200) Subject: System test for nxdomain-redirect combined with dns64 X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=4fc5d250125d24fc8ce9586182003c5b15bb2f76;p=thirdparty%2Fbind9.git System test for nxdomain-redirect combined with dns64 An AAAA query for a non-existent name into a view that combines nxdomain-redirect with dns64 used to abort named via the DNS64 fallback in query_nodata(). The new module exercises all three documented entry paths into query_redirect(): the authoritative NXDOMAIN path (ns7, tripping INSIST(!is_zone) in query_notfound()), the recursive NCACHENXRRSET path (ns8, tripping REQUIRE in dns_rdataset_first() on a disassociated rdataset), and the synth-from-dnssec path (ns10 validating against ns9's signed root, with a primer A query so the second AAAA reaches query_redirect() via query_coveringnsec()). ns9 serves as a neutral upstream so the cached and synthesized negatives land real NXRRSETs. Assisted-by: Claude:claude-opus-4-7 (cherry picked from commit 739a067de89834820372ae14171c0889f7aedc83) --- diff --git a/bin/tests/system/redirect/ns10/named.conf.j2 b/bin/tests/system/redirect/ns10/named.conf.j2 new file mode 100644 index 00000000000..9e019cc5173 --- /dev/null +++ b/bin/tests/system/redirect/ns10/named.conf.j2 @@ -0,0 +1,29 @@ +// NS10 — validating recursor used to drive the synth-from-dnssec entry +// into query_redirect (covering NSEC from ns9 synthesizes NXDOMAIN +// locally, then nxdomain-redirect+dns64 hit the same buggy path as ns7). + +options { + port @PORT@; + listen-on port @PORT@ { 10.53.0.10; }; + pid-file "named.pid"; + nxdomain-redirect redirect; + dnssec-validation yes; + synth-from-dnssec yes; + dns64 64:ff9b::/96 { + clients { any; }; + mapped { any; }; + suffix ::; + }; +}; + +include "trusted.conf"; + +zone "." { + type hint; + file "root.hints"; +}; + +zone "redirect" { + type primary; + file "redirect.db"; +}; diff --git a/bin/tests/system/redirect/ns10/redirect.db b/bin/tests/system/redirect/ns10/redirect.db new file mode 100644 index 00000000000..aa7dc60f3e3 --- /dev/null +++ b/bin/tests/system/redirect/ns10/redirect.db @@ -0,0 +1,5 @@ +$TTL 300 +@ IN SOA ns.redirect. admin.redirect. 1 3600 900 604800 86400 +@ IN NS ns.redirect. +ns IN A 10.53.0.10 +* IN A 203.0.113.1 diff --git a/bin/tests/system/redirect/ns10/root.hints b/bin/tests/system/redirect/ns10/root.hints new file mode 100644 index 00000000000..8150fabf266 --- /dev/null +++ b/bin/tests/system/redirect/ns10/root.hints @@ -0,0 +1,2 @@ +. 518400 IN NS a.root-servers.nil. +a.root-servers.nil. 518400 IN A 10.53.0.9 diff --git a/bin/tests/system/redirect/ns7/named.conf.j2 b/bin/tests/system/redirect/ns7/named.conf.j2 new file mode 100644 index 00000000000..0ceb769ac0f --- /dev/null +++ b/bin/tests/system/redirect/ns7/named.conf.j2 @@ -0,0 +1,24 @@ +// NS7 + +options { + port @PORT@; + listen-on port @PORT@ { 10.53.0.7; }; + pid-file "named.pid"; + nxdomain-redirect redirect; + dnssec-validation no; + dns64 64:ff9b::/96 { + clients { any; }; + mapped { any; }; + suffix ::; + }; +}; + +zone "." { + type primary; + file "root.db"; +}; + +zone "redirect" { + type primary; + file "redirect.db"; +}; diff --git a/bin/tests/system/redirect/ns7/redirect.db b/bin/tests/system/redirect/ns7/redirect.db new file mode 100644 index 00000000000..5d3d4352cb6 --- /dev/null +++ b/bin/tests/system/redirect/ns7/redirect.db @@ -0,0 +1,5 @@ +$TTL 300 +@ IN SOA ns.redirect. admin.redirect. 1 3600 900 604800 86400 +@ IN NS ns.redirect. +ns IN A 10.53.0.7 +* IN A 203.0.113.1 diff --git a/bin/tests/system/redirect/ns7/root.db b/bin/tests/system/redirect/ns7/root.db new file mode 100644 index 00000000000..f2fa9108edb --- /dev/null +++ b/bin/tests/system/redirect/ns7/root.db @@ -0,0 +1,3 @@ +. 86400 IN SOA a.root-servers.nil. hostmaster.example.net. 2019022100 1800 900 604800 86400 +. 518400 IN NS a.root-servers.nil. +a.root-servers.nil. 518400 IN A 10.53.0.7 diff --git a/bin/tests/system/redirect/ns8/named.conf.j2 b/bin/tests/system/redirect/ns8/named.conf.j2 new file mode 100644 index 00000000000..d1513b31bba --- /dev/null +++ b/bin/tests/system/redirect/ns8/named.conf.j2 @@ -0,0 +1,19 @@ +// NS8 + +options { + port @PORT@; + listen-on port @PORT@ { 10.53.0.8; }; + pid-file "named.pid"; + nxdomain-redirect redirect; + dnssec-validation no; + dns64 64:ff9b::/96 { + clients { any; }; + mapped { any; }; + suffix ::; + }; +}; + +zone "." { + type hint; + file "root.hints"; +}; diff --git a/bin/tests/system/redirect/ns8/root.hints b/bin/tests/system/redirect/ns8/root.hints new file mode 100644 index 00000000000..8150fabf266 --- /dev/null +++ b/bin/tests/system/redirect/ns8/root.hints @@ -0,0 +1,2 @@ +. 518400 IN NS a.root-servers.nil. +a.root-servers.nil. 518400 IN A 10.53.0.9 diff --git a/bin/tests/system/redirect/ns9/named.conf.j2 b/bin/tests/system/redirect/ns9/named.conf.j2 new file mode 100644 index 00000000000..1001396a443 --- /dev/null +++ b/bin/tests/system/redirect/ns9/named.conf.j2 @@ -0,0 +1,20 @@ +// NS9 — signed-root upstream for ns8 (advisor cached path) and ns10 +// (synth-from-dnssec path) + +options { + port @PORT@; + listen-on port @PORT@ { 10.53.0.9; }; + pid-file "named.pid"; + dnssec-validation no; + recursion no; +}; + +zone "." { + type primary; + file "root.db.signed"; +}; + +zone "redirect" { + type primary; + file "redirect.db"; +}; diff --git a/bin/tests/system/redirect/ns9/redirect.db b/bin/tests/system/redirect/ns9/redirect.db new file mode 100644 index 00000000000..a6ae61e964d --- /dev/null +++ b/bin/tests/system/redirect/ns9/redirect.db @@ -0,0 +1,5 @@ +$TTL 300 +@ IN SOA ns.redirect. admin.redirect. 1 3600 900 604800 86400 +@ IN NS ns.redirect. +ns IN A 10.53.0.9 +* IN A 203.0.113.1 diff --git a/bin/tests/system/redirect/ns9/root.db.in b/bin/tests/system/redirect/ns9/root.db.in new file mode 100644 index 00000000000..e512a4c3bdc --- /dev/null +++ b/bin/tests/system/redirect/ns9/root.db.in @@ -0,0 +1,3 @@ +. 86400 IN SOA a.root-servers.nil. hostmaster.example.net. 2019022100 1800 900 604800 86400 +. 518400 IN NS a.root-servers.nil. +a.root-servers.nil. 518400 IN A 10.53.0.9 diff --git a/bin/tests/system/redirect/ns9/sign.sh b/bin/tests/system/redirect/ns9/sign.sh new file mode 100644 index 00000000000..4de4111b579 --- /dev/null +++ b/bin/tests/system/redirect/ns9/sign.sh @@ -0,0 +1,27 @@ +#!/bin/sh -e + +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +. ../../conf.sh + +zone=. +infile=root.db.in +zonefile=root.db + +key1=$($KEYGEN -q -a $DEFAULT_ALGORITHM $zone) +key2=$($KEYGEN -q -a $DEFAULT_ALGORITHM -fk $zone) + +cat $infile $key1.key $key2.key >$zonefile + +$SIGNER -P -g -O full -o $zone $zonefile >sign.ns9.root.out + +keyfile_to_static_keys $key2 >../ns10/trusted.conf diff --git a/bin/tests/system/redirect/setup.sh b/bin/tests/system/redirect/setup.sh index b98c6595178..bbe3a9d4456 100644 --- a/bin/tests/system/redirect/setup.sh +++ b/bin/tests/system/redirect/setup.sh @@ -20,3 +20,4 @@ cp ns2/example.db.in ns2/example.db cp ns4/example.db.in ns4/example.db (cd ns3 && $SHELL sign.sh) (cd ns5 && $SHELL sign.sh) +(cd ns9 && $SHELL sign.sh) diff --git a/bin/tests/system/redirect/tests_redirect_dns64.py b/bin/tests/system/redirect/tests_redirect_dns64.py new file mode 100644 index 00000000000..7a4b121ee6b --- /dev/null +++ b/bin/tests/system/redirect/tests_redirect_dns64.py @@ -0,0 +1,102 @@ +#!/usr/bin/python3 + +# 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 pytest + +import isctest + +pytestmark = pytest.mark.extra_artifacts( + [ + "dig.out.*", + "ns1/K*", + "ns1/*.signed", + "ns1/dsset-nsec3.", + "ns1/dsset-signed.", + "ns1/nsec3.db", + "ns1/signed.db", + "ns2/example.db", + "ns2/named.stats", + "ns2/redirect.db", + "ns3/K*", + "ns3/*.signed", + "ns3/dsset-nsec3.", + "ns3/dsset-signed.", + "ns3/nsec3.db", + "ns3/signed.db", + "ns4/example.db", + "ns4/named.stats", + "ns5/K*", + "ns5/dsset-*", + "ns5/*.signed", + "ns5/root.db", + "ns5/sign.ns5.*", + "ns5/signed.db", + "ns6/signed.db.signed", + "ns9/K*", + "ns9/dsset-*", + "ns9/root.db", + "ns9/root.db.signed", + "ns9/sign.ns9.*", + "ns10/trusted.conf", + ] +) + + +def _no_crash(server, qname): + # DO=0 so nxdomain-redirect is not skipped on validated upstream + # responses; the documented landing-page deployment serves + # non-DNSSEC clients. + msg = isctest.query.create(qname, "AAAA", dnssec=False) + response = isctest.query.tcp(msg, server.ip) + isctest.check.noerror(response) + + +def _alive(server): + msg = isctest.query.create("ns.redirect.", "A", dnssec=False) + response = isctest.query.tcp(msg, server.ip) + isctest.check.noerror(response) + + +def test_nxdomain_redirect_dns64_authoritative(ns7): + # Direct AAAA to a server that is authoritative for both '.' (NXDOMAIN) + # and the redirect zone (wildcard A only). Reproduces the + # INSIST(!qctx->is_zone) abort in query_notfound() entered via + # authoritative NXDOMAIN. + _no_crash(ns7, "no-exist.") + _alive(ns7) + + +def test_nxdomain_redirect_dns64_recursive(ns8): + # Recursive resolver: the upstream returns a real NOERROR-empty AAAA + # for '*.redirect.', which the resolver caches as NCACHENXRRSET. + # Reproduces the REQUIRE(rdataset->methods != NULL) abort in + # dns_rdataset_first() reached via the disassociated rdataset on the + # second pass through redirect2(). + _no_crash(ns8, "no-exist.") + _alive(ns8) + + +def test_nxdomain_redirect_dns64_synth_from_dnssec(ns10): + # Validating recursor with synth-from-dnssec. Prime the NSEC chain + # with an A query (the redirect zone serves a wildcard A, so this + # path returns successfully without entering the DNS64 fallback). A + # subsequent AAAA query for a different nonexistent name is then + # synthesized via query_coveringnsec() and reaches query_redirect() + # through the third documented entry. Same downstream bug as the + # authoritative path. + msg = isctest.query.create("prime.", "A", dnssec=False) + response = isctest.query.tcp(msg, ns10.ip) + isctest.check.noerror(response) + + _no_crash(ns10, "trigger.") + _alive(ns10) diff --git a/bin/tests/system/redirect/tests_sh_redirect.py b/bin/tests/system/redirect/tests_sh_redirect.py index 5f20aad6f26..e985527c942 100644 --- a/bin/tests/system/redirect/tests_sh_redirect.py +++ b/bin/tests/system/redirect/tests_sh_redirect.py @@ -38,6 +38,12 @@ pytestmark = pytest.mark.extra_artifacts( "ns5/sign.ns5.*", "ns5/signed.db", "ns6/signed.db.signed", + "ns9/K*", + "ns9/dsset-*", + "ns9/root.db", + "ns9/root.db.signed", + "ns9/sign.ns9.*", + "ns10/trusted.conf", ] )