From 45afaf3e088fd636a88483f4032562b49df738aa Mon Sep 17 00:00:00 2001 From: Wouter Wijngaards Date: Wed, 8 Oct 2008 10:04:27 +0000 Subject: [PATCH] nsec negative DS. git-svn-id: file:///svn/unbound/trunk@1289 be551aaa-1e26-0410-a405-d3ace91eadb9 --- doc/Changelog | 3 ++ iterator/iterator.c | 8 ++++ services/cache/dns.c | 46 +++++++++--------- services/cache/dns.h | 26 ++++++++++ validator/val_neg.c | 110 +++++++++++++++++++++++++++++++++++++++++-- validator/val_neg.h | 3 +- validator/val_nsec.c | 10 +--- validator/val_nsec.h | 10 ++++ 8 files changed, 181 insertions(+), 35 deletions(-) diff --git a/doc/Changelog b/doc/Changelog index fa0eb4b75..b0321ab60 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,6 @@ +8 October 2008: Wouter + - NSEC negative cache for DS. + 6 October 2008: Wouter - jostle-timeout option, so you can config for slow links. - 0x20 fallback code. Tries 3xnumber of nameserver addresses diff --git a/iterator/iterator.c b/iterator/iterator.c index 4f2a25f5b..11226d2e8 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -50,6 +50,7 @@ #include "iterator/iter_resptype.h" #include "iterator/iter_scrub.h" #include "iterator/iter_priv.h" +#include "validator/val_neg.h" #include "services/cache/dns.h" #include "services/cache/infra.h" #include "util/module.h" @@ -754,6 +755,13 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, msg = dns_cache_lookup(qstate->env, iq->qchase.qname, iq->qchase.qname_len, iq->qchase.qtype, iq->qchase.qclass, qstate->region, qstate->env->scratch); + if(!msg && qstate->env->neg_cache) { + /* lookup in negative cache; may result in + * NOERROR/NODATA or NXDOMAIN answers that need validation */ + msg = val_neg_getmsg(qstate->env->neg_cache, &iq->qchase, + qstate->region, qstate->env->rrset_cache, + *qstate->env->now); + } if(msg) { /* handle positive cache response */ enum response_type type = response_type_from_cache(msg, diff --git a/services/cache/dns.c b/services/cache/dns.c index d9251165e..742f0387f 100644 --- a/services/cache/dns.c +++ b/services/cache/dns.c @@ -244,11 +244,9 @@ find_add_ds(struct module_env* env, struct regional* region, } } -/** create referral message with NS and query */ -static struct dns_msg* -create_msg(uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, - struct regional* region, struct ub_packed_rrset_key* nskey, - struct packed_rrset_data* nsdata, uint32_t now) +struct dns_msg* +dns_msg_create(uint8_t* qname, size_t qnamelen, uint16_t qtype, + uint16_t qclass, struct regional* region, size_t capacity) { struct dns_msg* msg = (struct dns_msg*)regional_alloc(region, sizeof(struct dns_msg)); @@ -261,32 +259,31 @@ create_msg(uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, msg->qinfo.qtype = qtype; msg->qinfo.qclass = qclass; /* non-packed reply_info, because it needs to grow the array */ - msg->rep = (struct reply_info*)regional_alloc(region, + msg->rep = (struct reply_info*)regional_alloc_zero(region, sizeof(struct reply_info)-sizeof(struct rrset_ref)); if(!msg->rep) return NULL; - memset(msg->rep, 0, - sizeof(struct reply_info)-sizeof(struct rrset_ref)); msg->rep->flags = BIT_QR; /* with QR, no AA */ msg->rep->qdcount = 1; - /* allocate the array to as much as we could need: - * NS rrset + DS/NSEC rrset + - * A rrset for every NS RR - * AAAA rrset for every NS RR - */ msg->rep->rrsets = (struct ub_packed_rrset_key**) regional_alloc(region, - (2 + nsdata->count*2)*sizeof(struct ub_packed_rrset_key*)); + capacity*sizeof(struct ub_packed_rrset_key*)); if(!msg->rep->rrsets) return NULL; - msg->rep->rrsets[0] = packed_rrset_copy_region(nskey, region, now); - if(!msg->rep->rrsets[0]) - return NULL; - msg->rep->ns_numrrsets++; - msg->rep->rrset_count++; return msg; } +int +dns_msg_authadd(struct dns_msg* msg, struct regional* region, + struct ub_packed_rrset_key* rrset, uint32_t now) +{ + if(!(msg->rep->rrsets[msg->rep->rrset_count++] = + packed_rrset_copy_region(rrset, region, now))) + return 0; + msg->rep->ns_numrrsets++; + return 1; +} + struct delegpt* dns_cache_find_delegation(struct module_env* env, uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, @@ -311,9 +308,14 @@ dns_cache_find_delegation(struct module_env* env, uint8_t* qname, } /* create referral message */ if(msg) { - *msg = create_msg(qname, qnamelen, qtype, qclass, region, - nskey, nsdata, now); - if(!*msg) { + /* allocate the array to as much as we could need: + * NS rrset + DS/NSEC rrset + + * A rrset for every NS RR + * AAAA rrset for every NS RR + */ + *msg = dns_msg_create(qname, qnamelen, qtype, qclass, region, + 2 + nsdata->count*2); + if(!*msg || !dns_msg_authadd(*msg, region, nskey, now)) { lock_rw_unlock(&nskey->entry.lock); log_err("find_delegation: out of memory"); return NULL; diff --git a/services/cache/dns.h b/services/cache/dns.h index 6d16022f2..acd0e2678 100644 --- a/services/cache/dns.h +++ b/services/cache/dns.h @@ -137,4 +137,30 @@ struct dns_msg* dns_cache_lookup(struct module_env* env, int cache_fill_missing(struct module_env* env, uint16_t qclass, struct regional* region, struct delegpt* dp); +/** + * Utility, create new, unpacked data structure for cache response. + * QR bit set, no AA. Query set as indicated. Space for number of rrsets. + * @param qname: query section name + * @param qnamelen: len of qname + * @param qtype: query section type + * @param qclass: query section class + * @param region: where to alloc. + * @param capacity: number of rrsets space to create in the array. + * @return new dns_msg struct or NULL on mem fail. + */ +struct dns_msg* dns_msg_create(uint8_t* qname, size_t qnamelen, uint16_t qtype, + uint16_t qclass, struct regional* region, size_t capacity); + +/** + * Add rrset to authority section in unpacked dns_msg message. Must have enough + * space left, does not grow the array. + * @param msg: msg to put it in. + * @param region: region to alloc in + * @param rrset: to add in authority section + * @param now: now. + * @return true if worked, false on fail + */ +int dns_msg_authadd(struct dns_msg* msg, struct regional* region, + struct ub_packed_rrset_key* rrset, uint32_t now); + #endif /* SERVICES_CACHE_DNS_H */ diff --git a/validator/val_neg.c b/validator/val_neg.c index 06adfa7fe..f2bcd61e6 100644 --- a/validator/val_neg.c +++ b/validator/val_neg.c @@ -51,6 +51,7 @@ #include "util/net_help.h" #include "util/config_file.h" #include "services/cache/rrset.h" +#include "services/cache/dns.h" int val_neg_data_compare(const void* a, const void* b) { @@ -1038,16 +1039,119 @@ void val_neg_addreferral(struct val_neg_cache* neg, struct reply_info* rep, lock_basic_unlock(&neg->lock); } +/** + * See if rrset exists in rrset cache. + * If it does, the bit is checked, and if not expired, it is returned + * allocated in region. + * @param rrset_cache: rrset cache + * @param qname: to lookup rrset name + * @param qname_len: length of qname. + * @param qtype: type of rrset to lookup, host order + * @param qclass: class of rrset to lookup, host order + * @param flags: flags for rrset to lookup + * @param region: where to alloc result + * @param checkbit: if true, a bit in the nsec typemap is checked for absence. + * @param checktype: which bit to check + * @param now: to check ttl against + * @return rrset or NULL + */ +static struct ub_packed_rrset_key* +grab_nsec(struct rrset_cache* rrset_cache, uint8_t* qname, size_t qname_len, + uint16_t qtype, uint16_t qclass, uint32_t flags, + struct regional* region, int checkbit, uint16_t checktype, + uint32_t now) +{ + struct ub_packed_rrset_key* r, *k = rrset_cache_lookup(rrset_cache, + qname, qname_len, qtype, qclass, flags, now, 0); + struct packed_rrset_data* d; + if(!k) return NULL; + d = (struct packed_rrset_data*)k->entry.data; + if(d->ttl < now) { + lock_rw_unlock(&k->entry.lock); + return NULL; + } + /* only secure or unchecked records that have signatures. */ + if( ! ( d->security == sec_status_secure || + (d->security == sec_status_unchecked && + d->rrsig_count > 0) ) ) { + lock_rw_unlock(&k->entry.lock); + return NULL; + } + /* check if checktype is absent */ + if(checkbit && qtype == LDNS_RR_TYPE_NSEC && + nsec_has_type(k, checktype)) { + lock_rw_unlock(&k->entry.lock); + return NULL; + } + /* looks OK! copy to region and return it */ + r = packed_rrset_copy_region(k, region, now); + /* if it failed, we return the NULL */ + lock_rw_unlock(&k->entry.lock); + return r; +} + struct dns_msg* val_neg_getmsg(struct val_neg_cache* neg, struct query_info* qinfo, - struct regional* region, struct rrset_cache* rrset_cache) + struct regional* region, struct rrset_cache* rrset_cache, uint32_t now) { struct dns_msg* msg; + struct ub_packed_rrset_key* rrset; + uint8_t* zname; + size_t zname_len; + int zname_labs; + struct val_neg_zone* zone; + struct val_neg_data* data; /* only for DS queries */ if(qinfo->qtype != LDNS_RR_TYPE_DS) return NULL; - /* see if info from neg cache is available */ + /* see if info from neg cache is available + * For NSECs, because there is no optout; a DS next to a delegation + * always has exactly an NSEC for it itself; check its DS bit. + * flags=0 (not the zone apex). + */ + rrset = grab_nsec(rrset_cache, qinfo->qname, qinfo->qname_len, + LDNS_RR_TYPE_NSEC, qinfo->qclass, 0, region, 1, + qinfo->qtype, now); + if(rrset) { + /* return msg with that rrset */ + if(!(msg = dns_msg_create(qinfo->qname, qinfo->qname_len, + qinfo->qtype, qinfo->qclass, region, 1))) + return NULL; + if(!dns_msg_authadd(msg, region, rrset, now)) + return NULL; + return msg; + } + + /* check NSEC3 neg cache for type DS */ + /* need to look one zone higher for DS type */ + zname = qinfo->qname; + zname_len = qinfo->qname_len; + dname_remove_label(&zname, &zname_len); + zname_labs = dname_count_labels(zname); + + /* lookup closest zone */ + lock_basic_lock(&neg->lock); + zone = neg_closest_zone_parent(neg, zname, zname_len, zname_labs, + qinfo->qclass); + while(zone && !zone->in_use) + zone = zone->parent; + if(!zone) { + lock_basic_unlock(&neg->lock); + return NULL; + } + + /* lookup closest data record TODO hash NSEC3 */ + (void)neg_closest_data(zone, qinfo->qname, qinfo->qname_len, + zname_labs+1, &data); + while(data && !data->in_use) + data = data->parent; + if(!data) { + lock_basic_unlock(&neg->lock); + return 0; + } - return msg; + /* get RR and check */ + + return 0; } diff --git a/validator/val_neg.h b/validator/val_neg.h index ff79c31cf..8bb60848e 100644 --- a/validator/val_neg.h +++ b/validator/val_neg.h @@ -227,12 +227,13 @@ int val_neg_dlvlookup(struct val_neg_cache* neg, uint8_t* qname, size_t len, * @param qinfo: query * @param region: where to allocate reply. * @param rrset_cache: rrset cache. + * @param now: to check TTLs against. * @return a reply message if something was found. * This reply may still need validation. * NULL if nothing found (or out of memory). */ struct dns_msg* val_neg_getmsg(struct val_neg_cache* neg, struct query_info* qinfo, struct regional* region, - struct rrset_cache* rrset_cache); + struct rrset_cache* rrset_cache, uint32_t now); #endif /* VALIDATOR_VAL_NEG_H */ diff --git a/validator/val_nsec.c b/validator/val_nsec.c index 016b33a55..2a47b2c05 100644 --- a/validator/val_nsec.c +++ b/validator/val_nsec.c @@ -91,15 +91,7 @@ nsecbitmap_has_type_rdata(uint8_t* bitmap, size_t len, uint16_t type) return 0; } -/** - * Check if type is present in the NSEC typemap - * @param nsec: the nsec RRset. - * If there are multiple RRs, then each must have the same typemap, - * since the typemap represents the types at this domain node. - * @param type: type to check for, host order. - * @return true if present - */ -static int +int nsec_has_type(struct ub_packed_rrset_key* nsec, uint16_t type) { struct packed_rrset_data* d = (struct packed_rrset_data*)nsec-> diff --git a/validator/val_nsec.h b/validator/val_nsec.h index c7e4b0bcd..d3f9f0b55 100644 --- a/validator/val_nsec.h +++ b/validator/val_nsec.h @@ -84,6 +84,16 @@ enum sec_status val_nsec_prove_nodata_dsreply(struct module_env* env, */ int nsecbitmap_has_type_rdata(uint8_t* bitmap, size_t len, uint16_t type); +/** + * Check if type is present in the NSEC typemap + * @param nsec: the nsec RRset. + * If there are multiple RRs, then each must have the same typemap, + * since the typemap represents the types at this domain node. + * @param type: type to check for, host order. + * @return true if present + */ +int nsec_has_type(struct ub_packed_rrset_key* nsec, uint16_t type); + /** * Determine if a NSEC proves the NOERROR/NODATA conditions. This will also * handle the empty non-terminal (ENT) case and partially handle the -- 2.47.3