]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
Fix problem with delegation where glue has expired.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Thu, 17 Sep 2009 14:36:06 +0000 (14:36 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Thu, 17 Sep 2009 14:36:06 +0000 (14:36 +0000)
git-svn-id: file:///svn/unbound/trunk@1837 be551aaa-1e26-0410-a405-d3ace91eadb9

iterator/iter_utils.c
iterator/iter_utils.h
iterator/iterator.c
testdata/iter_ns_badip.rpl [new file with mode: 0644]

index 765bc6135f6e29c0f38706a31dee1f368e588639..63b9a7d5335f000ea566e1e93524281b6bef52c4 100644 (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 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 */
index 0b3ec78560954e0cfb22553bc70b42d555d70141..a4bf06fbb126838808f4b1f3aba2295557ea6b09 100644 (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.
index 5f465dffe759c42483d111d242ed4926aa214f1d..5aac1c1f2faca648bf01ce2387aee05bced040be 100644 (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 
diff --git a/testdata/iter_ns_badip.rpl b/testdata/iter_ns_badip.rpl
new file mode 100644 (file)
index 0000000..49abdd6
--- /dev/null
@@ -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