]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
DLV work
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Thu, 14 Aug 2008 15:16:50 +0000 (15:16 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Thu, 14 Aug 2008 15:16:50 +0000 (15:16 +0000)
git-svn-id: file:///svn/unbound/trunk@1190 be551aaa-1e26-0410-a405-d3ace91eadb9

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

index 00682e24ad681e03d9f59f56b62c3ab6c06aecc0..92ad04c043de35df76cc2efb2fcf42cb5f0cb1ab 100644 (file)
@@ -1,3 +1,6 @@
+14 August 2008: Wouter
+       - synthesize DLV messages from the rrset cache, like done for DS.
+
 13 August 2008: Wouter
        - bug #203: nicer do-auto log message when user sets incompatible
          options.
index e1dcf325ea975f4cee910496102d8139625e6481..bea8182dc1ad496077e13938147289674decbce8 100644 (file)
@@ -595,8 +595,9 @@ dns_cache_lookup(struct module_env* env,
                lock_rw_unlock(&rrset->entry.lock);
        }
 
-       /* construct DS, DNSKEY messages from rrset cache. */
-       if((qtype == LDNS_RR_TYPE_DS || qtype == LDNS_RR_TYPE_DNSKEY) &&
+       /* construct DS, DNSKEY, DLV messages from rrset cache. */
+       if((qtype == LDNS_RR_TYPE_DS || qtype == LDNS_RR_TYPE_DNSKEY ||
+               qtype == LDNS_RR_TYPE_DLV) &&
                (rrset=rrset_cache_lookup(env->rrset_cache, qname, qnamelen, 
                qtype, qclass, 0, now, 0))) {
                /* if the rrset is from the additional section, and the
index f984a0dabb970dc6c0d769e943c1ae08be50117a..009aa814c626eba4c411084f56f9b50713912e23 100644 (file)
@@ -45,6 +45,7 @@
 #include "validator/val_utils.h"
 #include "util/data/msgreply.h"
 #include "util/data/dname.h"
+#include "util/net_help.h"
 
 /** get ttl of rrset */
 static uint32_t 
@@ -479,3 +480,79 @@ val_nsec_proves_no_wc(struct ub_packed_rrset_key* nsec, uint8_t* qname,
        }
        return 0;
 }
+
+/**
+ * Closest NONEMPTY encloser.
+ * Thus, no empty nonterminals are returned.
+ * @param qname: query name
+ * @param nsec: nsec record.
+ * @return the name (part of qname).
+ */
+static uint8_t* 
+nsec_closest_nonempty(uint8_t* qname, struct ub_packed_rrset_key* nsec)
+{
+       uint8_t* next;
+       size_t nlen;
+       uint8_t* common1, *common2;
+       if(!nsec_get_next(nsec, &next, &nlen))
+               return NULL;
+       /* shortest common with owner or next name */
+       common1 = dname_get_shared_topdomain(qname, nsec->rk.dname);
+       common2 = dname_get_shared_topdomain(qname, next);
+       if(dname_count_labels(common1) < dname_count_labels(common2))
+               return common1;
+       return common2;
+}
+
+int val_nsec_check_dlv(struct query_info* qinfo,
+        struct reply_info* rep, uint8_t** nm, size_t* nm_len)
+{
+       uint8_t* next;
+       size_t i, nlen;
+       int c;
+       /* we should now have a NOERROR/NODATA or NXDOMAIN message */
+       if(rep->an_numrrsets != 0) {
+               return 0;
+       }
+       /* is this NOERROR ? */
+       if(FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NOERROR) {
+               /* it can be a plain NSEC match - go up one more level. */
+               /* or its an empty nonterminal - go up to nonempty level */
+               for(i=0; i<rep->ns_numrrsets; i++) {
+                       if(!nsec_get_next(rep->rrsets[i], &next, &nlen))
+                               continue;
+                       c = dname_canonical_compare(
+                               rep->rrsets[i]->rk.dname, qinfo->qname);
+                       if(c == 0) {
+                               /* plain match */
+                               dname_remove_label(nm, nm_len);
+                               return 1;
+                       } else if(c < 0 && 
+                               dname_strict_subdomain_c(next, qinfo->qname)) {
+                               /* ENT */
+                               *nm = nsec_closest_nonempty(
+                                       *nm, rep->rrsets[i]);
+                               if(!*nm) return 0;
+                               return 1;
+                       }
+               }
+               return 0;
+       }
+
+       /* is this NXDOMAIN ? */
+       if(FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NXDOMAIN) {
+               /* find the qname denial NSEC record. It can tell us
+                * a closest encloser name; or that we not need bother */
+               for(i=0; i<rep->ns_numrrsets; i++) {
+                       if(val_nsec_proves_name_error(rep->rrsets[i], 
+                               qinfo->qname)) {
+                               *nm = nsec_closest_nonempty(
+                                       *nm, rep->rrsets[i]);
+                               if(!*nm) return 0;
+                               return 1;
+                       }
+               }
+               return 0;
+       }
+       return 0;
+}
index 68ab91a836036b48185f1bc8f373a38c7177bee8..c7e4b0bcd871afe81581edbc7ab3658b426d6cdf 100644 (file)
@@ -145,4 +145,17 @@ uint8_t* nsec_closest_encloser(uint8_t* qname,
 int val_nsec_proves_no_wc(struct ub_packed_rrset_key* nsec, uint8_t* qname, 
        size_t qnamelen);
 
+/**
+ * Determine the DLV result, what to do with NSEC DLV reply.
+ * @param qinfo: what was queried for.
+ * @param rep: the nonpositive reply.
+ * @param nm: dlv lookup name, to adjust for new lookup name (if needed).
+ * @param nm_len: length of lookup name.
+ * @return 0 on error, 1 if a higher point is found.
+ *     If the higher point is above the dlv repo anchor, the qname does 
+ *     not exist.
+ */
+int val_nsec_check_dlv(struct query_info* qinfo,
+       struct reply_info* rep, uint8_t** nm, size_t* nm_len);
+
 #endif /* VALIDATOR_VAL_NSEC_H */
index a1e1a8cee505b7c61661a378ec420d3002f47295..c05920be149e194de22c5dcac63cce19cfcef18a 100644 (file)
@@ -287,11 +287,12 @@ needs_validation(struct module_qstate* qstate, int ret_rc,
  * @param namelen: length of name.
  * @param qtype: query type.
  * @param qclass: query class.
+ * @param flags: additional flags, such as the CD bit (BIT_CD), or 0.
  * @return false on alloc failure.
  */
 static int
 generate_request(struct module_qstate* qstate, int id, uint8_t* name, 
-       size_t namelen, uint16_t qtype, uint16_t qclass)
+       size_t namelen, uint16_t qtype, uint16_t qclass, uint16_t flags)
 {
        struct module_qstate* newq;
        struct query_info ask;
@@ -302,7 +303,7 @@ generate_request(struct module_qstate* qstate, int id, uint8_t* name,
        log_query_info(VERB_ALGO, "generate request", &ask);
        fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub));
        if(!(*qstate->env->attach_sub)(qstate, &ask, 
-               (uint16_t)(BIT_RD|BIT_CD), 0, &newq)){
+               (uint16_t)(BIT_RD|flags), 0, &newq)){
                log_err("Could not generate request: out of memory");
                return 0;
        }
@@ -328,7 +329,7 @@ prime_trust_anchor(struct module_qstate* qstate, struct val_qstate* vq,
        int id, struct trust_anchor* toprime)
 {
        int ret = generate_request(qstate, id, toprime->name, toprime->namelen,
-               LDNS_RR_TYPE_DNSKEY, toprime->dclass);
+               LDNS_RR_TYPE_DNSKEY, toprime->dclass, BIT_CD);
        if(!ret) {
                log_err("Could not prime trust anchor: out of memory");
                return 0;
@@ -1303,7 +1304,7 @@ processFindKey(struct module_qstate* qstate, struct val_qstate* vq, int id)
                vq->key_entry->name) != 0) {
                if(!generate_request(qstate, id, vq->ds_rrset->rk.dname, 
                        vq->ds_rrset->rk.dname_len, LDNS_RR_TYPE_DNSKEY, 
-                       vq->qchase.qclass)) {
+                       vq->qchase.qclass, BIT_CD)) {
                        log_err("mem error generating DNSKEY request");
                        return val_error(qstate, id);
                }
@@ -1313,7 +1314,8 @@ processFindKey(struct module_qstate* qstate, struct val_qstate* vq, int id)
        if(!vq->ds_rrset || query_dname_compare(vq->ds_rrset->rk.dname,
                target_key_name) != 0) {
                if(!generate_request(qstate, id, target_key_name, 
-                       target_key_len, LDNS_RR_TYPE_DS, vq->qchase.qclass)) {
+                       target_key_len, LDNS_RR_TYPE_DS, vq->qchase.qclass,
+                       BIT_CD)) {
                        log_err("mem error generating DS request");
                        return val_error(qstate, id);
                }
@@ -1323,7 +1325,7 @@ processFindKey(struct module_qstate* qstate, struct val_qstate* vq, int id)
        /* Otherwise, it is time to query for the DNSKEY */
        if(!generate_request(qstate, id, vq->ds_rrset->rk.dname, 
                vq->ds_rrset->rk.dname_len, LDNS_RR_TYPE_DNSKEY, 
-               vq->qchase.qclass)) {
+               vq->qchase.qclass, BIT_CD)) {
                log_err("mem error generating DNSKEY request");
                return val_error(qstate, id);
        }
@@ -1472,6 +1474,130 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq,
        return 1;
 }
 
+/**
+ * Init DLV check.
+ * Called when a query is determined by other trust anchors to be insecure
+ * (or indeterminate).  Then we look if there is a key in the DLV.
+ * Performs aggressive negative cache check to see if there is no key.
+ * Otherwise, spawns a DLV query, and changes to the DLV wait state.
+ *
+ * @param qstate: query state.
+ * @param vq: validator query state.
+ * @param ve: validator shared global environment.
+ * @param id: module id.
+ * @return  true if there is no DLV.
+ *     false: processing is finished for the validator operate().
+ *     This function may exit in three ways:
+ *         o   no DLV (agressive cache), so insecure. (true)
+ *         o   error - stop processing (false)
+ *         o   DLV lookup was started, stop processing (false)
+ */
+static int
+val_dlv_init(struct module_qstate* qstate, struct val_qstate* vq, 
+       struct val_env* ve, int id)
+{
+       uint8_t* nm;
+       size_t nm_len;
+       /* there must be a DLV configured */
+       log_assert(qstate->env->anchors->dlv_anchor);
+       /* this bool is true to avoid looping in the DLV checks */
+       log_assert(vq->dlv_checked);
+
+       /* init the DLV lookup variables */
+       vq->dlv_lookup_name = NULL;
+       vq->dlv_lookup_name_len = 0;
+       vq->dlv_insecure_at = NULL;
+       vq->dlv_insecure_at_len = 0;
+
+       /* Determine the name for which we want to lookup DLV.
+        * This name is for the current message, or 
+        * for the current RRset for CNAME, referral subtypes.
+        * If there is a signer, use that, otherwise the domain name */
+       if(vq->signer_name) {
+               nm = vq->signer_name;
+               nm_len = vq->signer_len;
+       } else {
+               /* use qchase */
+               nm = vq->qchase.qname;
+               nm_len = vq->qchase.qname_len;
+       }
+       log_nametypeclass(VERB_ALGO, "DLV init look", nm, LDNS_RR_TYPE_DS,
+               vq->qchase.qclass);
+       log_assert(nm && nm_len)
+       /* sanity check: no DLV lookups below the DLV anchor itself.
+        * Like, an securely insecure delegation there makes no sense. */
+       if(dname_subdomain_c(nm, qstate->env->anchors->dlv_anchor->name)) {
+               verbose(VERB_ALGO, "DLV lookup within DLV repository denied");
+               return 1;
+       }
+       /* concat name (minus root label) + dlv name */
+       vq->dlv_lookup_name_len = nm_len - 1 + 
+               qstate->env->anchors->dlv_anchor->namelen;
+       vq->dlv_lookup_name = regional_alloc(qstate->region, 
+               vq->dlv_lookup_name_len);
+       if(!vq->dlv_lookup_name) {
+               log_err("Out of memory preparing DLV lookup");
+               return val_error(qstate, id);
+       }
+       memmove(vq->dlv_lookup_name, nm, nm_len-1);
+       memmove(vq->dlv_lookup_name+nm_len-1, 
+               qstate->env->anchors->dlv_anchor->name, 
+               qstate->env->anchors->dlv_anchor->namelen);
+       log_nametypeclass(VERB_ALGO, "DLV name", vq->dlv_lookup_name, 
+               LDNS_RR_TYPE_DLV, vq->qchase.qclass);
+
+       /* determine where the insecure point was determined, the DLV must 
+        * be equal or below that to continue building the trust chain 
+        * down. May be NULL if no trust chain was built yet */
+       nm = NULL;
+       if(vq->key_entry && key_entry_isnull(vq->key_entry)) {
+               nm = vq->key_entry->name;
+               nm_len = vq->key_entry->namelen;
+       }
+       if(nm) {
+               vq->dlv_insecure_at_len = nm_len - 1 +
+                       qstate->env->anchors->dlv_anchor->namelen;
+               vq->dlv_insecure_at = regional_alloc(qstate->region,
+                       vq->dlv_insecure_at_len);
+               if(!vq->dlv_insecure_at) {
+                       log_err("Out of memory preparing DLV lookup");
+                       return val_error(qstate, id);
+               }
+               memmove(vq->dlv_insecure_at, nm, nm_len-1);
+               memmove(vq->dlv_insecure_at+nm_len-1, 
+                       qstate->env->anchors->dlv_anchor->name, 
+                       qstate->env->anchors->dlv_anchor->namelen);
+               log_nametypeclass(VERB_ALGO, "insecure_at", 
+                       vq->dlv_insecure_at, 0, vq->qchase.qclass);
+       }
+
+       /* If we can find the name in the aggressive negative cache,
+        * give up; insecure is the answer */
+       /* lookup, several places in the tree may qualify 
+        *    between insecure_at and the lookup_name
+        * Check proof thoroughly
+        * TODO
+        * return 1;
+        */
+
+       /* perform a lookup for the DLV; with validation */
+       vq->state = VAL_DLVLOOKUP_STATE;
+       if(!generate_request(qstate, id, vq->dlv_lookup_name, 
+               vq->dlv_lookup_name_len, LDNS_RR_TYPE_DLV,
+               vq->qchase.qclass, 0)) {
+               return val_error(qstate, id);
+       }
+
+       /* Find the closest encloser DLV from the repository.
+        * then that is used to build another chain of trust 
+        * This may first require a query 'too low' that has NSECs in
+        * the answer, from which we determine the closest encloser DLV. 
+        * When determine the closest encloser, skip empty nonterminals,
+        * since we want a nonempty node in the DLV repository. */
+
+       return 0;
+}
+
 /**
  * The Finished state. The validation status (good or bad) has been determined.
  *
@@ -1490,6 +1616,18 @@ processFinished(struct module_qstate* qstate, struct val_qstate* vq,
                qstate->query_flags, &qstate->qinfo, &vq->qchase, 
                vq->orig_msg->rep, vq->rrset_skip);
 
+       /* if the result is insecure or indeterminate and we have not 
+        * checked the DLV yet, check the DLV */
+       verbose(VERB_ALGO, "check for DLV");
+       verbose(VERB_ALGO, "sec %s", sec_status_to_string(vq->chase_reply->security));
+       if((vq->chase_reply->security == sec_status_insecure ||
+               vq->chase_reply->security == sec_status_indeterminate) &&
+               qstate->env->anchors->dlv_anchor && !vq->dlv_checked) {
+               vq->dlv_checked = 1;
+               if(!val_dlv_init(qstate, vq, ve, id))
+                       return 0;
+       }
+
        /* store overall validation result in orig_msg */
        if(vq->rrset_skip == 0)
                vq->orig_msg->rep->security = vq->chase_reply->security;
@@ -1511,6 +1649,7 @@ processFinished(struct module_qstate* qstate, struct val_qstate* vq,
                        /* and restart for this rrset */
                        verbose(VERB_ALGO, "validator: go to next rrset");
                        vq->chase_reply->security = sec_status_unchecked;
+                       vq->dlv_checked = 0; /* can do DLV for this RR */
                        vq->state = VAL_INIT_STATE;
                        return 1;
                }
@@ -1528,6 +1667,7 @@ processFinished(struct module_qstate* qstate, struct val_qstate* vq,
                        log_query_info(VERB_ALGO, "validator: chased to",
                                &vq->qchase);
                        vq->chase_reply->security = sec_status_unchecked;
+                       vq->dlv_checked = 0; /* can do DLV for this RR */
                        vq->state = VAL_INIT_STATE;
                        return 1;
                }
@@ -1540,8 +1680,10 @@ processFinished(struct module_qstate* qstate, struct val_qstate* vq,
                 * 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);
-               log_query_info(VERB_DETAIL, "validation success", 
-                       &qstate->qinfo);
+               if(vq->orig_msg->rep->security == sec_status_secure) {
+                       log_query_info(VERB_DETAIL, "validation success", 
+                               &qstate->qinfo);
+               }
        }
 
        /* if the result is bogus - set message ttl to bogus ttl to avoid
@@ -1572,6 +1714,80 @@ processFinished(struct module_qstate* qstate, struct val_qstate* vq,
        return 0;
 }
 
+/**
+ * The DLVLookup state. Process DLV lookups.
+ *
+ * @param qstate: query state.
+ * @param vq: validator query state.
+ * @param ve: validator shared global environment.
+ * @param id: module id.
+ * @return true if the event should be processed further on return, false if
+ *         not.
+ */
+static int
+processDLVLookup(struct module_qstate* qstate, struct val_qstate* vq, 
+       struct val_env* ve, int id)
+{
+       /* see if this we are ready to continue normal resolution */
+       /* we may need more DLV lookups */
+       if(vq->dlv_status==dlv_error)
+               verbose(VERB_ALGO, "DLV woke up with status dlv_error");
+       else if(vq->dlv_status==dlv_success)
+               verbose(VERB_ALGO, "DLV woke up with status dlv_success");
+       else if(vq->dlv_status==dlv_ask_higher)
+               verbose(VERB_ALGO, "DLV woke up with status dlv_ask_higher");
+       else if(vq->dlv_status==dlv_there_is_no_dlv)
+               verbose(VERB_ALGO, "DLV woke up with status dlv_there_is_no_dlv");
+       else    verbose(VERB_ALGO, "DLV woke up with status unknown");
+       log_nametypeclass(VERB_ALGO, "next look", vq->dlv_lookup_name,
+               LDNS_RR_TYPE_DLV, vq->qchase.qclass);
+
+       if(vq->dlv_status == dlv_error) {
+               verbose(VERB_QUERY, "failed DLV lookup");
+               return val_error(qstate, id);
+       } else if(vq->dlv_status == dlv_success) {
+               /* chain continues with DNSKEY, continue in FINDKEY */
+               vq->state = VAL_FINDKEY_STATE;
+               if(!generate_request(qstate, id, vq->ds_rrset->rk.dname, 
+                       vq->ds_rrset->rk.dname_len, LDNS_RR_TYPE_DNSKEY, 
+                       vq->qchase.qclass, BIT_CD)) {
+                       log_err("mem error generating DNSKEY request");
+                       return val_error(qstate, id);
+               }
+               return 0;
+       } else if(vq->dlv_status == dlv_there_is_no_dlv) {
+               /* continue with the insecure result we got */
+               vq->state = VAL_FINISHED_STATE;
+               return 1;
+       } 
+       log_assert(vq->dlv_status == dlv_ask_higher);
+
+       /* ask higher, make sure we stay in DLV repo, below dlv_at */
+       if(!dname_subdomain_c(vq->dlv_lookup_name,
+               qstate->env->anchors->dlv_anchor->name)) {
+               /* just like, there is no DLV */
+               verbose(VERB_ALGO, "ask above dlv repo");
+               vq->state = VAL_FINISHED_STATE;
+               return 1;
+       }
+       if(vq->dlv_insecure_at && !dname_subdomain_c(vq->dlv_lookup_name,
+               vq->dlv_insecure_at)) {
+               /* already checked a chain lower than dlv_lookup_name */
+               verbose(VERB_ALGO, "ask above insecure endpoint");
+               log_nametypeclass(0, "enpt", vq->dlv_insecure_at, 0, 0);
+               vq->state = VAL_FINISHED_STATE;
+               return 1;
+       }
+
+       if(!generate_request(qstate, id, vq->dlv_lookup_name, 
+               vq->dlv_lookup_name_len, LDNS_RR_TYPE_DLV,
+               vq->qchase.qclass, 0)) {
+               return val_error(qstate, id);
+       }
+
+       return 0;
+}
+
 /** 
  * Handle validator state.
  * If a method returns true, the next state is started. If false, then
@@ -1602,6 +1818,9 @@ val_handle(struct module_qstate* qstate, struct val_qstate* vq,
                        case VAL_FINISHED_STATE: 
                                cont = processFinished(qstate, vq, ve, id);
                                break;
+                       case VAL_DLVLOOKUP_STATE: 
+                               cont = processDLVLookup(qstate, vq, ve, id);
+                               break;
                        default:
                                log_warn("validator: invalid state %d",
                                        vq->state);
@@ -2047,7 +2266,7 @@ process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq,
        /* If good, we stay in the FINDKEY state. */
        log_query_info(VERB_DETAIL, "validated DNSKEY", qinfo);
 }
-       
+
 /**
  * Process prime response
  * Sets the key entry in the state.
@@ -2075,6 +2294,96 @@ process_prime_response(struct module_qstate* qstate, struct val_qstate* vq,
        /* the qstate will be reactivated after inform_super is done */
 }
 
+/**
+ * Process DLV response. Called from inform_supers.
+ * Because it is in inform_supers, the mesh itself is busy doing callbacks
+ * for a state that is to be deleted soon; don't touch the mesh; instead
+ * set a state in the super, as the super will be reactivated soon.
+ * Perform processing to determine what state to set in the super.
+ *
+ * @param qstate: query state that is validating and asked for a DLV.
+ * @param vq: validator query state
+ * @param id: module id.
+ * @param rcode: rcode result value.
+ * @param msg: result message (if rcode is OK).
+ * @param qinfo: from the sub query state, query info.
+ */
+static void
+process_dlv_response(struct module_qstate* qstate, struct val_qstate* vq,
+       int id, int rcode, struct dns_msg* msg, struct query_info* qinfo)
+{
+       struct val_env* ve = (struct val_env*)qstate->env->modinfo[id];
+
+       verbose(VERB_ALGO, "process dlv response to super");
+       if(rcode != LDNS_RCODE_NOERROR) {
+               /* lookup failed, set in vq to give up */
+               vq->dlv_status = dlv_error;
+               verbose(VERB_ALGO, "response is error");
+               return;
+       }
+       if(msg->rep->security != sec_status_secure) {
+               vq->dlv_status = dlv_error;
+               verbose(VERB_ALGO, "response is not secure");
+               return;
+       }
+       /* was the lookup a success? validated DLV? */
+       if(FLAGS_GET_RCODE(msg->rep->flags) == LDNS_RCODE_NOERROR &&
+               msg->rep->an_numrrsets == 1 &&
+               msg->rep->security == sec_status_secure &&
+               ntohs(msg->rep->rrsets[0]->rk.type) == LDNS_RR_TYPE_DLV &&
+               ntohs(msg->rep->rrsets[0]->rk.rrset_class) == qinfo->qclass &&
+               query_dname_compare(msg->rep->rrsets[0]->rk.dname, 
+                       vq->dlv_lookup_name) == 0) {
+               /* yay! it is just like a DS */
+               vq->ds_rrset = (struct ub_packed_rrset_key*)
+                       regional_alloc_init(qstate->region,
+                       msg->rep->rrsets[0], sizeof(*vq->ds_rrset));
+               if(!vq->ds_rrset) {
+                       log_err("out of memory in process_dlv");
+                       return;
+               }
+               vq->ds_rrset->entry.key = vq->ds_rrset;
+               vq->ds_rrset->rk.dname = (uint8_t*)regional_alloc_init(
+                       qstate->region, vq->ds_rrset->rk.dname, 
+                       vq->ds_rrset->rk.dname_len);
+               if(!vq->ds_rrset->rk.dname) {
+                       log_err("out of memory in process_dlv");
+                       vq->dlv_status = dlv_error;
+                       return;
+               }
+               vq->ds_rrset->entry.data = regional_alloc_init(qstate->region,
+                       vq->ds_rrset->entry.data, 
+                       packed_rrset_sizeof(vq->ds_rrset->entry.data));
+               if(!vq->ds_rrset->entry.data) {
+                       log_err("out of memory in process_dlv");
+                       vq->dlv_status = dlv_error;
+                       return;
+               }
+               packed_rrset_ptr_fixup(vq->ds_rrset->entry.data);
+               /* make vq do a DNSKEY query next up */
+               vq->dlv_status = dlv_success;
+               return;
+       }
+
+       /* was the lookup a failure? 
+        *   if we have to go up into the DLV for a higher DLV anchor
+        *   then set this in the vq, so it can make queries when activated.
+        * See if the NSECs indicate that we should look for higher DLV
+        * or, that there is no DLV securely */
+       if(!val_nsec_check_dlv(qinfo, msg->rep, &vq->dlv_lookup_name, 
+               &vq->dlv_lookup_name_len)) {
+               vq->dlv_status = dlv_error;
+               verbose(VERB_ALGO, "nsec error");
+               return;
+       }
+       if(!dname_subdomain_c(vq->dlv_lookup_name, 
+               qstate->env->anchors->dlv_anchor->name)) {
+               vq->dlv_status = dlv_there_is_no_dlv;
+               return;
+       }
+       vq->dlv_status = dlv_ask_higher;
+}
+
 /* 
  * inform validator super.
  * 
@@ -2108,6 +2417,10 @@ val_inform_super(struct module_qstate* qstate, int id,
                process_dnskey_response(super, vq, id, qstate->return_rcode,
                        qstate->return_msg, &qstate->qinfo);
                return;
+       } else if(qstate->qinfo.qtype == LDNS_RR_TYPE_DLV) {
+               process_dlv_response(super, vq, id, qstate->return_rcode,
+                       qstate->return_msg, &qstate->qinfo);
+               return;
        }
        log_err("internal error in validator: no inform_supers possible");
 }
@@ -2155,6 +2468,7 @@ val_state_to_string(enum val_state state)
                case VAL_FINDKEY_STATE: return "VAL_FINDKEY_STATE";
                case VAL_VALIDATE_STATE: return "VAL_VALIDATE_STATE";
                case VAL_FINISHED_STATE: return "VAL_FINISHED_STATE";
+               case VAL_DLVLOOKUP_STATE: return "VAL_DLVLOOKUP_STATE";
        }
        return "UNKNOWN VALIDATOR STATE";
 }
index 8369aa76a792460ce16040013b6aeabd676d776c..daf65c34f055636785eb8252c753706837e1ff7a 100644 (file)
@@ -116,7 +116,9 @@ enum val_state {
        /** validate the answer, using found key entry */
        VAL_VALIDATE_STATE,
        /** finish up */
-       VAL_FINISHED_STATE
+       VAL_FINISHED_STATE,
+       /** DLV lookup state, processing DLV queries */
+       VAL_DLVLOOKUP_STATE
 };
 
 /**
@@ -184,6 +186,27 @@ struct val_qstate {
 
        /** true if this state is waiting to prime a trust anchor */
        int wait_prime_ta;
+
+       /** have we already checked the DLV? */
+       int dlv_checked;
+       /** The name for which the DLV is looked up. For the current message
+        * or for the current RRset (for CNAME, REFERRAL types).
+        * If there is signer name, that may be it, else a domain name */
+       uint8_t* dlv_lookup_name;
+       /** length of dlv lookup name */
+       size_t dlv_lookup_name_len;
+       /** Name at which chain of trust stopped with insecure, starting DLV
+        * DLV must result in chain going further down */
+       uint8_t* dlv_insecure_at;
+       /** length of dlv insecure point name */
+       size_t dlv_insecure_at_len;
+       /** status of DLV lookup. Indication to VAL_DLV_STATE what to do */
+       enum dlv_status {
+               dlv_error, /* server failure */
+               dlv_success, /* got a DLV */
+               dlv_ask_higher, /* ask again */
+               dlv_there_is_no_dlv /* got no DLV, sure of it */
+       } dlv_status;
 };
 
 /**