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;