Backport fixes for stale CNAME chains.
Resolves: RHEL-142289
This commit is contained in:
parent
5efcb6a5a9
commit
208f366e42
94
bind-9.20-stale-cname-tests.patch
Normal file
94
bind-9.20-stale-cname-tests.patch
Normal file
@ -0,0 +1,94 @@
|
||||
diff --git a/bin/tests/system/serve-stale/ns1/stale.test.db b/bin/tests/system/serve-stale/ns1/stale.test.db
|
||||
index d389e7c6a6..128fb25a10 100644
|
||||
--- a/bin/tests/system/serve-stale/ns1/stale.test.db
|
||||
+++ b/bin/tests/system/serve-stale/ns1/stale.test.db
|
||||
@@ -17,3 +17,11 @@ cname1.stale.test. 1 CNAME a1.stale.test.
|
||||
a1.stale.test. 1 A 192.0.2.1
|
||||
cname2.stale.test. 1 CNAME a2.stale.test.
|
||||
a2.stale.test. 300 A 192.0.2.2
|
||||
+
|
||||
+cname-a1 1 CNAME cname-a2
|
||||
+cname-a2 300 CNAME cname-a3
|
||||
+cname-a3 300 A 192.0.2.1
|
||||
+
|
||||
+cname-b1 300 CNAME cname-b2
|
||||
+cname-b2 1 CNAME cname-b3
|
||||
+cname-b3 1 A 192.0.2.2
|
||||
diff --git a/bin/tests/system/serve-stale/tests.sh b/bin/tests/system/serve-stale/tests.sh
|
||||
index 96cd26505f..a30896b6a4 100755
|
||||
--- a/bin/tests/system/serve-stale/tests.sh
|
||||
+++ b/bin/tests/system/serve-stale/tests.sh
|
||||
@@ -2256,6 +2256,73 @@ if [ $ret != 0 ]; then
|
||||
fi
|
||||
status=$((status + ret))
|
||||
|
||||
+# New CNAME scenario (GL #5243)
|
||||
+n=$((n + 1))
|
||||
+echo_i "prime cache cname-a1.stale.test A (stale-answer-client-timeout 0) ($n)"
|
||||
+ret=0
|
||||
+$DIG -p ${PORT} @10.53.0.3 cname-a1.stale.test A >dig.out.test$n || ret=1
|
||||
+grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
|
||||
+grep "ANSWER: 3," dig.out.test$n >/dev/null || ret=1
|
||||
+grep "cname-a1\.stale\.test\..*1.*IN.*CNAME.*cname-a2\.stale\.test\." dig.out.test$n >/dev/null || ret=1
|
||||
+grep "cname-a2\.stale\.test\..*300.*IN.*CNAME.*cname-a3\.stale\.test\." dig.out.test$n >/dev/null || ret=1
|
||||
+grep "cname-a3\.stale\.test\..*300.*IN.*A.*192\.0\.2\.1" dig.out.test$n >/dev/null || ret=1
|
||||
+if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
+status=$((status + ret))
|
||||
+
|
||||
+n=$((n + 1))
|
||||
+echo_i "prime cache cname-b1.stale.test A (stale-answer-client-timeout 0) ($n)"
|
||||
+ret=0
|
||||
+$DIG -p ${PORT} @10.53.0.3 cname-b1.stale.test A >dig.out.test$n || ret=1
|
||||
+grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
|
||||
+grep "ANSWER: 3," dig.out.test$n >/dev/null || ret=1
|
||||
+grep "cname-b1\.stale\.test\..*300.*IN.*CNAME.*cname-b2\.stale\.test\." dig.out.test$n >/dev/null || ret=1
|
||||
+grep "cname-b2\.stale\.test\..*1.*IN.*CNAME.*cname-b3\.stale\.test\." dig.out.test$n >/dev/null || ret=1
|
||||
+grep "cname-b3\.stale\.test\..*1.*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))
|
||||
+
|
||||
+# Allow RRset to become stale.
|
||||
+sleep 1
|
||||
+
|
||||
+n=$((n + 1))
|
||||
+ret=0
|
||||
+echo_i "check stale cname-a1.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 cname-a1.stale.test A >dig.out.test$n || ret=1
|
||||
+wait_for_log 5 "cname-a1.stale.test A stale answer used, an attempt to refresh the RRset" ns3/named.run || ret=1
|
||||
+# Other records in chain are still good, so do not attempt a refresh
|
||||
+grep "cname-a2.stale.test A stale answer used, an attempt to refresh the RRset" ns3/named.run && ret=1
|
||||
+grep "cname-a3.stale.test A stale answer used, an attempt to refresh the RRset" ns3/named.run && ret=1
|
||||
+# Check answer
|
||||
+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: 3," dig.out.test$n >/dev/null || ret=1
|
||||
+grep "cname-a1\.stale\.test\..*3.*IN.*CNAME.*cname-a2\.stale\.test\." dig.out.test$n >/dev/null || ret=1
|
||||
+grep "cname-a2\.stale\.test\..*29[0-9].*IN.*CNAME.*cname-a3\.stale\.test\." dig.out.test$n >/dev/null || ret=1
|
||||
+grep "cname-a3\.stale\.test\..*29[0-9].*IN.*A.*192\.0\.2\.1" 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 cname-b1.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 cname-b1.stale.test A >dig.out.test$n || ret=1
|
||||
+wait_for_log 5 "cname-b2.stale.test A stale answer used, an attempt to refresh the RRset" ns3/named.run || ret=1
|
||||
+# The next one in the chain (cname-b3.stale.test) is likely not logged because
|
||||
+# there is already a refresh in progress. And the first record in the chain is
|
||||
+# still good, so do not attempt a refresh.
|
||||
+grep "cname-b1.stale.test A stale answer used, an attempt to refresh the RRset" ns3/named.run && ret=1
|
||||
+# Check answer
|
||||
+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: 3," dig.out.test$n >/dev/null || ret=1
|
||||
+grep "cname-b1\.stale\.test\..*29[0-9].*IN.*CNAME.*cname-b2\.stale\.test\." dig.out.test$n >/dev/null || ret=1
|
||||
+grep "cname-b2\.stale\.test\..*3.*IN.*CNAME.*cname-b3\.stale\.test\." dig.out.test$n >/dev/null || ret=1
|
||||
+grep "cname-b3\.stale\.test\..*3.*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. #
|
||||
####################################################################
|
||||
709
bind-9.20-stale-cname.patch
Normal file
709
bind-9.20-stale-cname.patch
Normal file
@ -0,0 +1,709 @@
|
||||
diff --git a/bin/tests/system/serve-stale/tests.sh b/bin/tests/system/serve-stale/tests.sh
|
||||
index c001e7a071..96cd26505f 100755
|
||||
--- a/bin/tests/system/serve-stale/tests.sh
|
||||
+++ b/bin/tests/system/serve-stale/tests.sh
|
||||
@@ -2053,7 +2053,7 @@ ret=0
|
||||
echo_i "check stale nodata.example TXT comes from cache (stale-answer-client-timeout 0) ($n)"
|
||||
nextpart ns3/named.run >/dev/null
|
||||
$DIG -p ${PORT} @10.53.0.3 nodata.example TXT >dig.out.test$n || ret=1
|
||||
-wait_for_log 5 "nodata.example stale answer used, an attempt to refresh the RRset" ns3/named.run || ret=1
|
||||
+wait_for_log 5 "nodata.example TXT 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: 0," dig.out.test$n >/dev/null || ret=1
|
||||
@@ -2066,7 +2066,7 @@ ret=0
|
||||
echo_i "check stale data.example TXT comes from cache (stale-answer-client-timeout 0) ($n)"
|
||||
nextpart ns3/named.run >/dev/null
|
||||
$DIG -p ${PORT} @10.53.0.3 data.example TXT >dig.out.test$n || ret=1
|
||||
-wait_for_log 5 "data.example stale answer used, an attempt to refresh the RRset" ns3/named.run || ret=1
|
||||
+wait_for_log 5 "data.example TXT 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
|
||||
@@ -2196,7 +2196,7 @@ ret=0
|
||||
echo_i "check stale cname1.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 cname1.stale.test A >dig.out.test$n || ret=1
|
||||
-wait_for_log 5 "cname1.stale.test stale answer used, an attempt to refresh the RRset" ns3/named.run || ret=1
|
||||
+wait_for_log 5 "cname1.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
|
||||
@@ -2235,7 +2235,7 @@ 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 stale answer used, an attempt to refresh the RRset" ns3/named.run || 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
|
||||
@@ -2312,7 +2312,7 @@ ret=0
|
||||
echo_i "check stale data.example TXT comes from cache (stale-answer-client-timeout 0 stale-refresh-time 4) ($n)"
|
||||
nextpart ns3/named.run >/dev/null
|
||||
$DIG -p ${PORT} @10.53.0.3 data.example TXT >dig.out.test$n || ret=1
|
||||
-wait_for_log 5 "data.example stale answer used, an attempt to refresh the RRset" ns3/named.run || ret=1
|
||||
+wait_for_log 5 "data.example TXT 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
|
||||
@@ -2356,7 +2356,7 @@ ret=0
|
||||
echo_i "check stale data.example TXT comes from cache (stale-answer-client-timeout 0 stale-refresh-time 4) ($n)"
|
||||
nextpart ns3/named.run >/dev/null
|
||||
$DIG -p ${PORT} @10.53.0.3 data.example TXT >dig.out.test$n || ret=1
|
||||
-wait_for_log 5 "data.example stale answer used, an attempt to refresh the RRset" ns3/named.run || ret=1
|
||||
+wait_for_log 5 "data.example TXT 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
|
||||
@@ -2368,7 +2368,7 @@ status=$((status + ret))
|
||||
n=$((n + 1))
|
||||
ret=0
|
||||
echo_i "wait until resolver query times out, activating stale-refresh-time"
|
||||
-wait_for_log 15 "data.example resolver failure, stale answer used" ns3/named.run || ret=1
|
||||
+wait_for_log 15 "data.example/TXT stale refresh failed: timed out" ns3/named.run || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
@@ -2419,7 +2419,7 @@ n=$((n + 1))
|
||||
ret=0
|
||||
echo_i "check stale data.example TXT comes from cache (stale-answer-client-timeout 0 stale-refresh-time 4) ($n)"
|
||||
$DIG -p ${PORT} @10.53.0.3 data.example TXT >dig.out.test$n || ret=1
|
||||
-wait_for_log 5 "data.example stale answer used, an attempt to refresh the RRset" ns3/named.run || ret=1
|
||||
+wait_for_log 5 "data.example TXT 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
|
||||
diff --git a/lib/dns/include/dns/rdataset.h b/lib/dns/include/dns/rdataset.h
|
||||
index 3a85226836..b04e65afc7 100644
|
||||
--- a/lib/dns/include/dns/rdataset.h
|
||||
+++ b/lib/dns/include/dns/rdataset.h
|
||||
@@ -201,7 +201,7 @@ struct dns_rdataset {
|
||||
#define DNS_RDATASETATTR_STALE 0x01000000
|
||||
#define DNS_RDATASETATTR_ANCIENT 0x02000000
|
||||
#define DNS_RDATASETATTR_STALE_WINDOW 0x04000000
|
||||
-#define DNS_RDATASETATTR_STALE_ADDED 0x08000000
|
||||
+#define DNS_RDATASETATTR_KEEPCASE 0x10000000
|
||||
#define DNS_RDATASETATTR_STATICSTUB 0x20000000
|
||||
|
||||
/*%
|
||||
diff --git a/lib/ns/include/ns/client.h b/lib/ns/include/ns/client.h
|
||||
index ea2d83e079..db17f01d79 100644
|
||||
--- a/lib/ns/include/ns/client.h
|
||||
+++ b/lib/ns/include/ns/client.h
|
||||
@@ -184,6 +184,7 @@ struct ns_client {
|
||||
(query, update, notify) */
|
||||
isc_nmhandle_t *fetchhandle; /* Waiting for recursive fetch */
|
||||
isc_nmhandle_t *prefetchhandle; /* Waiting for prefetch / rpzfetch */
|
||||
+ isc_nmhandle_t *stalerefreshhandle;
|
||||
isc_nmhandle_t *updatehandle; /* Waiting for update callback */
|
||||
unsigned char *tcpbuf;
|
||||
size_t tcpbuf_size;
|
||||
diff --git a/lib/ns/include/ns/query.h b/lib/ns/include/ns/query.h
|
||||
index c090754526..10a5931dca 100644
|
||||
--- a/lib/ns/include/ns/query.h
|
||||
+++ b/lib/ns/include/ns/query.h
|
||||
@@ -69,6 +69,7 @@ struct ns_query {
|
||||
isc_mutex_t fetchlock;
|
||||
dns_fetch_t *fetch;
|
||||
dns_fetch_t *prefetch;
|
||||
+ dns_fetch_t *stalerefresh;
|
||||
ns_hookasync_t *hookactx;
|
||||
dns_rpz_st_t *rpz_st;
|
||||
isc_bufferlist_t namebufs;
|
||||
@@ -120,7 +121,6 @@ struct ns_query {
|
||||
#define NS_QUERYATTR_RRL_CHECKED 0x010000
|
||||
#define NS_QUERYATTR_REDIRECT 0x020000
|
||||
#define NS_QUERYATTR_ANSWERED 0x040000
|
||||
-#define NS_QUERYATTR_STALEOK 0x080000
|
||||
#define NS_QUERYATTR_STALEPENDING 0x100000
|
||||
|
||||
typedef struct query_ctx query_ctx_t;
|
||||
@@ -149,7 +149,6 @@ struct query_ctx {
|
||||
bool authoritative; /* authoritative query? */
|
||||
bool want_restart; /* CNAME chain or other
|
||||
* restart needed */
|
||||
- bool refresh_rrset; /* stale RRset refresh needed */
|
||||
bool need_wildcardproof; /* wildcard proof needed */
|
||||
bool nxrewrite; /* negative answer from RPZ */
|
||||
bool findcoveringnsec; /* lookup covering NSEC */
|
||||
diff --git a/lib/ns/query.c b/lib/ns/query.c
|
||||
index 11d2520c61..114cb0bf6b 100644
|
||||
--- a/lib/ns/query.c
|
||||
+++ b/lib/ns/query.c
|
||||
@@ -140,9 +140,6 @@
|
||||
#define QUERY_STALEPENDING(q) \
|
||||
(((q)->attributes & NS_QUERYATTR_STALEPENDING) != 0)
|
||||
|
||||
-/*% Does the query allow stale data in the response? */
|
||||
-#define QUERY_STALEOK(q) (((q)->attributes & NS_QUERYATTR_STALEOK) != 0)
|
||||
-
|
||||
/*% Does the query wants to check for stale RRset due to a timeout? */
|
||||
#define QUERY_STALETIMEOUT(q) (((q)->dboptions & DNS_DBFIND_STALETIMEOUT) != 0)
|
||||
|
||||
@@ -249,6 +246,15 @@ query_addanswer(query_ctx_t *qctx);
|
||||
static isc_result_t
|
||||
query_prepare_delegation_response(query_ctx_t *qctx);
|
||||
|
||||
+static isc_result_t
|
||||
+qctx_prepare_buffers(query_ctx_t *qctx, isc_buffer_t *buffer);
|
||||
+
|
||||
+static void
|
||||
+qctx_freedata(query_ctx_t *qctx);
|
||||
+
|
||||
+static void
|
||||
+qctx_destroy(query_ctx_t *qctx);
|
||||
+
|
||||
/*
|
||||
* Return the hooktable in use with 'qctx', or if there isn't one
|
||||
* set, return the default hooktable.
|
||||
@@ -504,9 +510,6 @@ query_addwildcardproof(query_ctx_t *qctx, bool ispositive, bool nodata);
|
||||
static void
|
||||
query_addauth(query_ctx_t *qctx);
|
||||
|
||||
-static void
|
||||
-query_clear_stale(ns_client_t *client);
|
||||
-
|
||||
/*
|
||||
* Increment query statistics counters.
|
||||
*/
|
||||
@@ -815,6 +818,7 @@ ns_query_init(ns_client_t *client) {
|
||||
|
||||
client->query.fetch = NULL;
|
||||
client->query.prefetch = NULL;
|
||||
+ client->query.stalerefresh = NULL;
|
||||
client->query.authdb = NULL;
|
||||
client->query.authzone = NULL;
|
||||
client->query.authdbset = false;
|
||||
@@ -2247,10 +2251,6 @@ query_addrrset(query_ctx_t *qctx, dns_name_t **namep,
|
||||
if ((rdataset->attributes & DNS_RDATASETATTR_REQUIRED) != 0) {
|
||||
mrdataset->attributes |= DNS_RDATASETATTR_REQUIRED;
|
||||
}
|
||||
- if ((rdataset->attributes & DNS_RDATASETATTR_STALE_ADDED) != 0)
|
||||
- {
|
||||
- mrdataset->attributes |= DNS_RDATASETATTR_STALE_ADDED;
|
||||
- }
|
||||
return;
|
||||
} else if (result == DNS_R_NXDOMAIN) {
|
||||
/*
|
||||
@@ -2538,6 +2538,88 @@ free_devent(ns_client_t *client, isc_event_t **eventp,
|
||||
isc_event_free(eventp);
|
||||
}
|
||||
|
||||
+static void
|
||||
+stale_refresh_aftermath(ns_client_t *client, isc_result_t result) {
|
||||
+ dns_db_t *db = NULL;
|
||||
+ unsigned int dboptions;
|
||||
+ isc_buffer_t buffer;
|
||||
+ query_ctx_t qctx;
|
||||
+ dns_clientinfomethods_t cm;
|
||||
+ dns_clientinfo_t ci;
|
||||
+ char namebuf[DNS_NAME_FORMATSIZE];
|
||||
+ char typebuf[DNS_RDATATYPE_FORMATSIZE];
|
||||
+
|
||||
+ /*
|
||||
+ * If refreshing a stale RRset failed, we need to set the
|
||||
+ * stale-refresh-time window, so that on future requests for this
|
||||
+ * RRset the stale entry may be used immediately.
|
||||
+ */
|
||||
+ switch (result) {
|
||||
+ case ISC_R_SUCCESS:
|
||||
+ case DNS_R_GLUE:
|
||||
+ case DNS_R_ZONECUT:
|
||||
+ case ISC_R_NOTFOUND:
|
||||
+ case DNS_R_DELEGATION:
|
||||
+ case DNS_R_EMPTYNAME:
|
||||
+ case DNS_R_NXRRSET:
|
||||
+ case DNS_R_EMPTYWILD:
|
||||
+ case DNS_R_NXDOMAIN:
|
||||
+ case DNS_R_COVERINGNSEC:
|
||||
+ case DNS_R_NCACHENXDOMAIN:
|
||||
+ case DNS_R_NCACHENXRRSET:
|
||||
+ case DNS_R_CNAME:
|
||||
+ case DNS_R_DNAME:
|
||||
+ break;
|
||||
+ default:
|
||||
+ dns_name_format(client->query.qname, namebuf, sizeof(namebuf));
|
||||
+ dns_rdatatype_format(client->query.qtype, typebuf,
|
||||
+ sizeof(typebuf));
|
||||
+ ns_client_log(client, NS_LOGCATEGORY_SERVE_STALE,
|
||||
+ NS_LOGMODULE_QUERY, ISC_LOG_NOTICE,
|
||||
+ "%s/%s stale refresh failed: timed out", namebuf,
|
||||
+ typebuf);
|
||||
+
|
||||
+ /*
|
||||
+ * Set up a short lived query context, solely to set the
|
||||
+ * last refresh failure time on the RRset in the cache
|
||||
+ * database, starting the stale-refresh-time window for it.
|
||||
+ * This is a condensed form of query_lookup().
|
||||
+ */
|
||||
+ isc_stdtime_get(&client->now);
|
||||
+ client->query.attributes &= ~NS_QUERYATTR_RECURSIONOK;
|
||||
+ qctx_init(client, NULL, 0, &qctx);
|
||||
+
|
||||
+ dns_clientinfomethods_init(&cm, ns_client_sourceip);
|
||||
+ dns_clientinfo_init(&ci, qctx.client, NULL);
|
||||
+ if (HAVEECS(qctx.client)) {
|
||||
+ dns_clientinfo_setecs(&ci, &qctx.client->ecs);
|
||||
+ }
|
||||
+
|
||||
+ result = qctx_prepare_buffers(&qctx, &buffer);
|
||||
+ if (result != ISC_R_SUCCESS) {
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+
|
||||
+ dboptions = qctx.client->query.dboptions;
|
||||
+ dboptions |= DNS_DBFIND_STALEOK;
|
||||
+ dboptions |= DNS_DBFIND_STALESTART;
|
||||
+
|
||||
+ dns_db_attach(qctx.client->view->cachedb, &db);
|
||||
+ (void)dns_db_findext(db, qctx.client->query.qname, NULL,
|
||||
+ qctx.client->query.qtype, dboptions,
|
||||
+ qctx.client->now, &qctx.node, qctx.fname,
|
||||
+ &cm, &ci, qctx.rdataset, qctx.sigrdataset);
|
||||
+ if (qctx.node != NULL) {
|
||||
+ dns_db_detachnode(db, &qctx.node);
|
||||
+ }
|
||||
+ dns_db_detach(&db);
|
||||
+
|
||||
+ cleanup:
|
||||
+ qctx_freedata(&qctx);
|
||||
+ qctx_destroy(&qctx);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
static void
|
||||
prefetch_done(isc_task_t *task, isc_event_t *event) {
|
||||
dns_fetchevent_t *devent = (dns_fetchevent_t *)event;
|
||||
@@ -2572,6 +2654,133 @@ prefetch_done(isc_task_t *task, isc_event_t *event) {
|
||||
isc_nmhandle_detach(&client->prefetchhandle);
|
||||
}
|
||||
|
||||
+static void
|
||||
+refresh_done(isc_task_t *task, isc_event_t *event) {
|
||||
+ dns_fetchevent_t *devent = (dns_fetchevent_t *)event;
|
||||
+ ns_client_t *client;
|
||||
+
|
||||
+ UNUSED(task);
|
||||
+
|
||||
+ REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE);
|
||||
+ client = devent->ev_arg;
|
||||
+ REQUIRE(NS_CLIENT_VALID(client));
|
||||
+ REQUIRE(task == client->task);
|
||||
+
|
||||
+ CTRACE(ISC_LOG_DEBUG(3), "refresh_done");
|
||||
+
|
||||
+ LOCK(&client->query.fetchlock);
|
||||
+ if (client->query.stalerefresh != NULL) {
|
||||
+ INSIST(devent->fetch == client->query.stalerefresh);
|
||||
+ client->query.stalerefresh = NULL;
|
||||
+ }
|
||||
+ UNLOCK(&client->query.fetchlock);
|
||||
+
|
||||
+ stale_refresh_aftermath(client, devent->result);
|
||||
+
|
||||
+ if (client->recursionquota != NULL) {
|
||||
+ isc_quota_detach(&client->recursionquota);
|
||||
+ ns_stats_decrement(client->sctx->nsstats,
|
||||
+ ns_statscounter_recursclients);
|
||||
+ }
|
||||
+
|
||||
+ free_devent(client, &event, &devent);
|
||||
+ isc_nmhandle_detach(&client->stalerefreshhandle);
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+query_stale_refresh(ns_client_t *client, dns_name_t *qname,
|
||||
+ dns_rdataset_t *rdataset) {
|
||||
+ isc_result_t result;
|
||||
+ isc_sockaddr_t *peeraddr;
|
||||
+ dns_rdataset_t *tmprdataset;
|
||||
+ unsigned int options;
|
||||
+
|
||||
+ CTRACE(ISC_LOG_DEBUG(3), "query_stale_refresh");
|
||||
+
|
||||
+ bool stale_refresh_window = false;
|
||||
+ bool stale_rrset = true;
|
||||
+
|
||||
+ if (rdataset != NULL) {
|
||||
+ stale_refresh_window =
|
||||
+ (STALE_WINDOW(rdataset) &&
|
||||
+ (client->query.dboptions & DNS_DBFIND_STALEENABLED) != 0);
|
||||
+ stale_rrset = STALE(rdataset);
|
||||
+ }
|
||||
+
|
||||
+ if (client->query.stalerefresh != NULL ||
|
||||
+ (client->query.dboptions & DNS_DBFIND_STALETIMEOUT) == 0 ||
|
||||
+ !stale_rrset || stale_refresh_window)
|
||||
+ {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ char namebuf[DNS_NAME_FORMATSIZE];
|
||||
+ char typebuf[DNS_RDATATYPE_FORMATSIZE];
|
||||
+ dns_name_format(qname, namebuf, sizeof(namebuf));
|
||||
+ dns_rdatatype_format(client->query.qtype, typebuf, sizeof(typebuf));
|
||||
+ isc_log_write(ns_lctx, NS_LOGCATEGORY_SERVE_STALE, NS_LOGMODULE_QUERY,
|
||||
+ ISC_LOG_INFO,
|
||||
+ "%s %s stale answer used, an attempt "
|
||||
+ "to refresh the RRset will still be "
|
||||
+ "made",
|
||||
+ namebuf, typebuf);
|
||||
+
|
||||
+ client->query.dboptions &= ~(DNS_DBFIND_STALETIMEOUT |
|
||||
+ DNS_DBFIND_STALEOK |
|
||||
+ DNS_DBFIND_STALEENABLED);
|
||||
+
|
||||
+ if (client->recursionquota == NULL) {
|
||||
+ result = isc_quota_attach(&client->sctx->recursionquota,
|
||||
+ &client->recursionquota);
|
||||
+ switch (result) {
|
||||
+ case ISC_R_SUCCESS:
|
||||
+ ns_stats_increment(client->sctx->nsstats,
|
||||
+ ns_statscounter_recursclients);
|
||||
+ break;
|
||||
+ case ISC_R_SOFTQUOTA:
|
||||
+ isc_quota_detach(&client->recursionquota);
|
||||
+ FALLTHROUGH;
|
||||
+ default:
|
||||
+ return;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ tmprdataset = ns_client_newrdataset(client);
|
||||
+ if (tmprdataset == NULL) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ if (!TCP(client)) {
|
||||
+ peeraddr = &client->peeraddr;
|
||||
+ } else {
|
||||
+ peeraddr = NULL;
|
||||
+ }
|
||||
+
|
||||
+ isc_nmhandle_attach(client->handle, &client->stalerefreshhandle);
|
||||
+ options = client->query.fetchoptions;
|
||||
+ result = dns_resolver_createfetch(
|
||||
+ client->view->resolver, qname, client->query.qtype, NULL, NULL, NULL,
|
||||
+ peeraddr, client->message->id, options, 0, NULL, NULL,
|
||||
+ client->task, refresh_done, client, tmprdataset, NULL,
|
||||
+ &client->query.stalerefresh);
|
||||
+ if (result != ISC_R_SUCCESS) {
|
||||
+ ns_client_putrdataset(client, &tmprdataset);
|
||||
+ isc_nmhandle_detach(&client->stalerefreshhandle);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+query_stale_refresh_ncache(ns_client_t *client) {
|
||||
+ dns_name_t *qname;
|
||||
+
|
||||
+ if (client->query.origqname != NULL) {
|
||||
+ qname = client->query.origqname;
|
||||
+ } else {
|
||||
+ qname = client->query.qname;
|
||||
+ }
|
||||
+ query_stale_refresh(client, qname, NULL);
|
||||
+}
|
||||
+
|
||||
static void
|
||||
query_prefetch(ns_client_t *client, dns_name_t *qname,
|
||||
dns_rdataset_t *rdataset) {
|
||||
@@ -2587,6 +2796,7 @@ query_prefetch(ns_client_t *client, dns_name_t *qname,
|
||||
rdataset->ttl > client->view->prefetch_trigger ||
|
||||
(rdataset->attributes & DNS_RDATASETATTR_PREFETCH) == 0)
|
||||
{
|
||||
+ query_stale_refresh(client, qname, rdataset);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2638,6 +2848,7 @@ query_prefetch(ns_client_t *client, dns_name_t *qname,
|
||||
|
||||
dns_rdataset_clearprefetch(rdataset);
|
||||
ns_stats_increment(client->sctx->nsstats, ns_statscounter_prefetch);
|
||||
+ return;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -5824,55 +6035,6 @@ error:
|
||||
return ISC_R_NOMEMORY;
|
||||
}
|
||||
|
||||
-/*
|
||||
- * Setup a new query context for resolving a query.
|
||||
- *
|
||||
- * This function is only called if both these conditions are met:
|
||||
- * 1. BIND is configured with stale-answer-client-timeout 0.
|
||||
- * 2. A stale RRset is found in cache during initial query
|
||||
- * database lookup.
|
||||
- *
|
||||
- * We continue with this function for refreshing/resolving an RRset
|
||||
- * after answering a client with stale data.
|
||||
- */
|
||||
-static void
|
||||
-query_refresh_rrset(query_ctx_t *orig_qctx) {
|
||||
- isc_buffer_t buffer;
|
||||
- query_ctx_t qctx;
|
||||
-
|
||||
- REQUIRE(orig_qctx != NULL);
|
||||
- REQUIRE(orig_qctx->client != NULL);
|
||||
-
|
||||
- qctx_copy(orig_qctx, &qctx);
|
||||
- qctx.client->query.dboptions &= ~(DNS_DBFIND_STALETIMEOUT |
|
||||
- DNS_DBFIND_STALEOK |
|
||||
- DNS_DBFIND_STALEENABLED);
|
||||
- qctx.client->nodetach = false;
|
||||
-
|
||||
- /*
|
||||
- * We'll need some resources...
|
||||
- */
|
||||
- if (qctx_prepare_buffers(&qctx, &buffer) != ISC_R_SUCCESS) {
|
||||
- dns_db_detach(&qctx.db);
|
||||
- qctx_destroy(&qctx);
|
||||
- return;
|
||||
- }
|
||||
-
|
||||
- /*
|
||||
- * Pretend we didn't find anything in cache.
|
||||
- */
|
||||
- (void)query_gotanswer(&qctx, ISC_R_NOTFOUND);
|
||||
-
|
||||
- if (qctx.fname != NULL) {
|
||||
- ns_client_releasename(qctx.client, &qctx.fname);
|
||||
- }
|
||||
- if (qctx.rdataset != NULL) {
|
||||
- ns_client_putrdataset(qctx.client, &qctx.rdataset);
|
||||
- }
|
||||
-
|
||||
- qctx_destroy(&qctx);
|
||||
-}
|
||||
-
|
||||
/*%
|
||||
* Depending on the db lookup result, we can respond to the
|
||||
* client this stale answer.
|
||||
@@ -5944,7 +6106,7 @@ query_lookup(query_ctx_t *qctx) {
|
||||
rpzqname = qctx->client->query.qname;
|
||||
}
|
||||
|
||||
- if ((qctx->options & DNS_GETDB_STALEFIRST) != 0) {
|
||||
+ if ((qctx->options & DNS_GETDB_STALEFIRST) != 0 && !qctx->is_zone) {
|
||||
/*
|
||||
* If DNS_GETDB_STALEFIRST is set, it means that a stale
|
||||
* RRset may be returned as part of this lookup. An attempt
|
||||
@@ -5954,19 +6116,19 @@ query_lookup(query_ctx_t *qctx) {
|
||||
qctx->client->query.dboptions |= DNS_DBFIND_STALETIMEOUT;
|
||||
}
|
||||
|
||||
- dboptions = qctx->client->query.dboptions;
|
||||
- if (!qctx->is_zone && qctx->findcoveringnsec &&
|
||||
- (qctx->type != dns_rdatatype_null || !dns_name_istat(rpzqname)))
|
||||
- {
|
||||
- dboptions |= DNS_DBFIND_COVERINGNSEC;
|
||||
- }
|
||||
-
|
||||
(void)dns_db_getservestalerefresh(qctx->client->view->cachedb,
|
||||
&stale_refresh);
|
||||
if (stale_refresh > 0 &&
|
||||
dns_view_staleanswerenabled(qctx->client->view))
|
||||
{
|
||||
- dboptions |= DNS_DBFIND_STALEENABLED;
|
||||
+ qctx->client->query.dboptions |= DNS_DBFIND_STALEENABLED;
|
||||
+ }
|
||||
+
|
||||
+ dboptions = qctx->client->query.dboptions;
|
||||
+ if (!qctx->is_zone && qctx->findcoveringnsec &&
|
||||
+ (qctx->type != dns_rdatatype_null || !dns_name_istat(rpzqname)))
|
||||
+ {
|
||||
+ dboptions |= DNS_DBFIND_COVERINGNSEC;
|
||||
}
|
||||
|
||||
result = dns_db_findext(qctx->db, rpzqname, qctx->version, qctx->type,
|
||||
@@ -6115,19 +6277,7 @@ query_lookup(query_ctx_t *qctx) {
|
||||
* Immediately return the stale answer, start a
|
||||
* resolver fetch to refresh the data in cache.
|
||||
*/
|
||||
- isc_log_write(
|
||||
- ns_lctx, NS_LOGCATEGORY_SERVE_STALE,
|
||||
- NS_LOGMODULE_QUERY, ISC_LOG_INFO,
|
||||
- "%s stale answer used, an attempt to "
|
||||
- "refresh the RRset will still be made",
|
||||
- namebuf);
|
||||
-
|
||||
- qctx->refresh_rrset = STALE(qctx->rdataset);
|
||||
- /*
|
||||
- * If we are refreshing the RRSet, we must not
|
||||
- * detach from the client in query_send().
|
||||
- */
|
||||
- qctx->client->nodetach = qctx->refresh_rrset;
|
||||
+ qctx->client->nodetach = false;
|
||||
|
||||
if (stale_found) {
|
||||
ns_client_extendederror(
|
||||
@@ -6167,76 +6317,12 @@ query_lookup(query_ctx_t *qctx) {
|
||||
}
|
||||
}
|
||||
|
||||
- if (stale_timeout && (answer_found || stale_found)) {
|
||||
- /*
|
||||
- * Mark RRsets that we are adding to the client message on a
|
||||
- * lookup during 'stale-answer-client-timeout', so we can
|
||||
- * clean it up if needed when we resume from recursion.
|
||||
- */
|
||||
- qctx->client->query.attributes |= NS_QUERYATTR_STALEOK;
|
||||
- qctx->rdataset->attributes |= DNS_RDATASETATTR_STALE_ADDED;
|
||||
- }
|
||||
-
|
||||
result = query_gotanswer(qctx, result);
|
||||
|
||||
cleanup:
|
||||
return result;
|
||||
}
|
||||
|
||||
-/*
|
||||
- * Clear all rdatasets from the message that are in the given section and
|
||||
- * that have the 'attr' attribute set.
|
||||
- */
|
||||
-static void
|
||||
-message_clearrdataset(dns_message_t *msg, unsigned int attr) {
|
||||
- unsigned int i;
|
||||
- dns_name_t *name, *next_name;
|
||||
- dns_rdataset_t *rds, *next_rds;
|
||||
-
|
||||
- /*
|
||||
- * Clean up name lists by calling the rdataset disassociate function.
|
||||
- */
|
||||
- for (i = DNS_SECTION_ANSWER; i < DNS_SECTION_MAX; i++) {
|
||||
- name = ISC_LIST_HEAD(msg->sections[i]);
|
||||
- while (name != NULL) {
|
||||
- next_name = ISC_LIST_NEXT(name, link);
|
||||
-
|
||||
- rds = ISC_LIST_HEAD(name->list);
|
||||
- while (rds != NULL) {
|
||||
- next_rds = ISC_LIST_NEXT(rds, link);
|
||||
- if ((rds->attributes & attr) != attr) {
|
||||
- rds = next_rds;
|
||||
- continue;
|
||||
- }
|
||||
- ISC_LIST_UNLINK(name->list, rds, link);
|
||||
- INSIST(dns_rdataset_isassociated(rds));
|
||||
- dns_rdataset_disassociate(rds);
|
||||
- isc_mempool_put(msg->rdspool, rds);
|
||||
- rds = next_rds;
|
||||
- }
|
||||
-
|
||||
- if (ISC_LIST_EMPTY(name->list)) {
|
||||
- ISC_LIST_UNLINK(msg->sections[i], name, link);
|
||||
- if (dns_name_dynamic(name)) {
|
||||
- dns_name_free(name, msg->mctx);
|
||||
- }
|
||||
- isc_mempool_put(msg->namepool, name);
|
||||
- }
|
||||
-
|
||||
- name = next_name;
|
||||
- }
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-/*
|
||||
- * Clear any rdatasets from the client's message that were added on a lookup
|
||||
- * due to a client timeout.
|
||||
- */
|
||||
-static void
|
||||
-query_clear_stale(ns_client_t *client) {
|
||||
- message_clearrdataset(client->message, DNS_RDATASETATTR_STALE_ADDED);
|
||||
-}
|
||||
-
|
||||
/*
|
||||
* Create a new query context with the sole intent of looking up for a stale
|
||||
* RRset in cache. If an entry is found, we mark the original query as
|
||||
@@ -6316,6 +6402,7 @@ fetch_callback(isc_task_t *task, isc_event_t *event) {
|
||||
}
|
||||
client->query.fetchoptions &= ~DNS_FETCHOPT_TRYSTALE_ONTIMEOUT;
|
||||
client->query.dboptions &= ~DNS_DBFIND_STALETIMEOUT;
|
||||
+ client->query.dboptions &= ~DNS_DBFIND_STALEENABLED;
|
||||
client->nodetach = false;
|
||||
|
||||
LOCK(&client->query.fetchlock);
|
||||
@@ -7709,14 +7796,6 @@ query_usestale(query_ctx_t *qctx, isc_result_t result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
- if (qctx->refresh_rrset) {
|
||||
- /*
|
||||
- * This is a refreshing query, we have already prioritized
|
||||
- * stale data, so don't enable serve-stale again.
|
||||
- */
|
||||
- return false;
|
||||
- }
|
||||
-
|
||||
if (result == DNS_R_DUPLICATE || result == DNS_R_DROP ||
|
||||
result == ISC_R_ALREADYRUNNING)
|
||||
{
|
||||
@@ -8256,24 +8335,6 @@ query_addanswer(query_ctx_t *qctx) {
|
||||
|
||||
CALL_HOOK(NS_QUERY_ADDANSWER_BEGIN, qctx);
|
||||
|
||||
- /*
|
||||
- * On normal lookups, clear any rdatasets that were added on a
|
||||
- * lookup due to stale-answer-client-timeout. Do not clear if we
|
||||
- * are going to refresh the RRset, because the stale contents are
|
||||
- * prioritized.
|
||||
- */
|
||||
- if (QUERY_STALEOK(&qctx->client->query) &&
|
||||
- !QUERY_STALETIMEOUT(&qctx->client->query) && !qctx->refresh_rrset)
|
||||
- {
|
||||
- CCTRACE(ISC_LOG_DEBUG(3), "query_clear_stale");
|
||||
- query_clear_stale(qctx->client);
|
||||
- /*
|
||||
- * We can clear the attribute to prevent redundant clearing
|
||||
- * in subsequent lookups.
|
||||
- */
|
||||
- qctx->client->query.attributes &= ~NS_QUERYATTR_STALEOK;
|
||||
- }
|
||||
-
|
||||
if (qctx->dns64) {
|
||||
result = query_dns64(qctx);
|
||||
qctx->noqname = NULL;
|
||||
@@ -8307,9 +8368,7 @@ query_addanswer(query_ctx_t *qctx) {
|
||||
query_filter64(qctx);
|
||||
ns_client_putrdataset(qctx->client, &qctx->rdataset);
|
||||
} else {
|
||||
- if (!qctx->is_zone && RECURSIONOK(qctx->client) &&
|
||||
- !QUERY_STALETIMEOUT(&qctx->client->query))
|
||||
- {
|
||||
+ if (!qctx->is_zone && RECURSIONOK(qctx->client)) {
|
||||
query_prefetch(qctx->client, qctx->fname,
|
||||
qctx->rdataset);
|
||||
}
|
||||
@@ -10591,6 +10650,10 @@ query_ncache(query_ctx_t *qctx, isc_result_t result) {
|
||||
}
|
||||
}
|
||||
|
||||
+ if (!qctx->is_zone && RECURSIONOK(qctx->client)) {
|
||||
+ query_stale_refresh_ncache(qctx->client);
|
||||
+ }
|
||||
+
|
||||
return query_nodata(qctx, result);
|
||||
|
||||
cleanup:
|
||||
@@ -12093,20 +12156,6 @@ ns_query_done(query_ctx_t *qctx) {
|
||||
nodetach = qctx->client->nodetach;
|
||||
query_send(qctx->client);
|
||||
|
||||
- if (qctx->refresh_rrset) {
|
||||
- /*
|
||||
- * If we reached this point then it means that we have found a
|
||||
- * stale RRset entry in cache and BIND is configured to allow
|
||||
- * queries to be answered with stale data if no active RRset
|
||||
- * is available, i.e. "stale-anwer-client-timeout 0". But, we
|
||||
- * still need to refresh the RRset. To prevent adding duplicate
|
||||
- * RRsets, clear the RRsets from the message before doing the
|
||||
- * refresh.
|
||||
- */
|
||||
- message_clearrdataset(qctx->client->message, 0);
|
||||
- query_refresh_rrset(qctx);
|
||||
- }
|
||||
-
|
||||
if (!nodetach) {
|
||||
qctx->detach_client = true;
|
||||
}
|
||||
@ -80,7 +80,7 @@ License: MPL-2.0 AND ISC AND MIT AND BSD-3-Clause AND BSD-2-Clause
|
||||
# Before rebasing bind, ensure bind-dyndb-ldap is ready to be rebuild and use side-tag with it.
|
||||
# Updating just bind will cause freeipa-dns-server package to be uninstallable.
|
||||
Version: 9.18.33
|
||||
Release: 16%{?dist}
|
||||
Release: 17%{?dist}
|
||||
Epoch: 32
|
||||
Url: https://www.isc.org/downloads/bind/
|
||||
#
|
||||
@ -160,6 +160,9 @@ Patch227: bind-9.20-CVE-2025-8677-dual-signing-test.patch
|
||||
# https://gitlab.isc.org/isc-projects/bind9/-/merge_requests/11329
|
||||
Patch228: bind-9.20-robust-key-rollovers.patch
|
||||
Patch229: bind-9.20-robust-key-rollovers-tests.patch
|
||||
# https://gitlab.isc.org/isc-projects/bind9/-/merge_requests/10767
|
||||
Patch230: bind-9.20-stale-cname.patch
|
||||
Patch231: bind-9.20-stale-cname-tests.patch
|
||||
|
||||
%{?systemd_ordering}
|
||||
# https://fedoraproject.org/wiki/Changes/RPMSuportForSystemdSysusers
|
||||
@ -961,6 +964,9 @@ fi;
|
||||
%endif
|
||||
|
||||
%changelog
|
||||
* Thu Feb 26 2026 Fedor Vorobev <fvorobev@redhat.com> - 32:9.18.33-17
|
||||
- Backport fixes for stale CNAME chains. (RHEL-142289)
|
||||
|
||||
* Wed Jan 28 2026 Fedor Vorobev <fvorobev@redhat.com> - 32:9.18.33-16
|
||||
- Backport fix for manual DNSSEC key rolllovers. (RHEL-144421)
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user