summaryrefslogtreecommitdiff
path: root/services/cache/dns.c
diff options
context:
space:
mode:
Diffstat (limited to 'services/cache/dns.c')
-rw-r--r--services/cache/dns.c56
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 */