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; }