From: Wouter Wijngaards Date: Wed, 21 Mar 2012 15:01:01 +0000 (+0000) Subject: - new approach to NS fetches for DS lookup that works with X-Git-Tag: release-1.4.17rc1~27 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=fa3337d42ac4258a243ec9bb9485b0685cdb3747;p=thirdparty%2Funbound.git - new approach to NS fetches for DS lookup that works with cornercases, and is more robust and considers forwarders. git-svn-id: file:///svn/unbound/trunk@2646 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/doc/Changelog b/doc/Changelog index 7f7dc8274..bf0368ed7 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -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. diff --git a/iterator/iter_utils.c b/iterator/iter_utils.c index 86082551f..160274bdd 100644 --- a/iterator/iter_utils.c +++ b/iterator/iter_utils.c @@ -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) diff --git a/iterator/iter_utils.h b/iterator/iter_utils.h index b83b35853..4fb8b005c 100644 --- a/iterator/iter_utils.h +++ b/iterator/iter_utils.h @@ -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 diff --git a/iterator/iterator.c b/iterator/iterator.c index c20f4b727..af20c4261 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -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. diff --git a/iterator/iterator.h b/iterator/iterator.h index fff5a5610..eb333157a 100644 --- a/iterator/iterator.h +++ b/iterator/iterator.h @@ -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 index 000000000..d99d301ce --- /dev/null +++ b/testdata/iter_ds_locate_ns_cname.rpl @@ -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 index 000000000..9c14cea44 --- /dev/null +++ b/testdata/iter_ds_locate_ns_nosoa.rpl @@ -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