view->staleanswerclienttimeout = (uint32_t)-1;
} else {
view->staleanswerclienttimeout = cfg_obj_asuint32(obj);
+
+ /*
+ * BIND 9 no longer supports non-zero values of
+ * stale-answer-client-timeout.
+ */
+ if (view->staleanswerclienttimeout != 0) {
+ view->staleanswerclienttimeout = 0;
+ isc_log_write(
+ named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "BIND 9 no longer supports non-zero values of "
+ "stale-answer-client-timeout, adjusted to 0");
+ }
}
obj = NULL;
query_timeout = cfg_obj_asuint32(obj);
dns_resolver_settimeout(view->resolver, query_timeout);
- /*
- * Adjust stale-answer-client-timeout upper bound
- * to be resolver-query-timeout - 1s.
- * This assignment is safe as dns_resolver_settimeout()
- * ensures that resolver->querytimeout value will be in the
- * [MINIMUM_QUERY_TIMEOUT, MAXIMUM_QUERY_TIMEOUT] range and
- * MINIMUM_QUERY_TIMEOUT is > 1000 (in ms).
- */
- if (view->staleanswerclienttimeout != (uint32_t)-1 &&
- view->staleanswerclienttimeout >
- (dns_resolver_gettimeout(view->resolver) - 1000))
- {
- view->staleanswerclienttimeout =
- dns_resolver_gettimeout(view->resolver) - 1000;
- isc_log_write(
- named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
- NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
- "stale-answer-client-timeout adjusted to %" PRIu32,
- view->staleanswerclienttimeout);
- }
-
/* Specify whether to use 0-TTL for negative response for SOA query */
dns_resolver_setzeronosoattl(view->resolver, zero_no_soattl);
dns_fetchresponse_t *resp = (dns_fetchresponse_t *)arg;
ns_tat_t *tat = NULL;
- INSIST(resp != NULL && resp->type == FETCHDONE);
+ INSIST(resp != NULL);
tat = resp->arg;
notify yes;
stale-answer-enable yes;
stale-cache-enable yes;
- stale-answer-client-timeout 1800;
+ stale-answer-client-timeout 0;
clients-per-query 5;
max-clients-per-query 10;
};
stale-answer-enable yes;
stale-cache-enable yes;
stale-refresh-time 30;
- stale-answer-client-timeout 1800;
+ stale-answer-client-timeout 0;
max-cache-ttl 24h;
};
stale-cache-enable yes;
stale-answer-ttl 3;
stale-refresh-time 0;
- stale-answer-client-timeout 1800; # 1.8 seconds
- recursive-clients 10; # CVE-2022-3924
+ stale-answer-client-timeout 0;
max-stale-ttl 3600;
resolver-query-timeout 30000; # 30 seconds
qname-minimization disabled;
dnssec-validation no;
stale-answer-enable yes;
stale-cache-enable yes;
- stale-answer-client-timeout 1800;
+ stale-answer-client-timeout 0;
prefetch 2 8;
dns64 2001:aaaa::/96 {
clients { any; };
status=$((status + ret))
if [ $ret != 0 ]; then echo_i "failed"; fi
-################################################
-# Test for stale-answer-client-timeout (1.8s). #
-################################################
-echo_i "test stale-answer-client-timeout (1.8)"
-
-n=$((n + 1))
-echo_i "updating ns3/named.conf ($n)"
-ret=0
-copy_setports ns3/named2.conf.in ns3/named.conf
-if [ $ret != 0 ]; then echo_i "failed"; fi
-status=$((status + ret))
-
-echo_i "restart ns3"
-stop_server --use-rndc --port ${CONTROLPORT} ns3
-start_server --noclean --restart --port ${PORT} ns3
-
-n=$((n + 1))
-echo_i "check 'rndc serve-stale status' ($n)"
-ret=0
-$RNDCCMD 10.53.0.3 serve-stale status >rndc.out.test$n 2>&1 || ret=1
-grep '_default: stale cache enabled; stale answers enabled (stale-answer-ttl=3 max-stale-ttl=3600 stale-refresh-time=0)' rndc.out.test$n >/dev/null || ret=1
-if [ $ret != 0 ]; then echo_i "failed"; fi
-status=$((status + ret))
-
-n=$((n + 1))
-echo_i "enable responses from authoritative server ($n)"
-ret=0
-$DIG -p ${PORT} @10.53.0.2 txt enable >dig.out.test$n || ret=1
-grep "ANSWER: 1," dig.out.test$n >/dev/null || ret=1
-grep "TXT.\"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 data.example TXT (stale-answer-client-timeout) ($n)"
-ret=0
-$DIG -p ${PORT} @10.53.0.3 data.example TXT >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
-if [ $ret != 0 ]; then echo_i "failed"; fi
-status=$((status + ret))
-
-n=$((n + 1))
-echo_i "prime cache nodata.example TXT (stale-answer-client-timeout) ($n)"
-ret=0
-$DIG -p ${PORT} @10.53.0.3 nodata.example TXT >dig.out.test$n || ret=1
-grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
-grep "ANSWER: 0," dig.out.test$n >/dev/null || ret=1
-if [ $ret != 0 ]; then echo_i "failed"; fi
-status=$((status + ret))
-
-n=$((n + 1))
-echo_i "delay responses from authoritative server ($n)"
-ret=0
-$DIG -p ${PORT} @10.53.0.2 txt slowdown >dig.out.test$n || ret=1
-grep "ANSWER: 1," dig.out.test$n >/dev/null || ret=1
-grep "TXT.\"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 data.slow TXT (stale-answer-client-timeout) ($n)"
-ret=0
-$DIG -p ${PORT} @10.53.0.3 data.slow TXT >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
-if [ $ret != 0 ]; then echo_i "failed"; fi
-status=$((status + ret))
-
-n=$((n + 1))
-echo_i "disable responses from authoritative server ($n)"
-ret=0
-$DIG -p ${PORT} @10.53.0.2 txt disable >dig.out.test$n || ret=1
-grep "ANSWER: 1," dig.out.test$n >/dev/null || ret=1
-grep "TXT.\"0\"" 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 2
-
-nextpart ns3/named.run >/dev/null
-
-echo_i "sending queries for tests $((n + 1))-$((n + 3))..."
-t1=$($PERL -e 'print time()')
-$DIG -p ${PORT} +tries=1 +timeout=11 @10.53.0.3 data.example TXT >dig.out.test$((n + 1)) &
-$DIG -p ${PORT} +tries=1 +timeout=11 @10.53.0.3 nodata.example TXT >dig.out.test$((n + 2)) &
-$DIG -p ${PORT} +tries=1 +timeout=11 @10.53.0.3 data.slow TXT >dig.out.test$((n + 3)) &
-wait
-t2=$($PERL -e 'print time()')
-
-# We configured a long value of 30 seconds for resolver-query-timeout.
-# That should give us enough time to receive an stale answer from cache
-# after stale-answer-client-timeout timer of 1.8 sec triggers.
-n=$((n + 1))
-echo_i "check stale data.example TXT comes from cache (stale-answer-client-timeout 1.8) ($n)"
-ret=0
-wait_for_log 5 "data.example TXT client timeout, stale answer used" ns3/named.run || ret=1
-grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
-grep "EDE: 3 (Stale Answer): (client timeout)" dig.out.test$n >/dev/null || ret=1
-grep "ANSWER: 1," dig.out.test$n >/dev/null || ret=1
-grep "data\.example\..*3.*IN.*TXT.*A text record with a 2 second ttl" dig.out.test$n >/dev/null || ret=1
-# Configured stale-answer-client-timeout is 1.8s, we allow some extra time
-# just in case other tests are taking too much cpu.
-[ $((t2 - t1)) -le 10 ] || {
- echo_i "query took $((t2 - t1))s to resolve."
- ret=1
-}
-if [ $ret != 0 ]; then echo_i "failed"; fi
-status=$((status + ret))
-
-n=$((n + 1))
-echo_i "check stale nodata.example TXT comes from cache (stale-answer-client-timeout 1.8) ($n)"
-ret=0
-grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
-grep "EDE: 3 (Stale Answer): (client timeout)" dig.out.test$n >/dev/null || ret=1
-grep "ANSWER: 0," dig.out.test$n >/dev/null || ret=1
-grep "example\..*3.*IN.*SOA" dig.out.test$n >/dev/null || ret=1
-if [ $ret != 0 ]; then echo_i "failed"; fi
-status=$((status + ret))
-
-n=$((n + 1))
-echo_i "check stale data.slow TXT comes from cache (stale-answer-client-timeout 1.8) ($n)"
-ret=0
-grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
-grep "EDE: 3 (Stale Answer): (client timeout)" dig.out.test$n >/dev/null || ret=1
-grep "ANSWER: 1," dig.out.test$n >/dev/null || ret=1
-grep "data\.slow\..*3.*IN.*TXT.*A slow text record with a 2 second ttl" dig.out.test$n >/dev/null || ret=1
-if [ $ret != 0 ]; then echo_i "failed"; fi
-status=$((status + ret))
-
-# Now query for RRset not in cache. The first query should time out, but once
-# we enable the authoritative server, the second query should be able to get a
-# response.
-
-nextpart ns3/named.run >/dev/null
-
-echo_i "sending queries for tests $((n + 2))-$((n + 4))..."
-# first dig runs in background for 10 seconds, second in background for 3
-# seconds and the last for 3 seconds in the foreground.
-# the second RRSIG lookup triggers the issue in [GL #3622]
-$DIG -p ${PORT} +tries=1 +timeout=10 @10.53.0.3 longttl.example TXT >dig.out.test$((n + 3)) &
-$DIG -p ${PORT} +tries=1 +timeout=3 @10.53.0.3 longttl.example RRSIG >dig.out.test$((n + 4)) &
-$DIG -p ${PORT} +tries=1 +timeout=3 @10.53.0.3 longttl.example TXT >dig.out.test$((n + 2)) || true
-
-# Enable the authoritative name server after stale-answer-client-timeout.
-n=$((n + 1))
-echo_i "enable responses from authoritative server ($n)"
-ret=0
-$DIG -p ${PORT} @10.53.0.2 txt enable >dig.out.test$n || ret=1
-grep "ANSWER: 1," dig.out.test$n >/dev/null || ret=1
-grep "TXT.\"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 "check not in cache longttl.example TXT times out (stale-answer-client-timeout 1.8) ($n)"
-ret=0
-wait_for_log 4 "longttl.example TXT client timeout, stale answer unavailable" ns3/named.run || ret=1
-grep "timed out" dig.out.test$n >/dev/null || ret=1
-grep ";; no servers could be reached" dig.out.test$n >/dev/null || ret=1
-if [ $ret != 0 ]; then echo_i "failed"; fi
-status=$((status + ret))
-
-wait
-
-n=$((n + 1))
-echo_i "check not in cache longttl.example TXT comes from authoritative (stale-answer-client-timeout 1.8) ($n)"
-ret=0
-grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
-grep "EDE" dig.out.test$n >/dev/null && ret=1
-grep "ANSWER: 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 "check not in cache longttl.example RRSIG times out (stale-answer-client-timeout 1.8) ($n)"
-ret=0
-grep "timed out" dig.out.test$n >/dev/null || ret=1
-grep ";; no servers could be reached" dig.out.test$n >/dev/null || ret=1
-if [ $ret != 0 ]; then echo_i "failed"; fi
-status=$((status + ret))
-
-# CVE-2022-3924, GL #3619
-n=$((n + 1))
-echo_i "check that named survives reaching recursive-clients quota (stale-answer-client-timeout 1.8) ($n)"
-ret=0
-num=0
-# Make sure to exceed the configured value of 'recursive-clients 10;' by running
-# 20 parallel queries with simulated network latency.
-while [ $num -lt 20 ]; do
- $DIG +tries=1 -p ${PORT} @10.53.0.3 "latency${num}.data.example" TXT >/dev/null 2>&1 &
- num=$((num + 1))
-done
-check_server_responds() {
- $DIG -p ${PORT} @10.53.0.3 version.bind txt ch >dig.out.test$n || return 1
- grep "status: NOERROR" dig.out.test$n >/dev/null || return 1
-}
-retry_quiet 5 check_server_responds || ret=1
-if [ $ret != 0 ]; then echo_i "failed"; fi
-status=$((status + ret))
-
#############################################
# Test for stale-answer-client-timeout off. #
#############################################
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
+check_server_responds() {
+ $DIG -p ${PORT} @10.53.0.3 version.bind txt ch >dig.out.test$n || return 1
+ grep "status: NOERROR" dig.out.test$n >/dev/null || return 1
+}
+
##############################################################
# Test for stale-answer-client-timeout off and CNAME record. #
##############################################################
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
-###########################################################
-# Test serve-stale's interaction with prefetch processing #
-###########################################################
-echo_i "test serve-stale's interaction with prefetch processing"
-
-# Test case for #2733, ensuring that prefetch queries do not trigger
-# a lookup due to stale-answer-client-timeout.
-#
-# 1. Cache the following records:
-# cname.example 7 IN CNAME target.example.
-# target.example 9 IN A <addr>.
-# 2. Let the CNAME RRset expire.
-# 3. Query for 'cname.example/A'.
-#
-# This starts recursion because cname.example/CNAME is expired.
-# The authoritative server is up so likely it will respond before
-# stale-answer-client-timeout is triggered.
-# The 'target.example/A' RRset is found in cache with a positive value
-# and is eligble for prefetching.
-# A prefetch is done for 'target.example/A', our ans2 server will
-# delay the request.
-# The 'prefetch_done()' callback should have the right event type
-# (DNS_EVENT_FETCHDONE).
-
-# flush cache
-n=$((n + 1))
-echo_i "flush cache ($n)"
-ret=0
-$RNDCCMD 10.53.0.3 flushtree example >rndc.out.test$n.1 2>&1 || ret=1
-if [ $ret != 0 ]; then echo_i "failed"; fi
-status=$((status + ret))
-
-# prime the cache with CNAME and A; CNAME expires sooner
-n=$((n + 1))
-echo_i "prime cache cname.example A (stale-answer-client-timeout 1.8) ($n)"
-ret=0
-$DIG -p ${PORT} @10.53.0.3 cname.example 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
-grep "cname\.example\..*7.*IN.*CNAME.*target\.example\." dig.out.test$n >/dev/null || ret=1
-grep "target\.example\..*9.*IN.*A" dig.out.test$n >/dev/null || ret=1
-if [ $ret != 0 ]; then echo_i "failed"; fi
-status=$((status + ret))
-
-# wait for the CNAME to be stale; A will still be valid and in prefetch window.
-# (the longer TTL is needed, otherwise data won't be prefetch-eligible.)
-sleep 7
-
-# re-enable auth responses, but with a delay answering the A
-n=$((n + 1))
-echo_i "delay responses from authoritative server ($n)"
-ret=0
-$DIG -p ${PORT} @10.53.0.2 txt slowdown >dig.out.test$n || ret=1
-grep "ANSWER: 1," dig.out.test$n >/dev/null || ret=1
-grep "TXT.\"1\"" dig.out.test$n >/dev/null || ret=1
-if [ $ret != 0 ]; then echo_i "failed"; fi
-status=$((status + ret))
-
-# resend the query and wait in the background; we should get a stale answer
-n=$((n + 1))
-echo_i "check prefetch processing of a stale CNAME target ($n)"
-ret=0
-$DIG -p ${PORT} @10.53.0.3 cname.example A >dig.out.test$n &
-sleep 2
-wait
-grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
-grep "ANSWER: 2," dig.out.test$n >/dev/null || ret=1
-grep "cname\.example\..*7.*IN.*CNAME.*target\.example\." dig.out.test$n >/dev/null || ret=1
-grep "target\.example\..*[1-2].*IN.*A" dig.out.test$n >/dev/null || ret=1
-if [ $ret != 0 ]; then echo_i "failed"; fi
-status=$((status + ret))
-
echo_i "exit status: $status"
[ $status -eq 0 ] || exit 1
``off`` or ``disabled``. It also has no effect if :any:`stale-answer-enable`
is disabled.
- The maximum value for this option is :any:`resolver-query-timeout` minus
- one second. The minimum value, ``0``, causes a cached (stale) RRset to be
+ The minimum value, ``0``, causes a cached (stale) RRset to be
immediately returned if it is available while still attempting to
- refresh the data in cache. :rfc:`8767` recommends a value of ``1800``
- (milliseconds).
+ refresh the data in cache.
+
+ When this option is enabled, the only supported value in the current version
+ of BIND 9 is ``0``. Non-zero values generate a warning message, and are
+ treated as ``0``.
.. namedconf:statement:: stale-cache-enable
:tags: server, query
isc_loop_t *loop;
isc_job_cb cb;
void *arg;
- enum { FETCHDONE, TRYSTALE } type;
ISC_LINK(dns_fetchresponse_t) link;
};
* on ip6.arpa. */
DNS_FETCHOPT_NOFORWARD = 1 << 15, /*%< Do not use forwarders if
* possible. */
- DNS_FETCHOPT_TRYSTALE_ONTIMEOUT = 1 << 16,
/*% EDNS version bits: */
DNS_FETCHOPT_EDNSVERSIONSET = 1 << 23,
* we figure out how selective forwarding will work.
*
*\li When the fetch completes (successfully or otherwise), a
- * dns_fetchresponse_t option is sent to callback 'cb' with
- * 'type' set to FETCHDONE.
+ * dns_fetchresponse_t option is sent to callback 'cb'.
*
*\li The values of 'rdataset' and 'sigrdataset' will be returned in
- * the FETCHDONE event.
+ * the fetch completion event.
*
*\li 'client' and 'id' are used for duplicate query detection. '*client'
* must remain stable until after 'action' has been called or
*
* Notes:
*
- *\li If 'fetch' has not completed, post its FETCHDONE event with a
+ *\li If 'fetch' has not completed, post its completion event with a
* result code of #ISC_R_CANCELED.
*
* Requires:
*
*\li '*fetchp' is a valid fetch.
*
- *\li The caller has received the FETCHDONE event (either because the
+ *\li The caller has received the fetch completion event (either because the
* fetch completed or because dns_resolver_cancelfetch() was called).
*
* Ensures:
typedef enum {
fetchstate_active,
- fetchstate_done /*%< FETCHDONE events posted. */
+ fetchstate_done /*%< Fetch completion events posted. */
} fetchstate_t;
typedef enum {
atomic_uint_fast32_t attributes;
isc_timer_t *timer;
isc_time_t expires;
- isc_time_t expires_try_stale;
isc_time_t next_timeout;
isc_interval_t interval;
dns_message_t *qmessage;
next = ISC_LIST_NEXT(resp, link);
ISC_LIST_UNLINK(fctx->resps, resp, link);
- /*
- * Only the regular fetch events should be counted for the
- * clients-per-query limit, in case if there are multiple events
- * registered for a single client.
- */
- if (resp->type == FETCHDONE) {
- count++;
- }
-
- if (resp->type == TRYSTALE) {
- /*
- * Not applicable to TRYSTALE resps; this function is
- * called when the fetch has either completed or timed
- * out due to resolver-query-timeout being reached.
- */
- isc_mem_putanddetach(&resp->mctx, resp, sizeof(*resp));
- continue;
- }
+ count++;
resp->vresult = fctx->vresult;
if (!HAVE_ANSWER(fctx)) {
}
/*
- * But don't wait past the stale timeout (if any), the final
- * expiration of the fetch, or for more than 10 seconds total.
+ * But don't wait past the the final expiration of the fetch,
+ * or for more than 10 seconds total.
*/
- if ((fctx->options & DNS_FETCHOPT_TRYSTALE_ONTIMEOUT) != 0) {
- uint64_t stale = isc_time_microdiff(&fctx->expires_try_stale,
- &now);
- if (stale >= US_PER_MS && us > stale) {
- FCTXTRACE("setting stale timeout");
- us = stale;
- }
- }
if (us > limit) {
us = limit;
}
isc_time_nowplusinterval(&fctx->next_timeout, &fctx->interval);
}
-static isc_result_t
-resquery_timeout(resquery_t *query) {
- fetchctx_t *fctx = query->fctx;
- dns_fetchresponse_t *resp = NULL, *next = NULL;
- uint64_t timeleft;
- isc_time_t now;
-
- FCTXTRACE("timeout");
-
- /*
- * If not configured for serve-stale, do nothing.
- */
- if ((fctx->options & DNS_FETCHOPT_TRYSTALE_ONTIMEOUT) == 0) {
- return (ISC_R_SUCCESS);
- }
-
- /*
- * If we haven't reached the serve-stale timeout, do nothing.
- * (Note that netmgr timeouts have millisecond accuracy, so
- * anything less than 1000 microseconds is close enough to zero.)
- */
- now = isc_time_now();
- timeleft = isc_time_microdiff(&fctx->expires_try_stale, &now);
- if (timeleft >= US_PER_MS) {
- return (ISC_R_SUCCESS);
- }
-
- /*
- * Send the TRYSTALE events.
- */
- LOCK(&fctx->lock);
- for (resp = ISC_LIST_HEAD(fctx->resps); resp != NULL; resp = next) {
- next = ISC_LIST_NEXT(resp, link);
- if (resp->type != TRYSTALE) {
- continue;
- }
-
- ISC_LIST_UNLINK(fctx->resps, resp, link);
- resp->vresult = ISC_R_TIMEDOUT;
- resp->result = ISC_R_TIMEDOUT;
- isc_async_run(resp->loop, resp->cb, resp);
- }
- UNLOCK(&fctx->lock);
-
- /*
- * If the next timeout is more than 1ms in the future,
- * resume waiting.
- */
- timeleft = isc_time_microdiff(&fctx->next_timeout, &now);
- if (timeleft >= US_PER_MS) {
- dns_dispatch_resume(query->dispentry, (timeleft / US_PER_MS));
- return (ISC_R_COMPLETE);
- }
-
- return (ISC_R_SUCCESS);
-}
-
static isc_result_t
fctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
unsigned int options) {
*/
if (fctx->minimized && !fctx->forwarding) {
unsigned int options = fctx->options;
- /*
- * Also clear DNS_FETCHOPT_TRYSTALE_ONTIMEOUT here,
- * otherwise every query minimization step will activate
- * the try-stale timer again.
- */
- options &= ~(DNS_FETCHOPT_QMINIMIZE |
- DNS_FETCHOPT_TRYSTALE_ONTIMEOUT);
+
+ options &= ~DNS_FETCHOPT_QMINIMIZE;
/*
* Is another QNAME minimization fetch still running?
dns_fixedname_t ffixed, dcfixed;
REQUIRE(VALID_FCTX(fctx));
- REQUIRE(resp->type == FETCHDONE);
res = fctx->res;
fctx_add_event(fetchctx_t *fctx, isc_loop_t *loop, const isc_sockaddr_t *client,
dns_messageid_t id, isc_job_cb cb, void *arg,
dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset,
- dns_fetch_t *fetch, int type) {
+ dns_fetch_t *fetch) {
dns_fetchresponse_t *resp = NULL;
FCTXTRACE("addevent");
.fetch = fetch,
.client = client,
.id = id,
- .type = type,
.loop = loop,
.cb = cb,
.arg = arg,
REQUIRE(!SHUTTINGDOWN(fctx));
fctx_add_event(fctx, loop, client, id, cb, arg, rdataset, sigrdataset,
- fetch, FETCHDONE);
+ fetch);
fetch->magic = DNS_FETCH_MAGIC;
fetchctx_attach(fctx, &fetch->private);
*/
isc_interval_set(&fctx->interval, 2, 0);
- /*
- * If stale answers are enabled, compute an expiration time
- * after which stale data will be served, if the target RRset is
- * available in cache.
- */
- if ((options & DNS_FETCHOPT_TRYSTALE_ONTIMEOUT) != 0) {
- INSIST(res->view->staleanswerclienttimeout <=
- (res->query_timeout - 1000));
- isc_interval_set(
- &interval, res->view->staleanswerclienttimeout / 1000,
- res->view->staleanswerclienttimeout % 1000 * 1000000);
- iresult = isc_time_nowplusinterval(&fctx->expires_try_stale,
- &interval);
- if (iresult != ISC_R_SUCCESS) {
- UNEXPECTED_ERROR("isc_time_nowplusinterval: %s",
- isc_result_totext(iresult));
- result = ISC_R_UNEXPECTED;
- goto cleanup_timer;
- }
- }
-
/*
* Attach to the view's cache and adb.
*/
return (ISC_R_SUCCESS);
-cleanup_timer:
- isc_timer_destroy(&fctx->timer);
-
cleanup_qmessage:
dns_message_detach(&fctx->qmessage);
continue;
}
- if (resp->type == TRYSTALE) {
- /*
- * We don't need to clone resulting data to this
- * type of event, as its associated callback is
- * only called when stale-answer-client-timeout
- * triggers, and the logic in there doesn't
- * expect any result as input, as it will itself
- * lookup for stale data in cache to use as
- * result, if any is available.
- *
- * Also, if we reached this point, then the
- * whole fetch context is done, it will cancel
- * timers, process associated callbacks of type
- * FETCHDONE, and silently remove/free events of
- * type TRYSTALE.
- */
- continue;
- }
-
resp->result = hresp->result;
dns_name_copy(hresp->foundname, resp->foundname);
dns_db_attach(hresp->db, &resp->db);
unsigned int n;
dns_fetch_t *fetch = NULL;
- REQUIRE(resp->type == FETCHDONE);
REQUIRE(VALID_FCTX(fctx));
res = fctx->res;
}
/*
- * resquery_response():
* Handles responses received in response to iterative queries sent by
* resquery_send(). Sets up a response context (respctx_t).
*/
QTRACE("response");
- if (eresult == ISC_R_TIMEDOUT) {
- result = resquery_timeout(query);
- if (result == ISC_R_COMPLETE) {
- return;
- }
- }
-
if (isc_sockaddr_pf(&query->addrinfo->sockaddr) == PF_INET) {
inc_stats(fctx->res, dns_resstatscounter_responsev4);
} else {
dns_fetch_t *fetch = NULL;
dns_db_t *db = NULL;
- REQUIRE(resp->type == FETCHDONE);
REQUIRE(VALID_RESOLVER(res));
isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
goto unlock;
}
- /*
- * Only the regular fetch events should be
- * counted for the clients-per-query limit, in
- * case if there are multiple events registered
- * for a single client.
- */
- if (resp->type == FETCHDONE) {
- count++;
- }
+ count++;
}
}
if (count >= spillatmin && spillatmin != 0) {
fctx_join(fctx, loop, client, id, cb, arg, rdataset, sigrdataset,
fetch);
- if ((options & DNS_FETCHOPT_TRYSTALE_ONTIMEOUT) != 0) {
- fctx_add_event(fctx, loop, client, id, cb, arg, NULL, NULL,
- fetch, TRYSTALE);
- }
-
if (new_fctx) {
fetchctx_ref(fctx);
isc_async_run(fctx->loop, fctx_start, fctx);
void
dns_resolver_cancelfetch(dns_fetch_t *fetch) {
fetchctx_t *fctx = NULL;
- dns_fetchresponse_t *trystale = NULL;
- dns_fetchresponse_t *fetchdone = NULL;
REQUIRE(DNS_FETCH_VALID(fetch));
fctx = fetch->private;
LOCK(&fctx->lock);
/*
- * Find completion events associated with this fetch (as opposed
- * to those for other fetches that have joined the same fctx).
- * The event(s) found are only sent and removed from the fctx->resps
- * list after this loop is finished (i.e. the fctx->resps list is not
- * modified during iteration).
+ * Find the completion event associated with this fetch (as opposed
+ * to those for other fetches that have joined the same fctx) and run
+ * the callback asynchronously with a ISC_R_CANCELED result.
*/
if (fctx->state != fetchstate_done) {
dns_fetchresponse_t *next = NULL;
{
next = ISC_LIST_NEXT(resp, link);
- /*
- * Only process events associated with the provided
- * 'fetch'.
- */
- if (resp->fetch != fetch) {
- continue;
- }
-
- /*
- * Track various events associated with the
- * provided 'fetch' in separate variables as they
- * will need to be sent in a specific order.
- */
- switch (resp->type) {
- case TRYSTALE:
- INSIST(trystale == NULL);
- trystale = resp;
- break;
- case FETCHDONE:
- INSIST(fetchdone == NULL);
- fetchdone = resp;
- break;
- default:
- UNREACHABLE();
- }
-
- /*
- * Break out of the loop once all possible types of
- * events associated with the provided 'fetch' are
- * found.
- */
- if (trystale != NULL && fetchdone != NULL) {
+ if (resp->fetch == fetch) {
+ resp->result = ISC_R_CANCELED;
+ ISC_LIST_UNLINK(fctx->resps, resp, link);
+ isc_async_run(resp->loop, resp->cb, resp);
break;
}
}
}
- /*
- * The "trystale" event must be sent before the "fetchdone" event,
- * because the latter clears the "recursing" query attribute, which is
- * required by both events (handled by the same callback function).
- */
- if (trystale != NULL) {
- trystale->result = ISC_R_CANCELED;
- ISC_LIST_UNLINK(fctx->resps, trystale, link);
- isc_async_run(trystale->loop, trystale->cb, trystale);
- }
- if (fetchdone != NULL) {
- fetchdone->result = ISC_R_CANCELED;
- ISC_LIST_UNLINK(fctx->resps, fetchdone, link);
- isc_async_run(fetchdone->loop, fetchdone->cb, fetchdone);
- }
-
/*
* The fctx continues running even if no fetches remain;
* the answer is still cached.
isc_result_t eresult = resp->result;
isc_result_t result;
- INSIST(resp->type == FETCHDONE);
-
/* Free resources which are not of interest. */
if (resp->node != NULL) {
dns_db_detachnode(resp->db, &resp->node);
isc_result_t result;
bool trustchain;
- INSIST(resp->type == FETCHDONE);
-
/*
* Set 'trustchain' to true if we're walking a chain of
* trust; false if we're attempting to prove insecurity.
dns_rdataset_t *dnskeys = NULL, *dnskeysigs = NULL;
dns_rdataset_t *keydataset = NULL, dsset;
- INSIST(resp != NULL && resp->type == FETCHDONE);
+ INSIST(resp != NULL);
kfetch = resp->arg;
dns_rdataset_t *nsrrset = NULL;
dns_rdataset_t *nssigset = NULL;
- INSIST(resp != NULL && resp->type == FETCHDONE);
+ INSIST(resp != NULL);
nsfetch = resp->arg;
#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;
/*% Was the client already sent a response? */
#define QUERY_ANSWERED(q) (((q)->attributes & NS_QUERYATTR_ANSWERED) != 0)
-/*% Have we already processed an answer via stale-answer-client-timeout? */
-#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)
dns_fetch_t **fetchp = NULL;
isc_result_t result;
- REQUIRE(resp->type == FETCHDONE);
REQUIRE(NS_CLIENT_VALID(client));
CTRACE(ISC_LOG_DEBUG(3), ctracestr);
}
}
- if (!qctx->is_zone && (qctx->view->staleanswerclienttimeout == 0) &&
+ if (!qctx->is_zone && qctx->view->staleanswerclienttimeout == 0 &&
dns_view_staleanswerenabled(qctx->view))
{
/*
* If DNS_DBFIND_STALETIMEOUT is set, a stale answer is requested.
* This can happen if 'stale-answer-client-timeout' is enabled.
*
- * If 'stale-answer-client-timeout' is set to 0, and a stale
- * answer is found, send it to the client, and try to refresh the
- * RRset.
- *
- * If 'stale-answer-client-timeout' is non-zero, and a stale
- * answer is found, send it to the client. Don't try to refresh the
- * RRset because a fetch is already in progress.
+ * If a stale answer is found, send it to the client, and try to refresh
+ * the RRset.
*/
stale_timeout = ((dboptions & DNS_DBFIND_STALETIMEOUT) != 0);
}
}
} else {
- /*
- * The 'stale-answer-client-timeout' triggered, return
- * the stale answer if available, otherwise wait until
- * the resolver finishes.
- */
- isc_log_write(
- ns_lctx, NS_LOGCATEGORY_SERVE_STALE,
- NS_LOGMODULE_QUERY, ISC_LOG_INFO,
- "%s %s client timeout, stale answer %s (%s)",
- namebuf, typebuf,
- stale_found ? "used" : "unavailable",
- isc_result_totext(result));
- if (stale_found) {
- ns_client_extendederror(qctx->client, ede,
- "client timeout");
- } else if (!answer_found) {
- return (result);
- }
-
- if (!stale_client_answer(result)) {
- return (result);
- }
-
- /*
- * There still might be real answer later. Mark the
- * query so we'll know we can skip answering.
- */
- qctx->client->query.attributes |=
- NS_QUERYATTR_STALEPENDING;
+ UNREACHABLE();
}
}
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
- * answered, in order to avoid answering the query twice, when the original
- * fetch finishes.
- */
-static void
-query_lookup_stale(ns_client_t *client) {
- query_ctx_t qctx;
-
- qctx_init(client, NULL, client->query.qtype, &qctx);
- if (DNS64(client)) {
- qctx.qtype = qctx.type = dns_rdatatype_a;
- qctx.dns64 = true;
- }
- if (DNS64EXCLUDE(client)) {
- qctx.dns64_exclude = true;
- }
- dns_db_attach(client->view->cachedb, &qctx.db);
- client->query.attributes &= ~NS_QUERYATTR_RECURSIONOK;
- client->query.dboptions |= DNS_DBFIND_STALETIMEOUT;
- client->nodetach = true;
- (void)query_lookup(&qctx);
- if (qctx.node != NULL) {
- dns_db_detachnode(qctx.db, &qctx.node);
- }
- qctx_freedata(&qctx);
- qctx_destroy(&qctx);
-}
-
/*
* Event handler to resume processing a query after recursion, or when a
* client timeout is triggered. If the query has timed out or been cancelled
CTRACE(ISC_LOG_DEBUG(3), "fetch_callback");
- if (resp->type == TRYSTALE) {
- if (resp->result != ISC_R_CANCELED) {
- query_lookup_stale(client);
- }
- isc_mem_putanddetach(&resp->mctx, resp, sizeof(*resp));
- return;
- }
/*
* We are resuming from recursion. Reset any attributes, options
* that a lookup due to stale-answer-client-timeout may have set.
if (client->view->cachedb != NULL && client->view->recursion) {
client->query.attributes |= NS_QUERYATTR_RECURSIONOK;
}
- client->query.fetchoptions &= ~DNS_FETCHOPT_TRYSTALE_ONTIMEOUT;
client->query.dboptions &= ~DNS_DBFIND_STALETIMEOUT;
client->nodetach = false;
LOCK(&client->query.fetchlock);
INSIST(FETCH_RECTYPE_NORMAL(client) == resp->fetch ||
FETCH_RECTYPE_NORMAL(client) == NULL);
- if (QUERY_STALEPENDING(&client->query)) {
- /*
- * We've gotten an authoritative answer to a query that
- * was left pending after a stale timeout. We don't need
- * to do anything with it; free all the data and go home.
- */
- FETCH_RECTYPE_NORMAL(client) = NULL;
- fetch_answered = true;
- } else if (FETCH_RECTYPE_NORMAL(client) != NULL) {
+ if (FETCH_RECTYPE_NORMAL(client) != NULL) {
/*
* This is the fetch we've been waiting for.
*/
peeraddr = &client->peeraddr;
}
- if (client->view->staleanswerclienttimeout > 0 &&
- client->view->staleanswerclienttimeout != (uint32_t)-1 &&
- dns_view_staleanswerenabled(client->view))
- {
- client->query.fetchoptions |= DNS_FETCHOPT_TRYSTALE_ONTIMEOUT;
- }
-
isc_nmhandle_attach(client->handle, &HANDLE_RECTYPE_NORMAL(client));
result = dns_resolver_createfetch(
client->view->resolver, qname, qtype, qdomain, nameservers,