From: Colin Vidal Date: Mon, 1 Jun 2026 15:38:18 +0000 (+0200) Subject: System test covering SERVFAIL cache and CNAME X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=d238b8eece7eeca182e7d9321fc2679b6b98d2ca;p=thirdparty%2Fbind9.git System test covering SERVFAIL cache and CNAME Add a system test for the case where resolution SERVFAILs because the fetch context reaches the `max-query-count` threshold while following a CNAME. Resolving the CNAME target independently should still work, because the SERVFAIL cache stores the original query name rather than the target. --- diff --git a/bin/tests/system/sfcache_cname/ans1/ans.py b/bin/tests/system/sfcache_cname/ans1/ans.py new file mode 100644 index 00000000000..049a2d2009f --- /dev/null +++ b/bin/tests/system/sfcache_cname/ans1/ans.py @@ -0,0 +1,53 @@ +# 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.name +import dns.rcode +import dns.rdataclass +import dns.rdatatype +import dns.rrset + +from isctest.asyncserver import AsyncDnsServer, QnameQtypeHandler, StaticResponseHandler + + +def rrset( + qname: dns.name.Name | str, + rtype: dns.rdatatype.RdataType, + rdata: str, + ttl: int = 300, +) -> dns.rrset.RRset: + return dns.rrset.from_text(qname, ttl, dns.rdataclass.IN, rtype, rdata) + + +class Tld1Handler(QnameQtypeHandler, StaticResponseHandler): + qnames = ["foo.tld1."] + qtypes = [dns.rdatatype.A] + answer = [rrset("foo.tld1.", dns.rdatatype.CNAME, "tld2.")] + + +class Tld2Handler(QnameQtypeHandler, StaticResponseHandler): + qnames = ["tld2."] + qtypes = [dns.rdatatype.A] + answer = [rrset("tld2.", dns.rdatatype.A, "1.2.3.4")] + + +def main() -> None: + server = AsyncDnsServer(default_aa=True, default_rcode=dns.rcode.NOERROR) + server.install_response_handlers( + Tld1Handler(), + Tld2Handler(), + ) + server.run() + + +if __name__ == "__main__": + main() diff --git a/bin/tests/system/sfcache_cname/ns2/named.conf.j2 b/bin/tests/system/sfcache_cname/ns2/named.conf.j2 new file mode 100644 index 00000000000..c501ad6416f --- /dev/null +++ b/bin/tests/system/sfcache_cname/ns2/named.conf.j2 @@ -0,0 +1,26 @@ +options { + query-source address @ns.ip@; + port @PORT@; + pid-file "named.pid"; + listen-on { @ns.ip@; }; + listen-on-v6 { none; }; + recursion yes; + dnssec-validation no; + qname-minimization off; + servfail-ttl 5; + max-query-count 2; +}; + +{% include "_common/controls.conf.j2" %} + +zone "tld1." { + type forward; + forward only; + forwarders { 10.53.0.1; }; +}; + +zone "tld2." { + type forward; + forward only; + forwarders { 10.53.0.1; }; +}; diff --git a/bin/tests/system/sfcache_cname/tests_sfcache_cname.py b/bin/tests/system/sfcache_cname/tests_sfcache_cname.py new file mode 100644 index 00000000000..df838ddebd5 --- /dev/null +++ b/bin/tests/system/sfcache_cname/tests_sfcache_cname.py @@ -0,0 +1,34 @@ +# 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.message + +import isctest + + +# A SERVFAIL produced while following a CNAME must be cached against the +# original query name, not the CNAME target. +# +# ans1 serves "foo.tld1 CNAME tld2" and "tld2 A 1.2.3.4"; ns2 forwards +# both zones to it with "max-query-count 2". Resolving "foo.tld1/A" +# follows the CNAME to "tld2" and then exhausts the query budget, so the +# client gets SERVFAIL. That failure must be cached under the original +# name ("foo.tld1"), so a subsequent direct query for the CNAME target +# ("tld2") is not blocked by the SERVFAIL cache and resolves normally. +def test_sfcache_cname(ns2): + msg = dns.message.make_query("foo.tld1.", "A") + res = isctest.query.udp(msg, ns2.ip) + isctest.check.servfail(res) + + msg = dns.message.make_query("tld2.", "A") + res = isctest.query.udp(msg, ns2.ip) + isctest.check.noerror(res)