]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- Fix the novel ghost domain issues CVE-2022-30698 and CVE-2022-30699.
authorW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Mon, 1 Aug 2022 11:24:40 +0000 (13:24 +0200)
committerW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Mon, 1 Aug 2022 11:24:40 +0000 (13:24 +0200)
17 files changed:
cachedb/cachedb.c
daemon/cachedump.c
daemon/worker.c
dns64/dns64.c
doc/Changelog
ipsecmod/ipsecmod.c
iterator/iter_utils.c
iterator/iter_utils.h
iterator/iterator.c
pythonmod/interface.i
pythonmod/pythonmod_utils.c
services/cache/dns.c
services/cache/dns.h
services/mesh.c
testdata/iter_prefetch_change.rpl
util/module.h
validator/validator.c

index 725bc6ce8b38dbec32c1470f0b9ed40ba6e0468e..b07743d8525962b92ceeb99d7547d68a209244e2 100644 (file)
@@ -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);
 }
 
 /**
index b929f909bab289e26169b256d9c537482230648f..baf8008ea80f47063533bcb7c06cf246569aec92 100644 (file)
@@ -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");
index 27626ce938ca202aab95fb3530c01d26935891b1..010c4dc0a281f0f5befee1c977a0b2fb88627315 100644 (file)
@@ -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;
        }
index d01b436e1d6c523f4d1abdd43ce2aee4d73d28b2..4b98b609e2d30994f714a4b2d2ae9f39c344ecbc 100644 (file)
@@ -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");
 }
 
index 30e694fcf34cc43a5753817f48065732bb0f885b..0dc8418fc6fe4e3153d5cf329e978ee08617ed55 100644 (file)
@@ -1,3 +1,6 @@
+1 August 2022: Wouter
+       - Fix the novel ghost domain issues CVE-2022-30698 and CVE-2022-30699.
+
 19 July 2022: George
        - Update documentation for 'outbound-msg-retry:'.
 
index 577f7112e194011f98f5efe4fc69f9bf9654834d..19549d4eefe082bcc289c13da801070da9681338 100644 (file)
@@ -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;
index 1ac8623fe7a554d6f0b6a60eb2e3c92e64ab25cb..3e13e595c63d6627dc40c25aab9c3c415116ce18 100644 (file)
@@ -659,10 +659,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");
 }
 
index c0e5181573f529424143fc07808a13adfcf21f7b..8583fde58a443fef4edb2e627451af006623f4e7 100644 (file)
@@ -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.
index 754b0ef59e5542fcf39bd542118c034687f71044..25e5cfee46453533be20e6b5e9fcf0798a31d181 100644 (file)
@@ -375,7 +375,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);
 }
@@ -1489,7 +1489,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 
@@ -1781,7 +1782,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(
@@ -2947,7 +2949,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;
@@ -3036,7 +3039,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);
@@ -3150,7 +3154,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;
@@ -3756,7 +3760,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;
index 2fac5f8840063ebdf18163c21b21a9a6a35fb631..0d95613f8c97a8e1754a39df0cb17d583277ace7 100644 (file)
@@ -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,
index 34a20ba764853941e12155bd48ee15bbac8d5f25..1f6f2512918f20cc5e3ea5283a7aebaee665c91f 100644 (file)
@@ -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 */
index f6c11451c93a76df61d412e94115c88775eb1acb..6bca8d85fadb5f6b0a885705ba4d41127762e64d 100644 (file)
  *     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;
@@ -462,7 +542,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;
@@ -470,7 +551,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;
@@ -840,7 +921,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,
@@ -973,7 +1054,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) */
@@ -996,9 +1077,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;
@@ -1020,7 +1101,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);
index bece837029609335955ada4cf4a69a917dab2b99..147f992cbc7430ca6cc54843d444bb7cd4c332c8 100644 (file)
@@ -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
index c40eb50dc55c8b985e2e4f2caea072be3a62a26b..30bcf7cda15598c75e0d544751ceecdf8e8ccdd3 100644 (file)
@@ -954,6 +954,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++) {
index 007025ad010ca5283484ba9dfdfa4999c82f6eac..1be9e6abee3bf033b0064760fc275308eff183c3 100644 (file)
@@ -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
index 33068a71ce6eff1ddeec7fc53215437848a1a97b..013c65b02dcffa593a1b0eb8c781ef16f54d8b8f 100644 (file)
@@ -665,6 +665,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
index 9d3808a5d4b68e008bdf5ae67c39d75cc5ecb1d4..1723afefe353e4643ad5ed2d4b7c3751c6a4c597 100644 (file)
@@ -2202,7 +2202,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");
                        }
                }
@@ -2211,7 +2211,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");
                }
        }