+21 March 2012: Wouter
+ - new approach to NS fetches for DS lookup that works with
+ cornercases, and is more robust and considers forwarders.
+
19 March 2012: Wouter
- iana portlist updated.
- fix to locate nameservers for DS lookup with NS fetches.
}
int
-iter_ds_toolow(struct dns_msg* msg)
+iter_ds_toolow(struct dns_msg* msg, struct delegpt* dp)
{
/* if for query example.com, there is example.com SOA or a subdomain
* of example.com, then we are too low and need to fetch NS. */
- size_t i = msg->rep->an_numrrsets;
- for(; i < msg->rep->an_numrrsets + msg->rep->ns_numrrsets; i++) {
+ size_t i;
+ /* if we have a DNAME or CNAME we are probably wrong */
+ /* if we have a qtype DS in the answer section, its fine */
+ for(i=0; i < msg->rep->an_numrrsets; i++) {
struct ub_packed_rrset_key* s = msg->rep->rrsets[i];
- if(ntohs(s->rk.type) == LDNS_RR_TYPE_SOA &&
- dname_subdomain_c(s->rk.dname, msg->qinfo.qname)) {
- /* point is too low */
+ if(ntohs(s->rk.type) == LDNS_RR_TYPE_DNAME ||
+ ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME) {
+ /* not the right answer, maybe too low, check the
+ * RRSIG signer name (if there is any) for a hint
+ * that it is from the dp zone anyway */
+ uint8_t* sname;
+ size_t slen;
+ val_find_rrset_signer(s, &sname, &slen);
+ if(sname && query_dname_compare(dp->name, sname)==0)
+ return 0; /* it is fine, from the right dp */
return 1;
}
+ if(ntohs(s->rk.type) == LDNS_RR_TYPE_DS)
+ return 0; /* fine, we have a DS record */
}
- return 0;
+ for(i=msg->rep->an_numrrsets;
+ i < msg->rep->an_numrrsets + msg->rep->ns_numrrsets; i++) {
+ struct ub_packed_rrset_key* s = msg->rep->rrsets[i];
+ if(ntohs(s->rk.type) == LDNS_RR_TYPE_SOA) {
+ if(dname_subdomain_c(s->rk.dname, msg->qinfo.qname))
+ return 1; /* point is too low */
+ if(query_dname_compare(s->rk.dname, dp->name)==0)
+ return 0; /* right dp */
+ }
+ if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC ||
+ ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) {
+ uint8_t* sname;
+ size_t slen;
+ val_find_rrset_signer(s, &sname, &slen);
+ if(sname && query_dname_compare(dp->name, sname)==0)
+ return 0; /* it is fine, from the right dp */
+ return 1;
+ }
+ }
+ /* we do not know */
+ return 1;
}
int iter_dp_cangodown(struct query_info* qinfo, struct delegpt* dp)
/**
* See if a DS response (type ANSWER) is too low: a nodata answer with
* a SOA record in the authority section at-or-below the qchase.qname.
+ * Also returns true if we are not sure (i.e. empty message, CNAME nosig).
* @param msg: the response.
+ * @param dp: the dp name is used to check if the RRSIG gives a clue that
+ * it was originated from the correct nameserver.
* @return true if too low.
*/
-int iter_ds_toolow(struct dns_msg* msg);
+int iter_ds_toolow(struct dns_msg* msg, struct delegpt* dp);
/**
* See if delegpt can go down a step to the qname or not
* 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.
+ * for this lookup. We remove labels from the query to find the right point.
+ * If we end up at the old dp name, then there is no solution.
*
* @param qstate: query state.
* @param iq: iterator query state.
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) */
+
+ if(!iq->dsns_point) {
+ /* initialize */
+ iq->dsns_point = iq->qchase.qname;
+ iq->dsns_point_len = iq->qchase.qname_len;
+ }
+ /* robustcheck for internal error: we are not underneath the dp */
+ if(!dname_subdomain_c(iq->dsns_point, iq->dp->name)) {
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;
+ }
+
+ /* go up one (more) step, until we hit the dp, if so, end */
+ dname_remove_label(&iq->dsns_point, &iq->dsns_point_len);
+ if(query_dname_compare(iq->dsns_point, iq->dp->name) == 0) {
+ /* there was no inbetween nameserver, use the old delegation
+ * point again. And this time, because dsns_point is nonNULL
+ * we are going to accept the (bad) result */
+ iq->state = QUERYTARGETS_STATE;
+ return 1;
+ }
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,
+ iq->dsns_point, LDNS_RR_TYPE_NS, iq->qchase.qclass);
+ if(!generate_sub_request(iq->dsns_point, iq->dsns_point_len,
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);
}
/* 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)
+ if(iq->qchase.qtype == LDNS_RR_TYPE_DS && !iq->dsns_point
+ && !(iq->chase_flags&BIT_RD)
+ && iter_ds_toolow(iq->response, iq->dp)
&& iter_dp_cangodown(&iq->qchase, iq->dp))
return processDSNSFind(qstate, iq, id);
if(!iter_dns_store(qstate->env, &iq->response->qinfo,
if(verbosity >= VERB_ALGO)
log_dns_msg("cname msg", &iq->response->qinfo,
iq->response->rep);
+ /* 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 && !iq->dsns_point
+ && !(iq->chase_flags&BIT_RD)
+ && iter_ds_toolow(iq->response, iq->dp)
+ && iter_dp_cangodown(&iq->qchase, iq->dp))
+ return processDSNSFind(qstate, iq, id);
/* Process the CNAME response. */
if(!handle_cname_response(qstate, iq, iq->response,
&sname, &snamelen))
/* prefetchleeway applied because this updates answer parts */
if(!iter_dns_store(qstate->env, &iq->response->qinfo,
iq->response->rep, 1, qstate->prefetch_leeway,
- iq->dp&&iq->dp->has_parent_side_NS,
- NULL))
+ iq->dp&&iq->dp->has_parent_side_NS, NULL))
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
/* set the current request's qname to the new value. */
iq->qchase.qname = sname;
/* Clear the query state, since this is a query restart. */
iq->deleg_msg = NULL;
iq->dp = NULL;
+ iq->dsns_point = NULL;
/* Note the query restart. */
iq->query_restart_count++;
iq->sent_count = 0;
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 */
+ * up 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) */
}
/* 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.
/** the parent-side-glue element (NULL if none, its first match) */
struct ub_packed_rrset_key* pside_glue;
+ /** If nonNULL we are walking upwards from DS query to find NS */
+ uint8_t* dsns_point;
+ /** length of the dname in dsns_point */
+ size_t dsns_point_len;
+
/**
* expected dnssec information for this iteration step.
* If dnssec rrsigs are expected and not given, the server is marked
--- /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 DS with CNAME
+
+; 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
+root-servers.net. IN CNAME blabla.com.
+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
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NXDOMAIN
+SECTION QUESTION
+blabla.com. IN DS
+SECTION ANSWER
+SECTION AUTHORITY
+. IN SOA K.ROOT-SERVERS.NET. hostmaster. 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 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
--- /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 DS without a SOA record
+
+; 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