625 lines
25 KiB
Diff
625 lines
25 KiB
Diff
From f6753a0f1018133df552347a199e0362fc1dac68 Mon Sep 17 00:00:00 2001
|
|
From: "W.C.A. Wijngaards" <wouter@nlnetlabs.nl>
|
|
Date: Mon, 1 Aug 2022 13:24:40 +0200
|
|
Subject: [PATCH] - Fix the novel ghost domain issues CVE-2022-30698 and
|
|
CVE-2022-30699.
|
|
|
|
---
|
|
cachedb/cachedb.c | 2 +-
|
|
daemon/cachedump.c | 5 +-
|
|
daemon/worker.c | 2 +-
|
|
dns64/dns64.c | 4 +-
|
|
ipsecmod/ipsecmod.c | 2 +-
|
|
iterator/iter_utils.c | 4 +-
|
|
iterator/iter_utils.h | 3 +-
|
|
iterator/iterator.c | 19 +--
|
|
pythonmod/interface.i | 5 +-
|
|
pythonmod/pythonmod_utils.c | 3 +-
|
|
services/cache/dns.c | 111 +++++++++++++++---
|
|
services/cache/dns.h | 18 ++-
|
|
services/mesh.c | 1 +
|
|
testdata/iter_prefetch_change.rpl | 16 +--
|
|
util/module.h | 6 +
|
|
validator/validator.c | 4 +-
|
|
16 files changed, 157 insertions(+), 48 deletions(-)
|
|
|
|
diff --git a/cachedb/cachedb.c b/cachedb/cachedb.c
|
|
index af4ffe5..6e3dcfd 100644
|
|
--- a/cachedb/cachedb.c
|
|
+++ b/cachedb/cachedb.c
|
|
@@ -662,7 +662,7 @@ cachedb_intcache_store(struct module_qstate* qstate)
|
|
return;
|
|
(void)dns_cache_store(qstate->env, &qstate->qinfo,
|
|
qstate->return_msg->rep, 0, qstate->prefetch_leeway, 0,
|
|
- qstate->region, store_flags);
|
|
+ qstate->region, store_flags, qstate->qstarttime);
|
|
}
|
|
|
|
/**
|
|
diff --git a/daemon/cachedump.c b/daemon/cachedump.c
|
|
index b1ce53b..908d2f9 100644
|
|
--- a/daemon/cachedump.c
|
|
+++ b/daemon/cachedump.c
|
|
@@ -677,7 +677,8 @@ load_msg(RES* ssl, sldns_buffer* buf, struct worker* worker)
|
|
if(!go_on)
|
|
return 1; /* skip this one, not all references satisfied */
|
|
|
|
- if(!dns_cache_store(&worker->env, &qinf, &rep, 0, 0, 0, NULL, flags)) {
|
|
+ if(!dns_cache_store(&worker->env, &qinf, &rep, 0, 0, 0, NULL, flags,
|
|
+ *worker->env.now)) {
|
|
log_warn("error out of memory");
|
|
return 0;
|
|
}
|
|
@@ -848,7 +849,7 @@ int print_deleg_lookup(RES* ssl, struct worker* worker, uint8_t* nm,
|
|
while(1) {
|
|
dp = dns_cache_find_delegation(&worker->env, nm, nmlen,
|
|
qinfo.qtype, qinfo.qclass, region, &msg,
|
|
- *worker->env.now);
|
|
+ *worker->env.now, 0, NULL, 0);
|
|
if(!dp) {
|
|
return ssl_printf(ssl, "no delegation from "
|
|
"cache; goes to configured roots\n");
|
|
diff --git a/daemon/worker.c b/daemon/worker.c
|
|
index e9e163a..9c0263f 100644
|
|
--- a/daemon/worker.c
|
|
+++ b/daemon/worker.c
|
|
@@ -460,7 +460,7 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo,
|
|
|
|
dp = dns_cache_find_delegation(&worker->env, qinfo->qname,
|
|
qinfo->qname_len, qinfo->qtype, qinfo->qclass,
|
|
- worker->scratchpad, &msg, timenow);
|
|
+ worker->scratchpad, &msg, timenow, 0, NULL, 0);
|
|
if(!dp) { /* no delegation, need to reprime */
|
|
return 0;
|
|
}
|
|
diff --git a/dns64/dns64.c b/dns64/dns64.c
|
|
index c79bc9c..0546364 100644
|
|
--- a/dns64/dns64.c
|
|
+++ b/dns64/dns64.c
|
|
@@ -652,7 +652,7 @@ handle_event_moddone(struct module_qstate* qstate, int id)
|
|
if ( (!iq || !iq->started_no_cache_store) &&
|
|
qstate->return_msg && qstate->return_msg->rep &&
|
|
!dns_cache_store(qstate->env, &qstate->qinfo, qstate->return_msg->rep,
|
|
- 0, 0, 0, NULL, qstate->query_flags))
|
|
+ 0, 0, 0, NULL, qstate->query_flags, qstate->qstarttime))
|
|
log_err("out of memory");
|
|
|
|
/* do nothing */
|
|
@@ -986,7 +986,7 @@ dns64_inform_super(struct module_qstate* qstate, int id,
|
|
/* Store the generated response in cache. */
|
|
if ( (!super_dq || !super_dq->started_no_cache_store) &&
|
|
!dns_cache_store(super->env, &super->qinfo, super->return_msg->rep,
|
|
- 0, 0, 0, NULL, super->query_flags))
|
|
+ 0, 0, 0, NULL, super->query_flags, qstate->qstarttime))
|
|
log_err("out of memory");
|
|
}
|
|
|
|
diff --git a/ipsecmod/ipsecmod.c b/ipsecmod/ipsecmod.c
|
|
index e42af6f..a35f6e6 100644
|
|
--- a/ipsecmod/ipsecmod.c
|
|
+++ b/ipsecmod/ipsecmod.c
|
|
@@ -456,7 +456,7 @@ ipsecmod_handle_query(struct module_qstate* qstate,
|
|
/* Store A/AAAA in cache. */
|
|
if(!dns_cache_store(qstate->env, &qstate->qinfo,
|
|
qstate->return_msg->rep, 0, qstate->prefetch_leeway,
|
|
- 0, qstate->region, qstate->query_flags)) {
|
|
+ 0, qstate->region, qstate->query_flags, qstate->qstarttime)) {
|
|
log_err("ipsecmod: out of memory caching record");
|
|
}
|
|
qstate->ext_state[id] = module_finished;
|
|
diff --git a/iterator/iter_utils.c b/iterator/iter_utils.c
|
|
index 668f898..25060ab 100644
|
|
--- a/iterator/iter_utils.c
|
|
+++ b/iterator/iter_utils.c
|
|
@@ -656,10 +656,10 @@ dns_copy_msg(struct dns_msg* from, struct regional* region)
|
|
void
|
|
iter_dns_store(struct module_env* env, struct query_info* msgqinf,
|
|
struct reply_info* msgrep, int is_referral, time_t leeway, int pside,
|
|
- struct regional* region, uint16_t flags)
|
|
+ struct regional* region, uint16_t flags, time_t qstarttime)
|
|
{
|
|
if(!dns_cache_store(env, msgqinf, msgrep, is_referral, leeway,
|
|
- pside, region, flags))
|
|
+ pside, region, flags, qstarttime))
|
|
log_err("out of memory: cannot store data in cache");
|
|
}
|
|
|
|
diff --git a/iterator/iter_utils.h b/iterator/iter_utils.h
|
|
index 509d292..6253b5f 100644
|
|
--- a/iterator/iter_utils.h
|
|
+++ b/iterator/iter_utils.h
|
|
@@ -132,6 +132,7 @@ struct dns_msg* dns_copy_msg(struct dns_msg* from, struct regional* regional);
|
|
* can be prefetch-updates.
|
|
* @param region: to copy modified (cache is better) rrs back to.
|
|
* @param flags: with BIT_CD for dns64 AAAA translated queries.
|
|
+ * @param qstarttime: time of query start.
|
|
* return void, because we are not interested in alloc errors,
|
|
* the iterator and validator can operate on the results in their
|
|
* scratch space (the qstate.region) and are not dependent on the cache.
|
|
@@ -140,7 +141,7 @@ struct dns_msg* dns_copy_msg(struct dns_msg* from, struct regional* regional);
|
|
*/
|
|
void iter_dns_store(struct module_env* env, struct query_info* qinf,
|
|
struct reply_info* rep, int is_referral, time_t leeway, int pside,
|
|
- struct regional* region, uint16_t flags);
|
|
+ struct regional* region, uint16_t flags, time_t qstarttime);
|
|
|
|
/**
|
|
* Select randomly with n/m probability.
|
|
diff --git a/iterator/iterator.c b/iterator/iterator.c
|
|
index f0105ad..40afbc8 100644
|
|
--- a/iterator/iterator.c
|
|
+++ b/iterator/iterator.c
|
|
@@ -370,7 +370,7 @@ error_response_cache(struct module_qstate* qstate, int id, int rcode)
|
|
err.security = sec_status_indeterminate;
|
|
verbose(VERB_ALGO, "store error response in message cache");
|
|
iter_dns_store(qstate->env, &qstate->qinfo, &err, 0, 0, 0, NULL,
|
|
- qstate->query_flags);
|
|
+ qstate->query_flags, qstate->qstarttime);
|
|
}
|
|
return error_response(qstate, id, rcode);
|
|
}
|
|
@@ -1477,7 +1477,8 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
|
|
iq->dp = dns_cache_find_delegation(qstate->env, delname,
|
|
delnamelen, iq->qchase.qtype, iq->qchase.qclass,
|
|
qstate->region, &iq->deleg_msg,
|
|
- *qstate->env->now+qstate->prefetch_leeway);
|
|
+ *qstate->env->now+qstate->prefetch_leeway, 1,
|
|
+ dpname, dpnamelen);
|
|
else iq->dp = NULL;
|
|
|
|
/* If the cache has returned nothing, then we have a
|
|
@@ -1799,7 +1800,8 @@ generate_parentside_target_query(struct module_qstate* qstate,
|
|
subiq->dp = dns_cache_find_delegation(qstate->env,
|
|
name, namelen, qtype, qclass, subq->region,
|
|
&subiq->deleg_msg,
|
|
- *qstate->env->now+subq->prefetch_leeway);
|
|
+ *qstate->env->now+subq->prefetch_leeway,
|
|
+ 1, NULL, 0);
|
|
/* if no dp, then it's from root, refetch unneeded */
|
|
if(subiq->dp) {
|
|
subiq->dnssec_expected = iter_indicates_dnssec(
|
|
@@ -2847,7 +2849,8 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
|
|
iter_dns_store(qstate->env, &iq->response->qinfo,
|
|
iq->response->rep, 0, qstate->prefetch_leeway,
|
|
iq->dp&&iq->dp->has_parent_side_NS,
|
|
- qstate->region, qstate->query_flags);
|
|
+ qstate->region, qstate->query_flags,
|
|
+ qstate->qstarttime);
|
|
/* close down outstanding requests to be discarded */
|
|
outbound_list_clear(&iq->outlist);
|
|
iq->num_current_queries = 0;
|
|
@@ -2944,7 +2947,8 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
|
|
/* Store the referral under the current query */
|
|
/* no prefetch-leeway, since its not the answer */
|
|
iter_dns_store(qstate->env, &iq->response->qinfo,
|
|
- iq->response->rep, 1, 0, 0, NULL, 0);
|
|
+ iq->response->rep, 1, 0, 0, NULL, 0,
|
|
+ qstate->qstarttime);
|
|
if(iq->store_parent_NS)
|
|
iter_store_parentside_NS(qstate->env,
|
|
iq->response->rep);
|
|
@@ -3057,7 +3061,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
|
|
iter_dns_store(qstate->env, &iq->response->qinfo,
|
|
iq->response->rep, 1, qstate->prefetch_leeway,
|
|
iq->dp&&iq->dp->has_parent_side_NS, NULL,
|
|
- qstate->query_flags);
|
|
+ qstate->query_flags, qstate->qstarttime);
|
|
/* set the current request's qname to the new value. */
|
|
iq->qchase.qname = sname;
|
|
iq->qchase.qname_len = snamelen;
|
|
@@ -3626,7 +3630,8 @@ processFinished(struct module_qstate* qstate, struct iter_qstate* iq,
|
|
iter_dns_store(qstate->env, &qstate->qinfo,
|
|
iq->response->rep, 0, qstate->prefetch_leeway,
|
|
iq->dp&&iq->dp->has_parent_side_NS,
|
|
- qstate->region, qstate->query_flags);
|
|
+ qstate->region, qstate->query_flags,
|
|
+ qstate->qstarttime);
|
|
}
|
|
}
|
|
qstate->return_rcode = LDNS_RCODE_NOERROR;
|
|
diff --git a/pythonmod/interface.i b/pythonmod/interface.i
|
|
index ce7dcde..bdd6906 100644
|
|
--- a/pythonmod/interface.i
|
|
+++ b/pythonmod/interface.i
|
|
@@ -1371,7 +1371,8 @@ int set_return_msg(struct module_qstate* qstate,
|
|
/* Functions which we will need to lookup delegations */
|
|
struct delegpt* dns_cache_find_delegation(struct module_env* env,
|
|
uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
|
|
- struct regional* region, struct dns_msg** msg, uint32_t timenow);
|
|
+ struct regional* region, struct dns_msg** msg, uint32_t timenow,
|
|
+ int noexpiredabove, uint8_t* expiretop, size_t expiretoplen);
|
|
int iter_dp_is_useless(struct query_info* qinfo, uint16_t qflags,
|
|
struct delegpt* dp);
|
|
struct iter_hints_stub* hints_lookup_stub(struct iter_hints* hints,
|
|
@@ -1400,7 +1401,7 @@ struct delegpt* find_delegation(struct module_qstate* qstate, char *nm, size_t n
|
|
qinfo.qclass = LDNS_RR_CLASS_IN;
|
|
|
|
while(1) {
|
|
- dp = dns_cache_find_delegation(qstate->env, (uint8_t*)nm, nmlen, qinfo.qtype, qinfo.qclass, region, &msg, timenow);
|
|
+ dp = dns_cache_find_delegation(qstate->env, (uint8_t*)nm, nmlen, qinfo.qtype, qinfo.qclass, region, &msg, timenow, 0, NULL, 0);
|
|
if(!dp)
|
|
return NULL;
|
|
if(iter_dp_is_useless(&qinfo, BIT_RD, dp)) {
|
|
diff --git a/pythonmod/pythonmod_utils.c b/pythonmod/pythonmod_utils.c
|
|
index 21a16bb..05ce136 100644
|
|
--- a/pythonmod/pythonmod_utils.c
|
|
+++ b/pythonmod/pythonmod_utils.c
|
|
@@ -72,7 +72,8 @@ int storeQueryInCache(struct module_qstate* qstate, struct query_info* qinfo,
|
|
}
|
|
|
|
return dns_cache_store(qstate->env, qinfo, msgrep, is_referral,
|
|
- qstate->prefetch_leeway, 0, NULL, qstate->query_flags);
|
|
+ qstate->prefetch_leeway, 0, NULL, qstate->query_flags,
|
|
+ qstate->qstarttime);
|
|
}
|
|
|
|
/* Invalidate the message associated with query_info stored in message cache */
|
|
diff --git a/services/cache/dns.c b/services/cache/dns.c
|
|
index 5b64fe4..c29b909 100644
|
|
--- a/services/cache/dns.c
|
|
+++ b/services/cache/dns.c
|
|
@@ -68,11 +68,16 @@
|
|
* in a prefetch situation to be updated (without becoming sticky).
|
|
* @param qrep: update rrsets here if cache is better
|
|
* @param region: for qrep allocs.
|
|
+ * @param qstarttime: time when delegations were looked up, this is perhaps
|
|
+ * earlier than the time in now. The time is used to determine if RRsets
|
|
+ * of type NS have expired, so that they can only be updated using
|
|
+ * lookups of delegation points that did not use them, since they had
|
|
+ * expired then.
|
|
*/
|
|
static void
|
|
store_rrsets(struct module_env* env, struct reply_info* rep, time_t now,
|
|
time_t leeway, int pside, struct reply_info* qrep,
|
|
- struct regional* region)
|
|
+ struct regional* region, time_t qstarttime)
|
|
{
|
|
size_t i;
|
|
/* see if rrset already exists in cache, if not insert it. */
|
|
@@ -81,8 +86,8 @@ store_rrsets(struct module_env* env, struct reply_info* rep, time_t now,
|
|
rep->ref[i].id = rep->rrsets[i]->id;
|
|
/* update ref if it was in the cache */
|
|
switch(rrset_cache_update(env->rrset_cache, &rep->ref[i],
|
|
- env->alloc, now + ((ntohs(rep->ref[i].key->rk.type)==
|
|
- LDNS_RR_TYPE_NS && !pside)?0:leeway))) {
|
|
+ env->alloc, ((ntohs(rep->ref[i].key->rk.type)==
|
|
+ LDNS_RR_TYPE_NS && !pside)?qstarttime:now + leeway))) {
|
|
case 0: /* ref unchanged, item inserted */
|
|
break;
|
|
case 2: /* ref updated, cache is superior */
|
|
@@ -155,7 +160,8 @@ msg_del_servfail(struct module_env* env, struct query_info* qinfo,
|
|
void
|
|
dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
|
|
hashvalue_type hash, struct reply_info* rep, time_t leeway, int pside,
|
|
- struct reply_info* qrep, uint32_t flags, struct regional* region)
|
|
+ struct reply_info* qrep, uint32_t flags, struct regional* region,
|
|
+ time_t qstarttime)
|
|
{
|
|
struct msgreply_entry* e;
|
|
time_t ttl = rep->ttl;
|
|
@@ -170,7 +176,8 @@ dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
|
|
/* there was a reply_info_sortref(rep) here but it seems to be
|
|
* unnecessary, because the cache gets locked per rrset. */
|
|
reply_info_set_ttls(rep, *env->now);
|
|
- store_rrsets(env, rep, *env->now, leeway, pside, qrep, region);
|
|
+ store_rrsets(env, rep, *env->now, leeway, pside, qrep, region,
|
|
+ qstarttime);
|
|
if(ttl == 0 && !(flags & DNSCACHE_STORE_ZEROTTL)) {
|
|
/* we do not store the message, but we did store the RRs,
|
|
* which could be useful for delegation information */
|
|
@@ -194,10 +201,51 @@ dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
|
|
slabhash_insert(env->msg_cache, hash, &e->entry, rep, env->alloc);
|
|
}
|
|
|
|
+/** see if an rrset is expired above the qname, return upper qname. */
|
|
+static int
|
|
+rrset_expired_above(struct module_env* env, uint8_t** qname, size_t* qnamelen,
|
|
+ uint16_t searchtype, uint16_t qclass, time_t now, uint8_t* expiretop,
|
|
+ size_t expiretoplen)
|
|
+{
|
|
+ struct ub_packed_rrset_key *rrset;
|
|
+ uint8_t lablen;
|
|
+
|
|
+ while(*qnamelen > 0) {
|
|
+ /* look one label higher */
|
|
+ lablen = **qname;
|
|
+ *qname += lablen + 1;
|
|
+ *qnamelen -= lablen + 1;
|
|
+ if(*qnamelen <= 0)
|
|
+ break;
|
|
+
|
|
+ /* looks up with a time of 0, to see expired entries */
|
|
+ if((rrset = rrset_cache_lookup(env->rrset_cache, *qname,
|
|
+ *qnamelen, searchtype, qclass, 0, 0, 0))) {
|
|
+ struct packed_rrset_data* data =
|
|
+ (struct packed_rrset_data*)rrset->entry.data;
|
|
+ if(now > data->ttl) {
|
|
+ /* it is expired, this is not wanted */
|
|
+ lock_rw_unlock(&rrset->entry.lock);
|
|
+ log_nametypeclass(VERB_ALGO, "this rrset is expired", *qname, searchtype, qclass);
|
|
+ return 1;
|
|
+ }
|
|
+ /* it is not expired, continue looking */
|
|
+ lock_rw_unlock(&rrset->entry.lock);
|
|
+ }
|
|
+
|
|
+ /* do not look above the expiretop. */
|
|
+ if(expiretop && *qnamelen == expiretoplen &&
|
|
+ query_dname_compare(*qname, expiretop)==0)
|
|
+ break;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
/** find closest NS or DNAME and returns the rrset (locked) */
|
|
static struct ub_packed_rrset_key*
|
|
find_closest_of_type(struct module_env* env, uint8_t* qname, size_t qnamelen,
|
|
- uint16_t qclass, time_t now, uint16_t searchtype, int stripfront)
|
|
+ uint16_t qclass, time_t now, uint16_t searchtype, int stripfront,
|
|
+ int noexpiredabove, uint8_t* expiretop, size_t expiretoplen)
|
|
{
|
|
struct ub_packed_rrset_key *rrset;
|
|
uint8_t lablen;
|
|
@@ -212,8 +260,40 @@ find_closest_of_type(struct module_env* env, uint8_t* qname, size_t qnamelen,
|
|
/* snip off front part of qname until the type is found */
|
|
while(qnamelen > 0) {
|
|
if((rrset = rrset_cache_lookup(env->rrset_cache, qname,
|
|
- qnamelen, searchtype, qclass, 0, now, 0)))
|
|
- return rrset;
|
|
+ qnamelen, searchtype, qclass, 0, now, 0))) {
|
|
+ uint8_t* origqname = qname;
|
|
+ size_t origqnamelen = qnamelen;
|
|
+ if(!noexpiredabove)
|
|
+ return rrset;
|
|
+ /* if expiretop set, do not look above it, but
|
|
+ * qname is equal, so the just found result is also
|
|
+ * the nonexpired above part. */
|
|
+ if(expiretop && qnamelen == expiretoplen &&
|
|
+ query_dname_compare(qname, expiretop)==0)
|
|
+ return rrset;
|
|
+ /* check for expiry, but we have to let go of the rrset
|
|
+ * for the lock ordering */
|
|
+ lock_rw_unlock(&rrset->entry.lock);
|
|
+ /* the expired_above function always takes off one
|
|
+ * label (if qnamelen>0) and returns the final qname
|
|
+ * where it searched, so we can continue from there
|
|
+ * turning the O N*N search into O N. */
|
|
+ if(!rrset_expired_above(env, &qname, &qnamelen,
|
|
+ searchtype, qclass, now, expiretop,
|
|
+ expiretoplen)) {
|
|
+ /* we want to return rrset, but it may be
|
|
+ * gone from cache, if so, just loop like
|
|
+ * it was not in the cache in the first place.
|
|
+ */
|
|
+ if((rrset = rrset_cache_lookup(env->
|
|
+ rrset_cache, origqname, origqnamelen,
|
|
+ searchtype, qclass, 0, now, 0))) {
|
|
+ return rrset;
|
|
+ }
|
|
+ }
|
|
+ log_nametypeclass(VERB_ALGO, "ignoring rrset because expired rrsets exist above it", origqname, searchtype, qclass);
|
|
+ continue;
|
|
+ }
|
|
|
|
/* snip off front label */
|
|
lablen = *qname;
|
|
@@ -461,7 +541,8 @@ dns_msg_ansadd(struct dns_msg* msg, struct regional* region,
|
|
struct delegpt*
|
|
dns_cache_find_delegation(struct module_env* env, uint8_t* qname,
|
|
size_t qnamelen, uint16_t qtype, uint16_t qclass,
|
|
- struct regional* region, struct dns_msg** msg, time_t now)
|
|
+ struct regional* region, struct dns_msg** msg, time_t now,
|
|
+ int noexpiredabove, uint8_t* expiretop, size_t expiretoplen)
|
|
{
|
|
/* try to find closest NS rrset */
|
|
struct ub_packed_rrset_key* nskey;
|
|
@@ -469,7 +550,7 @@ dns_cache_find_delegation(struct module_env* env, uint8_t* qname,
|
|
struct delegpt* dp;
|
|
|
|
nskey = find_closest_of_type(env, qname, qnamelen, qclass, now,
|
|
- LDNS_RR_TYPE_NS, 0);
|
|
+ LDNS_RR_TYPE_NS, 0, noexpiredabove, expiretop, expiretoplen);
|
|
if(!nskey) /* hope the caller has hints to prime or something */
|
|
return NULL;
|
|
nsdata = (struct packed_rrset_data*)nskey->entry.data;
|
|
@@ -835,7 +916,7 @@ dns_cache_lookup(struct module_env* env,
|
|
* consistent with the DNAME */
|
|
if(!no_partial &&
|
|
(rrset=find_closest_of_type(env, qname, qnamelen, qclass, now,
|
|
- LDNS_RR_TYPE_DNAME, 1))) {
|
|
+ LDNS_RR_TYPE_DNAME, 1, 0, NULL, 0))) {
|
|
/* synthesize a DNAME+CNAME message based on this */
|
|
enum sec_status sec_status = sec_status_unchecked;
|
|
struct dns_msg* msg = synth_dname_msg(rrset, region, now, &k,
|
|
@@ -968,7 +1049,7 @@ dns_cache_lookup(struct module_env* env,
|
|
int
|
|
dns_cache_store(struct module_env* env, struct query_info* msgqinf,
|
|
struct reply_info* msgrep, int is_referral, time_t leeway, int pside,
|
|
- struct regional* region, uint32_t flags)
|
|
+ struct regional* region, uint32_t flags, time_t qstarttime)
|
|
{
|
|
struct reply_info* rep = NULL;
|
|
/* alloc, malloc properly (not in region, like msg is) */
|
|
@@ -991,9 +1072,9 @@ dns_cache_store(struct module_env* env, struct query_info* msgqinf,
|
|
/*ignore ret: it was in the cache, ref updated */
|
|
/* no leeway for typeNS */
|
|
(void)rrset_cache_update(env->rrset_cache, &ref,
|
|
- env->alloc, *env->now +
|
|
+ env->alloc,
|
|
((ntohs(ref.key->rk.type)==LDNS_RR_TYPE_NS
|
|
- && !pside) ? 0:leeway));
|
|
+ && !pside) ? qstarttime:*env->now + leeway));
|
|
}
|
|
free(rep);
|
|
return 1;
|
|
@@ -1015,7 +1096,7 @@ dns_cache_store(struct module_env* env, struct query_info* msgqinf,
|
|
rep->flags &= ~(BIT_AA | BIT_CD);
|
|
h = query_info_hash(&qinf, (uint16_t)flags);
|
|
dns_cache_store_msg(env, &qinf, h, rep, leeway, pside, msgrep,
|
|
- flags, region);
|
|
+ flags, region, qstarttime);
|
|
/* qname is used inside query_info_entrysetup, and set to
|
|
* NULL. If it has not been used, free it. free(0) is safe. */
|
|
free(qinf.qname);
|
|
diff --git a/services/cache/dns.h b/services/cache/dns.h
|
|
index bece837..6ae4514 100644
|
|
--- a/services/cache/dns.h
|
|
+++ b/services/cache/dns.h
|
|
@@ -88,11 +88,13 @@ struct dns_msg {
|
|
* @param flags: flags with BIT_CD for AAAA queries in dns64 translation.
|
|
* The higher 16 bits are used internally to customize the cache policy.
|
|
* (See DNSCACHE_STORE_xxx flags).
|
|
+ * @param qstarttime: time when the query was started, and thus when the
|
|
+ * delegations were looked up.
|
|
* @return 0 on alloc error (out of memory).
|
|
*/
|
|
int dns_cache_store(struct module_env* env, struct query_info* qinf,
|
|
struct reply_info* rep, int is_referral, time_t leeway, int pside,
|
|
- struct regional* region, uint32_t flags);
|
|
+ struct regional* region, uint32_t flags, time_t qstarttime);
|
|
|
|
/**
|
|
* Store message in the cache. Stores in message cache and rrset cache.
|
|
@@ -112,11 +114,14 @@ int dns_cache_store(struct module_env* env, struct query_info* qinf,
|
|
* can be updated to full TTL even in prefetch situations.
|
|
* @param qrep: message that can be altered with better rrs from cache.
|
|
* @param flags: customization flags for the cache policy.
|
|
+ * @param qstarttime: time when the query was started, and thus when the
|
|
+ * delegations were looked up.
|
|
* @param region: to allocate into for qmsg.
|
|
*/
|
|
void dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
|
|
hashvalue_type hash, struct reply_info* rep, time_t leeway, int pside,
|
|
- struct reply_info* qrep, uint32_t flags, struct regional* region);
|
|
+ struct reply_info* qrep, uint32_t flags, struct regional* region,
|
|
+ time_t qstarttime);
|
|
|
|
/**
|
|
* Find a delegation from the cache.
|
|
@@ -129,11 +134,18 @@ void dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
|
|
* @param msg: if not NULL, delegation message is returned here, synthesized
|
|
* from the cache.
|
|
* @param timenow: the time now, for checking if TTL on cache entries is OK.
|
|
+ * @param noexpiredabove: if set, no expired NS rrsets above the one found
|
|
+ * are tolerated. It only returns delegations where the delegations above
|
|
+ * it are valid.
|
|
+ * @param expiretop: if not NULL, name where check for expiry ends for
|
|
+ * noexpiredabove.
|
|
+ * @param expiretoplen: length of expiretop dname.
|
|
* @return new delegation or NULL on error or if not found in cache.
|
|
*/
|
|
struct delegpt* dns_cache_find_delegation(struct module_env* env,
|
|
uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
|
|
- struct regional* region, struct dns_msg** msg, time_t timenow);
|
|
+ struct regional* region, struct dns_msg** msg, time_t timenow,
|
|
+ int noexpiredabove, uint8_t* expiretop, size_t expiretoplen);
|
|
|
|
/**
|
|
* generate dns_msg from cached message
|
|
diff --git a/services/mesh.c b/services/mesh.c
|
|
index 5679a8b..b9aac54 100644
|
|
--- a/services/mesh.c
|
|
+++ b/services/mesh.c
|
|
@@ -839,6 +839,7 @@ mesh_state_create(struct module_env* env, struct query_info* qinfo,
|
|
mstate->s.no_cache_store = 0;
|
|
mstate->s.need_refetch = 0;
|
|
mstate->s.was_ratelimited = 0;
|
|
+ mstate->s.qstarttime = *env->now;
|
|
|
|
/* init modules */
|
|
for(i=0; i<env->mesh->mods.num; i++) {
|
|
diff --git a/testdata/iter_prefetch_change.rpl b/testdata/iter_prefetch_change.rpl
|
|
index 007025a..1be9e6a 100644
|
|
--- a/testdata/iter_prefetch_change.rpl
|
|
+++ b/testdata/iter_prefetch_change.rpl
|
|
@@ -22,9 +22,9 @@ REPLY QR NOERROR
|
|
SECTION QUESTION
|
|
. IN NS
|
|
SECTION ANSWER
|
|
-. IN NS K.ROOT-SERVERS.NET.
|
|
+. 86400 IN NS K.ROOT-SERVERS.NET.
|
|
SECTION ADDITIONAL
|
|
-K.ROOT-SERVERS.NET. IN A 193.0.14.129
|
|
+K.ROOT-SERVERS.NET. 86400 IN A 193.0.14.129
|
|
ENTRY_END
|
|
|
|
ENTRY_BEGIN
|
|
@@ -34,9 +34,9 @@ REPLY QR NOERROR
|
|
SECTION QUESTION
|
|
com. IN A
|
|
SECTION AUTHORITY
|
|
-com. IN NS a.gtld-servers.net.
|
|
+com. 86400 IN NS a.gtld-servers.net.
|
|
SECTION ADDITIONAL
|
|
-a.gtld-servers.net. IN A 192.5.6.30
|
|
+a.gtld-servers.net. 86400 IN A 192.5.6.30
|
|
ENTRY_END
|
|
RANGE_END
|
|
|
|
@@ -50,9 +50,9 @@ REPLY QR NOERROR
|
|
SECTION QUESTION
|
|
com. IN NS
|
|
SECTION ANSWER
|
|
-com. IN NS a.gtld-servers.net.
|
|
+com. 86400 IN NS a.gtld-servers.net.
|
|
SECTION ADDITIONAL
|
|
-a.gtld-servers.net. IN A 192.5.6.30
|
|
+a.gtld-servers.net. 86400 IN A 192.5.6.30
|
|
ENTRY_END
|
|
|
|
ENTRY_BEGIN
|
|
@@ -78,9 +78,9 @@ REPLY QR NOERROR
|
|
SECTION QUESTION
|
|
com. IN NS
|
|
SECTION ANSWER
|
|
-com. IN NS a.gtld-servers.net.
|
|
+com. 86400 IN NS a.gtld-servers.net.
|
|
SECTION ADDITIONAL
|
|
-a.gtld-servers.net. IN A 192.5.6.30
|
|
+a.gtld-servers.net. 86400 IN A 192.5.6.30
|
|
ENTRY_END
|
|
|
|
ENTRY_BEGIN
|
|
diff --git a/util/module.h b/util/module.h
|
|
index 81a31a9..09854a1 100644
|
|
--- a/util/module.h
|
|
+++ b/util/module.h
|
|
@@ -652,6 +652,12 @@ struct module_qstate {
|
|
int need_refetch;
|
|
/** whether the query (or a subquery) was ratelimited */
|
|
int was_ratelimited;
|
|
+ /** time when query was started. This is when the qstate is created.
|
|
+ * This is used so that type NS data cannot be overwritten by them
|
|
+ * expiring while the lookup is in progress, using data fetched from
|
|
+ * those servers. By comparing expiry time with qstarttime for type NS.
|
|
+ */
|
|
+ time_t qstarttime;
|
|
|
|
/**
|
|
* Attributes of clients that share the qstate that may affect IP-based
|
|
diff --git a/validator/validator.c b/validator/validator.c
|
|
index d4d48d9..4b731f5 100644
|
|
--- a/validator/validator.c
|
|
+++ b/validator/validator.c
|
|
@@ -2145,7 +2145,7 @@ processFinished(struct module_qstate* qstate, struct val_qstate* vq,
|
|
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->query_flags, qstate->qstarttime)) {
|
|
log_err("out of memory caching validator results");
|
|
}
|
|
}
|
|
@@ -2154,7 +2154,7 @@ processFinished(struct module_qstate* qstate, struct val_qstate* vq,
|
|
/* 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,
|
|
- qstate->query_flags)) {
|
|
+ qstate->query_flags, qstate->qstarttime)) {
|
|
log_err("out of memory caching validator results");
|
|
}
|
|
}
|
|
--
|
|
2.33.0
|
|
|