diff options
Diffstat (limited to 'services/cache/dns.c')
-rw-r--r-- | services/cache/dns.c | 56 |
1 files changed, 42 insertions, 14 deletions
diff --git a/services/cache/dns.c b/services/cache/dns.c index f9dc5922f682f..411793c6c2709 100644 --- a/services/cache/dns.c +++ b/services/cache/dns.c @@ -395,8 +395,7 @@ dns_msg_authadd(struct dns_msg* msg, struct regional* region, return 1; } -/** add rrset to answer section */ -static int +int dns_msg_ansadd(struct dns_msg* msg, struct regional* region, struct ub_packed_rrset_key* rrset, time_t now) { @@ -568,7 +567,7 @@ rrset_msg(struct ub_packed_rrset_key* rrset, struct regional* region, /** synthesize DNAME+CNAME response from cached DNAME item */ static struct dns_msg* synth_dname_msg(struct ub_packed_rrset_key* rrset, struct regional* region, - time_t now, struct query_info* q) + time_t now, struct query_info* q, enum sec_status* sec_status) { struct dns_msg* msg; struct ub_packed_rrset_key* ck; @@ -580,8 +579,9 @@ synth_dname_msg(struct ub_packed_rrset_key* rrset, struct regional* region, return NULL; /* only allow validated (with DNSSEC) DNAMEs used from cache * for insecure DNAMEs, query again. */ - if(d->security != sec_status_secure) - return NULL; + *sec_status = d->security; + /* return sec status, so the status of the CNAME can be checked + * by the calling routine. */ msg = gen_dns_msg(region, q, 2); /* DNAME + CNAME RRset */ if(!msg) return NULL; @@ -711,7 +711,8 @@ fill_any(struct module_env* env, struct dns_msg* dns_cache_lookup(struct module_env* env, uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, - uint16_t flags, struct regional* region, struct regional* scratch) + uint16_t flags, struct regional* region, struct regional* scratch, + int no_partial) { struct lruhash_entry* e; struct query_info k; @@ -743,27 +744,54 @@ dns_cache_lookup(struct module_env* env, /* see if a DNAME exists. Checked for first, to enforce that DNAMEs * are more important, the CNAME is resynthesized and thus * consistent with the DNAME */ - if( (rrset=find_closest_of_type(env, qname, qnamelen, qclass, now, + if(!no_partial && + (rrset=find_closest_of_type(env, qname, qnamelen, qclass, now, LDNS_RR_TYPE_DNAME, 1))) { /* synthesize a DNAME+CNAME message based on this */ - struct dns_msg* msg = synth_dname_msg(rrset, region, now, &k); + enum sec_status sec_status = sec_status_unchecked; + struct dns_msg* msg = synth_dname_msg(rrset, region, now, &k, + &sec_status); if(msg) { + struct ub_packed_rrset_key* cname_rrset; + lock_rw_unlock(&rrset->entry.lock); + /* now, after unlocking the DNAME rrset lock, + * check the sec_status, and see if we need to look + * up the CNAME record associated before it can + * be used */ + /* normally, only secure DNAMEs allowed from cache*/ + if(sec_status == sec_status_secure) + return msg; + /* but if we have a CNAME cached with this name, then we + * have previously already allowed this name to pass. + * the next cache lookup is going to fetch that CNAME itself, + * but it is better to have the (unsigned)DNAME + CNAME in + * that case */ + cname_rrset = rrset_cache_lookup( + env->rrset_cache, qname, qnamelen, + LDNS_RR_TYPE_CNAME, qclass, 0, now, 0); + if(cname_rrset) { + /* CNAME already synthesized by + * synth_dname_msg routine, so we can + * straight up return the msg */ + lock_rw_unlock(&cname_rrset->entry.lock); + return msg; + } + } else { lock_rw_unlock(&rrset->entry.lock); - return msg; } - lock_rw_unlock(&rrset->entry.lock); } /* see if we have CNAME for this domain, * but not for DS records (which are part of the parent) */ - if( qtype != LDNS_RR_TYPE_DS && + if(!no_partial && qtype != LDNS_RR_TYPE_DS && (rrset=rrset_cache_lookup(env->rrset_cache, qname, qnamelen, LDNS_RR_TYPE_CNAME, qclass, 0, now, 0))) { uint8_t* wc = NULL; + size_t wl; /* if the rrset is not a wildcard expansion, with wcname */ /* because, if we return that CNAME rrset on its own, it is * missing the NSEC or NSEC3 proof */ - if(!(val_rrset_wildcard(rrset, &wc) && wc != NULL)) { + if(!(val_rrset_wildcard(rrset, &wc, &wl) && wc != NULL)) { struct dns_msg* msg = rrset_msg(rrset, region, now, &k); if(msg) { lock_rw_unlock(&rrset->entry.lock); @@ -842,7 +870,7 @@ dns_cache_lookup(struct module_env* env, return NULL; } -int +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) @@ -852,7 +880,7 @@ dns_cache_store(struct module_env* env, struct query_info* msgqinf, rep = reply_info_copy(msgrep, env->alloc, NULL); if(!rep) return 0; - /* ttl must be relative ;i.e. 0..86400 not time(0)+86400. + /* ttl must be relative ;i.e. 0..86400 not time(0)+86400. * the env->now is added to message and RRsets in this routine. */ /* the leeway is used to invalidate other rrsets earlier */ |