]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
fixup glue timeout problems, and priming with safety belt.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Wed, 19 Sep 2007 12:17:42 +0000 (12:17 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Wed, 19 Sep 2007 12:17:42 +0000 (12:17 +0000)
git-svn-id: file:///svn/unbound/trunk@622 be551aaa-1e26-0410-a405-d3ace91eadb9

doc/Changelog
iterator/iter_utils.c
iterator/iter_utils.h
iterator/iterator.c
testcode/fake_event.c
testdata/iter_emptydp.rpl [new file with mode: 0644]
testdata/iter_primenoglue.rpl [new file with mode: 0644]

index 9ffc9c40d8f735f31a381795ca148110b2e376e5..bdd30189ece899c5bc5341856d6d79b0e6b39a29 100644 (file)
@@ -1,6 +1,10 @@
 19 September 2007: Wouter
        - comments about non-packed usage.
        - plan for overload support in 0.6.
+       - added testbound tests for a failed resolution from the logs
+         and for failed prime when missing glue.
+       - fixup so useless delegation points are not returned from the
+         cache. Also the safety belt is used if priming fails to complete.
 
 18 September 2007: Wouter
        - wildcard nsec3 testcases, and fixup to get correct wildcard name.
index ac1d7c42eb336513ac55b92879c48bcc502ce4c4..ca64e7dddc35800cc1c419762b0acc68c9b5629c 100644 (file)
@@ -55,6 +55,7 @@
 #include "util/config_file.h"
 #include "util/region-allocator.h"
 #include "util/data/msgparse.h"
+#include "util/data/dname.h"
 #include "util/random.h"
 
 /** fillup fetch policy array */
@@ -320,3 +321,27 @@ iter_mark_cycle_targets(struct module_qstate* qstate, struct delegpt* dp)
                }
        }
 }
+
+int 
+iter_dp_is_useless(uint16_t flags, struct delegpt* dp)
+{
+       struct delegpt_ns* ns;
+       /* check:
+        *      o all NS items are required glue.
+        *      o no addresses are provided.
+        *      o RD qflag is on.
+        */
+       if(!(flags&BIT_RD))
+               return 0;
+       /* either available or unused targets */
+       if(dp->usable_list || dp->result_list) 
+               return 0;
+       
+       for(ns = dp->nslist; ns; ns = ns->next) {
+               if(ns->resolved) /* skip failed targets */
+                       continue;
+               if(!dname_subdomain_c(ns->name, dp->name))
+                       return 0; /* one address is not required glue */
+       }
+       return 1;
+}
index 7e3a5ccabd086cfbfca6697d234e2d3381d6628c..f18adf61467176ad808c5ca6e9c603516113886c 100644 (file)
@@ -128,4 +128,12 @@ 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 delegation is useful or offers immediately no targets for 
+ * further recursion.
+ * @param flags: query flags.
+ * @param dp: delegpt to check.
+ */
+int iter_dp_is_useless(uint16_t flags, struct delegpt* dp);
+
 #endif /* ITERATOR_ITER_UTILS_H */
index 091b4165ce4038658f4f909c7a8ee87269d129c2..462edcc638bcd44e0a2cec844cb6770d6d2773df 100644 (file)
@@ -484,6 +484,12 @@ prime_root(struct module_qstate* qstate, struct iter_qstate* iq,
                verbose(VERB_ALGO, "Cannot prime due to lack of hints");
                return 0;
        }
+       /* copy dp; to avoid messing up available list for other thr/queries */
+       dp = delegpt_copy(dp, qstate->region);
+       if(!dp) {
+               log_err("out of memory priming root, copydp");
+               return 0;
+       }
        /* Priming requests start at the QUERYTARGETS state, skipping 
         * the normal INIT state logic (which would cause an infloop). */
        if(!generate_sub_request((uint8_t*)"\000", 1, LDNS_RR_TYPE_NS, 
@@ -550,7 +556,14 @@ prime_stub(struct module_qstate* qstate, struct iter_qstate* iq,
                        (struct iter_qstate*)subq->minfo[id];
 
                /* Set the initial delegation point to the hint. */
-               subiq->dp = stub_dp;
+               /* make copy to avoid use of stub dp by different qs/threads */
+               subiq->dp = delegpt_copy(stub_dp, subq->region);
+               if(!subiq->dp) {
+                       log_err("out of memory priming stub, copydp");
+                       (*qstate->env->kill_sub)(subq);
+                       (void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+                       return 1; /* return 1 to make module stop, with error */
+               }
                /* there should not be any target queries -- although there 
                 * wouldn't be anyway, since stub hints never have 
                 * missing targets. */
@@ -704,29 +717,75 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
                /* do not adjust root label, remove first label from delname */
                dname_remove_label(&delname, &delnamelen);
        }
-       
-       /* 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, delnamelen,
-               iq->qchase.qtype, iq->qchase.qclass, qstate->region, 
-               &iq->deleg_msg, (uint32_t)time(NULL));
-
-       /* If the cache has returned nothing, then we have a root priming
-        * situation. */
-       if(iq->dp == NULL) {
-               /* Note that the result of this will set a new
-                * DelegationPoint based on the result of priming. */
-               if(!prime_root(qstate, iq, ie, id, iq->qchase.qclass))
-                       return error_response(qstate, id, LDNS_RCODE_REFUSED);
-
-               /* priming creates and sends a subordinate query, with 
-                * this query as the parent. So further processing for 
-                * this event will stop until reactivated by the results 
-                * of priming. */
-               return 0;
+       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, 
+                       delnamelen, iq->qchase.qtype, iq->qchase.qclass, 
+                       qstate->region, &iq->deleg_msg, (uint32_t)time(NULL));
+
+               /* If the cache has returned nothing, then we have a 
+                * root priming situation. */
+               if(iq->dp == NULL) {
+                       /* Note that the result of this will set a new
+                        * DelegationPoint based on the result of priming. */
+                       if(!prime_root(qstate, iq, ie, id, iq->qchase.qclass))
+                               return error_response(qstate, id, 
+                                       LDNS_RCODE_REFUSED);
+
+                       /* priming creates and sends a subordinate query, with 
+                        * this query as the parent. So further processing for 
+                        * this event will stop until reactivated by the 
+                        * results of priming. */
+                       return 0;
+               }
+
+               /* see if this dp not useless.
+                * It is useless if:
+                *      o all NS items are required glue.
+                *      o no addresses are provided.
+                *      o RD qflag is on.
+                * Instead, go up one level, and try to get even further
+                * If the root was useless, use safety belt information. 
+                * Only check cache returns, because replies for servers
+                * could be useless but lead to loops (bumping into the
+                * same server reply) if useless-checked.
+                */
+               if(iter_dp_is_useless(qstate->query_flags, iq->dp)) {
+                       if(dname_is_root(iq->dp->name)) {
+                               /* use safety belt */
+                               verbose(VERB_OPS, "Priming problem: NS but "
+                               "no addresses. Fallback to the safety belt.");
+                               iq->dp = hints_lookup_root(ie->hints, 
+                                       iq->qchase.qclass);
+                               /* note deleg_msg is from previous lookup,
+                                * but RD is on, so it is not used */
+                               if(!iq->dp) {
+                                       log_err("internal error: no hints dp");
+                                       return error_response(qstate, id, 
+                                               LDNS_RCODE_REFUSED);
+                               }
+                               iq->dp = delegpt_copy(iq->dp, qstate->region);
+                               if(!iq->dp) {
+                                       log_err("out of memory in safety belt");
+                                       return error_response(qstate, id, 
+                                               LDNS_RCODE_SERVFAIL);
+                               }
+                               break;
+                       } else {
+                               log_info("cache delegation was useless:");
+                               delegpt_log(iq->dp);
+                               /* go up */
+                               delname = iq->dp->name;
+                               delnamelen = iq->dp->namelen;
+                               dname_remove_label(&delname, &delnamelen);
+                       }
+               } else break;
        }
+
        if(verbosity >= VERB_ALGO) {
-               log_info("dns_cache_find_delegation returns delegpt");
+               log_info("cache delegation returns delegpt");
                delegpt_log(iq->dp);
        }
 
index 2a7913e40f83ccb74b33d8cdd97a533f1702a343..b1f86d285697855f49ac455df394d20ff4d7d901 100644 (file)
@@ -815,6 +815,11 @@ struct serviced_query* outnet_serviced_query(struct outside_network* outnet,
        ldns_status status;
        (void)arg_compare;
        log_assert(pend);
+       log_nametypeclass(VERB_OPS, "pending serviced query", 
+               qname, qtype, qclass);
+       verbose(VERB_OPS, "pending serviced query flags%s%s%s%s", 
+               (flags&BIT_RD)?" RD":"", (flags&BIT_CD)?" CD":"",
+               (flags&~(BIT_RD|BIT_CD))?" MORE":"", (dnssec)?" DO":"");
 
        /* create packet with EDNS */
        pend->buffer = ldns_buffer_new(512);
@@ -856,7 +861,7 @@ struct serviced_query* outnet_serviced_query(struct outside_network* outnet,
                        ldns_get_errorstr_by_id(status));
                fatal_exit("internal error");
        }
-       log_pkt("pending serviced query: ", pend->pkt);
+       /*log_pkt("pending serviced query: ", pend->pkt);*/
 
        /* see if it matches the current moment */
        if(runtime->now && runtime->now->evt_type == repevt_back_query &&
diff --git a/testdata/iter_emptydp.rpl b/testdata/iter_emptydp.rpl
new file mode 100644 (file)
index 0000000..40c9e9c
--- /dev/null
@@ -0,0 +1,190 @@
+; config options
+; The island of trust is at example.com
+server:
+       trust-anchor: "example.com.    3600    IN      DS      2854 3 1 46e4ffc6e9a4793b488954bd3f0cc6af0dfb201b"
+       val-override-date: "20070916134226"
+       target-fetch-policy: "3 2 1 0 0" # make sure it fetches for test
+
+stub-zone:
+       name: "."
+       stub-addr: 193.0.14.129         # K.ROOT-SERVERS.NET.
+CONFIG_END
+
+SCENARIO_BEGIN Test iterator with empty delegation from cache.
+
+; 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 qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION AUTHORITY
+com.   IN NS   a.gtld-servers.net.
+
+; sneak in some data into the cache to simulate partial data after timeouts
+example.net.   NS      ns.example.net.
+
+SECTION ADDITIONAL
+a.gtld-servers.net.    IN      A       192.5.6.30
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qname
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+ns.example.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
+; com zone
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION AUTHORITY
+example.com.   IN NS   ns.example.net.
+SECTION ADDITIONAL
+ENTRY_END
+
+; net zone
+ENTRY_BEGIN
+MATCH opcode qname
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+ns.example.net. IN A
+SECTION AUTHORITY
+example.net. NS ns.example.net.
+SECTION ADDITIONAL
+ns.example.net. IN A 1.2.3.4
+ENTRY_END
+RANGE_END
+
+; ns.example.net.
+RANGE_BEGIN 0 100
+       ADDRESS 1.2.3.4
+; example.net. zone
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+ns.example.net. IN A
+SECTION ANSWER
+ns.example.net. IN A 1.2.3.4
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+ns.example.net. IN AAAA
+SECTION ANSWER
+ENTRY_END
+
+; example.com. zone
+; response to DNSKEY priming query
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN DNSKEY
+SECTION ANSWER
+example.com.    3600    IN      DNSKEY  256 3 3 ALXLUsWqUrY3JYER3T4TBJII s70j+sDS/UT2QRp61SE7S3E EXopNXoFE73JLRmvpi/UrOO/Vz4Se 6wXv/CYCKjGw06U4WRgR YXcpEhJROyNapmdIKSx hOzfLVE1gqA0PweZR8d tY3aNQSRn3sPpwJr6Mi /PqQKAMMrZ9ckJpf1+b QMOOvxgzz2U1GS18b3y ZKcgTMEaJzd/GZYzi/B N2DzQ0MsrSwYXfsNLFO Bbs8PJMW4LYIxeeOe6rUgkWOF 7CC9Dh/dduQ1QrsJhmZAEFfd6ByYV+ ;{id = 2854 (zsk), size = 1688b}
+example.com.    3600    IN      RRSIG   DNSKEY 3 2 3600 20070926134802 20070829134802 2854 example.com. MCwCFG1yhRNtTEa3Eno2zhVVuy2EJX3wAhQeLyUp6+UXcpC5qGNu9tkrTEgPUg== ;{id = 2854}
+SECTION AUTHORITY
+example.com.   IN NS   ns.example.com.
+example.com.    3600    IN      RRSIG   NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+SECTION ADDITIONAL
+ns.example.com.                IN      A       1.2.3.4
+ns.example.com. 3600    IN      RRSIG   A 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCMSWxVehgOQLoYclB9PIAbNP229AIUeH0vNNGJhjnZiqgIOKvs1EhzqAo= ;{id = 2854}
+ENTRY_END
+
+; response to query of interest
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A  10.20.30.40
+ns.example.com. 3600    IN      RRSIG   A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCQMyTjn7WWwpwAR1LlVeLpRgZGuQIUCcJDEkwAuzytTDRlYK7nIMwH1CM= ;{id = 2854}
+SECTION AUTHORITY
+example.com.   IN NS   ns.example.com.
+example.com.    3600    IN      RRSIG   NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+SECTION ADDITIONAL
+ns.example.com.                IN      A       1.2.3.4
+www.example.com.        3600    IN      RRSIG   A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFC99iE9K5y2WNgI0gFvBWaTi9wm6AhUAoUqOpDtG5Zct+Qr9F3mSdnbc6V4= ;{id = 2854}
+ENTRY_END
+RANGE_END
+
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; recursion happens here.
+STEP 10 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA AD NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A  10.20.30.40
+SECTION AUTHORITY
+example.com.   IN NS   ns.example.com.
+SECTION ADDITIONAL
+ns.example.com.                IN      A       1.2.3.4
+ENTRY_END
+
+; make sure glue fetch is done.
+STEP 11 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+ns.example.net. IN AAAA
+ENTRY_END
+
+STEP 12 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+ns.example.net. IN AAAA
+SECTION ANSWER
+SECTION AUTHORITY
+SECTION ADDITIONAL
+ENTRY_END
+
+SCENARIO_END
diff --git a/testdata/iter_primenoglue.rpl b/testdata/iter_primenoglue.rpl
new file mode 100644 (file)
index 0000000..74227f2
--- /dev/null
@@ -0,0 +1,257 @@
+; config options
+; The island of trust is at example.com
+server:
+       trust-anchor: "example.com.    3600    IN      DS      2854 3 1 46e4ffc6e9a4793b488954bd3f0cc6af0dfb201b"
+       val-override-date: "20070916134226"
+       target-fetch-policy: "3 2 1 0 0" # make sure it fetches for test
+
+stub-zone:
+       name: "."
+       stub-addr: 193.0.14.129         # K.ROOT-SERVERS.NET.
+CONFIG_END
+
+SCENARIO_BEGIN Test iterator with root prime answer without glue.
+
+; 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
+; glue ommitted!
+;K.ROOT-SERVERS.NET.   IN      A       193.0.14.129
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qname
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+K.ROOT-SERVERS.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 NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION AUTHORITY
+com.   IN NS   a.gtld-servers.net.
+
+; sneak in some data into the cache to simulate partial data after timeouts
+example.net.   NS      ns.example.net.
+
+SECTION ADDITIONAL
+a.gtld-servers.net.    IN      A       192.5.6.30
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qname
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+ns.example.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
+; com zone
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION AUTHORITY
+example.com.   IN NS   ns.example.net.
+SECTION ADDITIONAL
+ENTRY_END
+
+; net zone
+ENTRY_BEGIN
+MATCH opcode qname
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+K.ROOT-SERVERS.NET.    IN      A
+SECTION AUTHORITY
+ROOT-SERVERS.NET.      IN      NS A.ROOT-SERVERS.NET.
+SECTION ADDITIONAL
+A.ROOT-SERVERS.NET.    IN      A       198.41.0.4
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qname
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+ns.example.net. IN A
+SECTION AUTHORITY
+example.net. NS ns.example.net.
+SECTION ADDITIONAL
+ns.example.net. IN A 1.2.3.4
+ENTRY_END
+RANGE_END
+
+; A.ROOT-SERVERS.NET.
+RANGE_BEGIN 0 100
+       ADDRESS 198.41.0.4
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+K.ROOT-SERVERS.NET.    IN      A
+SECTION ANSWER
+K.ROOT-SERVERS.NET.    IN      A       193.0.14.129
+ENTRY_END
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+K.ROOT-SERVERS.NET.    IN      AAAA
+SECTION ANSWER
+; no ip6 address: we want to use only one address for K. to avoid having
+; to duplicate the entries in this file for both addresses.
+ENTRY_END
+RANGE_END
+
+; ns.example.net.
+RANGE_BEGIN 0 100
+       ADDRESS 1.2.3.4
+; example.net. zone
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+ns.example.net. IN A
+SECTION ANSWER
+ns.example.net. IN A 1.2.3.4
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+ns.example.net. IN AAAA
+SECTION ANSWER
+ENTRY_END
+
+; example.com. zone
+; response to DNSKEY priming query
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN DNSKEY
+SECTION ANSWER
+example.com.    3600    IN      DNSKEY  256 3 3 ALXLUsWqUrY3JYER3T4TBJII s70j+sDS/UT2QRp61SE7S3E EXopNXoFE73JLRmvpi/UrOO/Vz4Se 6wXv/CYCKjGw06U4WRgR YXcpEhJROyNapmdIKSx hOzfLVE1gqA0PweZR8d tY3aNQSRn3sPpwJr6Mi /PqQKAMMrZ9ckJpf1+b QMOOvxgzz2U1GS18b3y ZKcgTMEaJzd/GZYzi/B N2DzQ0MsrSwYXfsNLFO Bbs8PJMW4LYIxeeOe6rUgkWOF 7CC9Dh/dduQ1QrsJhmZAEFfd6ByYV+ ;{id = 2854 (zsk), size = 1688b}
+example.com.    3600    IN      RRSIG   DNSKEY 3 2 3600 20070926134802 20070829134802 2854 example.com. MCwCFG1yhRNtTEa3Eno2zhVVuy2EJX3wAhQeLyUp6+UXcpC5qGNu9tkrTEgPUg== ;{id = 2854}
+SECTION AUTHORITY
+example.com.   IN NS   ns.example.com.
+example.com.    3600    IN      RRSIG   NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+SECTION ADDITIONAL
+ns.example.com.                IN      A       1.2.3.4
+ns.example.com. 3600    IN      RRSIG   A 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCMSWxVehgOQLoYclB9PIAbNP229AIUeH0vNNGJhjnZiqgIOKvs1EhzqAo= ;{id = 2854}
+ENTRY_END
+
+; response to query of interest
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A  10.20.30.40
+ns.example.com. 3600    IN      RRSIG   A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCQMyTjn7WWwpwAR1LlVeLpRgZGuQIUCcJDEkwAuzytTDRlYK7nIMwH1CM= ;{id = 2854}
+SECTION AUTHORITY
+example.com.   IN NS   ns.example.com.
+example.com.    3600    IN      RRSIG   NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+SECTION ADDITIONAL
+ns.example.com.                IN      A       1.2.3.4
+www.example.com.        3600    IN      RRSIG   A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFC99iE9K5y2WNgI0gFvBWaTi9wm6AhUAoUqOpDtG5Zct+Qr9F3mSdnbc6V4= ;{id = 2854}
+ENTRY_END
+RANGE_END
+
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; recursion happens here.
+STEP 10 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA AD NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A  10.20.30.40
+SECTION AUTHORITY
+example.com.   IN NS   ns.example.com.
+SECTION ADDITIONAL
+ns.example.com.                IN      A       1.2.3.4
+ENTRY_END
+
+; make sure glue fetch is done.
+STEP 11 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+ns.example.net. IN AAAA
+ENTRY_END
+
+STEP 12 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+ns.example.net. IN AAAA
+SECTION ANSWER
+SECTION AUTHORITY
+SECTION ADDITIONAL
+ENTRY_END
+
+STEP 13 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+K.ROOT-SERVERS.NET.  IN      AAAA
+ENTRY_END
+
+STEP 14 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+K.ROOT-SERVERS.NET.  IN      AAAA
+SECTION ANSWER
+SECTION AUTHORITY
+SECTION ADDITIONAL
+ENTRY_END
+
+SCENARIO_END