Fix problem with delegation where glue has expired.

git-svn-id: file:///svn/unbound/trunk@1837 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
Wouter Wijngaards 2009-09-17 14:36:06 +00:00
parent 75fbd42ca3
commit c4cfcdab6b
4 changed files with 324 additions and 9 deletions

View file

@ -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 RD qflag is off.
* o no addresses are provided.
* o all NS items are required glue.
* o no addresses are provided.
* o RD qflag is on.
* 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 */

View file

@ -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.

View file

@ -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

237
testdata/iter_ns_badip.rpl vendored Normal file
View file

@ -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