return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
+/**
+ * Try to find the NS record set that will resolve a qtype DS query. Due
+ * to grandparent/grandchild reasons we did not get a proper lookup right
+ * away. We need to create type NS queries until we get the right parent
+ * for this lookup. We add labels to the delegation point - whose name is
+ * storage for where we are (with empty other content). Until we find an
+ * NS record, then we try the query again (which can result in doing this
+ * again). Or we go too low, it is not possible to resolve, servfail.
+ *
+ * @param qstate: query state.
+ * @param iq: iterator query state.
+ * @param id: module id.
+ * @return true if the event requires more immediate processing, false if
+ * not. This is generally only true when forwarding the request to
+ * the final state (i.e., on answer).
+ */
+static int
+processDSNSFind(struct module_qstate* qstate, struct iter_qstate* iq,
+ int id)
+{
+ int qlab = dname_count_labels(iq->qchase.qname);
+ struct module_qstate* subq = NULL;
+ verbose(VERB_ALGO, "processDSNSFind");
+ if(dname_subdomain_c(iq->dp->name, iq->qchase.qname) ||
+ qlab == iq->dp->namelabs+1)
+ /* we are too low - fail (robust check) */
+ return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
+ /* dp is used for storage, this is our state */
+ log_assert(!iq->dp->dp_type_mlc); /* if malloced this would leak */
+ iq->dp->nslist = NULL;
+ iq->dp->target_list = NULL;
+ iq->dp->usable_list = NULL;
+ iq->dp->result_list = NULL;
+ iq->state = DSNS_FIND_STATE;
+
+ /* add one label to the dp */
+ iq->dp->name = iq->qchase.qname;
+ iq->dp->namelen = iq->qchase.qname_len;
+ /* we have qlab labels we want namelabs+1 labels */
+ dname_remove_labels(&iq->dp->name, &iq->dp->namelen,
+ qlab - (iq->dp->namelabs+1));
+ iq->dp->namelabs++;
+ /* spawn NS lookup (validation not needed, this is for DS lookup) */
+ log_nametypeclass(VERB_ALGO, "fetch nameservers",
+ iq->dp->name, LDNS_RR_TYPE_NS, iq->qchase.qclass);
+ if(!generate_sub_request(iq->dp->name, iq->dp->namelen,
+ LDNS_RR_TYPE_NS, iq->qchase.qclass, qstate, id, iq,
+ INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0)) {
+ return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
+ }
+
+ return 0;
+}
+
/**
* This is the request event state where the request will be sent to one of
* its current query targets. This state also handles issuing target lookup
(iq->response->rep->an_numrrsets?"ANSWER":
"nodata ANSWER"));
}
+ /* if qtype is DS, check we have the right level of answer,
+ * like grandchild answer but we need the middle, reject it */
+ if(iq->qchase.qtype == LDNS_RR_TYPE_DS
+ && iter_ds_toolow(iq->response)
+ && iter_dp_cangodown(&iq->qchase, iq->dp))
+ return processDSNSFind(qstate, iq, id);
if(!iter_dns_store(qstate->env, &iq->response->qinfo,
iq->response->rep, 0, qstate->prefetch_leeway,
iq->dp&&iq->dp->has_parent_side_NS,
}
}
+/**
+ * Process response for DS NS Find queries, that attempt to find the delegation
+ * point where we ask the DS query from.
+ *
+ * @param qstate: query state.
+ * @param id: module id.
+ * @param forq: super query state.
+ */
+static void
+processDSNSResponse(struct module_qstate* qstate, int id,
+ struct module_qstate* forq)
+{
+ struct iter_qstate* foriq = (struct iter_qstate*)forq->minfo[id];
+
+ /* if the finished (iq->response) query has no NS set: continue
+ * down to look for the right dp; nothing to change, do DPNSstate */
+ if(qstate->return_rcode != LDNS_RCODE_NOERROR)
+ return; /* seek further */
+ /* find the NS RRset (without allowing CNAMEs) */
+ if(!reply_find_rrset(qstate->return_msg->rep, qstate->qinfo.qname,
+ qstate->qinfo.qname_len, LDNS_RR_TYPE_NS,
+ qstate->qinfo.qclass)){
+ return; /* seek further */
+ }
+
+ /* else, store as DP and continue at querytargets */
+ foriq->state = QUERYTARGETS_STATE;
+ foriq->dp = delegpt_from_message(qstate->return_msg, forq->region);
+ if(!foriq->dp) {
+ log_err("out of memory in dsns dp alloc");
+ return; /* dp==NULL in QUERYTARGETS makes SERVFAIL */
+ }
+ /* success, go query the querytargets in the new dp (and go down) */
+}
+
/**
* Process response for qclass=ANY queries for a particular class.
* Append to result or error-exit.
{
if(!qstate->is_priming && super->qinfo.qclass == LDNS_RR_CLASS_ANY)
processClassResponse(qstate, id, super);
+ else if(super->qinfo.qtype == LDNS_RR_TYPE_DS && ((struct iter_qstate*)
+ super->minfo[id])->state == DSNS_FIND_STATE)
+ processDSNSResponse(qstate, id, super);
else if(qstate->return_rcode != LDNS_RCODE_NOERROR)
error_supers(qstate, id, super);
else if(qstate->is_priming)
case COLLECT_CLASS_STATE:
cont = processCollectClass(qstate, id);
break;
+ case DSNS_FIND_STATE:
+ cont = processDSNSFind(qstate, iq, id);
+ break;
case FINISHED_STATE:
cont = processFinished(qstate, iq, id);
break;
return "PRIME RESPONSE STATE";
case COLLECT_CLASS_STATE :
return "COLLECT CLASS STATE";
+ case DSNS_FIND_STATE :
+ return "DSNS FIND STATE";
case QUERY_RESP_STATE :
return "QUERY RESPONSE STATE";
case FINISHED_STATE :
--- /dev/null
+; config options
+server:
+ target-fetch-policy: "0 0 0 0 0"
+
+stub-zone:
+ name: "."
+ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
+CONFIG_END
+
+SCENARIO_BEGIN Test locate of NS records for parent nameservers of DS
+
+; K.ROOT-SERVERS.NET.
+RANGE_BEGIN 0 100
+ ADDRESS 193.0.14.129
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA 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
+
+; content of root-servers.net
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+root-servers.net. IN NS
+SECTION ANSWER
+root-servers.net. 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 AA NOERROR
+SECTION QUESTION
+k.root-servers.net. IN A
+SECTION ANSWER
+K.ROOT-SERVERS.NET. IN A 193.0.14.129
+SECTION AUTHORITY
+root-servers.net. IN NS K.ROOT-SERVERS.NET.
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+k.root-servers.net. IN AAAA
+SECTION ANSWER
+SECTION AUTHORITY
+root-servers.net. IN SOA K.ROOT-SERVERS.NET. hostmaster. 1 2 3 4 5
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+root-servers.net. IN DS
+SECTION ANSWER
+SECTION AUTHORITY
+root-servers.net. IN SOA K.ROOT-SERVERS.NET. hostmaster. 1 2 3 4 5
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+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
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+net. IN NS
+SECTION ANSWER
+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 AA NOERROR
+SECTION QUESTION
+root-servers.net. IN DS
+SECTION AUTHORITY
+net. IN SOA a.gtld-servers.net. hostmaster. 2 3 4 5 6
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+root-servers.net. IN A
+SECTION AUTHORITY
+root-servers.net. IN NS K.ROOT-SERVERS.NET.
+SECTION ADDITIONAL
+K.ROOT-SERVERS.NET. IN A 193.0.14.129
+ENTRY_END
+RANGE_END
+
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+root-servers.net. IN DS
+ENTRY_END
+
+; recursion happens here.
+STEP 10 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+root-servers.net. IN DS
+SECTION ANSWER
+SECTION AUTHORITY
+net. IN SOA a.gtld-servers.net. hostmaster. 2 3 4 5 6
+SECTION ADDITIONAL
+ENTRY_END
+
+SCENARIO_END