From: Wouter Wijngaards Date: Mon, 27 Aug 2007 13:46:05 +0000 (+0000) Subject: do not cache nonRD replies in msg cache, the rrset cache is used for that. X-Git-Tag: release-0.5~91 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=542b8126900b5b4a11307d17c9d9b642812c8db1;p=thirdparty%2Funbound.git do not cache nonRD replies in msg cache, the rrset cache is used for that. store verification status in rrset cache to enable security for nonRD replies and also speed up processing. git-svn-id: file:///svn/unbound/trunk@550 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/daemon/worker.c b/daemon/worker.c index 2f0057a43..2897540e7 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -314,13 +314,15 @@ check_delegation_secure(struct reply_info *rep) size_t num = rep->an_numrrsets + rep->ns_numrrsets; /* check if answer and authority are OK */ for(i=0; irrsets[i])->security; + s = ((struct packed_rrset_data*)rep->rrsets[i]->entry.data) + ->security; if(s < sec) sec = s; } /* in additional, only unchecked triggers revalidation */ for(i=num; irrset_count; i++) { - s = ((struct packed_rrset_data*)rep->rrsets[i])->security; + s = ((struct packed_rrset_data*)rep->rrsets[i]->entry.data) + ->security; if(s == sec_status_unchecked) return s; } @@ -336,7 +338,8 @@ deleg_remove_nonsecure_additional(struct reply_info* rep) enum sec_status s; for(i = rep->an_numrrsets+rep->ns_numrrsets; irrset_count; i++) { - s = ((struct packed_rrset_data*)rep->rrsets[i])->security; + s = ((struct packed_rrset_data*)rep->rrsets[i]->entry.data) + ->security; if(s != sec_status_secure) { memmove(rep->rrsets+i, rep->rrsets+i+1, sizeof(struct ub_packed_rrset_key*)* diff --git a/doc/Changelog b/doc/Changelog index 9bf81ad68..dc32fb53d 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -3,6 +3,9 @@ - answer norecursive from cache if possible. - honor clean_additional setting when returning secure non-recursive referrals. + - do not store referral in msg cache for nonRD queries. + - store verification status in the rrset cache to speed up future + verification. 24 August 2007: Wouter - message is bogus if unsecure authority rrsets are present. diff --git a/iterator/iterator.c b/iterator/iterator.c index 6b3380db8..6d63d6c6c 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -1355,10 +1355,15 @@ processFinished(struct module_qstate* qstate, struct iter_qstate* iq, log_err("prepend rrsets: out of memory"); return error_response(qstate, id, LDNS_RCODE_SERVFAIL); } - /* store message with the finished prepended items */ - if(!iter_dns_store(qstate->env, &qstate->qinfo, - iq->response->rep, 0)) - return error_response(qstate, id, LDNS_RCODE_SERVFAIL); + /* store message with the finished prepended items, + * but only if we did recursion. The nonrecursion referral + * from cache does not need to be stored in the msg cache. */ + if(qstate->query_flags&BIT_RD) { + if(!iter_dns_store(qstate->env, &qstate->qinfo, + iq->response->rep, 0)) + return error_response(qstate, id, + LDNS_RCODE_SERVFAIL); + } } if(query_dname_compare(qstate->qinfo.qname, iq->response->qinfo.qname) == 0) { diff --git a/services/cache/rrset.c b/services/cache/rrset.c index 4a9789b1a..4d8d661df 100644 --- a/services/cache/rrset.c +++ b/services/cache/rrset.c @@ -115,6 +115,10 @@ need_to_update_rrset(void* nd, void* cd, uint32_t timenow, int equal) { struct packed_rrset_data* newd = (struct packed_rrset_data*)nd; struct packed_rrset_data* cached = (struct packed_rrset_data*)cd; + /* o store if rrset has been validated */ + if( newd->security > cached->security) { + return 1; + } /* o if current RRset is more trustworthy - insert it */ if( newd->trust > cached->trust ) { /* if the cached rrset is bogus, and this one equal, @@ -306,3 +310,58 @@ rrset_array_unlock_touch(struct rrset_cache* r, struct region* scratch, } } } + +void +rrset_update_sec_status(struct rrset_cache* r, + struct ub_packed_rrset_key* rrset) +{ + uint32_t now = (uint32_t)time(0); + struct packed_rrset_data* updata = + (struct packed_rrset_data*)rrset->entry.data; + struct lruhash_entry* e; + struct packed_rrset_data* cachedata; + + e = slabhash_lookup(&r->table, rrset->entry.hash, rrset, 1); + if(!e) + return; /* not in the cache anymore */ + cachedata = (struct packed_rrset_data*)e->data; + if(!rrsetdata_equal(updata, cachedata)) { + lock_rw_unlock(&e->lock); + return; /* rrset has changed in the meantime */ + } + /* update the cached rrset */ + cachedata->trust = updata->trust; + cachedata->security = updata->security; + cachedata->ttl = updata->ttl + now; + lock_rw_unlock(&e->lock); +} + +void +rrset_check_sec_status(struct rrset_cache* r, + struct ub_packed_rrset_key* rrset) +{ + uint32_t now = (uint32_t)time(0); + struct packed_rrset_data* updata = + (struct packed_rrset_data*)rrset->entry.data; + struct lruhash_entry* e; + struct packed_rrset_data* cachedata; + + /* hash it again to make sure it has a hash */ + rrset->entry.hash = rrset_key_hash(&rrset->rk); + + e = slabhash_lookup(&r->table, rrset->entry.hash, rrset, 0); + if(!e) + return; /* not in the cache anymore */ + cachedata = (struct packed_rrset_data*)e->data; + if(now > cachedata->ttl || !rrsetdata_equal(updata, cachedata)) { + lock_rw_unlock(&e->lock); + return; /* expired, or rrset has changed in the meantime */ + } + if(cachedata->security > updata->security) { + updata->security = cachedata->security; + if(cachedata->security == sec_status_bogus) + updata->ttl = cachedata->ttl - now; + updata->trust = cachedata->trust; + } + lock_rw_unlock(&e->lock); +} diff --git a/services/cache/rrset.h b/services/cache/rrset.h index 19fbbe09e..22ae29583 100644 --- a/services/cache/rrset.h +++ b/services/cache/rrset.h @@ -184,4 +184,26 @@ void rrset_array_unlock(struct rrset_ref* ref, size_t count); void rrset_array_unlock_touch(struct rrset_cache* r, struct region* scratch, struct rrset_ref* ref, size_t count); +/** + * Update security status of an rrset. Looks up the rrset. + * If found, checks if rdata is equal. + * If so, it will update the security, trust and rrset-ttl values. + * @param r: the rrset cache. + * @param rrset: which rrset to attempt to update. This rrset is left + * untouched. The rrset in the cache is updated in-place. + */ +void rrset_update_sec_status(struct rrset_cache* r, + struct ub_packed_rrset_key* rrset); + +/** + * Looks up security status of an rrset. Looks up the rrset. + * If found, checks if rdata is equal, and entry did not expire. + * If so, it will update the security, trust and rrset-ttl values. + * @param r: the rrset cache. + * @param rrset: This rrset may change security status due to the cache. + * But its status will only improve, towards secure. + */ +void rrset_check_sec_status(struct rrset_cache* r, + struct ub_packed_rrset_key* rrset); + #endif /* SERVICES_CACHE_RRSET_H */ diff --git a/validator/val_utils.c b/validator/val_utils.c index 48e809238..34ac447d6 100644 --- a/validator/val_utils.c +++ b/validator/val_utils.c @@ -43,10 +43,12 @@ #include "validator/validator.h" #include "validator/val_kentry.h" #include "validator/val_sigcrypt.h" +#include "services/cache/rrset.h" #include "util/data/msgreply.h" #include "util/data/packed_rrset.h" #include "util/data/dname.h" #include "util/net_help.h" +#include "util/module.h" enum val_classification val_classify_response(struct query_info* qinf, struct reply_info* rep, @@ -270,6 +272,14 @@ val_verify_rrset(struct module_env* env, struct val_env* ve, ntohs(rrset->rk.rrset_class)); return d->security; } + /* check in the cache if verification has already been done */ + rrset_check_sec_status(env->rrset_cache, rrset); + if(d->security == sec_status_secure) { + log_nametypeclass(VERB_ALGO, "verify rrset from cache", + rrset->rk.dname, ntohs(rrset->rk.type), + ntohs(rrset->rk.rrset_class)); + return d->security; + } log_nametypeclass(VERB_ALGO, "verify rrset", rrset->rk.dname, ntohs(rrset->rk.type), ntohs(rrset->rk.rrset_class)); sec = dnskeyset_verify_rrset(env, ve, rrset, keys); @@ -284,10 +294,12 @@ val_verify_rrset(struct module_env* env, struct val_env* ve, d->trust = rrset_trust_validated; else if(sec == sec_status_bogus) { /* update ttl for rrset to fixed value. */ - d->ttl = time(0) + ve->bogus_ttl; + d->ttl = ve->bogus_ttl; /* leave RR specific TTL: not used for determine * if RRset timed out and clients see proper value. */ } + /* if status updated - store in cache for reuse */ + rrset_update_sec_status(env->rrset_cache, rrset); } return sec; diff --git a/validator/validator.c b/validator/validator.c index 8dcedf265..ed3ac6bb9 100644 --- a/validator/validator.c +++ b/validator/validator.c @@ -1163,10 +1163,11 @@ processFinished(struct module_qstate* qstate, struct val_qstate* vq, } if(vq->orig_msg->rep->security == sec_status_secure) { - /* Do not store the validated status of the dropped RRsets. - * (only secure is reused). These rrsets are apparantly - * added on maliciously, or are unsigned additional data - * This may cause the message to become bogus. */ + /* If the message is secure, check that all rrsets are + * secure (i.e. some inserted RRset for CNAME chain with + * a different signer name). And drop additional rrsets + * that are not secure (if clean-additional option is set) */ + /* this may cause the msg to be marked bogus */ val_check_nonsecure(ve, vq->orig_msg->rep); } @@ -1177,9 +1178,11 @@ processFinished(struct module_qstate* qstate, struct val_qstate* vq, } /* store results in cache */ - if(!dns_cache_store(qstate->env, &vq->orig_msg->qinfo, - vq->orig_msg->rep, 0)) { - log_err("out of memory caching validator results"); + if(qstate->query_flags&BIT_RD) { + if(!dns_cache_store(qstate->env, &vq->orig_msg->qinfo, + vq->orig_msg->rep, 0)) { + log_err("out of memory caching validator results"); + } } qstate->return_rcode = LDNS_RCODE_NOERROR; qstate->return_msg = vq->orig_msg;