]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- fix to locate nameservers for DS lookup with NS fetches.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Mon, 19 Mar 2012 15:44:21 +0000 (15:44 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Mon, 19 Mar 2012 15:44:21 +0000 (15:44 +0000)
git-svn-id: file:///svn/unbound/trunk@2645 be551aaa-1e26-0410-a405-d3ace91eadb9

doc/Changelog
iterator/iter_utils.c
iterator/iter_utils.h
iterator/iterator.c
iterator/iterator.h
testdata/iter_ds_locate_ns.rpl [new file with mode: 0644]

index d6c66b31cfc8e0e26c6d67401313816372e4b624..7f7dc82746efaa74e8670975f1139c387e21d37c 100644 (file)
@@ -1,5 +1,6 @@
 19 March 2012: Wouter
        - iana portlist updated.
+       - fix to locate nameservers for DS lookup with NS fetches.
 
 16 March 2012: Wouter
        - Patch for access to full DNS packet data in unbound python module
index ac0aa318fe9153aa77d682697e92d14b1d2f3861..86082551fb2593dcd0cbbbe923c91d3e789128b6 100644 (file)
@@ -968,3 +968,35 @@ void iter_merge_retry_counts(struct delegpt* dp, struct delegpt* old)
                a = a->next_usable;
        }
 }
+
+int
+iter_ds_toolow(struct dns_msg* msg)
+{
+       /* if for query example.com, there is example.com SOA or a subdomain
+        * of example.com, then we are too low and need to fetch NS. */
+       size_t i = msg->rep->an_numrrsets;
+       for(; i < msg->rep->an_numrrsets + msg->rep->ns_numrrsets; i++) {
+               struct ub_packed_rrset_key* s = msg->rep->rrsets[i];
+               if(ntohs(s->rk.type) == LDNS_RR_TYPE_SOA &&
+                       dname_subdomain_c(s->rk.dname, msg->qinfo.qname)) {
+                       /* point is too low */
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+int iter_dp_cangodown(struct query_info* qinfo, struct delegpt* dp)
+{
+       /* no delegation point, do not see how we can go down,
+        * robust check, it should really exist */
+       if(!dp) return 0;
+
+       /* see if dp equals the qname, then we cannot go down further */
+       if(query_dname_compare(qinfo->qname, dp->name) == 0)
+               return 0;
+       /* if dp is one label above the name we also cannot go down further */
+       if(dname_count_labels(qinfo->qname) == dp->namelabs+1)
+               return 0;
+       return 1;
+}
index f77240751bf2997fad956f147f6d507a42ff5072..b83b35853285903de1cdfafb53e8b42c28d78f56 100644 (file)
@@ -311,4 +311,21 @@ void iter_dec_attempts(struct delegpt* dp, int d);
  */
 void iter_merge_retry_counts(struct delegpt* dp, struct delegpt* old);
 
+/**
+ * See if a DS response (type ANSWER) is too low: a nodata answer with 
+ * a SOA record in the authority section at-or-below the qchase.qname.
+ * @param msg: the response.
+ * @return true if too low.
+ */
+int iter_ds_toolow(struct dns_msg* msg);
+
+/**
+ * See if delegpt can go down a step to the qname or not
+ * @param qinfo: the query name looked up.
+ * @param dp: checked if the name can go lower to the qname
+ * @return true if can go down, false if that would not be possible.
+ * the current response seems to be the one and only, best possible, response.
+ */
+int iter_dp_cangodown(struct query_info* qinfo, struct delegpt* dp);
+
 #endif /* ITERATOR_ITER_UTILS_H */
index b32b738a19bc703e588a9f0c210b0b085c4142f6..c20f4b727c74cee0f52738a5e2811744cf0ac517 100644 (file)
@@ -1526,6 +1526,60 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,
        return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
 }
 
+/** 
+ * Try to find the NS record set that will resolve a qtype DS query. Due
+ * to grandparent/grandchild reasons we did not get a proper lookup right
+ * away.  We need to create type NS queries until we get the right parent
+ * for this lookup.  We add labels to the delegation point - whose name is
+ * storage for where we are (with empty other content).  Until we find an
+ * NS record, then we try the query again (which can result in doing this
+ * again).  Or we go too low, it is not possible to resolve, servfail.
+ * 
+ * @param qstate: query state.
+ * @param iq: iterator query state.
+ * @param id: module id.
+ * @return true if the event requires more immediate processing, false if
+ *         not. This is generally only true when forwarding the request to
+ *         the final state (i.e., on answer).
+ */
+static int
+processDSNSFind(struct module_qstate* qstate, struct iter_qstate* iq,
+       int id)
+{
+       int qlab = dname_count_labels(iq->qchase.qname);
+       struct module_qstate* subq = NULL;
+       verbose(VERB_ALGO, "processDSNSFind");
+       if(dname_subdomain_c(iq->dp->name, iq->qchase.qname) ||
+               qlab == iq->dp->namelabs+1)
+               /* we are too low - fail (robust check) */
+               return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
+       /* dp is used for storage, this is our state */
+       log_assert(!iq->dp->dp_type_mlc); /* if malloced this would leak */
+       iq->dp->nslist = NULL;
+       iq->dp->target_list = NULL;
+       iq->dp->usable_list = NULL;
+       iq->dp->result_list = NULL;
+       iq->state = DSNS_FIND_STATE;
+
+       /* add one label to the dp */
+       iq->dp->name = iq->qchase.qname;
+       iq->dp->namelen = iq->qchase.qname_len;
+       /* we have qlab labels we want namelabs+1 labels */
+       dname_remove_labels(&iq->dp->name, &iq->dp->namelen, 
+               qlab - (iq->dp->namelabs+1));
+       iq->dp->namelabs++;
+       /* spawn NS lookup (validation not needed, this is for DS lookup) */
+       log_nametypeclass(VERB_ALGO, "fetch nameservers", 
+               iq->dp->name, LDNS_RR_TYPE_NS, iq->qchase.qclass);
+       if(!generate_sub_request(iq->dp->name, iq->dp->namelen, 
+               LDNS_RR_TYPE_NS, iq->qchase.qclass, qstate, id, iq,
+               INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0)) {
+               return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
+       }
+
+       return 0;
+}
+       
 /** 
  * This is the request event state where the request will be sent to one of
  * its current query targets. This state also handles issuing target lookup
@@ -1847,6 +1901,12 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
                                (iq->response->rep->an_numrrsets?"ANSWER":
                                "nodata ANSWER"));
                }
+               /* if qtype is DS, check we have the right level of answer,
+                * like grandchild answer but we need the middle, reject it */
+               if(iq->qchase.qtype == LDNS_RR_TYPE_DS
+                       && iter_ds_toolow(iq->response)
+                       && iter_dp_cangodown(&iq->qchase, iq->dp))
+                       return processDSNSFind(qstate, iq, id);
                if(!iter_dns_store(qstate->env, &iq->response->qinfo,
                        iq->response->rep, 0, qstate->prefetch_leeway,
                        iq->dp&&iq->dp->has_parent_side_NS,
@@ -2246,6 +2306,41 @@ processTargetResponse(struct module_qstate* qstate, int id,
        }
 }
 
+/**
+ * Process response for DS NS Find queries, that attempt to find the delegation
+ * point where we ask the DS query from.
+ *
+ * @param qstate: query state.
+ * @param id: module id.
+ * @param forq: super query state.
+ */
+static void
+processDSNSResponse(struct module_qstate* qstate, int id,
+       struct module_qstate* forq)
+{
+       struct iter_qstate* foriq = (struct iter_qstate*)forq->minfo[id];
+
+       /* if the finished (iq->response) query has no NS set: continue
+        * down to look for the right dp; nothing to change, do DPNSstate */
+       if(qstate->return_rcode != LDNS_RCODE_NOERROR)
+               return; /* seek further */
+       /* find the NS RRset (without allowing CNAMEs) */
+       if(!reply_find_rrset(qstate->return_msg->rep, qstate->qinfo.qname,
+               qstate->qinfo.qname_len, LDNS_RR_TYPE_NS,
+               qstate->qinfo.qclass)){
+               return; /* seek further */
+       }
+
+       /* else, store as DP and continue at querytargets */
+       foriq->state = QUERYTARGETS_STATE;
+       foriq->dp = delegpt_from_message(qstate->return_msg, forq->region);
+       if(!foriq->dp) {
+               log_err("out of memory in dsns dp alloc");
+               return; /* dp==NULL in QUERYTARGETS makes SERVFAIL */
+       }
+       /* success, go query the querytargets in the new dp (and go down) */
+}
+       
 /**
  * Process response for qclass=ANY queries for a particular class.
  * Append to result or error-exit.
@@ -2481,6 +2576,9 @@ iter_inform_super(struct module_qstate* qstate, int id,
 {
        if(!qstate->is_priming && super->qinfo.qclass == LDNS_RR_CLASS_ANY)
                processClassResponse(qstate, id, super);
+       else if(super->qinfo.qtype == LDNS_RR_TYPE_DS && ((struct iter_qstate*)
+               super->minfo[id])->state == DSNS_FIND_STATE)
+               processDSNSResponse(qstate, id, super);
        else if(qstate->return_rcode != LDNS_RCODE_NOERROR)
                error_supers(qstate, id, super);
        else if(qstate->is_priming)
@@ -2530,6 +2628,9 @@ iter_handle(struct module_qstate* qstate, struct iter_qstate* iq,
                        case COLLECT_CLASS_STATE:
                                cont = processCollectClass(qstate, id);
                                break;
+                       case DSNS_FIND_STATE:
+                               cont = processDSNSFind(qstate, iq, id);
+                               break;
                        case FINISHED_STATE:
                                cont = processFinished(qstate, iq, id);
                                break;
@@ -2760,6 +2861,8 @@ iter_state_to_string(enum iter_state state)
                return "PRIME RESPONSE STATE";
        case COLLECT_CLASS_STATE :
                return "COLLECT CLASS STATE";
+       case DSNS_FIND_STATE :
+               return "DSNS FIND STATE";
        case QUERY_RESP_STATE :
                return "QUERY RESPONSE STATE";
        case FINISHED_STATE :
index a50a0c520537f834494edf1ed85b8a6b63bdc3fd..fff5a56101419aeb2aa57d295f401ee9d5608f9c 100644 (file)
@@ -155,6 +155,10 @@ enum iter_state {
         * it spawns off queries for every class, it returns here. */
        COLLECT_CLASS_STATE,
 
+       /** Find NS record to resolve DS record from, walking to the right
+        * NS spot until we find it */
+       DSNS_FIND_STATE,
+
        /** Responses that are to be returned upstream end at this state. 
         * As well as responses to target queries. */
        FINISHED_STATE
diff --git a/testdata/iter_ds_locate_ns.rpl b/testdata/iter_ds_locate_ns.rpl
new file mode 100644 (file)
index 0000000..d3b613e
--- /dev/null
@@ -0,0 +1,145 @@
+; config options
+server:
+       target-fetch-policy: "0 0 0 0 0"
+
+stub-zone:
+       name: "."
+       stub-addr: 193.0.14.129         # K.ROOT-SERVERS.NET.
+CONFIG_END
+
+SCENARIO_BEGIN Test locate of NS records for parent nameservers of DS
+
+; K.ROOT-SERVERS.NET.
+RANGE_BEGIN 0 100
+       ADDRESS 193.0.14.129 
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+. IN NS
+SECTION ANSWER
+. IN NS        K.ROOT-SERVERS.NET.
+SECTION ADDITIONAL
+K.ROOT-SERVERS.NET.    IN      A       193.0.14.129
+ENTRY_END
+
+; content of root-servers.net
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id 
+REPLY QR AA NOERROR
+SECTION QUESTION
+root-servers.net. IN NS
+SECTION ANSWER
+root-servers.net. IN NS        K.ROOT-SERVERS.NET.
+SECTION ADDITIONAL
+K.ROOT-SERVERS.NET.    IN      A       193.0.14.129
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id 
+REPLY QR AA NOERROR
+SECTION QUESTION
+k.root-servers.net. IN A
+SECTION ANSWER
+K.ROOT-SERVERS.NET.    IN      A       193.0.14.129
+SECTION AUTHORITY
+root-servers.net. IN NS        K.ROOT-SERVERS.NET.
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id 
+REPLY QR AA NOERROR
+SECTION QUESTION
+k.root-servers.net. IN AAAA
+SECTION ANSWER
+SECTION AUTHORITY
+root-servers.net. IN SOA       K.ROOT-SERVERS.NET. hostmaster. 1 2 3 4 5
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id 
+REPLY QR AA NOERROR
+SECTION QUESTION
+root-servers.net. IN DS
+SECTION ANSWER
+SECTION AUTHORITY
+root-servers.net. IN SOA       K.ROOT-SERVERS.NET. hostmaster. 1 2 3 4 5
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+net. IN A
+SECTION AUTHORITY
+net.   IN NS   a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net.    IN      A       192.5.6.30
+ENTRY_END
+RANGE_END
+
+; a.gtld-servers.net.
+RANGE_BEGIN 0 100
+       ADDRESS 192.5.6.30
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+net. IN NS
+SECTION ANSWER
+net.   IN NS   a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net.    IN      A       192.5.6.30
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+root-servers.net. IN DS
+SECTION AUTHORITY
+net. IN SOA a.gtld-servers.net. hostmaster. 2 3 4 5 6
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+root-servers.net. IN A
+SECTION AUTHORITY
+root-servers.net. IN NS        K.ROOT-SERVERS.NET.
+SECTION ADDITIONAL
+K.ROOT-SERVERS.NET.    IN      A       193.0.14.129
+ENTRY_END
+RANGE_END
+
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+root-servers.net. IN DS
+ENTRY_END
+
+; recursion happens here.
+STEP 10 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+root-servers.net. IN DS
+SECTION ANSWER
+SECTION AUTHORITY
+net. IN SOA a.gtld-servers.net. hostmaster. 2 3 4 5 6
+SECTION ADDITIONAL
+ENTRY_END
+
+SCENARIO_END