]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Add serve-stale test case for CNAME to A
authorMatthijs Mekking <matthijs@isc.org>
Fri, 27 Mar 2026 09:32:48 +0000 (10:32 +0100)
committerMatthijs Mekking <matthijs@isc.org>
Thu, 28 May 2026 07:59:15 +0000 (09:59 +0200)
Add a serve-stale system test case where the authority changes a
CNAME RRset to A (at cname2.stale.test). The CNAME that is in the
cache is stale and should be refreshed. The target A record (at
a2.stale.test) has a longer TTL and is also still in the cache. The
next query should return the refreshed A RRset to the client.

Then the authority changes back the A RRset to CNAME. The A RRset
has become stale and should be refreshed. The next query should
return the refreshed CNAME RRset plus the already cached
a2.stale.test A record.

This test requires ns1 to allow dynamic updates to stale.test, and
prefetch to be disabled. The latter is to ensure the record is not
prefetched, but only refreshed when stale (and logs the expected
"an attempt to refresh the RRset" messages).

(cherry picked from commit 4ee526cb6d3c34ea2736154e15dbc19211e08321)

13 files changed:
bin/tests/system/serve_stale/ns1/named.conf.j2
bin/tests/system/serve_stale/ns1/named4.conf.in
bin/tests/system/serve_stale/ns3/named.conf.j2
bin/tests/system/serve_stale/ns3/named1.conf.j2
bin/tests/system/serve_stale/ns3/named2.conf.j2
bin/tests/system/serve_stale/ns3/named3.conf.j2
bin/tests/system/serve_stale/ns3/named4.conf.j2
bin/tests/system/serve_stale/ns3/named5.conf.j2
bin/tests/system/serve_stale/ns3/named6.conf.j2
bin/tests/system/serve_stale/ns3/named7.conf.j2
bin/tests/system/serve_stale/ns3/named9.conf.j2
bin/tests/system/serve_stale/tests.sh
bin/tests/system/serve_stale/tests_sh_serve_stale.py

index a34c24fb32f251212625c9881ff392085b059d6a..402d7945a5a3f43fa7106ffcb850aea45c4e96ba 100644 (file)
@@ -55,5 +55,6 @@ zone "." {
 zone "stale.test" {
        type primary;
        file "stale.test.db";
+       allow-update { any; };
 };
 {% endif %}
index 9bf2f76710bd252d7a8ef105d608e81ea382c19e..b2d0c3e6557fefeca47fc99c51c5ac30db398764 100644 (file)
@@ -47,4 +47,5 @@ zone "." {
 zone "stale.test" {
        type primary;
        file "stale.test.db";
+       allow-update { any; };
 };
index 16b3e0421242e13473588cd7b9dd98e2b57b2898..e6e85536b7b57f51805a90b01cf984a76ea3391a 100644 (file)
@@ -31,6 +31,7 @@ options {
        recursion yes;
        dnssec-validation no;
        qname-minimization off;
+       prefetch 0;
 
        stale-answer-enable yes;
        stale-cache-enable yes;
index 5c7dddd807d0ece78b72f000b66d74499af0d254..929ef295ad4c9878ca7fd5c13c9172497b0a73d1 100644 (file)
@@ -32,6 +32,7 @@ options {
        dump-file "named_dump3.db";
        stale-cache-enable yes;
        dnssec-validation no;
+       prefetch 0;
 };
 
 zone "." {
index 51a70a89d6c38bc07884f7422a2e1c9efc434178..75b7d6954a13bf32db3aefa77ccce827a2497a89 100644 (file)
@@ -42,6 +42,7 @@ options {
        max-stale-ttl 3600;
        resolver-query-timeout 30000; # 30 seconds
        qname-minimization disabled;
+       prefetch 0;
 };
 
 zone "." {
index cef5e5276791fa73c454e4bd501e44d37d8e4614..e2e537b1ef569b3a26f86b72102b6059102282cc 100644 (file)
@@ -40,6 +40,7 @@ options {
        stale-refresh-time 0;
        max-stale-ttl 3600;
        resolver-query-timeout 10000; # 10 seconds
+       prefetch 0;
 };
 
 zone "." {
index abc1eb75afcb0ab305c19bfb2445f5b3bf68f139..7a89cd4b590ae56547a9d0384f602bd4b8021990 100644 (file)
@@ -42,6 +42,7 @@ options {
        resolver-query-timeout 10000; # 10 seconds
        max-stale-ttl 3600;
        recursive-clients 10;
+       prefetch 0;
 };
 
 zone "." {
index b695901413de4425d8e5765986865875ff64ba14..21151c0088ca92c9512d2a26e80f9057fee56137 100644 (file)
@@ -41,6 +41,7 @@ options {
        stale-refresh-time 4;
        resolver-query-timeout 10000; # 10 seconds
        max-stale-ttl 3600;
+       prefetch 0;
 };
 
 zone "." {
index 307c3bcedc5fab61d26503d7948e4a38014daa25..f8d272189f4c04b5ec07629a58df1ba50f2c266b 100644 (file)
@@ -38,6 +38,7 @@ options {
        fetches-per-zone 1 fail;
        fetches-per-server 1 fail;
        max-stale-ttl 3600;
+       prefetch 0;
 };
 
 zone "." {
index 12d9ee38bc342d65e6910135fe68ccb32b08ca6f..a2abcd268a984f552d27567f78bdae2d5f0b376d 100644 (file)
@@ -46,6 +46,7 @@ options {
        fetches-per-zone 1 fail;
        fetches-per-server 1 fail;
        max-stale-ttl 3600;
+       prefetch 0;
 };
 
 zone "." {
index cb097135192ac9c5ffc0835473693fa82ef0610c..5ecac2d494eda8f879ae87adb29958abcbba03e0 100644 (file)
@@ -34,6 +34,7 @@ options {
        stale-cache-enable yes;
        stale-answer-ttl 3;
        stale-answer-client-timeout 0;
+       prefetch 0;
 };
 
 zone "." {
index bdf54b54335e82f6bb802fdcbd9bdaf5e9deaf42..9cf328656ed910d52164b3f76a8068e8d735cba7 100755 (executable)
@@ -2358,6 +2358,93 @@ grep "cname-b3\.stale\.test\..*3.*IN.*A.*192\.0\.2\.2" dig.out.test$n >/dev/null
 if [ $ret != 0 ]; then echo_i "failed"; fi
 status=$((status + ret))
 
+# Change CNAME to A.
+n=$((n + 1))
+echo_i "change cname2.stale.test. CNAME to A (stale-answer-client-timeout 0) ($n)"
+ret=0
+(
+  echo zone stale.test.
+  echo server 10.53.0.1 "${PORT}"
+  echo update del cname2.stale.test. CNAME a2.stale.test.
+  echo update add cname2.stale.test. 1 A 192.0.0.2
+  echo send
+) | $NSUPDATE || ret=1
+test "$ret" -eq 0 || echo_i "failed"
+status=$((status + ret))
+
+n=$((n + 1))
+ret=0
+echo_i "check stale cname2.stale.test A comes from cache (stale-answer-client-timeout 0) ($n)"
+nextpart ns3/named.run >/dev/null
+$DIG -p ${PORT} @10.53.0.3 cname2.stale.test A >dig.out.test$n || ret=1
+wait_for_log 5 "cname2.stale.test A stale answer used, an attempt to refresh the RRset" ns3/named.run || ret=1
+grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
+grep "EDE: 3 (Stale Answer): (stale data prioritized over lookup)" dig.out.test$n >/dev/null || ret=1
+grep "ANSWER: 2," dig.out.test$n >/dev/null || ret=1
+grep "cname2\.stale\.test\..*3.*IN.*CNAME.*a2\.stale\.test\." dig.out.test$n >/dev/null || ret=1
+# We can't reliably test the TTL of the a2.stale.test A record.
+grep "a2\.stale\.test\..*IN.*A.*192\.0\.2\.2" dig.out.test$n >/dev/null || ret=1
+if [ $ret != 0 ]; then echo_i "failed"; fi
+status=$((status + ret))
+
+n=$((n + 1))
+ret=0
+echo_i "check stale cname2.stale.test A is refreshed (stale-answer-client-timeout 0) ($n)"
+nextpart ns3/named.run >/dev/null
+$DIG -p ${PORT} @10.53.0.3 cname2.stale.test A >dig.out.test$n || ret=1
+grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
+grep "ANSWER: 1," dig.out.test$n >/dev/null || ret=1
+grep "cname2\.stale\.test\..*IN.*A.*192\.0\.0\.2" dig.out.test$n >/dev/null || ret=1
+if [ $ret != 0 ]; then echo_i "failed"; fi
+status=$((status + ret))
+
+# Change A to CNAME.
+n=$((n + 1))
+echo_i "change cname2.stale.test. A to CNAME (stale-answer-client-timeout 0) ($n)"
+ret=0
+(
+  echo zone stale.test.
+  echo server 10.53.0.1 "${PORT}"
+  echo update del cname2.stale.test. A 192.0.0.2
+  echo update add cname2.stale.test. 1 CNAME a2.stale.test.
+  echo send
+) | $NSUPDATE || ret=1
+test "$ret" -eq 0 || echo_i "failed"
+status=$((status + ret))
+
+# Allow A record to become stale.
+sleep 1
+
+n=$((n + 1))
+ret=0
+echo_i "check stale cname2.stale.test A comes from cache (stale-answer-client-timeout 0) ($n)"
+nextpart ns3/named.run >/dev/null
+$DIG -p ${PORT} @10.53.0.3 cname2.stale.test A >dig.out.test$n || ret=1
+wait_for_log 5 "cname2.stale.test A stale answer used, an attempt to refresh the RRset" ns3/named.run || ret=1
+grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
+grep "EDE: 3 (Stale Answer): (stale data prioritized over lookup)" dig.out.test$n >/dev/null || ret=1
+grep "ANSWER: 1," dig.out.test$n >/dev/null || ret=1
+grep "cname2\.stale\.test\..*3.*IN.*A.*192\.0\.0\.2" dig.out.test$n >/dev/null || ret=1
+if [ $ret != 0 ]; then echo_i "failed"; fi
+status=$((status + ret))
+
+wait_for_cname_refresh() {
+  $DIG -p ${PORT} @10.53.0.3 cname2.stale.test A >dig.out.test$n || return 1
+  grep "status: NOERROR" dig.out.test$n >/dev/null || return 1
+  grep "ANSWER: 2," dig.out.test$n >/dev/null || return 1
+  # We can't reliably test the TTL of the these records.
+  grep "cname2\.stale\.test\..*IN.*CNAME.*a2\.stale\.test\." dig.out.test$n >/dev/null || return 1
+  grep "a2\.stale\.test\..*IN.*A.*192\.0\.2\.2" dig.out.test$n >/dev/null || return 1
+}
+
+n=$((n + 1))
+ret=0
+echo_i "check stale cname2.stale.test A is refreshed (stale-answer-client-timeout 0) ($n)"
+nextpart ns3/named.run >/dev/null
+retry 10 wait_for_cname_refresh || ret=1
+if [ $ret != 0 ]; then echo_i "failed"; fi
+status=$((status + ret))
+
 ####################################################################
 # Test for stale-answer-client-timeout 0 and stale-refresh-time 4. #
 ####################################################################
index 48b8cf1f17eaa56f9b645fdb78ee4db021ae36c7..5d7cf452190088771f8863efd71d4b9364ee0e07 100644 (file)
@@ -20,6 +20,7 @@ pytestmark = pytest.mark.extra_artifacts(
         "ns*/named_dump*",
         "ns*/named.stats*",
         "ns*/root.bk",
+        "ns1/stale.test.db.jnl",
     ]
 )