diff --git a/cachedb/cachedb.c b/cachedb/cachedb.c index 725bc6ce..b07743d8 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 b929f909..baf8008e 100644 --- a/daemon/cachedump.c +++ b/daemon/cachedump.c @@ -679,7 +679,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; } @@ -850,7 +851,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 7209bc4c..7b7293ee 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -459,7 +459,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 d01b436e..4b98b609 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 */ @@ -991,7 +991,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 577f7112..19549d4e 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 f3bea46d..6e924e90 100644 --- a/iterator/iter_utils.c +++ b/iterator/iter_utils.c @@ -657,10 +657,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 c0e51815..8583fde5 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 29d2e79e..3422dab8 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 @@ -1769,7 +1770,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( @@ -2836,7 +2838,8 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, iq->qchase.qtype != iq->response->qinfo.qtype, 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; @@ -2925,7 +2928,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); @@ -3039,7 +3043,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; @@ -3642,7 +3646,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 2fac5f88..0d95613f 100644 --- a/pythonmod/interface.i +++ b/pythonmod/interface.i @@ -1375,7 +1375,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, int supports_ipv4, int supports_ipv6); struct iter_hints_stub* hints_lookup_stub(struct iter_hints* hints, @@ -1404,7 +1405,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 34a20ba7..1f6f2512 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 5b64fe47..99faaf67 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 bece8370..147f992c 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 4b022d47..dd28287c 100644 --- a/services/mesh.c +++ b/services/mesh.c @@ -844,6 +844,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; imesh->mods.num; i++) { diff --git a/testdata/iter_prefetch_change.rpl b/testdata/iter_prefetch_change.rpl index 007025ad..1be9e6ab 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 1ae7477c..4b6363f1 100644 --- a/util/module.h +++ b/util/module.h @@ -657,6 +657,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 e6307284..19619030 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"); } }