Add serve-stale test case for CNAME to A

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).
This commit is contained in:
Matthijs Mekking 2026-03-27 10:32:48 +01:00
parent c95128ed47
commit 4ee526cb6d
13 changed files with 95 additions and 0 deletions

View file

@ -42,5 +42,6 @@ zone "." {
zone "stale.test" {
type primary;
file "stale.test.db";
allow-update { any; };
};
{% endif %}

View file

@ -34,4 +34,5 @@ zone "." {
zone "stale.test" {
type primary;
file "stale.test.db";
allow-update { any; };
};

View file

@ -18,6 +18,7 @@ options {
recursion yes;
dnssec-validation no;
qname-minimization off;
prefetch 0;
stale-answer-enable yes;
stale-cache-enable yes;

View file

@ -19,6 +19,7 @@ options {
dump-file "named_dump3.db";
stale-cache-enable yes;
dnssec-validation no;
prefetch 0;
};
zone "." {

View file

@ -29,6 +29,7 @@ options {
max-stale-ttl 3600;
resolver-query-timeout 30000; # 30 seconds
qname-minimization disabled;
prefetch 0;
};
zone "." {

View file

@ -27,6 +27,7 @@ options {
stale-refresh-time 0;
max-stale-ttl 3600;
resolver-query-timeout 10000; # 10 seconds
prefetch 0;
};
zone "." {

View file

@ -29,6 +29,7 @@ options {
resolver-query-timeout 10000; # 10 seconds
max-stale-ttl 3600;
recursive-clients 10;
prefetch 0;
};
zone "." {

View file

@ -28,6 +28,7 @@ options {
stale-refresh-time 4;
resolver-query-timeout 10000; # 10 seconds
max-stale-ttl 3600;
prefetch 0;
};
zone "." {

View file

@ -25,6 +25,7 @@ options {
fetches-per-zone 1 fail;
fetches-per-server 1 fail;
max-stale-ttl 3600;
prefetch 0;
};
zone "." {

View file

@ -33,6 +33,7 @@ options {
fetches-per-zone 1 fail;
fetches-per-server 1 fail;
max-stale-ttl 3600;
prefetch 0;
};
zone "." {

View file

@ -21,6 +21,7 @@ options {
stale-cache-enable yes;
stale-answer-ttl 3;
stale-answer-client-timeout 0;
prefetch 0;
};
zone "." {

View file

@ -2359,6 +2359,89 @@ 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))
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: 2," dig.out.test$n >/dev/null || ret=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 || ret=1
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))
####################################################################
# Test for stale-answer-client-timeout 0 and stale-refresh-time 4. #
####################################################################

View file

@ -22,6 +22,7 @@ pytestmark = pytest.mark.extra_artifacts(
"ns*/named_dump*",
"ns*/named.stats*",
"ns*/root.bk",
"ns1/stale.test.db.jnl",
]
)