From: Wouter Wijngaards Date: Mon, 19 Mar 2012 15:44:21 +0000 (+0000) Subject: - fix to locate nameservers for DS lookup with NS fetches. X-Git-Tag: release-1.4.17rc1~28 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0d5441bd8a809e8462a9b8e75f45352b92005dfb;p=thirdparty%2Funbound.git - fix to locate nameservers for DS lookup with NS fetches. git-svn-id: file:///svn/unbound/trunk@2645 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/doc/Changelog b/doc/Changelog index d6c66b31c..7f7dc8274 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -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 diff --git a/iterator/iter_utils.c b/iterator/iter_utils.c index ac0aa318f..86082551f 100644 --- a/iterator/iter_utils.c +++ b/iterator/iter_utils.c @@ -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; +} diff --git a/iterator/iter_utils.h b/iterator/iter_utils.h index f77240751..b83b35853 100644 --- a/iterator/iter_utils.h +++ b/iterator/iter_utils.h @@ -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 */ diff --git a/iterator/iterator.c b/iterator/iterator.c index b32b738a1..c20f4b727 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -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 : diff --git a/iterator/iterator.h b/iterator/iterator.h index a50a0c520..fff5a5610 100644 --- a/iterator/iterator.h +++ b/iterator/iterator.h @@ -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 index 000000000..d3b613e89 --- /dev/null +++ b/testdata/iter_ds_locate_ns.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 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