/* In case we have expired data but there is a client timer for expired
* answers, pass execution to next module in order to try updating the
* data first.
- * TODO: this needs revisit. The expired data stored from cachedb has
- * 0 TTL which is picked up by iterator later when looking in the cache.
*/
if(qstate->env->cfg->serve_expired && msg_expired) {
qstate->return_msg = NULL;
struct reply_info* d = (struct reply_info*)e->data;
if(d->security == sec_status_bogus) {
d->ttl = inf->expired;
+ d->prefetch_ttl = inf->expired;
+ d->serve_expired_ttl = inf->expired;
inf->num_msgs++;
#ifdef USE_CACHEDB
if(inf->remcachedb && inf->worker->env.cachedb_enabled)
* or NOERROR rcode with ANCOUNT==0: a NODATA answer */
if(FLAGS_GET_RCODE(d->flags) != 0 || d->an_numrrsets == 0) {
d->ttl = inf->expired;
+ d->prefetch_ttl = inf->expired;
+ d->serve_expired_ttl = inf->expired;
inf->num_msgs++;
#ifdef USE_CACHEDB
if(inf->remcachedb && inf->worker->env.cachedb_enabled)
if(rep->ttl < timenow) {
/* Check if we need to serve expired now */
if(worker->env.cfg->serve_expired &&
- !worker->env.cfg->serve_expired_client_timeout
+ /* if serve-expired-client-timeout is set, serve
+ * an expired record without attempting recursion
+ * if the serve_expired_norec_ttl is set for the record
+ * as we know that recursion is currently failing. */
+ (!worker->env.cfg->serve_expired_client_timeout ||
+ timenow < rep->serve_expired_norec_ttl)
#ifdef USE_CACHEDB
&& !(worker->env.cachedb_enabled &&
worker->env.cfg->cachedb_check_when_serve_expired)
#endif
) {
- if(worker->env.cfg->serve_expired_ttl &&
- rep->serve_expired_ttl < timenow)
- return 0;
- /* Ignore expired failure answers */
- if(FLAGS_GET_RCODE(rep->flags) !=
- LDNS_RCODE_NOERROR &&
- FLAGS_GET_RCODE(rep->flags) !=
- LDNS_RCODE_NXDOMAIN &&
- FLAGS_GET_RCODE(rep->flags) !=
- LDNS_RCODE_YXDOMAIN)
+ if(!reply_info_can_answer_expired(rep, timenow))
return 0;
if(!rrset_array_lock(rep->ref, rep->rrset_count, 0))
return 0;
*/
cp = construct_reply_info_base(super->region, rep->flags, rep->qdcount,
rep->ttl, rep->prefetch_ttl, rep->serve_expired_ttl,
+ rep->serve_expired_norec_ttl,
rep->an_numrrsets, rep->ns_numrrsets, rep->ar_numrrsets,
rep->rrset_count, rep->security, LDNS_EDE_NONE);
if(!cp)
qstate->qinfo.qname, qstate->qinfo.qname_len,
qstate->qinfo.qtype, qstate->qinfo.qclass,
qstate->query_flags, 0,
- qstate->env->cfg->serve_expired_ttl_reset)) != NULL) {
+ qstate->env->cfg->serve_expired)) != NULL) {
struct reply_info* rep = (struct reply_info*)msg->entry.data;
- if(qstate->env->cfg->serve_expired &&
- qstate->env->cfg->serve_expired_ttl_reset && rep &&
- *qstate->env->now + qstate->env->cfg->serve_expired_ttl
- > rep->serve_expired_ttl) {
- verbose(VERB_ALGO, "reset serve-expired-ttl for "
+ if(qstate->env->cfg->serve_expired && rep) {
+ if(qstate->env->cfg->serve_expired_ttl_reset &&
+ *qstate->env->now + qstate->env->cfg->serve_expired_ttl
+ > rep->serve_expired_ttl) {
+ verbose(VERB_ALGO, "reset serve-expired-ttl for "
+ "response in cache");
+ rep->serve_expired_ttl = *qstate->env->now +
+ qstate->env->cfg->serve_expired_ttl;
+ }
+ verbose(VERB_ALGO, "set serve-expired-norec-ttl for "
"response in cache");
- rep->serve_expired_ttl = *qstate->env->now +
- qstate->env->cfg->serve_expired_ttl;
+ rep->serve_expired_norec_ttl = NORR_TTL +
+ *qstate->env->now;
}
if(rep && (FLAGS_GET_RCODE(rep->flags) ==
LDNS_RCODE_NOERROR ||
to->rep->prefetch_ttl = from->rep->prefetch_ttl;
if(from->rep->serve_expired_ttl < to->rep->serve_expired_ttl)
to->rep->serve_expired_ttl = from->rep->serve_expired_ttl;
+ if(from->rep->serve_expired_norec_ttl < to->rep->serve_expired_norec_ttl)
+ to->rep->serve_expired_norec_ttl = from->rep->serve_expired_norec_ttl;
}
/* are we done? */
foriq->num_current_queries --;
size_t i;
/* store RRsets */
- for(i=0; i<rep->rrset_count; i++) {
+ for(i=0; i<rep->rrset_count; i++) {
rep->ref[i].key = rep->rrsets[i];
rep->ref[i].id = rep->rrsets[i]->id;
}
reply_info_sortref(rep);
if(!(e = query_info_entrysetup(qinfo, rep, hash))) {
log_err("store_msg: malloc failed");
+ reply_info_delete(rep, NULL);
return;
}
slabhash_insert(env->msg_cache, hash, &e->entry, rep, env->alloc);
time_t now_control = now;
if(now > r->ttl) {
/* Check if we are allowed to serve expired */
- if(allow_expired) {
- if(env->cfg->serve_expired_ttl &&
- r->serve_expired_ttl < now) {
- return NULL;
- }
- /* Ignore expired failure answers */
- if(FLAGS_GET_RCODE(r->flags) !=
- LDNS_RCODE_NOERROR &&
- FLAGS_GET_RCODE(r->flags) !=
- LDNS_RCODE_NXDOMAIN &&
- FLAGS_GET_RCODE(r->flags) !=
- LDNS_RCODE_YXDOMAIN)
- return 0;
- } else {
+ if(!allow_expired || !reply_info_can_answer_expired(r, now))
return NULL;
- }
/* Change the current time so we can pass the below TTL checks when
* serving expired data. */
now_control = r->ttl - env->cfg->serve_expired_reply_ttl;
else
msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl);
msg->rep->serve_expired_ttl = msg->rep->ttl + SERVE_EXPIRED_TTL;
+ msg->rep->serve_expired_norec_ttl = 0;
msg->rep->security = r->security;
msg->rep->an_numrrsets = r->an_numrrsets;
msg->rep->ns_numrrsets = r->ns_numrrsets;
msg->rep->ttl = d->ttl - now;
msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl);
msg->rep->serve_expired_ttl = msg->rep->ttl + SERVE_EXPIRED_TTL;
+ msg->rep->serve_expired_norec_ttl = 0;
msg->rep->security = sec_status_unchecked;
msg->rep->an_numrrsets = 1;
msg->rep->ns_numrrsets = 0;
msg->rep->ttl = d->ttl - now;
msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl);
msg->rep->serve_expired_ttl = msg->rep->ttl + SERVE_EXPIRED_TTL;
+ msg->rep->serve_expired_norec_ttl = 0;
msg->rep->security = sec_status_unchecked;
msg->rep->an_numrrsets = 1;
msg->rep->ns_numrrsets = 0;
struct regional* region, uint32_t flags, time_t qstarttime)
{
struct reply_info* rep = NULL;
+ if(SERVE_EXPIRED) {
+ /* We are serving expired records. Before caching, check if a
+ * useful expired record exists. */
+ struct msgreply_entry* e = msg_cache_lookup(env,
+ msgqinf->qname, msgqinf->qname_len, msgqinf->qtype,
+ msgqinf->qclass, flags, 0, 0);
+ if(e) {
+ struct reply_info* cached = e->entry.data;
+ if(cached->ttl < *env->now
+ && reply_info_could_use_expired(cached, *env->now)
+ /* If we are validating make sure only
+ * validating modules can update such messages.
+ * In that case don't cache it and let a
+ * subsequent module handle the caching. For
+ * example, the iterator should not replace an
+ * expired secure answer with a fresh unchecked
+ * one and let the validator manage caching. */
+ && cached->security != sec_status_bogus
+ && (env->need_to_validate &&
+ msgrep->security == sec_status_unchecked)) {
+ verbose(VERB_ALGO, "a validated expired entry "
+ "could be overwritten, skip caching "
+ "the new message at this stage");
+ lock_rw_unlock(&e->entry.lock);
+ return 1;
+ }
+ lock_rw_unlock(&e->entry.lock);
+ }
+ }
/* alloc, malloc properly (not in region, like msg is) */
rep = reply_info_copy(msgrep, env->alloc, NULL);
if(!rep)
{
struct packed_rrset_data* newd = (struct packed_rrset_data*)nd;
struct packed_rrset_data* cached = (struct packed_rrset_data*)cd;
- /* o if new data is expired, current data is better */
- if( newd->ttl < timenow && cached->ttl >= timenow)
+ /* o if new data is expired, cached data is better */
+ if( newd->ttl < timenow && timenow <= cached->ttl)
return 0;
/* o store if rrset has been validated
* everything better than bogus data
if( cached->security == sec_status_bogus &&
newd->security != sec_status_bogus && !equal)
return 1;
- /* o if current RRset is more trustworthy - insert it */
+ /* o if new RRset is more trustworthy - insert it */
if( newd->trust > cached->trust ) {
- /* if the cached rrset is bogus, and this one equal,
+ /* if the cached rrset is bogus, and new is equal,
* do not update the TTL - let it expire. */
if(equal && cached->ttl >= timenow &&
cached->security == sec_status_bogus)
/* o same trust, but different in data - insert it */
if( newd->trust == cached->trust && !equal ) {
/* if this is type NS, do not 'stick' to owner that changes
- * the NS RRset, but use the old TTL for the new data, and
+ * the NS RRset, but use the cached TTL for the new data, and
* update to fetch the latest data. ttl is not expired, because
* that check was before this one. */
if(ns) {
struct dns_msg*
mesh_serve_expired_lookup(struct module_qstate* qstate,
- struct query_info* lookup_qinfo)
+ struct query_info* lookup_qinfo, int* is_expired)
{
hashvalue_type h;
struct lruhash_entry* e;
time_t timenow = *qstate->env->now;
int must_validate = (!(qstate->query_flags&BIT_CD)
|| qstate->env->cfg->ignore_cd) && qstate->env->need_to_validate;
+ *is_expired = 0;
/* Lookup cache */
h = query_info_hash(lookup_qinfo, qstate->query_flags);
e = slabhash_lookup(qstate->env->msg_cache, h, lookup_qinfo, 0);
key = (struct msgreply_entry*)e->key;
data = (struct reply_info*)e->data;
+ if(data->ttl < timenow) *is_expired = 1;
msg = tomsg(qstate->env, &key->key, data, qstate->region, timenow,
qstate->env->cfg->serve_expired, qstate->env->scratch);
if(!msg)
int must_validate = (!(qstate->query_flags&BIT_CD)
|| qstate->env->cfg->ignore_cd) && qstate->env->need_to_validate;
int i = 0;
+ int is_expired;
if(!qstate->serve_expired_data) return;
verbose(VERB_ALGO, "Serve expired: Trying to reply with expired data");
comm_timer_delete(qstate->serve_expired_data->timer);
fptr_ok(fptr_whitelist_serve_expired_lookup(
qstate->serve_expired_data->get_cached_answer));
msg = (*qstate->serve_expired_data->get_cached_answer)(qstate,
- lookup_qinfo);
+ lookup_qinfo, &is_expired);
if(!msg)
return;
/* Reset these in case we pass a second time from here. */
/* Add EDE Stale Answer (RCF8914). Ignore global ede as this is
* warning instead of an error */
- if (r->edns.edns_present && qstate->env->cfg->ede_serve_expired &&
- qstate->env->cfg->ede) {
+ if(r->edns.edns_present &&
+ qstate->env->cfg->ede_serve_expired &&
+ qstate->env->cfg->ede &&
+ is_expired) {
edns_opt_list_append_ede(&r->edns.opt_list_out,
mstate->s.region, LDNS_EDE_STALE_ANSWER, NULL);
}
* the same behavior as when replying from cache.
* @param qstate: the module qstate.
* @param lookup_qinfo: the query info to look for in the cache.
+ * @param is_expired: set if the cached answer is expired.
* @return dns_msg if a cached answer was found, otherwise NULL.
*/
struct dns_msg*
mesh_serve_expired_lookup(struct module_qstate* qstate,
- struct query_info* lookup_qinfo);
+ struct query_info* lookup_qinfo, int* is_expired);
/**
* See if the mesh has space for more queries. You can allocate queries
0, /* ttl */
0, /* prettl */
0, /* expttl */
+ 0, /* norecttl */
0, /* an */
0, /* ns */
0, /* ar */
0, /* ttl */
0, /* prettl */
0, /* expttl */
+ 0, /* norecttl */
0, /* an */
0, /* ns */
0, /* ar */
0, /* ttl */
0, /* prettl */
0, /* expttl */
+ 0, /* norecttl */
1, /* an */
0, /* ns */
0, /* ar */
0, /* ttl */
0, /* prettl */
0, /* expttl */
+ 0, /* norecttl */
1, /* an */
0, /* ns */
0, /* ar */
unit_assert(region);
rep = construct_reply_info_base(region,
LDNS_RCODE_NOERROR | BIT_QR, 1,
- 3600, 3600, 3600,
+ 3600, 3600, 3600, 0,
0, 0, 0, 0,
sec_status_unchecked, LDNS_EDE_NONE);
unit_assert(rep);
RANGE_END
; ns.example.com.
-RANGE_BEGIN 30 100
+RANGE_BEGIN 40 100
ADDRESS 1.2.3.4
ENTRY_BEGIN
MATCH opcode qtype qname
stub-addr: 1.2.3.4
CONFIG_END
-SCENARIO_BEGIN Test serve-expired with client-timeout and a SERVFAIL upstream reply
+SCENARIO_BEGIN Test serve-expired without client-timeout and a SERVFAIL upstream reply
; Scenario overview:
; - query for example.com. IN A
; - answer from upstream is SERVFAIL; will be cached for NORR_TTL(5)
; - check that we get an answer for example.com. IN A with the correct TTL
; - query again right after the TTL expired
; - answer from upstream is servfail
-; - check that we get the expired cached answer instead
+; - (expired cached answer will not be replaced, instead marked as unresolvable for NORR_TTL(5))
+; - check that we get the expired cached answer
+; - query again (the answer is available on the upstream server now)
+; - check that we get the immediate expired answer back instead
+; - (the upstream query does happen after the expired reply and updates the cache)
+; - query again (the upstream has no answer)
+; - check that we get the freshly cached answer
; ns.example.com.
RANGE_BEGIN 0 20
RANGE_END
; ns.example.com.
-RANGE_BEGIN 30 70
+RANGE_BEGIN 30 40
ADDRESS 1.2.3.4
; response to A query
ENTRY_BEGIN
ENTRY_END
RANGE_END
+; ns.example.com.
+RANGE_BEGIN 50 60
+ ADDRESS 1.2.3.4
+ ; response to A query
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ example.com. IN A
+ SECTION ANSWER
+ example.com. 10 IN A 5.6.7.8
+ SECTION AUTHORITY
+ example.com. 10 IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. 10 IN A 1.2.3.4
+ ENTRY_END
+RANGE_END
+
; Query with RD flag
STEP 1 QUERY
ENTRY_BEGIN
example.com. IN A
ENTRY_END
-; Check that we got a stale answer
+; Check that we got a stale answer because of the upstream SERVFAIL
STEP 40 CHECK_ANSWER
ENTRY_BEGIN
MATCH all ttl ede=3
ns.example.com. 123 IN A 1.2.3.4
ENTRY_END
+; Query again
+STEP 50 QUERY
+ENTRY_BEGIN
+ REPLY RD DO
+ SECTION QUESTION
+ example.com. IN A
+ENTRY_END
+
+; Allow for upstream query to resolve.
+STEP 51 TRAFFIC
+
+; Check that we got an immediate stale answer because of the previous failure,
+; regardless if upstream has the answer already in this range. The query will
+; be resolved after the immediate cached answer and will cache the result.
+STEP 60 CHECK_ANSWER
+ENTRY_BEGIN
+ MATCH all ttl ede=3
+ REPLY QR RD RA DO NOERROR
+ SECTION QUESTION
+ example.com. IN A
+ SECTION ANSWER
+ example.com. 123 IN A 5.6.7.8
+ SECTION AUTHORITY
+ example.com. 123 IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. 123 IN A 1.2.3.4
+ENTRY_END
+
+; Query again
+STEP 70 QUERY
+ENTRY_BEGIN
+ REPLY RD
+ SECTION QUESTION
+ example.com. IN A
+ENTRY_END
+
+; Check that we got the cached updated answer from the previous step since
+; there is no upstream in this range.
+STEP 80 CHECK_ANSWER
+ENTRY_BEGIN
+ MATCH all ttl
+ REPLY QR RD RA NOERROR
+ SECTION QUESTION
+ example.com. IN A
+ SECTION ANSWER
+ example.com. 10 IN A 5.6.7.8
+ SECTION AUTHORITY
+ example.com. 10 IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. 10 IN A 1.2.3.4
+ENTRY_END
+
SCENARIO_END
--- /dev/null
+; config options
+; The island of trust is at example.com
+server:
+ trust-anchor: "example.com. 3600 IN DS 2854 3 1 46e4ffc6e9a4793b488954bd3f0cc6af0dfb201b"
+ val-override-date: "20070916134226"
+ target-fetch-policy: "0 0 0 0 0"
+ qname-minimisation: "no"
+ fake-sha1: yes
+ trust-anchor-signaling: no
+ minimal-responses: no
+
+ serve-expired: yes
+ serve-expired-client-timeout: 1
+ serve-expired-reply-ttl: 123
+ ede: yes
+ ede-serve-expired: yes
+
+ # No need for AAAA nameserver queries
+ do-ip6: no
+
+stub-zone:
+ name: "."
+ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
+CONFIG_END
+
+SCENARIO_BEGIN Test serve-expired with client-timeout and bogus answer
+; Scenario overview:
+; - query for www.example.com. IN A
+; - check the answer
+; - wait for the record to expire
+; - (upstream now has a bogus response)
+; - query again for www.example.com. IN A
+; - check that we get the expired valid response instead
+; - query once more
+; - (upstream has the valid response again)
+; - check that we get the immediate expired valid response
+; - (the prefetch query updates the cache with the valid response)
+; - query one last time
+; - check that we get the immediate valid cache response; upstream does not have an answer at this moment
+
+;;
+;; K.ROOT-SERVERS.NET.
+;;
+RANGE_BEGIN 0 100
+ ADDRESS 193.0.14.129
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ . IN NS
+ SECTION ANSWER
+ . IN NS K.ROOT-SERVERS.NET.
+ SECTION ADDITIONAL
+ K.ROOT-SERVERS.NET. IN A 193.0.14.129
+ ENTRY_END
+
+ ENTRY_BEGIN
+ MATCH opcode
+ ADJUST copy_id copy_query
+ REPLY QR NOERROR
+ SECTION QUESTION
+ www.example.com. IN A
+ SECTION AUTHORITY
+ com. IN NS a.gtld-servers.net.
+ SECTION ADDITIONAL
+ a.gtld-servers.net. IN A 192.5.6.30
+ ENTRY_END
+RANGE_END
+
+;;
+;; a.gtld-servers.net.
+;;
+RANGE_BEGIN 0 100
+ ADDRESS 192.5.6.30
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ com. IN NS
+ SECTION ANSWER
+ com. IN NS a.gtld-servers.net.
+ SECTION ADDITIONAL
+ a.gtld-servers.net. IN A 192.5.6.30
+ ENTRY_END
+
+ ENTRY_BEGIN
+ MATCH opcode
+ ADJUST copy_id copy_query
+ REPLY QR NOERROR
+ SECTION QUESTION
+ www.example.com. IN A
+ SECTION AUTHORITY
+ example.com. IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. IN A 1.2.3.4
+ ENTRY_END
+RANGE_END
+
+;;
+;; ns.example.com. with generic valid data
+;;
+RANGE_BEGIN 0 100
+ ADDRESS 1.2.3.4
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ example.com. IN NS
+ SECTION ANSWER
+ example.com. IN NS ns.example.com.
+ example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+ SECTION ADDITIONAL
+ ns.example.com. IN A 1.2.3.4
+ ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCMSWxVehgOQLoYclB9PIAbNP229AIUeH0vNNGJhjnZiqgIOKvs1EhzqAo= ;{id = 2854}
+ ENTRY_END
+
+ ; response to DNSKEY priming query
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ example.com. IN DNSKEY
+ SECTION ANSWER
+ example.com. 3600 IN DNSKEY 256 3 3 ALXLUsWqUrY3JYER3T4TBJII s70j+sDS/UT2QRp61SE7S3E EXopNXoFE73JLRmvpi/UrOO/Vz4Se 6wXv/CYCKjGw06U4WRgR YXcpEhJROyNapmdIKSx hOzfLVE1gqA0PweZR8d tY3aNQSRn3sPpwJr6Mi /PqQKAMMrZ9ckJpf1+b QMOOvxgzz2U1GS18b3y ZKcgTMEaJzd/GZYzi/B N2DzQ0MsrSwYXfsNLFO Bbs8PJMW4LYIxeeOe6rUgkWOF 7CC9Dh/dduQ1QrsJhmZAEFfd6ByYV+ ;{id = 2854 (zsk), size = 1688b}
+ example.com. 3600 IN RRSIG DNSKEY 3 2 3600 20070926134802 20070829134802 2854 example.com. MCwCFG1yhRNtTEa3Eno2zhVVuy2EJX3wAhQeLyUp6+UXcpC5qGNu9tkrTEgPUg== ;{id = 2854}
+ SECTION AUTHORITY
+ example.com. IN NS ns.example.com.
+ example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+ SECTION ADDITIONAL
+ ns.example.com. IN A 1.2.3.4
+ ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCMSWxVehgOQLoYclB9PIAbNP229AIUeH0vNNGJhjnZiqgIOKvs1EhzqAo= ;{id = 2854}
+ ENTRY_END
+RANGE_END
+
+;;
+;; ns.example.com with valid data
+;;
+RANGE_BEGIN 0 10
+ ADDRESS 1.2.3.4
+ ; response to query of interest
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ www.example.com. IN A
+ SECTION ANSWER
+ www.example.com. IN A 10.20.30.40
+ ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCQMyTjn7WWwpwAR1LlVeLpRgZGuQIUCcJDEkwAuzytTDRlYK7nIMwH1CM= ;{id = 2854}
+ SECTION AUTHORITY
+ example.com. IN NS ns.example.com.
+ example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+ SECTION ADDITIONAL
+ ns.example.com. IN A 1.2.3.4
+ www.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFC99iE9K5y2WNgI0gFvBWaTi9wm6AhUAoUqOpDtG5Zct+Qr9F3mSdnbc6V4= ;{id = 2854}
+ ENTRY_END
+RANGE_END
+
+;;
+;; ns.example.com. with bogus data
+;;
+RANGE_BEGIN 20 30
+ ADDRESS 1.2.3.4
+ ; response to query of interest (bogus answer)
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ www.example.com. IN A
+ SECTION ANSWER
+ www.example.com. IN A 10.20.30.40
+ ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCQMyTjn7WWwpwAR1LlVeLpRgZGuQIUCcJDEkwAuzytTDRlYK7nIMwH1CM= ;{id = 2854}
+ SECTION AUTHORITY
+ example.com. IN NS ns.example.com.
+ example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+ SECTION ADDITIONAL
+ ns.example.com. IN A 1.2.3.4
+ ;; (valid signature)
+ ;; www.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFC99iE9K5y2WNgI0gFvBWaTi9wm6AhUAoUqOpDtG5Zct+Qr9F3mSdnbc6V4= ;{id = 2854}
+ ;; (bogus signature)
+ www.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com.
+ ENTRY_END
+RANGE_END
+
+;;
+;; ns.example.com. with valid data again
+;;
+RANGE_BEGIN 40 60
+ ADDRESS 1.2.3.4
+ ; response to query of interest
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ www.example.com. IN A
+ SECTION ANSWER
+ www.example.com. IN A 10.20.30.40
+ ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCQMyTjn7WWwpwAR1LlVeLpRgZGuQIUCcJDEkwAuzytTDRlYK7nIMwH1CM= ;{id = 2854}
+ SECTION AUTHORITY
+ example.com. IN NS ns.example.com.
+ example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+ SECTION ADDITIONAL
+ ns.example.com. IN A 1.2.3.4
+ www.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFC99iE9K5y2WNgI0gFvBWaTi9wm6AhUAoUqOpDtG5Zct+Qr9F3mSdnbc6V4= ;{id = 2854}
+ ENTRY_END
+RANGE_END
+
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD DO
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; recursion happens here.
+STEP 10 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ttl
+REPLY QR RD RA AD DO NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 10.20.30.40
+www.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFC99iE9K5y2WNgI0gFvBWaTi9wm6AhUAoUqOpDtG5Zct+Qr9F3mSdnbc6V4= ;{id = 2854}
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCQMyTjn7WWwpwAR1LlVeLpRgZGuQIUCcJDEkwAuzytTDRlYK7nIMwH1CM= ;{id = 2854}
+ENTRY_END
+
+STEP 11 TIME_PASSES ELAPSE 3601
+
+STEP 20 QUERY
+ENTRY_BEGIN
+REPLY RD DO
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; expired answer because upstream is bogus
+STEP 30 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ttl ede=3
+REPLY QR RD RA AD DO NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 123 IN A 10.20.30.40
+www.example.com. 123 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFC99iE9K5y2WNgI0gFvBWaTi9wm6AhUAoUqOpDtG5Zct+Qr9F3mSdnbc6V4= ;{id = 2854}
+SECTION AUTHORITY
+example.com. 123 IN NS ns.example.com.
+example.com. 123 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+SECTION ADDITIONAL
+ns.example.com. 123 IN A 1.2.3.4
+ns.example.com. 123 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCQMyTjn7WWwpwAR1LlVeLpRgZGuQIUCcJDEkwAuzytTDRlYK7nIMwH1CM= ;{id = 2854}
+ENTRY_END
+
+STEP 40 QUERY
+ENTRY_BEGIN
+REPLY RD DO
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; immediate cached answer because upstream is valid again
+STEP 50 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ttl ede=3
+REPLY QR RD RA AD DO NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 123 IN A 10.20.30.40
+www.example.com. 123 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFC99iE9K5y2WNgI0gFvBWaTi9wm6AhUAoUqOpDtG5Zct+Qr9F3mSdnbc6V4= ;{id = 2854}
+SECTION AUTHORITY
+example.com. 123 IN NS ns.example.com.
+example.com. 123 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+SECTION ADDITIONAL
+ns.example.com. 123 IN A 1.2.3.4
+ns.example.com. 123 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCQMyTjn7WWwpwAR1LlVeLpRgZGuQIUCcJDEkwAuzytTDRlYK7nIMwH1CM= ;{id = 2854}
+ENTRY_END
+
+; upstream query is resolved before this query comes in
+STEP 60 QUERY
+ENTRY_BEGIN
+REPLY RD DO
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; prefetch query updated the cache, since there is no upstream response in this range
+STEP 70 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ttl
+REPLY QR RD RA AD DO NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 10.20.30.40
+www.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFC99iE9K5y2WNgI0gFvBWaTi9wm6AhUAoUqOpDtG5Zct+Qr9F3mSdnbc6V4= ;{id = 2854}
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCQMyTjn7WWwpwAR1LlVeLpRgZGuQIUCcJDEkwAuzytTDRlYK7nIMwH1CM= ;{id = 2854}
+ENTRY_END
+
+SCENARIO_END
--- /dev/null
+; config options
+server:
+ trust-anchor: "example. DNSKEY 257 3 7 AwEAAcUlFV1vhmqx6NSOUOq2R/dsR7Xm3upJ ( j7IommWSpJABVfW8Q0rOvXdM6kzt+TAu92L9 AbsUdblMFin8CVF3n4s= )"
+ val-override-date: "20120420235959"
+ val-max-restart: 0
+ outbound-msg-retry: 0
+ target-fetch-policy: "0 0 0 0 0"
+ qname-minimisation: "no"
+ fake-sha1: yes
+ trust-anchor-signaling: no
+ minimal-responses: no
+ rrset-roundrobin: no
+
+ serve-expired: yes
+ serve-expired-client-timeout: 1
+ serve-expired-reply-ttl: 123
+ ede: yes
+ ede-serve-expired: yes
+
+ # No need for AAAA nameserver queries
+ do-ip6: no
+
+stub-zone:
+ name: "."
+ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
+CONFIG_END
+
+SCENARIO_BEGIN Test serve-expired with client-timeout and failed DNSSEC parent of insecure zone
+; Scenario overview:
+; - query for mc.c.example. IN MX
+; - check the answer
+; - wait for all the records to expire
+; - (example. now has a bogus DNSKEY response)
+; - query again for mc.c.example. IN MX
+; - (validator fails priming the trust anchor because of the bogus DNSKEY)
+; - check that we get the expired insecure response instead
+
+;;
+;; K.ROOT-SERVERS.NET.
+;;
+RANGE_BEGIN 0 100
+ ADDRESS 193.0.14.129
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+. IN NS
+SECTION ANSWER
+. IN NS K.ROOT-SERVERS.NET.
+SECTION ADDITIONAL
+K.ROOT-SERVERS.NET. IN A 193.0.14.129
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+. IN A
+SECTION AUTHORITY
+example. IN NS ns1.example.
+SECTION ADDITIONAL
+ns1.example. IN A 192.0.2.1
+ENTRY_END
+RANGE_END
+
+;;
+;; ns1.example. generic data
+;;
+RANGE_BEGIN 0 100
+ ADDRESS 192.0.2.1
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id copy_query
+REPLY QR REFUSED
+SECTION QUESTION
+example. IN NS
+SECTION ANSWER
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+ns1.example. IN A
+SECTION ANSWER
+ns1.example. IN A 192.0.2.1
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA DO NOERROR
+SECTION QUESTION
+mc.c.example. IN MX
+SECTION AUTHORITY
+;; NSEC3 RR that covers the "next closer" name (c.example)
+;; H(c.example) = 4g6p9u5gvfshp30pqecj98b3maqbn1ck
+35mthgpgcu1qg68fab165klnsnk3dpvl.example. NSEC3 1 1 12 aabbccdd ( b4um86eghhds6nea196smvmlo4ors995 NS DS RRSIG )
+35mthgpgcu1qg68fab165klnsnk3dpvl.example. RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 ( 40430 example. g6jPUUpduAJKRljUsN8gB4UagAX0NxY9shwQ Aynzo8EUWH+z6hEIBlUTPGj15eZll6VhQqgZ XtAIR3chwgW+SA== )
+;; NSEC3 RR that matches the closest encloser (example)
+;; H(example) = 0p9mhaveqvm6t7vbl5lop2u3t2rp3tom
+0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. NSEC3 1 1 12 aabbccdd ( 2t7b4g4vsa5smi47k61mv5bv1a22bojr MX DNSKEY NS SOA NSEC3PARAM RRSIG )
+0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 ( 40430 example. OSgWSm26B+cS+dDL8b5QrWr/dEWhtCsKlwKL IBHYH6blRxK9rC0bMJPwQ4mLIuw85H2EY762 BOCXJZMnpuwhpA== )
+c.example. NS ns1.c.example.
+SECTION ADDITIONAL
+ns1.c.example. A 192.0.2.7
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA DO NOERROR
+SECTION QUESTION
+c.example. IN DS
+SECTION AUTHORITY
+;; NSEC3 RR that covers the "next closer" name (c.example)
+;; H(c.example) = 4g6p9u5gvfshp30pqecj98b3maqbn1ck
+35mthgpgcu1qg68fab165klnsnk3dpvl.example. NSEC3 1 1 12 aabbccdd ( b4um86eghhds6nea196smvmlo4ors995 NS DS RRSIG )
+35mthgpgcu1qg68fab165klnsnk3dpvl.example. RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 ( 40430 example. g6jPUUpduAJKRljUsN8gB4UagAX0NxY9shwQ Aynzo8EUWH+z6hEIBlUTPGj15eZll6VhQqgZ XtAIR3chwgW+SA== )
+;; NSEC3 RR that matches the closest encloser (example)
+;; H(example) = 0p9mhaveqvm6t7vbl5lop2u3t2rp3tom
+0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. NSEC3 1 1 12 aabbccdd ( 2t7b4g4vsa5smi47k61mv5bv1a22bojr MX DNSKEY NS SOA NSEC3PARAM RRSIG )
+0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 ( 40430 example. OSgWSm26B+cS+dDL8b5QrWr/dEWhtCsKlwKL IBHYH6blRxK9rC0bMJPwQ4mLIuw85H2EY762 BOCXJZMnpuwhpA== )
+ENTRY_END
+RANGE_END
+
+;;
+;; ns1.example. with valid DNSKEY data
+;;
+RANGE_BEGIN 0 20
+ ADDRESS 192.0.2.1
+; response to DNSKEY priming query
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example. IN DNSKEY
+SECTION ANSWER
+example. DNSKEY 256 3 7 AwEAAaetidLzsKWUt4swWR8yu0wPHPiUi8LU ( sAD0QPWU+wzt89epO6tHzkMBVDkC7qphQO2h TY4hHn9npWFRw5BYubE= )
+example. DNSKEY 257 3 7 AwEAAcUlFV1vhmqx6NSOUOq2R/dsR7Xm3upJ ( j7IommWSpJABVfW8Q0rOvXdM6kzt+TAu92L9 AbsUdblMFin8CVF3n4s= )
+example. RRSIG DNSKEY 7 1 3600 20150420235959 ( 20051021000000 12708 example. AuU4juU9RaxescSmStrQks3Gh9FblGBlVU31 uzMZ/U/FpsUb8aC6QZS+sTsJXnLnz7flGOsm MGQZf3bH+QsCtg== )
+ENTRY_END
+RANGE_END
+
+;;
+;; ns1.example. with bogus DNSKEY data
+;;
+RANGE_BEGIN 30 40
+ ADDRESS 192.0.2.1
+; response to DNSKEY priming query
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example. IN DNSKEY
+SECTION ANSWER
+example. DNSKEY 256 3 7 AwEAAaetidLzsKWUt4swWR8yu0wPHPiUi8LU ( sAD0QPWU+wzt89epO6tHzkMBVDkC7qphQO2h TY4hHn9npWFRw5BYubE= )
+example. DNSKEY 257 3 7 AwEAAcUlFV1vhmqx6NSOUOq2R/dsR7Xm3upJ ( j7IommWSpJABVfW8Q0rOvXdM6kzt+TAu92L9 AbsUdblMFin8CVF3n4s= )
+;; (bogus signature)
+example. RRSIG DNSKEY 7 1 3600 20150420235959 20051021000000 12708 example.
+ENTRY_END
+RANGE_END
+
+;;
+;; ns1.c.example.
+;;
+RANGE_BEGIN 0 100
+ ADDRESS 192.0.2.7
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+c.example. IN NS
+SECTION ANSWER
+c.example. NS ns1.c.example.
+SECTION ADDITIONAL
+ns1.c.example. A 192.0.2.7
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+mc.c.example. IN MX
+SECTION ANSWER
+mc.c.example. IN MX 50 mx.c.example.
+SECTION AUTHORITY
+c.example. NS ns1.c.example.
+SECTION ADDITIONAL
+ns1.c.example. A 192.0.2.7
+ENTRY_END
+RANGE_END
+
+
+STEP 0 QUERY
+ENTRY_BEGIN
+REPLY RD DO
+SECTION QUESTION
+mc.c.example. IN MX
+ENTRY_END
+
+; recursion happens here.
+STEP 10 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ttl
+REPLY QR RD RA DO NOERROR
+SECTION QUESTION
+mc.c.example. IN MX
+SECTION ANSWER
+mc.c.example. IN MX 50 mx.c.example.
+SECTION AUTHORITY
+c.example. NS ns1.c.example.
+SECTION ADDITIONAL
+ns1.c.example. A 192.0.2.7
+ENTRY_END
+
+STEP 20 TIME_PASSES ELAPSE 3601
+
+STEP 30 QUERY
+ENTRY_BEGIN
+REPLY RD DO
+SECTION QUESTION
+mc.c.example. IN MX
+ENTRY_END
+
+STEP 40 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ttl ede=3
+REPLY QR RD RA DO NOERROR
+SECTION QUESTION
+mc.c.example. IN MX
+SECTION ANSWER
+mc.c.example. 123 IN MX 50 mx.c.example.
+SECTION AUTHORITY
+c.example. 123 NS ns1.c.example.
+SECTION ADDITIONAL
+ns1.c.example. 123 A 192.0.2.7
+ENTRY_END
+
+SCENARIO_END
--- /dev/null
+; config options go here.
+server:
+ serve-expired: yes
+ serve-expired-ttl: 1
+ serve-expired-ttl-reset: yes
+ serve-expired-reply-ttl: 123
+ ede: yes
+ ede-serve-expired: yes
+forward-zone: name: "." forward-addr: 216.0.0.1
+CONFIG_END
+SCENARIO_BEGIN Serve expired ttl with reset on forwarder with a timeout on upstream query
+; Scenario overview:
+; - Send query
+; - Get reply
+; - Wait for it to expire (+ serve-expired-ttl)
+; - Send query again
+; - Upstream timeouts
+; - Error response from iterator SERVFAIL, resets expired-ttl on cache
+; - Check we are getting the SERVFAIL response
+; - Query again
+; - Check we are getting the expired answer
+; - Upstream still timeouts
+
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; Upstream reply
+STEP 2 REPLY
+ENTRY_BEGIN
+REPLY QR AA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 10 IN A 0.0.0.0
+ENTRY_END
+
+STEP 3 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ttl
+REPLY QR RA RD NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 10 IN A 0.0.0.0
+ENTRY_END
+
+; Expire the record (+ serve-expired-ttl)
+STEP 4 TIME_PASSES ELAPSE 12
+
+STEP 5 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; But the pending query times out!
+; outbound-msg-retry times timeout.
+STEP 6 TIMEOUT
+STEP 7 TIMEOUT
+STEP 8 TIMEOUT
+STEP 9 TIMEOUT
+STEP 10 TIMEOUT
+
+; Returns servfail
+; but error response from iterator resets the expired ttl
+STEP 11 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ttl
+REPLY QR RA RD SERVFAIL
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; Query again
+STEP 12 QUERY
+ENTRY_BEGIN
+REPLY RD DO
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; Check that we get the expired answer
+STEP 13 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ttl ede=3
+REPLY QR RA RD DO NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 123 IN A 0.0.0.0
+ENTRY_END
+
+; But the pending query times out!
+; Only one because RTT reached the limit.
+STEP 16 TIMEOUT
+
+SCENARIO_END
--- /dev/null
+; config options
+; The island of trust is at example.com
+server:
+ trust-anchor: "example.com. 3600 IN DS 2854 3 1 46e4ffc6e9a4793b488954bd3f0cc6af0dfb201b"
+ val-override-date: "20070916134226"
+ target-fetch-policy: "0 0 0 0 0"
+ qname-minimisation: "no"
+ fake-sha1: yes
+ trust-anchor-signaling: no
+ minimal-responses: no
+
+ serve-expired: yes
+ serve-expired-reply-ttl: 123
+ ede: yes
+ ede-serve-expired: yes
+
+ # No need for AAAA nameserver queries
+ do-ip6: no
+
+stub-zone:
+ name: "."
+ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
+CONFIG_END
+
+SCENARIO_BEGIN Test serve-expired with client-timeout and bogus answer
+; Scenario overview:
+; - query for www.example.com. IN A
+; - check the answer
+; - wait for the record to expire
+; - (upstream now has a bogus response)
+; - query again for www.example.com. IN A
+; - check that we get the immediate expired valid response
+; - (prefetch response is bogus and is not cached)
+; - query once more
+; - check that we still get the immediate expired valid response and not the fresh bogus one
+; - (upstream has a valid response again; prefetch will update the cache)
+; - query one last time
+; - check that we get an immediate valid cache response
+
+;;
+;; K.ROOT-SERVERS.NET.
+;;
+RANGE_BEGIN 0 100
+ ADDRESS 193.0.14.129
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ . IN NS
+ SECTION ANSWER
+ . IN NS K.ROOT-SERVERS.NET.
+ SECTION ADDITIONAL
+ K.ROOT-SERVERS.NET. IN A 193.0.14.129
+ ENTRY_END
+
+ ENTRY_BEGIN
+ MATCH opcode
+ ADJUST copy_id copy_query
+ REPLY QR NOERROR
+ SECTION QUESTION
+ www.example.com. IN A
+ SECTION AUTHORITY
+ com. IN NS a.gtld-servers.net.
+ SECTION ADDITIONAL
+ a.gtld-servers.net. IN A 192.5.6.30
+ ENTRY_END
+RANGE_END
+
+;;
+;; a.gtld-servers.net.
+;;
+RANGE_BEGIN 0 100
+ ADDRESS 192.5.6.30
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ com. IN NS
+ SECTION ANSWER
+ com. IN NS a.gtld-servers.net.
+ SECTION ADDITIONAL
+ a.gtld-servers.net. IN A 192.5.6.30
+ ENTRY_END
+
+ ENTRY_BEGIN
+ MATCH opcode
+ ADJUST copy_id copy_query
+ REPLY QR NOERROR
+ SECTION QUESTION
+ www.example.com. IN A
+ SECTION AUTHORITY
+ example.com. IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. IN A 1.2.3.4
+ ENTRY_END
+RANGE_END
+
+;;
+;; ns.example.com. with generic data
+;;
+RANGE_BEGIN 0 100
+ ADDRESS 1.2.3.4
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ example.com. IN NS
+ SECTION ANSWER
+ example.com. IN NS ns.example.com.
+ example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+ SECTION ADDITIONAL
+ ns.example.com. IN A 1.2.3.4
+ ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCMSWxVehgOQLoYclB9PIAbNP229AIUeH0vNNGJhjnZiqgIOKvs1EhzqAo= ;{id = 2854}
+ ENTRY_END
+
+ ; response to DNSKEY priming query
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ example.com. IN DNSKEY
+ SECTION ANSWER
+ example.com. 3600 IN DNSKEY 256 3 3 ALXLUsWqUrY3JYER3T4TBJII s70j+sDS/UT2QRp61SE7S3E EXopNXoFE73JLRmvpi/UrOO/Vz4Se 6wXv/CYCKjGw06U4WRgR YXcpEhJROyNapmdIKSx hOzfLVE1gqA0PweZR8d tY3aNQSRn3sPpwJr6Mi /PqQKAMMrZ9ckJpf1+b QMOOvxgzz2U1GS18b3y ZKcgTMEaJzd/GZYzi/B N2DzQ0MsrSwYXfsNLFO Bbs8PJMW4LYIxeeOe6rUgkWOF 7CC9Dh/dduQ1QrsJhmZAEFfd6ByYV+ ;{id = 2854 (zsk), size = 1688b}
+ example.com. 3600 IN RRSIG DNSKEY 3 2 3600 20070926134802 20070829134802 2854 example.com. MCwCFG1yhRNtTEa3Eno2zhVVuy2EJX3wAhQeLyUp6+UXcpC5qGNu9tkrTEgPUg== ;{id = 2854}
+ SECTION AUTHORITY
+ example.com. IN NS ns.example.com.
+ example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+ SECTION ADDITIONAL
+ ns.example.com. IN A 1.2.3.4
+ ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCMSWxVehgOQLoYclB9PIAbNP229AIUeH0vNNGJhjnZiqgIOKvs1EhzqAo= ;{id = 2854}
+ ENTRY_END
+RANGE_END
+
+;;
+;; ns.example.com. with valid data
+;;
+RANGE_BEGIN 0 10
+ ADDRESS 1.2.3.4
+ ; response to query of interest
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ www.example.com. IN A
+ SECTION ANSWER
+ www.example.com. IN A 10.20.30.40
+ ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCQMyTjn7WWwpwAR1LlVeLpRgZGuQIUCcJDEkwAuzytTDRlYK7nIMwH1CM= ;{id = 2854}
+ SECTION AUTHORITY
+ example.com. IN NS ns.example.com.
+ example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+ SECTION ADDITIONAL
+ ns.example.com. IN A 1.2.3.4
+ www.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFC99iE9K5y2WNgI0gFvBWaTi9wm6AhUAoUqOpDtG5Zct+Qr9F3mSdnbc6V4= ;{id = 2854}
+ ENTRY_END
+RANGE_END
+
+;;
+;; ns.example.com. with bogus data
+;;
+RANGE_BEGIN 20 40
+ ADDRESS 1.2.3.4
+ ; response to query of interest (bogus answer)
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ www.example.com. IN A
+ SECTION ANSWER
+ www.example.com. IN A 10.20.30.40
+ ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCQMyTjn7WWwpwAR1LlVeLpRgZGuQIUCcJDEkwAuzytTDRlYK7nIMwH1CM= ;{id = 2854}
+ SECTION AUTHORITY
+ example.com. IN NS ns.example.com.
+ example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+ SECTION ADDITIONAL
+ ns.example.com. IN A 1.2.3.4
+ ;; (valid signature)
+ ;; www.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFC99iE9K5y2WNgI0gFvBWaTi9wm6AhUAoUqOpDtG5Zct+Qr9F3mSdnbc6V4= ;{id = 2854}
+ ;; (bogus signature)
+ www.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com.
+ ENTRY_END
+RANGE_END
+
+;;
+;; ns.example.com. with valid data again
+;;
+RANGE_BEGIN 50 100
+ ADDRESS 1.2.3.4
+ ; response to query of interest
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ www.example.com. IN A
+ SECTION ANSWER
+ www.example.com. IN A 10.20.30.40
+ ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCQMyTjn7WWwpwAR1LlVeLpRgZGuQIUCcJDEkwAuzytTDRlYK7nIMwH1CM= ;{id = 2854}
+ SECTION AUTHORITY
+ example.com. IN NS ns.example.com.
+ example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+ SECTION ADDITIONAL
+ ns.example.com. IN A 1.2.3.4
+ www.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFC99iE9K5y2WNgI0gFvBWaTi9wm6AhUAoUqOpDtG5Zct+Qr9F3mSdnbc6V4= ;{id = 2854}
+ ENTRY_END
+RANGE_END
+
+
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD DO
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; this is the valid answer
+STEP 10 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ttl
+REPLY QR RD RA AD DO NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 10.20.30.40
+www.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFC99iE9K5y2WNgI0gFvBWaTi9wm6AhUAoUqOpDtG5Zct+Qr9F3mSdnbc6V4= ;{id = 2854}
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCQMyTjn7WWwpwAR1LlVeLpRgZGuQIUCcJDEkwAuzytTDRlYK7nIMwH1CM= ;{id = 2854}
+ENTRY_END
+
+STEP 11 TIME_PASSES ELAPSE 3601
+
+STEP 20 QUERY
+ENTRY_BEGIN
+REPLY RD DO
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; this is the immediate expired cache response
+STEP 30 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ttl ede=3
+REPLY QR RD RA AD DO NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 123 IN A 10.20.30.40
+www.example.com. 123 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFC99iE9K5y2WNgI0gFvBWaTi9wm6AhUAoUqOpDtG5Zct+Qr9F3mSdnbc6V4= ;{id = 2854}
+SECTION AUTHORITY
+example.com. 123 IN NS ns.example.com.
+example.com. 123 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+SECTION ADDITIONAL
+ns.example.com. 123 IN A 1.2.3.4
+ns.example.com. 123 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCQMyTjn7WWwpwAR1LlVeLpRgZGuQIUCcJDEkwAuzytTDRlYK7nIMwH1CM= ;{id = 2854}
+ENTRY_END
+
+STEP 40 QUERY
+ENTRY_BEGIN
+REPLY RD DO
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; this is still the immediate cache response because the previous upstream response was bogus
+STEP 50 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ttl ede=3
+REPLY QR RD RA AD DO NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 123 IN A 10.20.30.40
+www.example.com. 123 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFC99iE9K5y2WNgI0gFvBWaTi9wm6AhUAoUqOpDtG5Zct+Qr9F3mSdnbc6V4= ;{id = 2854}
+SECTION AUTHORITY
+example.com. 123 IN NS ns.example.com.
+example.com. 123 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+SECTION ADDITIONAL
+ns.example.com. 123 IN A 1.2.3.4
+ns.example.com. 123 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCQMyTjn7WWwpwAR1LlVeLpRgZGuQIUCcJDEkwAuzytTDRlYK7nIMwH1CM= ;{id = 2854}
+ENTRY_END
+
+STEP 60 QUERY
+ENTRY_BEGIN
+REPLY RD DO
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; this is the immediate cache response because the previous upstream response was valid
+STEP 70 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ttl
+REPLY QR RD RA AD DO NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 10.20.30.40
+www.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFC99iE9K5y2WNgI0gFvBWaTi9wm6AhUAoUqOpDtG5Zct+Qr9F3mSdnbc6V4= ;{id = 2854}
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCQMyTjn7WWwpwAR1LlVeLpRgZGuQIUCcJDEkwAuzytTDRlYK7nIMwH1CM= ;{id = 2854}
+ENTRY_END
+
+SCENARIO_END
SERVE_EXPIRED = cfg->serve_expired; }
else if(strcmp(opt, "serve-expired-ttl:") == 0)
{ IS_NUMBER_OR_ZERO; cfg->serve_expired_ttl = atoi(val); SERVE_EXPIRED_TTL=(time_t)cfg->serve_expired_ttl;}
- else S_YNO("serve-expired-ttl-reset:", serve_expired_ttl_reset)
+ else if(strcmp(opt, "serve-expired-ttl-reset:") == 0)
+ { IS_YES_OR_NO; cfg->serve_expired_ttl_reset = (strcmp(val, "yes") == 0);
+ SERVE_EXPIRED_TTL_RESET = cfg->serve_expired_ttl_reset; }
else if(strcmp(opt, "serve-expired-reply-ttl:") == 0)
{ IS_NUMBER_OR_ZERO; cfg->serve_expired_reply_ttl = atoi(val); SERVE_EXPIRED_REPLY_TTL=(time_t)cfg->serve_expired_reply_ttl;}
else S_NUMBER_OR_ZERO("serve-expired-client-timeout:", serve_expired_client_timeout)
MIN_TTL = (time_t)config->min_ttl;
SERVE_EXPIRED = config->serve_expired;
SERVE_EXPIRED_TTL = (time_t)config->serve_expired_ttl;
+ SERVE_EXPIRED_TTL_RESET = config->serve_expired_ttl_reset;
SERVE_EXPIRED_REPLY_TTL = (time_t)config->serve_expired_reply_ttl;
SERVE_ORIGINAL_TTL = config->serve_original_ttl;
MAX_NEG_TTL = (time_t)config->max_negative_ttl;
extern int SERVE_EXPIRED;
/** Time to serve records after expiration */
extern time_t SERVE_EXPIRED_TTL;
+/** Reset serve expired TTL after failed update attempt */
+extern time_t SERVE_EXPIRED_TTL_RESET;
/** TTL to use for expired records */
extern time_t SERVE_EXPIRED_REPLY_TTL;
/** Negative cache time (for entries without any RRs.) */
int SERVE_EXPIRED = 0;
/** Time to serve records after expiration */
time_t SERVE_EXPIRED_TTL = 0;
+/** Reset serve expired TTL after failed update attempt */
+time_t SERVE_EXPIRED_TTL_RESET = 0;
/** TTL to use for expired records */
time_t SERVE_EXPIRED_REPLY_TTL = 30;
/** If we serve the original TTL or decrementing TTLs */
/** constructor for replyinfo */
struct reply_info*
construct_reply_info_base(struct regional* region, uint16_t flags, size_t qd,
- time_t ttl, time_t prettl, time_t expttl, size_t an, size_t ns,
- size_t ar, size_t total, enum sec_status sec, sldns_ede_code reason_bogus)
+ time_t ttl, time_t prettl, time_t expttl, time_t norecttl, size_t an,
+ size_t ns, size_t ar, size_t total, enum sec_status sec,
+ sldns_ede_code reason_bogus)
{
struct reply_info* rep;
/* rrset_count-1 because the first ref is part of the struct. */
rep->ttl = ttl;
rep->prefetch_ttl = prettl;
rep->serve_expired_ttl = expttl;
+ rep->serve_expired_norec_ttl = norecttl;
rep->an_numrrsets = an;
rep->ns_numrrsets = ns;
rep->ar_numrrsets = ar;
parse_create_repinfo(struct msg_parse* msg, struct reply_info** rep,
struct regional* region)
{
- *rep = construct_reply_info_base(region, msg->flags, msg->qdcount, 0,
- 0, 0, msg->an_rrsets, msg->ns_rrsets, msg->ar_rrsets,
+ *rep = construct_reply_info_base(region, msg->flags, msg->qdcount, 0,
+ 0, 0, 0, msg->an_rrsets, msg->ns_rrsets, msg->ar_rrsets,
msg->rrset_count, sec_status_unchecked, LDNS_EDE_NONE);
if(!*rep)
return 0;
return 1;
}
+int
+reply_info_can_answer_expired(struct reply_info* rep, time_t timenow)
+{
+ log_assert(rep->ttl < timenow);
+ /* Really expired */
+ if(SERVE_EXPIRED_TTL && rep->serve_expired_ttl < timenow) return 0;
+ /* Ignore expired failure answers */
+ if(FLAGS_GET_RCODE(rep->flags) != LDNS_RCODE_NOERROR &&
+ FLAGS_GET_RCODE(rep->flags) != LDNS_RCODE_NXDOMAIN &&
+ FLAGS_GET_RCODE(rep->flags) != LDNS_RCODE_YXDOMAIN) return 0;
+ return 1;
+}
+
+int reply_info_could_use_expired(struct reply_info* rep, time_t timenow)
+{
+ log_assert(rep->ttl < timenow);
+ /* Really expired */
+ if(SERVE_EXPIRED_TTL && rep->serve_expired_ttl < timenow &&
+ !SERVE_EXPIRED_TTL_RESET) return 0;
+ /* Ignore expired failure answers */
+ if(FLAGS_GET_RCODE(rep->flags) != LDNS_RCODE_NOERROR &&
+ FLAGS_GET_RCODE(rep->flags) != LDNS_RCODE_NXDOMAIN &&
+ FLAGS_GET_RCODE(rep->flags) != LDNS_RCODE_YXDOMAIN) return 0;
+ return 1;
+}
+
struct reply_info *
make_new_reply_info(const struct reply_info* rep, struct regional* region,
size_t an_numrrsets, size_t copy_rrsets)
* so the total number of RRsets is an_numrrsets. */
new_rep = construct_reply_info_base(region, rep->flags,
rep->qdcount, rep->ttl, rep->prefetch_ttl,
- rep->serve_expired_ttl, an_numrrsets, 0, 0, an_numrrsets,
+ rep->serve_expired_ttl, rep->serve_expired_norec_ttl,
+ an_numrrsets, 0, 0, an_numrrsets,
sec_status_insecure, LDNS_EDE_NONE);
if(!new_rep)
return NULL;
}
rep->prefetch_ttl = PREFETCH_TTL_CALC(rep->ttl);
rep->serve_expired_ttl = rep->ttl + SERVE_EXPIRED_TTL;
+ /* rep->serve_expired_norec_ttl should stay at 0 */
+ log_assert(rep->serve_expired_norec_ttl == 0);
return 1;
}
rep->ttl += timenow;
rep->prefetch_ttl += timenow;
rep->serve_expired_ttl += timenow;
+ /* Don't set rep->serve_expired_norec_ttl; this should only be set
+ * on cached records when encountering an error */
+ log_assert(rep->serve_expired_norec_ttl == 0);
for(i=0; i<rep->rrset_count; i++) {
struct packed_rrset_data* data = (struct packed_rrset_data*)
rep->ref[i].key->entry.data;
struct reply_info* cp;
cp = construct_reply_info_base(region, rep->flags, rep->qdcount,
rep->ttl, rep->prefetch_ttl, rep->serve_expired_ttl,
+ rep->serve_expired_norec_ttl,
rep->an_numrrsets, rep->ns_numrrsets, rep->ar_numrrsets,
rep->rrset_count, rep->security, rep->reason_bogus);
if(!cp)
/** 32 bit padding to pad struct member alignment to 64 bits. */
uint32_t padding;
- /**
+ /**
* TTL of the entire reply (for negative caching).
* only for use when there are 0 RRsets in this message.
* if there are RRsets, check those instead.
*/
time_t prefetch_ttl;
- /**
+ /**
* Reply TTL extended with serve expired TTL, to limit time to serve
* expired message.
*/
time_t serve_expired_ttl;
+ /**
+ * TTL for an expired entry to be used without attempting recursion
+ * since a previous recursion attempt failed to update the message.
+ * This is just an efficiency timer when serve-expired-client-timeout
+ * is configured. It will make Unbound immediately reply with the
+ * expired entry instead of trying resolution first.
+ * It is set on cached entries by modules that identified problems
+ * while resolving, e.g., failed upstreams from Iterator, or failed
+ * validation from Validator.
+ */
+ time_t serve_expired_norec_ttl;
+
/**
* The security status from DNSSEC validation of this message.
*/
* @param ttl: TTL of replyinfo
* @param prettl: prefetch ttl
* @param expttl: serve expired ttl
+ * @param norecttl: serve expired no recursion ttl
* @param an: an count
* @param ns: ns count
* @param ar: ar count
*/
struct reply_info*
construct_reply_info_base(struct regional* region, uint16_t flags, size_t qd,
- time_t ttl, time_t prettl, time_t expttl, size_t an, size_t ns,
- size_t ar, size_t total, enum sec_status sec,
+ time_t ttl, time_t prettl, time_t expttl, time_t norecttl, size_t an,
+ size_t ns, size_t ar, size_t total, enum sec_status sec,
sldns_ede_code reason_bogus);
/**
int reply_info_alloc_rrset_keys(struct reply_info* rep,
struct alloc_cache* alloc, struct regional* region);
+/**
+ * Check if an *expired* (checked by the caller already) reply info can be used
+ * as an expired answer.
+ * @param rep: expired reply info to check.
+ * @param timenow: the current time.
+ * @return 1 if it can be used as an answer, 0 otherwise.
+ */
+int reply_info_can_answer_expired(struct reply_info* rep, time_t timenow);
+
+/**
+ * Check if an *expired* (checked by the caller already) reply info could be
+ * useful data to stay in the cache.
+ * @param rep: expired reply info to check.
+ * @param timenow: the current time.
+ * @return 1 if it is useful, 0 otherwise.
+ */
+int reply_info_could_use_expired(struct reply_info* rep, time_t timenow);
+
/*
* Create a new reply_info based on 'rep'. The new info is based on
* the passed 'rep', but ignores any rrsets except for the first 'an_numrrsets'
/**
* Function called when looking for (expired) cached answers during the serve
* expired logic.
- * Called as func(qstate, lookup_qinfo)
+ * Called as func(qstate, lookup_qinfo, &is_expired)
* Where:
* qstate: the query state.
* lookup_qinfo: the qinfo to lookup for.
+ * is_expired: set if the cached answer is expired.
*/
typedef struct dns_msg* serve_expired_lookup_func_type(
- struct module_qstate* qstate, struct query_info* lookup_qinfo);
+ struct module_qstate* qstate, struct query_info* lookup_qinfo,
+ int* is_expired);
/**
* Module environment.
/* if the result is bogus - set message ttl to bogus ttl to avoid
* endless bogus revalidation */
if(vq->orig_msg->rep->security == sec_status_bogus) {
+ struct msgreply_entry* e;
+
/* see if we can try again to fetch data */
if(vq->restart_count < ve->max_restart) {
verbose(VERB_ALGO, "validation failed, "
return 0;
}
+ if(qstate->env->cfg->serve_expired &&
+ (e=msg_cache_lookup(qstate->env, qstate->qinfo.qname,
+ qstate->qinfo.qname_len, qstate->qinfo.qtype,
+ qstate->qinfo.qclass, qstate->query_flags,
+ 0 /*now; allow expired*/,
+ 1 /*wr; we may update the data*/))) {
+ struct reply_info* rep = (struct reply_info*)e->entry.data;
+ if(rep && rep->security > sec_status_bogus &&
+ (!qstate->env->cfg->serve_expired_ttl ||
+ qstate->env->cfg->serve_expired_ttl_reset ||
+ *qstate->env->now <= rep->serve_expired_ttl)) {
+ verbose(VERB_ALGO, "validation failed but "
+ "previously cached valid response "
+ "exists; set serve-expired-norec-ttl "
+ "for response in cache");
+ rep->serve_expired_norec_ttl = NORR_TTL +
+ *qstate->env->now;
+ if(qstate->env->cfg->serve_expired_ttl_reset &&
+ *qstate->env->now + qstate->env->cfg->serve_expired_ttl
+ > rep->serve_expired_ttl) {
+ verbose(VERB_ALGO, "reset serve-expired-ttl for "
+ "valid response in cache");
+ rep->serve_expired_ttl = *qstate->env->now +
+ qstate->env->cfg->serve_expired_ttl;
+ }
+ /* Return an error response.
+ * If serve-expired-client-timeout is enabled,
+ * the client-timeout logic will try to find an
+ * (expired) answer in the cache as last
+ * resort. If it is not enabled, expired
+ * answers are already used before the mesh
+ * activation. */
+ qstate->return_rcode = LDNS_RCODE_SERVFAIL;
+ qstate->return_msg = NULL;
+ qstate->ext_state[id] = module_finished;
+ lock_rw_unlock(&e->entry.lock);
+ return 0;
+ }
+ lock_rw_unlock(&e->entry.lock);
+ }
+
vq->orig_msg->rep->ttl = ve->bogus_ttl;
vq->orig_msg->rep->prefetch_ttl =
PREFETCH_TTL_CALC(vq->orig_msg->rep->ttl);
- vq->orig_msg->rep->serve_expired_ttl =
+ vq->orig_msg->rep->serve_expired_ttl =
vq->orig_msg->rep->ttl + qstate->env->cfg->serve_expired_ttl;
if((qstate->env->cfg->val_log_level >= 1 ||
qstate->env->cfg->log_servfail) &&
* to check if from parentNS */
if(!qstate->no_cache_store) {
if(!dns_cache_store(qstate->env, &vq->orig_msg->qinfo,
- vq->orig_msg->rep, 0, qstate->prefetch_leeway, 0, NULL,
- qstate->query_flags, qstate->qstarttime)) {
+ vq->orig_msg->rep, 0, qstate->prefetch_leeway,
+ 0, qstate->region, qstate->query_flags,
+ qstate->qstarttime)) {
log_err("out of memory caching validator results");
}
}
/* for a referral, store the verified RRsets */
/* and this does not get prefetched, so no leeway */
if(!dns_cache_store(qstate->env, &vq->orig_msg->qinfo,
- vq->orig_msg->rep, 1, 0, 0, NULL,
+ vq->orig_msg->rep, 1, 0, 0, qstate->region,
qstate->query_flags, qstate->qstarttime)) {
log_err("out of memory caching validator results");
}