From c4cfcdab6bfb3ab409b809641ee7157c5c8ccab2 Mon Sep 17 00:00:00 2001 From: Wouter Wijngaards Date: Thu, 17 Sep 2009 14:36:06 +0000 Subject: [PATCH] Fix problem with delegation where glue has expired. git-svn-id: file:///svn/unbound/trunk@1837 be551aaa-1e26-0410-a405-d3ace91eadb9 --- iterator/iter_utils.c | 59 ++++++++- iterator/iter_utils.h | 13 ++ iterator/iterator.c | 24 +++- testdata/iter_ns_badip.rpl | 237 +++++++++++++++++++++++++++++++++++++ 4 files changed, 324 insertions(+), 9 deletions(-) create mode 100644 testdata/iter_ns_badip.rpl diff --git a/iterator/iter_utils.c b/iterator/iter_utils.c index 765bc6135..63b9a7d53 100644 --- a/iterator/iter_utils.c +++ b/iterator/iter_utils.c @@ -61,6 +61,9 @@ #include "util/fptr_wlist.h" #include "validator/val_anchor.h" +/** time when nameserver glue is said to be 'recent' */ +#define SUSPICION_RECENT_EXPIRY 86400 + /** fillup fetch policy array */ static void fetch_fill(struct iter_env* ie, const char* str) @@ -402,6 +405,54 @@ iter_ns_probability(struct ub_randstate* rnd, int n, int m) return (sel < n); } +int iter_suspect_exists(struct query_info* qinfo, struct delegpt* dp, + struct module_env* env) +{ + struct ub_packed_rrset_key* r; + if(qinfo->qtype != LDNS_RR_TYPE_A && qinfo->qtype != LDNS_RR_TYPE_AAAA) + return 0; /* not glue type */ + if(!dname_subdomain_c(qinfo->qname, dp->name)) + return 0; /* not in-zone */ + if(!delegpt_find_ns(dp, qinfo->qname, qinfo->qname_len)) + return 0; /* not glue */ + + /* do we suspect that it exists? lookup with time=0 */ + r = rrset_cache_lookup(env->rrset_cache, qinfo->qname, + qinfo->qname_len, qinfo->qtype, qinfo->qclass, 0, 0, 0); + if(r) { + struct packed_rrset_data* d = (struct packed_rrset_data*) + r->entry.data; + /* if it is valid, no need for queries to parent zone */ + if(*env->now <= d->ttl) { + lock_rw_unlock(&r->entry.lock); + return 0; + } + /* was it recently expired? */ + if( (*env->now - d->ttl) <= SUSPICION_RECENT_EXPIRY) { + verbose(VERB_ALGO, "suspect glue at parent: " + "rrset recently expired"); + lock_rw_unlock(&r->entry.lock); + return 1; + } + lock_rw_unlock(&r->entry.lock); + } + + /* so, qinfo not there, does the other A/AAAA type exist in cache? */ + r=rrset_cache_lookup(env->rrset_cache, qinfo->qname, qinfo->qname_len, + (qinfo->qtype==LDNS_RR_TYPE_A)?LDNS_RR_TYPE_AAAA:LDNS_RR_TYPE_A, + qinfo->qclass, 0, *env->now, 0); + if(r) { + /* it exists and explains why the glue is there */ + lock_rw_unlock(&r->entry.lock); + return 0; + } + + /* neither exist, so logically, one should exist for a nameserver */ + verbose(VERB_ALGO, "suspect glue at parent: " + "neither A nor AAAA exist in cache"); + return 1; +} + /** detect dependency cycle for query and target */ static int causes_cycle(struct module_qstate* qstate, uint8_t* name, size_t namelen, @@ -446,19 +497,19 @@ iter_dp_is_useless(struct query_info* qinfo, uint16_t qflags, { struct delegpt_ns* ns; /* check: - * o all NS items are required glue. + * o RD qflag is off. * o no addresses are provided. - * o RD qflag is on. + * o all NS items are required glue. * OR + * o RD qflag is off. * o no addresses are provided. - * o RD qflag is on. * o the query is for one of the nameservers in dp, * and that nameserver is a glue-name for this dp. */ if(!(qflags&BIT_RD)) return 0; /* either available or unused targets */ - if(dp->usable_list || dp->result_list) + if(dp->usable_list || dp->result_list) return 0; /* see if query is for one of the nameservers, which is glue */ diff --git a/iterator/iter_utils.h b/iterator/iter_utils.h index 0b3ec7856..a4bf06fbb 100644 --- a/iterator/iter_utils.h +++ b/iterator/iter_utils.h @@ -137,6 +137,19 @@ int iter_ns_probability(struct ub_randstate* rnd, int n, int m); */ void iter_mark_cycle_targets(struct module_qstate* qstate, struct delegpt* dp); +/** + * See if query is in-zone glue and we suspect that it exists. + * Suspicion that it exists, is if there is no A or AAAA in cache (since + * one of them is expected for an NS record) or the qtype is in cache but + * was recently expired (so we have seen this data recently). + * @param qinfo: query info. + * @param dp: delegation point we are at. + * @param env: environment with rrset cache. + * @return true if suspect that this glue exists. + */ +int iter_suspect_exists(struct query_info* qinfo, struct delegpt* dp, + struct module_env* env); + /** * See if delegation is useful or offers immediately no targets for * further recursion. diff --git a/iterator/iterator.c b/iterator/iterator.c index 5f465dffe..5aac1c1f2 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -934,19 +934,22 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, delname = iq->qchase.qname; delnamelen = iq->qchase.qname_len; } - if((iq->qchase.qtype == LDNS_RR_TYPE_DS || iq->refetch_glue) - && !dname_is_root(delname)) { - /* do not adjust root label, remove first label from delname */ - dname_remove_label(&delname, &delnamelen); + if(iq->qchase.qtype == LDNS_RR_TYPE_DS || iq->refetch_glue) { + /* remove first label from delname, root goes to hints */ + if(dname_is_root(delname)) + delname = NULL; /* go to root priming */ + else dname_remove_label(&delname, &delnamelen); iq->refetch_glue = 0; /* if CNAME causes restart, no refetch */ } while(1) { /* Lookup the delegation in the cache. If null, then the * cache needs to be primed for the qclass. */ - iq->dp = dns_cache_find_delegation(qstate->env, delname, + if(delname) + iq->dp = dns_cache_find_delegation(qstate->env, delname, delnamelen, iq->qchase.qtype, iq->qchase.qclass, qstate->region, &iq->deleg_msg, *qstate->env->now); + else iq->dp = NULL; /* If the cache has returned nothing, then we have a * root priming situation. */ @@ -1376,6 +1379,17 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, /* Since a target query might have been made, we * need to check again. */ if(iq->num_target_queries == 0) { + /* is it glue and we suspect that it exists?*/ + if(iter_suspect_exists(&iq->qchase, iq->dp, + qstate->env)) { + /* try at parent */ + iq->deleg_msg = NULL; + iq->refetch_glue = 1; + iq->query_restart_count++; + return next_state(iq, + INIT_REQUEST_STATE); + } + verbose(VERB_QUERY, "out of query targets -- " "returning SERVFAIL"); /* fail -- no more targets, no more hope diff --git a/testdata/iter_ns_badip.rpl b/testdata/iter_ns_badip.rpl new file mode 100644 index 000000000..49abdd688 --- /dev/null +++ b/testdata/iter_ns_badip.rpl @@ -0,0 +1,237 @@ +; config options +server: + target-fetch-policy: "3 2 1 0 0" + +stub-zone: + name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test iterator with delagation with bad IP address + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR 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 + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +com. IN A +SECTION AUTHORITY +com. 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 copy_query +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN A +SECTION ANSWER +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN AAAA +SECTION AUTHORITY +. SOA bla bla 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 subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +example.com. IN A +SECTION AUTHORITY +example.com. IN NS ns.example.com. +example.com. IN NS ns2.example.com. +SECTION ADDITIONAL +ns.example.com. 10 IN A 1.2.3.4 +ns2.example.com. 3600 IN A 1.2.3.5 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +foo.com. IN A +SECTION AUTHORITY +foo.com. IN NS ns.example.com. +foo.com. IN NS ns2.example.com. +ENTRY_END +RANGE_END + +; ns.example.com. +RANGE_BEGIN 0 100 + ADDRESS 1.2.3.4 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION ANSWER +www.example.com. 10 IN A 10.20.30.40 +SECTION AUTHORITY +example.com. 3600 IN NS ns.example.com. +example.com. 3600 IN NS ns2.example.com. +SECTION ADDITIONAL +ns.example.com. 10 IN A 1.2.3.4 +ns2.example.com. 3600 IN A 1.2.3.5 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +ns.example.com. IN A +SECTION ANSWER +ns.example.com. 10 IN A 1.2.3.4 +SECTION AUTHORITY +example.com. 3600 IN NS ns.example.com. +example.com. 3600 IN NS ns2.example.com. +SECTION ADDITIONAL +ns2.example.com. 3600 IN A 1.2.3.5 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +ns2.example.com. IN A +SECTION ANSWER +ns2.example.com. 3600 IN A 1.2.3.5 +SECTION AUTHORITY +example.com. 3600 IN NS ns.example.com. +example.com. 3600 IN NS ns2.example.com. +SECTION ADDITIONAL +ns.example.com. 10 IN A 1.2.3.4 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +ns.example.com. IN AAAA +SECTION ANSWER +SECTION AUTHORITY +example.com. 3600 IN SOA bla. bla. 1 2 3 4 5 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +ns2.example.com. IN AAAA +SECTION ANSWER +SECTION AUTHORITY +example.com. 3600 IN SOA bla. bla. 1 2 3 4 5 +ENTRY_END + +; foo.com contents. +ENTRY_BEGIN +MATCH opcode qtype qname +REPLY QR NOERROR +SECTION QUESTION +www.foo.com. IN A +SECTION ANSWER +www.foo.com. 10 IN A 10.20.30.40 +SECTION AUTHORITY +foo.com. 3600 IN NS ns.example.com. +foo.com. 3600 IN NS ns2.example.com. +ENTRY_END + +RANGE_END + +; ns2.example.com. (lame) +RANGE_BEGIN 0 100 + ADDRESS 1.2.3.5 +ENTRY_BEGIN +MATCH opcode +ADJUST copy_id copy_query +REPLY QR SERVFAIL +SECTION QUESTION +www.example.com. IN A +ENTRY_END +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +www.foo.com. IN A +ENTRY_END + +; recursion happens here. +STEP 10 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +www.foo.com. IN A +SECTION ANSWER +www.foo.com. 10 IN A 10.20.30.40 +SECTION AUTHORITY +foo.com. 3600 IN NS ns.example.com. +foo.com. 3600 IN NS ns2.example.com. +ENTRY_END + +STEP 15 TRAFFIC + +; Now move the time so good server times out and bad remains. +STEP 20 TIME_PASSES ELAPSE 20 + +; Try query again. +STEP 30 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +www.foo.com. IN A +ENTRY_END + +STEP 35 TRAFFIC + +; recursion happens here. +STEP 40 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +www.foo.com. IN A +SECTION ANSWER +www.foo.com. 10 IN A 10.20.30.40 +SECTION AUTHORITY +foo.com. 3600 IN NS ns.example.com. +foo.com. 3600 IN NS ns2.example.com. +ENTRY_END + +SCENARIO_END -- 2.47.3