]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
nsec negative DS.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Wed, 8 Oct 2008 10:04:27 +0000 (10:04 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Wed, 8 Oct 2008 10:04:27 +0000 (10:04 +0000)
git-svn-id: file:///svn/unbound/trunk@1289 be551aaa-1e26-0410-a405-d3ace91eadb9

doc/Changelog
iterator/iterator.c
services/cache/dns.c
services/cache/dns.h
validator/val_neg.c
validator/val_neg.h
validator/val_nsec.c
validator/val_nsec.h

index fa0eb4b753211e22dd0b426e19c1bc6f5afd8f04..b0321ab60d1b6e5a3070eea327c0832b42a63704 100644 (file)
@@ -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
index 4f2a25f5b499472a2761723f49336806b94caad6..11226d2e8393c8f7ef12d5738124fb622193fb0a 100644 (file)
@@ -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, 
index d9251165e2fbc9b3404733f5a0ff0d0a5c68083d..742f0387f60edac1e6f16987f91b00a6acdbc44a 100644 (file)
@@ -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;
index 6d16022f238aa0be3af1c7f96bbbd9d4419b9933..acd0e26789eb95518855070bd95d204b9cee9ba7 100644 (file)
@@ -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 */
index 06adfa7fea2cb8de5ff71353997154c842b1affd..f2bcd61e66f5f12f146bc7cff9dca274e4ec14e8 100644 (file)
@@ -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;
 }
index ff79c31cff931cb5f261727a30678a5164243061..8bb60848ee29066c21a6cd148b6aab6f97a1c1de 100644 (file)
@@ -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 */
index 016b33a55d09895c07391f6a6fe93f6978614b7b..2a47b2c053dec168fbb2d592b37ae635bbd445e1 100644 (file)
@@ -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->
index c7e4b0bcd871afe81581edbc7ab3658b426d6cdf..d3f9f0b559332490a767c5008892319aa7d0d981 100644 (file)
@@ -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