]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- new approach to NS fetches for DS lookup that works with
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Wed, 21 Mar 2012 15:01:01 +0000 (15:01 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Wed, 21 Mar 2012 15:01:01 +0000 (15:01 +0000)
         cornercases, and is more robust and considers forwarders.

git-svn-id: file:///svn/unbound/trunk@2646 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_cname.rpl [new file with mode: 0644]
testdata/iter_ds_locate_ns_nosoa.rpl [new file with mode: 0644]

index 7f7dc82746efaa74e8670975f1139c387e21d37c..bf0368ed708deb69c59468f520aee4c5cadc2532 100644 (file)
@@ -1,3 +1,7 @@
+21 March 2012: Wouter
+       - new approach to NS fetches for DS lookup that works with
+         cornercases, and is more robust and considers forwarders.
+
 19 March 2012: Wouter
        - iana portlist updated.
        - fix to locate nameservers for DS lookup with NS fetches.
index 86082551fb2593dcd0cbbbe923c91d3e789128b6..160274bddfa836f7215dd65a8d2e133b05a57dca 100644 (file)
@@ -970,20 +970,51 @@ void iter_merge_retry_counts(struct delegpt* dp, struct delegpt* old)
 }
 
 int
-iter_ds_toolow(struct dns_msg* msg)
+iter_ds_toolow(struct dns_msg* msg, struct delegpt* dp)
 {
        /* 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++) {
+       size_t i;
+       /* if we have a DNAME or CNAME we are probably wrong */
+       /* if we have a qtype DS in the answer section, its fine */
+       for(i=0; i < msg->rep->an_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 */
+               if(ntohs(s->rk.type) == LDNS_RR_TYPE_DNAME ||
+                       ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME) {
+                       /* not the right answer, maybe too low, check the
+                        * RRSIG signer name (if there is any) for a hint
+                        * that it is from the dp zone anyway */
+                       uint8_t* sname;
+                       size_t slen;
+                       val_find_rrset_signer(s, &sname, &slen);
+                       if(sname && query_dname_compare(dp->name, sname)==0)
+                               return 0; /* it is fine, from the right dp */
                        return 1;
                }
+               if(ntohs(s->rk.type) == LDNS_RR_TYPE_DS)
+                       return 0; /* fine, we have a DS record */
        }
-       return 0;
+       for(i=msg->rep->an_numrrsets;
+               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) {
+                       if(dname_subdomain_c(s->rk.dname, msg->qinfo.qname))
+                               return 1; /* point is too low */
+                       if(query_dname_compare(s->rk.dname, dp->name)==0)
+                               return 0; /* right dp */
+               }
+               if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC ||
+                       ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) {
+                       uint8_t* sname;
+                       size_t slen;
+                       val_find_rrset_signer(s, &sname, &slen);
+                       if(sname && query_dname_compare(dp->name, sname)==0)
+                               return 0; /* it is fine, from the right dp */
+                       return 1;
+               }
+       }
+       /* we do not know */
+       return 1;
 }
 
 int iter_dp_cangodown(struct query_info* qinfo, struct delegpt* dp)
index b83b35853285903de1cdfafb53e8b42c28d78f56..4fb8b005c1971201ab36a0e6b28be9aa2b4bf2e0 100644 (file)
@@ -314,10 +314,13 @@ 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.
+ * Also returns true if we are not sure (i.e. empty message, CNAME nosig).
  * @param msg: the response.
+ * @param dp: the dp name is used to check if the RRSIG gives a clue that
+ *     it was originated from the correct nameserver.
  * @return true if too low.
  */
-int iter_ds_toolow(struct dns_msg* msg);
+int iter_ds_toolow(struct dns_msg* msg, struct delegpt* dp);
 
 /**
  * See if delegpt can go down a step to the qname or not
index c20f4b727c74cee0f52738a5e2811744cf0ac517..af20c4261b7dbcb39ad335353239ef724bd998a6 100644 (file)
@@ -1530,10 +1530,8 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,
  * 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.
+ * for this lookup.  We remove labels from the query to find the right point.
+ * If we end up at the old dp name, then there is no solution.
  * 
  * @param qstate: query state.
  * @param iq: iterator query state.
@@ -1546,32 +1544,34 @@ 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) */
+
+       if(!iq->dsns_point) {
+               /* initialize */
+               iq->dsns_point = iq->qchase.qname;
+               iq->dsns_point_len = iq->qchase.qname_len;
+       }
+       /* robustcheck for internal error: we are not underneath the dp */
+       if(!dname_subdomain_c(iq->dsns_point, iq->dp->name)) {
                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;
+       }
+
+       /* go up one (more) step, until we hit the dp, if so, end */
+       dname_remove_label(&iq->dsns_point, &iq->dsns_point_len);
+       if(query_dname_compare(iq->dsns_point, iq->dp->name) == 0) {
+               /* there was no inbetween nameserver, use the old delegation
+                * point again.  And this time, because dsns_point is nonNULL
+                * we are going to accept the (bad) result */
+               iq->state = QUERYTARGETS_STATE;
+               return 1;
+       }
        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, 
+               iq->dsns_point, LDNS_RR_TYPE_NS, iq->qchase.qclass);
+       if(!generate_sub_request(iq->dsns_point, iq->dsns_point_len, 
                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);
@@ -1903,8 +1903,9 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
                }
                /* 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)
+               if(iq->qchase.qtype == LDNS_RR_TYPE_DS && !iq->dsns_point
+                       && !(iq->chase_flags&BIT_RD)
+                       && iter_ds_toolow(iq->response, iq->dp)
                        && iter_dp_cangodown(&iq->qchase, iq->dp))
                        return processDSNSFind(qstate, iq, id);
                if(!iter_dns_store(qstate->env, &iq->response->qinfo,
@@ -2026,6 +2027,13 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
                if(verbosity >= VERB_ALGO)
                        log_dns_msg("cname msg", &iq->response->qinfo, 
                                iq->response->rep);
+               /* 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 && !iq->dsns_point
+                       && !(iq->chase_flags&BIT_RD)
+                       && iter_ds_toolow(iq->response, iq->dp)
+                       && iter_dp_cangodown(&iq->qchase, iq->dp))
+                       return processDSNSFind(qstate, iq, id);
                /* Process the CNAME response. */
                if(!handle_cname_response(qstate, iq, iq->response, 
                        &sname, &snamelen))
@@ -2036,8 +2044,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
                /* prefetchleeway applied because this updates answer parts */
                if(!iter_dns_store(qstate->env, &iq->response->qinfo,
                        iq->response->rep, 1, qstate->prefetch_leeway,
-                       iq->dp&&iq->dp->has_parent_side_NS,
-                       NULL))
+                       iq->dp&&iq->dp->has_parent_side_NS, NULL))
                        return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
                /* set the current request's qname to the new value. */
                iq->qchase.qname = sname;
@@ -2045,6 +2052,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
                /* Clear the query state, since this is a query restart. */
                iq->deleg_msg = NULL;
                iq->dp = NULL;
+               iq->dsns_point = NULL;
                /* Note the query restart. */
                iq->query_restart_count++;
                iq->sent_count = 0;
@@ -2321,7 +2329,7 @@ processDSNSResponse(struct module_qstate* qstate, int id,
        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 */
+        * up 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) */
@@ -2340,7 +2348,7 @@ processDSNSResponse(struct module_qstate* qstate, int id,
        }
        /* 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.
index fff5a56101419aeb2aa57d295f401ee9d5608f9c..eb333157a4f770a5374bab67c86075ae555f53ab 100644 (file)
@@ -275,6 +275,11 @@ struct iter_qstate {
        /** the parent-side-glue element (NULL if none, its first match) */
        struct ub_packed_rrset_key* pside_glue;
 
+       /** If nonNULL we are walking upwards from DS query to find NS */
+       uint8_t* dsns_point;
+       /** length of the dname in dsns_point */
+       size_t dsns_point_len;
+
        /** 
         * expected dnssec information for this iteration step. 
         * If dnssec rrsigs are expected and not given, the server is marked
diff --git a/testdata/iter_ds_locate_ns_cname.rpl b/testdata/iter_ds_locate_ns_cname.rpl
new file mode 100644 (file)
index 0000000..d99d301
--- /dev/null
@@ -0,0 +1,155 @@
+; 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 DS with CNAME
+
+; 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
+root-servers.net. IN CNAME blabla.com.
+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
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id 
+REPLY QR AA NXDOMAIN
+SECTION QUESTION
+blabla.com. IN DS
+SECTION ANSWER
+SECTION AUTHORITY
+. IN SOA       K.ROOT-SERVERS.NET. hostmaster. 1 2 3 4 5
+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
diff --git a/testdata/iter_ds_locate_ns_nosoa.rpl b/testdata/iter_ds_locate_ns_nosoa.rpl
new file mode 100644 (file)
index 0000000..9c14cea
--- /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 DS without a SOA record
+
+; 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