Commit e892520de7 for bind
commit e892520de712326fc0493e7c856371e6b202ca33
Author: Nicki Křížek <nicki@isc.org>
Date: Fri Sep 11 09:27:50 2026 +0000
Add test for CNAME target recursion in runtime-added zones
An RD=1 query for a name in an authoritative zone whose answer is a
CNAME pointing out of the zone must recurse to resolve the CNAME
target. Zones added at runtime ("rndc addzone" or catalog zones)
share the view's allow-query ACL object; zones from named.conf get
their own copy. In 9.20.28, evaluating the shared ACL cleared all
other client query attributes, so the restarted lookup for the CNAME
target could neither use the cache nor recurse. Main never had the
bug; the test guards against it.
Assisted-by: Claude:claude-fable-5
diff --git a/bin/tests/system/cname_recursion/ns1/catalog.db b/bin/tests/system/cname_recursion/ns1/catalog.db
new file mode 100644
index 0000000000..3068ac5e41
--- /dev/null
+++ b/bin/tests/system/cname_recursion/ns1/catalog.db
@@ -0,0 +1,16 @@
+; 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 . . 1 86400 3600 86400 3600
+@ IN NS invalid.
+version IN TXT "2"
+mbr1.zones IN PTR member.
diff --git a/bin/tests/system/cname_recursion/ns1/external.db b/bin/tests/system/cname_recursion/ns1/external.db
new file mode 100644
index 0000000000..dd3b1286f0
--- /dev/null
+++ b/bin/tests/system/cname_recursion/ns1/external.db
@@ -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.
+
+$TTL 300
+external. IN SOA ns1.external. hostmaster.external. 1 600 600 1200 600
+external. NS ns1.external.
+ns1.external. A 10.53.0.1
+target.external. A 10.0.0.99
+target2.external. A 10.0.0.98
+target3.external. A 10.0.0.97
diff --git a/bin/tests/system/cname_recursion/ns1/member.db b/bin/tests/system/cname_recursion/ns1/member.db
new file mode 100644
index 0000000000..d6853445b4
--- /dev/null
+++ b/bin/tests/system/cname_recursion/ns1/member.db
@@ -0,0 +1,16 @@
+; 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
+member. IN SOA ns1.member. hostmaster.member. 1 600 600 1200 600
+member. NS ns1.member.
+ns1.member. A 10.53.0.1
+alias.member. CNAME target3.external.
diff --git a/bin/tests/system/cname_recursion/ns1/named.conf.j2 b/bin/tests/system/cname_recursion/ns1/named.conf.j2
new file mode 100644
index 0000000000..95a96b9ef1
--- /dev/null
+++ b/bin/tests/system/cname_recursion/ns1/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.
+ */
+
+// NS1: root server, authoritative for the CNAME target zone, and
+// primary for the catalog zone and its member zone
+
+options {
+ {% include_indented "_common/options.conf.j2" %}
+ recursion no;
+ dnssec-validation no;
+ allow-transfer { any; };
+};
+
+{% include "_common/controls.conf.j2" %}
+
+zone "." {
+ type primary;
+ file "root.db";
+};
+
+zone "external" {
+ type primary;
+ file "external.db";
+};
+
+zone "catalog" {
+ type primary;
+ file "catalog.db";
+};
+
+zone "member" {
+ type primary;
+ file "member.db";
+};
diff --git a/bin/tests/system/cname_recursion/ns1/root.db b/bin/tests/system/cname_recursion/ns1/root.db
new file mode 100644
index 0000000000..e5253b6444
--- /dev/null
+++ b/bin/tests/system/cname_recursion/ns1/root.db
@@ -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.
+
+$TTL 300
+. IN SOA a.root-servers.nil. hostmaster.nil. 1 600 600 1200 600
+. NS a.root-servers.nil.
+a.root-servers.nil. A 10.53.0.1
+
+external. NS ns1.external.
+ns1.external. A 10.53.0.1
diff --git a/bin/tests/system/cname_recursion/ns2/added.db b/bin/tests/system/cname_recursion/ns2/added.db
new file mode 100644
index 0000000000..0713dbb3c7
--- /dev/null
+++ b/bin/tests/system/cname_recursion/ns2/added.db
@@ -0,0 +1,16 @@
+; 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
+added. IN SOA ns2.added. hostmaster.added. 1 600 600 1200 600
+added. NS ns2.added.
+ns2.added. A 10.53.0.2
+alias.added. CNAME target2.external.
diff --git a/bin/tests/system/cname_recursion/ns2/internal.db b/bin/tests/system/cname_recursion/ns2/internal.db
new file mode 100644
index 0000000000..3938b9459c
--- /dev/null
+++ b/bin/tests/system/cname_recursion/ns2/internal.db
@@ -0,0 +1,16 @@
+; 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
+internal. IN SOA ns2.internal. hostmaster.internal. 1 600 600 1200 600
+internal. NS ns2.internal.
+ns2.internal. A 10.53.0.2
+alias.internal. CNAME target.external.
diff --git a/bin/tests/system/cname_recursion/ns2/named.conf.j2 b/bin/tests/system/cname_recursion/ns2/named.conf.j2
new file mode 100644
index 0000000000..ca9abeb925
--- /dev/null
+++ b/bin/tests/system/cname_recursion/ns2/named.conf.j2
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+// NS2: recursive resolver, also authoritative for zones with a CNAME
+// pointing to a name outside of the zone: one static, one added at
+// runtime with "rndc addzone", one added at runtime as a catalog zone
+// member
+
+options {
+ {% include_indented "_common/options.conf.j2" %}
+ dnssec-validation no;
+ allow-query { any; };
+ allow-new-zones yes;
+ catalog-zones {
+ zone "catalog"
+ default-primaries { 10.53.0.1; }
+ min-update-interval 1s
+ in-memory yes;
+ };
+};
+
+{% include "_common/controls.conf.j2" %}
+
+{% include "_common/root.hint.conf" %}
+
+zone "internal" {
+ type primary;
+ file "internal.db";
+};
+
+zone "catalog" {
+ type secondary;
+ file "catalog.db";
+ primaries { 10.53.0.1; };
+};
diff --git a/bin/tests/system/cname_recursion/tests_cname_recursion.py b/bin/tests/system/cname_recursion/tests_cname_recursion.py
new file mode 100644
index 0000000000..f12d7e246a
--- /dev/null
+++ b/bin/tests/system/cname_recursion/tests_cname_recursion.py
@@ -0,0 +1,87 @@
+# 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.
+
+"""
+An RD=1 query to a server which is authoritative for the queried zone,
+where the answer is a CNAME pointing to a name outside of that zone, must
+recurse to resolve the CNAME target and return the complete chain.
+
+Regression test: when a zone shares the view's "allow-query" ACL object
+(zones added at runtime via "rndc addzone" or as catalog zone members do;
+zones configured in named.conf get their own copy), evaluating the ACL
+during the zone lookup used to clear all other client query attributes
+(NS_QUERYATTR_RECURSIONOK and NS_QUERYATTR_CACHEOK among them), so the
+restarted query for the CNAME target could not recurse and the response
+contained only the CNAME record without the target's address record.
+"""
+
+import dns.name
+import dns.rdataclass
+import dns.rdatatype
+import pytest
+
+import isctest
+
+pytestmark = pytest.mark.extra_artifacts(
+ [
+ "ns2/_default.nz*",
+ "ns2/catalog.db*",
+ ]
+)
+
+
+def assert_full_chain(res, alias, target, address):
+ isctest.check.noerror(res)
+
+ cname = res.get_rrset(
+ res.answer,
+ dns.name.from_text(alias),
+ dns.rdataclass.IN,
+ dns.rdatatype.CNAME,
+ )
+ assert cname is not None, "CNAME missing from the answer section"
+
+ answer = res.get_rrset(
+ res.answer,
+ dns.name.from_text(target),
+ dns.rdataclass.IN,
+ dns.rdatatype.A,
+ )
+ assert answer is not None, "CNAME target not resolved (partial response)"
+ assert str(answer[0]) == address
+
+
+@pytest.mark.requires_zones_loaded("ns1", "ns2")
+def test_cname_recursion_static_zone(ns2):
+ msg = isctest.query.create("alias.internal.", "A")
+ res = isctest.query.udp(msg, ns2.ip)
+ assert_full_chain(res, "alias.internal.", "target.external.", "10.0.0.99")
+
+
+@pytest.mark.requires_zones_loaded("ns1", "ns2")
+def test_cname_recursion_added_zone(ns2):
+ with ns2.watch_log_from_here() as watcher:
+ ns2.rndc('addzone added { type primary; file "added.db"; };')
+ watcher.wait_for_line("zone added/IN: loaded serial 1")
+
+ msg = isctest.query.create("alias.added.", "A")
+ res = isctest.query.udp(msg, ns2.ip)
+ assert_full_chain(res, "alias.added.", "target2.external.", "10.0.0.98")
+
+
+@pytest.mark.requires_zones_loaded("ns1", "ns2")
+def test_cname_recursion_catz_member_zone(ns2):
+ with ns2.watch_log_from_start() as watcher:
+ watcher.wait_for_line("zone member/IN: transferred serial 1")
+
+ msg = isctest.query.create("alias.member.", "A")
+ res = isctest.query.udp(msg, ns2.ip)
+ assert_full_chain(res, "alias.member.", "target3.external.", "10.0.0.97")