#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)
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,
{
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 */
*/
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.
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. */
/* 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
--- /dev/null
+; 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