diff --git a/iterator/iter_delegpt.c b/iterator/iter_delegpt.c
index ecf88b2..1ababa1 100644
--- a/iterator/iter_delegpt.c
+++ b/iterator/iter_delegpt.c
@@ -84,7 +84,7 @@ struct delegpt* delegpt_copy(struct delegpt* dp, struct regional* region)
}
for(a = dp->target_list; a; a = a->next_target) {
if(!delegpt_add_addr(copy, region, &a->addr, a->addrlen,
- a->bogus, a->lame))
+ a->bogus, a->lame, NULL))
return NULL;
}
return copy;
@@ -161,7 +161,7 @@ delegpt_find_addr(struct delegpt* dp, struct sockaddr_storage* addr,
int
delegpt_add_target(struct delegpt* dp, struct regional* region,
uint8_t* name, size_t namelen, struct sockaddr_storage* addr,
- socklen_t addrlen, uint8_t bogus, uint8_t lame)
+ socklen_t addrlen, uint8_t bogus, uint8_t lame, int* additions)
{
struct delegpt_ns* ns = delegpt_find_ns(dp, name, namelen);
log_assert(!dp->dp_type_mlc);
@@ -176,13 +176,13 @@ delegpt_add_target(struct delegpt* dp, struct regional* region,
if(ns->got4 && ns->got6)
ns->resolved = 1;
}
- return delegpt_add_addr(dp, region, addr, addrlen, bogus, lame);
+ return delegpt_add_addr(dp, region, addr, addrlen, bogus, lame, additions);
}
int
delegpt_add_addr(struct delegpt* dp, struct regional* region,
struct sockaddr_storage* addr, socklen_t addrlen, uint8_t bogus,
- uint8_t lame)
+ uint8_t lame, int* additions)
{
struct delegpt_addr* a;
log_assert(!dp->dp_type_mlc);
@@ -195,6 +195,9 @@ delegpt_add_addr(struct delegpt* dp, struct regional* region,
return 1;
}
+ if(additions)
+ *additions = 1;
+
a = (struct delegpt_addr*)regional_alloc(region,
sizeof(struct delegpt_addr));
if(!a)
@@ -370,10 +373,10 @@ delegpt_from_message(struct dns_msg* msg, struct regional* region)
continue;
if(ntohs(s->rk.type) == LDNS_RR_TYPE_A) {
- if(!delegpt_add_rrset_A(dp, region, s, 0))
+ if(!delegpt_add_rrset_A(dp, region, s, 0, NULL))
return NULL;
} else if(ntohs(s->rk.type) == LDNS_RR_TYPE_AAAA) {
- if(!delegpt_add_rrset_AAAA(dp, region, s, 0))
+ if(!delegpt_add_rrset_AAAA(dp, region, s, 0, NULL))
return NULL;
}
}
@@ -404,7 +407,7 @@ delegpt_rrset_add_ns(struct delegpt* dp, struct regional* region,
int
delegpt_add_rrset_A(struct delegpt* dp, struct regional* region,
- struct ub_packed_rrset_key* ak, uint8_t lame)
+ struct ub_packed_rrset_key* ak, uint8_t lame, int* additions)
{
struct packed_rrset_data* d=(struct packed_rrset_data*)ak->entry.data;
size_t i;
@@ -420,7 +423,7 @@ delegpt_add_rrset_A(struct delegpt* dp, struct regional* region,
memmove(&sa.sin_addr, d->rr_data[i]+2, INET_SIZE);
if(!delegpt_add_target(dp, region, ak->rk.dname,
ak->rk.dname_len, (struct sockaddr_storage*)&sa,
- len, (d->security==sec_status_bogus), lame))
+ len, (d->security==sec_status_bogus), lame, additions))
return 0;
}
return 1;
@@ -428,7 +431,7 @@ delegpt_add_rrset_A(struct delegpt* dp, struct regional* region,
int
delegpt_add_rrset_AAAA(struct delegpt* dp, struct regional* region,
- struct ub_packed_rrset_key* ak, uint8_t lame)
+ struct ub_packed_rrset_key* ak, uint8_t lame, int* additions)
{
struct packed_rrset_data* d=(struct packed_rrset_data*)ak->entry.data;
size_t i;
@@ -444,7 +447,7 @@ delegpt_add_rrset_AAAA(struct delegpt* dp, struct regional* region,
memmove(&sa.sin6_addr, d->rr_data[i]+2, INET6_SIZE);
if(!delegpt_add_target(dp, region, ak->rk.dname,
ak->rk.dname_len, (struct sockaddr_storage*)&sa,
- len, (d->security==sec_status_bogus), lame))
+ len, (d->security==sec_status_bogus), lame, additions))
return 0;
}
return 1;
@@ -452,20 +455,32 @@ delegpt_add_rrset_AAAA(struct delegpt* dp, struct regional* region,
int
delegpt_add_rrset(struct delegpt* dp, struct regional* region,
- struct ub_packed_rrset_key* rrset, uint8_t lame)
+ struct ub_packed_rrset_key* rrset, uint8_t lame, int* additions)
{
if(!rrset)
return 1;
if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_NS)
return delegpt_rrset_add_ns(dp, region, rrset, lame);
else if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_A)
- return delegpt_add_rrset_A(dp, region, rrset, lame);
+ return delegpt_add_rrset_A(dp, region, rrset, lame, additions);
else if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_AAAA)
- return delegpt_add_rrset_AAAA(dp, region, rrset, lame);
+ return delegpt_add_rrset_AAAA(dp, region, rrset, lame, additions);
log_warn("Unknown rrset type added to delegpt");
return 1;
}
+void delegpt_mark_neg(struct delegpt_ns* ns, uint16_t qtype)
+{
+ if(ns) {
+ if(qtype == LDNS_RR_TYPE_A)
+ ns->got4 = 2;
+ else if(qtype == LDNS_RR_TYPE_AAAA)
+ ns->got6 = 2;
+ if(ns->got4 && ns->got6)
+ ns->resolved = 1;
+ }
+}
+
void delegpt_add_neg_msg(struct delegpt* dp, struct msgreply_entry* msg)
{
struct reply_info* rep = (struct reply_info*)msg->entry.data;
@@ -475,14 +490,7 @@ void delegpt_add_neg_msg(struct delegpt* dp, struct msgreply_entry* msg)
if(FLAGS_GET_RCODE(rep->flags) != 0 || rep->an_numrrsets == 0) {
struct delegpt_ns* ns = delegpt_find_ns(dp, msg->key.qname,
msg->key.qname_len);
- if(ns) {
- if(msg->key.qtype == LDNS_RR_TYPE_A)
- ns->got4 = 1;
- else if(msg->key.qtype == LDNS_RR_TYPE_AAAA)
- ns->got6 = 1;
- if(ns->got4 && ns->got6)
- ns->resolved = 1;
- }
+ delegpt_mark_neg(ns, msg->key.qtype);
}
}
diff --git a/iterator/iter_delegpt.h b/iterator/iter_delegpt.h
index 4bd79c8..e640fc6 100644
--- a/iterator/iter_delegpt.h
+++ b/iterator/iter_delegpt.h
@@ -102,9 +102,10 @@ struct delegpt_ns {
* and marked true if got4 and got6 are both true.
*/
int resolved;
- /** if the ipv4 address is in the delegpt */
+ /** if the ipv4 address is in the delegpt, 0=not, 1=yes 2=negative,
+ * * negative means it was done, but no content. */
uint8_t got4;
- /** if the ipv6 address is in the delegpt */
+ /** if the ipv6 address is in the delegpt, 0=not, 1=yes 2=negative */
uint8_t got6;
/**
* If the name is parent-side only and thus dispreferred.
@@ -209,11 +210,12 @@ int delegpt_rrset_add_ns(struct delegpt* dp, struct regional* regional,
* @param addrlen: the length of addr.
* @param bogus: security status for the address, pass true if bogus.
* @param lame: address is lame.
+ * @param additions: will be set to 1 if a new address is added
* @return false on error.
*/
int delegpt_add_target(struct delegpt* dp, struct regional* regional,
uint8_t* name, size_t namelen, struct sockaddr_storage* addr,
- socklen_t addrlen, uint8_t bogus, uint8_t lame);
+ socklen_t addrlen, uint8_t bogus, uint8_t lame, int* additions);
/**
* Add A RRset to delegpt.
@@ -221,21 +223,23 @@ int delegpt_add_target(struct delegpt* dp, struct regional* regional,
* @param regional: where to allocate the info.
* @param rrset: RRset A to add.
* @param lame: rrset is lame, disprefer it.
+ * @param additions: will be set to 1 if a new address is added
* @return 0 on alloc error.
*/
int delegpt_add_rrset_A(struct delegpt* dp, struct regional* regional,
- struct ub_packed_rrset_key* rrset, uint8_t lame);
+ struct ub_packed_rrset_key* rrset, uint8_t lame, int* additions);
/**
* Add AAAA RRset to delegpt.
* @param dp: delegation point.
* @param regional: where to allocate the info.
* @param rrset: RRset AAAA to add.
+ * @param additions: will be set to 1 if a new address is added
* @param lame: rrset is lame, disprefer it.
* @return 0 on alloc error.
*/
int delegpt_add_rrset_AAAA(struct delegpt* dp, struct regional* regional,
- struct ub_packed_rrset_key* rrset, uint8_t lame);
+ struct ub_packed_rrset_key* rrset, uint8_t lame, int* additions);
/**
* Add any RRset to delegpt.
@@ -244,10 +248,11 @@ int delegpt_add_rrset_AAAA(struct delegpt* dp, struct regional* regional,
* @param regional: where to allocate the info.
* @param rrset: RRset to add, NS, A, AAAA.
* @param lame: rrset is lame, disprefer it.
+ * @param additions: will be set to 1 if a new address is added
* @return 0 on alloc error.
*/
int delegpt_add_rrset(struct delegpt* dp, struct regional* regional,
- struct ub_packed_rrset_key* rrset, uint8_t lame);
+ struct ub_packed_rrset_key* rrset, uint8_t lame, int* additions);
/**
* Add address to the delegation point. No servername is associated or checked.
@@ -257,11 +262,13 @@ int delegpt_add_rrset(struct delegpt* dp, struct regional* regional,
* @param addrlen: the length of addr.
* @param bogus: if address is bogus.
* @param lame: if address is lame.
+ * @param additions: will be set to 1 if a new address is added
+ * @return 0 on alloc error.
* @return false on error.
*/
int delegpt_add_addr(struct delegpt* dp, struct regional* regional,
struct sockaddr_storage* addr, socklen_t addrlen,
- uint8_t bogus, uint8_t lame);
+ uint8_t bogus, uint8_t lame, int* additions);
/**
* Find NS record in name list of delegation point.
@@ -334,6 +341,14 @@ size_t delegpt_count_targets(struct delegpt* dp);
struct delegpt* delegpt_from_message(struct dns_msg* msg,
struct regional* regional);
+/**
+* Mark negative return in delegation point for specific nameserver.
+* sets the got4 or got6 to negative, updates the ns->resolved.
+* @param ns: the nameserver in the delegpt.
+* @param qtype: A or AAAA (host order).
+*/
+void delegpt_mark_neg(struct delegpt_ns* ns, uint16_t qtype);
+
/**
* Add negative message to delegation point.
* @param dp: delegation point.
diff --git a/iterator/iter_scrub.c b/iterator/iter_scrub.c
index 6bdfe41..36ff766 100644
--- a/iterator/iter_scrub.c
+++ b/iterator/iter_scrub.c
@@ -185,8 +185,9 @@ mark_additional_rrset(sldns_buffer* pkt, struct msg_parse* msg,
/** Get target name of a CNAME */
static int
parse_get_cname_target(struct rrset_parse* rrset, uint8_t** sname,
- size_t* snamelen)
+ size_t* snamelen, sldns_buffer* pkt)
{
+ size_t oldpos, dlen;
if(rrset->rr_count != 1) {
struct rr_parse* sig;
verbose(VERB_ALGO, "Found CNAME rrset with "
@@ -204,6 +205,19 @@ parse_get_cname_target(struct rrset_parse* rrset, uint8_t** sname,
*sname = rrset->rr_first->ttl_data + sizeof(uint32_t)
+ sizeof(uint16_t); /* skip ttl, rdatalen */
*snamelen = rrset->rr_first->size - sizeof(uint16_t);
+
+ if(rrset->rr_first->outside_packet) {
+ if(!dname_valid(*sname, *snamelen))
+ return 0;
+ return 1;
+ }
+ oldpos = sldns_buffer_position(pkt);
+ sldns_buffer_set_position(pkt, (size_t)(*sname - sldns_buffer_begin(pkt)));
+ dlen = pkt_dname_len(pkt);
+ sldns_buffer_set_position(pkt, oldpos);
+ if(dlen == 0)
+ return 0; /* parse fail on the rdata name */
+ *snamelen = dlen;
return 1;
}
@@ -215,7 +229,7 @@ synth_cname(uint8_t* qname, size_t qnamelen, struct rrset_parse* dname_rrset,
/* we already know that sname is a strict subdomain of DNAME owner */
uint8_t* dtarg = NULL;
size_t dtarglen;
- if(!parse_get_cname_target(dname_rrset, &dtarg, &dtarglen))
+ if(!parse_get_cname_target(dname_rrset, &dtarg, &dtarglen, pkt))
return 0;
log_assert(qnamelen > dname_rrset->dname_len);
/* DNAME from com. to net. with qname example.com. -> example.net. */
@@ -372,7 +386,7 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg,
/* check next cname */
uint8_t* t = NULL;
size_t tlen = 0;
- if(!parse_get_cname_target(nx, &t, &tlen))
+ if(!parse_get_cname_target(nx, &t, &tlen, pkt))
return 0;
if(dname_pkt_compare(pkt, alias, t) == 0) {
/* it's OK and better capitalized */
@@ -423,7 +437,7 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg,
size_t tlen = 0;
if(synth_cname(sname, snamelen, nx, alias,
&aliaslen, pkt) &&
- parse_get_cname_target(rrset, &t, &tlen) &&
+ parse_get_cname_target(rrset, &t, &tlen, pkt) &&
dname_pkt_compare(pkt, alias, t) == 0) {
/* the synthesized CNAME equals the
* current CNAME. This CNAME is the
@@ -441,7 +455,7 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg,
}
/* move to next name in CNAME chain */
- if(!parse_get_cname_target(rrset, &sname, &snamelen))
+ if(!parse_get_cname_target(rrset, &sname, &snamelen, pkt))
return 0;
prev = rrset;
rrset = rrset->rrset_all_next;
diff --git a/iterator/iter_utils.c b/iterator/iter_utils.c
index 70cab40..fc7d625 100644
--- a/iterator/iter_utils.c
+++ b/iterator/iter_utils.c
@@ -997,7 +997,7 @@ int iter_lookup_parent_glue_from_cache(struct module_env* env,
log_rrset_key(VERB_ALGO, "found parent-side", akey);
ns->done_pside4 = 1;
/* a negative-cache-element has no addresses it adds */
- if(!delegpt_add_rrset_A(dp, region, akey, 1))
+ if(!delegpt_add_rrset_A(dp, region, akey, 1, NULL))
log_err("malloc failure in lookup_parent_glue");
lock_rw_unlock(&akey->entry.lock);
}
@@ -1009,7 +1009,7 @@ int iter_lookup_parent_glue_from_cache(struct module_env* env,
log_rrset_key(VERB_ALGO, "found parent-side", akey);
ns->done_pside6 = 1;
/* a negative-cache-element has no addresses it adds */
- if(!delegpt_add_rrset_AAAA(dp, region, akey, 1))
+ if(!delegpt_add_rrset_AAAA(dp, region, akey, 1, NULL))
log_err("malloc failure in lookup_parent_glue");
lock_rw_unlock(&akey->entry.lock);
}
diff --git a/iterator/iterator.c b/iterator/iterator.c
index 01ac883..edf5751 100644
--- a/iterator/iterator.c
+++ b/iterator/iterator.c
@@ -68,6 +68,8 @@
#include "sldns/parseutil.h"
#include "sldns/sbuffer.h"
+static void target_count_increase_nx(struct iter_qstate* iq, int num);
+
int
iter_init(struct module_env* env, int id)
{
@@ -146,6 +148,7 @@ iter_new(struct module_qstate* qstate, int id)
iq->sent_count = 0;
iq->ratelimit_ok = 0;
iq->target_count = NULL;
+ iq->dp_target_count = 0;
iq->wait_priming_stub = 0;
iq->refetch_glue = 0;
iq->dnssec_expected = 0;
@@ -217,6 +220,7 @@ final_state(struct iter_qstate* iq)
static void
error_supers(struct module_qstate* qstate, int id, struct module_qstate* super)
{
+ struct iter_env* ie = (struct iter_env*)qstate->env->modinfo[id];
struct iter_qstate* super_iq = (struct iter_qstate*)super->minfo[id];
if(qstate->qinfo.qtype == LDNS_RR_TYPE_A ||
@@ -241,7 +245,11 @@ error_supers(struct module_qstate* qstate, int id, struct module_qstate* super)
super->region, super_iq->dp))
log_err("out of memory adding missing");
}
+ delegpt_mark_neg(dpns, qstate->qinfo.qtype);
dpns->resolved = 1; /* mark as failed */
+ if((dpns->got4 == 2 || !ie->supports_ipv4) &&
+ (dpns->got6 == 2 || !ie->supports_ipv6))
+ target_count_increase_nx(super_iq, 1);
}
if(qstate->qinfo.qtype == LDNS_RR_TYPE_NS) {
/* prime failed to get delegation */
@@ -574,7 +582,7 @@ static void
target_count_create(struct iter_qstate* iq)
{
if(!iq->target_count) {
- iq->target_count = (int*)calloc(2, sizeof(int));
+ iq->target_count = (int*)calloc(3, sizeof(int));
/* if calloc fails we simply do not track this number */
if(iq->target_count)
iq->target_count[0] = 1;
@@ -587,6 +595,15 @@ target_count_increase(struct iter_qstate* iq, int num)
target_count_create(iq);
if(iq->target_count)
iq->target_count[1] += num;
+ iq->dp_target_count++;
+}
+
+static void
+target_count_increase_nx(struct iter_qstate* iq, int num)
+{
+ target_count_create(iq);
+ if(iq->target_count)
+ iq->target_count[2] += num;
}
/**
@@ -609,13 +626,15 @@ target_count_increase(struct iter_qstate* iq, int num)
* @param subq_ret: if newly allocated, the subquerystate, or NULL if it does
* not need initialisation.
* @param v: if true, validation is done on the subquery.
+ * @param detached: true if this qstate should not attach to the subquery
* @return false on error (malloc).
*/
static int
generate_sub_request(uint8_t* qname, size_t qnamelen, uint16_t qtype,
uint16_t qclass, struct module_qstate* qstate, int id,
struct iter_qstate* iq, enum iter_state initial_state,
- enum iter_state finalstate, struct module_qstate** subq_ret, int v)
+ enum iter_state finalstate, struct module_qstate** subq_ret, int v,
+ int detached)
{
struct module_qstate* subq = NULL;
struct iter_qstate* subiq = NULL;
@@ -642,12 +661,24 @@ generate_sub_request(uint8_t* qname, size_t qnamelen, uint16_t qtype,
valrec = 1;
}
- /* attach subquery, lookup existing or make a new one */
- fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub));
- if(!(*qstate->env->attach_sub)(qstate, &qinf, qflags, prime, valrec,
- &subq)) {
- return 0;
- }
+ if(detached) {
+ struct mesh_state* sub = NULL;
+ fptr_ok(fptr_whitelist_modenv_add_sub(
+ qstate->env->add_sub));
+ if(!(*qstate->env->add_sub)(qstate, &qinf,
+ qflags, prime, valrec, &subq, &sub)){
+ return 0;
+ }
+ }
+ else {
+ /* attach subquery, lookup existing or make a new one */
+ fptr_ok(fptr_whitelist_modenv_attach_sub(
+ qstate->env->attach_sub));
+ if(!(*qstate->env->attach_sub)(qstate, &qinf, qflags, prime,
+ valrec, &subq)) {
+ return 0;
+ }
+ }
*subq_ret = subq;
if(subq) {
/* initialise the new subquery */
@@ -669,6 +700,7 @@ generate_sub_request(uint8_t* qname, size_t qnamelen, uint16_t qtype,
subiq->target_count = iq->target_count;
if(iq->target_count)
iq->target_count[0] ++; /* extra reference */
+ subiq->dp_target_count = 0;
subiq->num_current_queries = 0;
subiq->depth = iq->depth+1;
outbound_list_init(&subiq->outlist);
@@ -712,7 +744,7 @@ prime_root(struct module_qstate* qstate, struct iter_qstate* iq, int id,
* the normal INIT state logic (which would cause an infloop). */
if(!generate_sub_request((uint8_t*)"\000", 1, LDNS_RR_TYPE_NS,
qclass, qstate, id, iq, QUERYTARGETS_STATE, PRIME_RESP_STATE,
- &subq, 0)) {
+ &subq, 0, 0)) {
verbose(VERB_ALGO, "could not prime root");
return 0;
}
@@ -797,7 +829,7 @@ prime_stub(struct module_qstate* qstate, struct iter_qstate* iq, int id,
* redundant INIT state processing. */
if(!generate_sub_request(stub_dp->name, stub_dp->namelen,
LDNS_RR_TYPE_NS, qclass, qstate, id, iq,
- QUERYTARGETS_STATE, PRIME_RESP_STATE, &subq, 0)) {
+ QUERYTARGETS_STATE, PRIME_RESP_STATE, &subq, 0, 0)) {
verbose(VERB_ALGO, "could not prime stub");
(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
return 1; /* return 1 to make module stop, with error */
@@ -878,7 +910,7 @@ generate_a_aaaa_check(struct module_qstate* qstate, struct iter_qstate* iq,
if(!generate_sub_request(s->rk.dname, s->rk.dname_len,
ntohs(s->rk.type), ntohs(s->rk.rrset_class),
qstate, id, iq,
- INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1)) {
+ INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1, 0)) {
verbose(VERB_ALGO, "could not generate addr check");
return;
}
@@ -919,7 +951,7 @@ generate_ns_check(struct module_qstate* qstate, struct iter_qstate* iq, int id)
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, 1)) {
+ INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1, 0)) {
verbose(VERB_ALGO, "could not generate ns check");
return;
}
@@ -976,7 +1008,7 @@ generate_dnskey_prefetch(struct module_qstate* qstate,
iq->dp->name, LDNS_RR_TYPE_DNSKEY, iq->qchase.qclass);
if(!generate_sub_request(iq->dp->name, iq->dp->namelen,
LDNS_RR_TYPE_DNSKEY, iq->qchase.qclass, qstate, id, iq,
- INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0)) {
+ INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0, 0)) {
/* we'll be slower, but it'll work */
verbose(VERB_ALGO, "could not generate dnskey prefetch");
return;
@@ -1150,6 +1182,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
iq->refetch_glue = 0;
iq->query_restart_count++;
iq->sent_count = 0;
+ iq->dp_target_count = 0;
sock_list_insert(&qstate->reply_origin, NULL, 0, qstate->region);
if(qstate->env->cfg->qname_minimisation)
iq->minimisation_state = INIT_MINIMISE_STATE;
@@ -1468,7 +1501,7 @@ generate_parentside_target_query(struct module_qstate* qstate,
{
struct module_qstate* subq;
if(!generate_sub_request(name, namelen, qtype, qclass, qstate,
- id, iq, INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0))
+ id, iq, INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0, 0))
return 0;
if(subq) {
struct iter_qstate* subiq =
@@ -1519,7 +1552,7 @@ generate_target_query(struct module_qstate* qstate, struct iter_qstate* iq,
{
struct module_qstate* subq;
if(!generate_sub_request(name, namelen, qtype, qclass, qstate,
- id, iq, INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0))
+ id, iq, INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0, 0))
return 0;
log_nametypeclass(VERB_QUERY, "new target", name, qtype, qclass);
return 1;
@@ -1558,6 +1591,14 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq,
"number of glue fetches %d", s, iq->target_count[1]);
return 0;
}
+ if(iq->dp_target_count > MAX_DP_TARGET_COUNT) {
+ char s[LDNS_MAX_DOMAINLEN+1];
+ dname_str(qstate->qinfo.qname, s);
+ verbose(VERB_QUERY, "request %s has exceeded the maximum "
+ "number of glue fetches %d to a single delegation point",
+ s, iq->dp_target_count);
+ return 0;
+ }
iter_mark_cycle_targets(qstate, iq->dp);
missing = (int)delegpt_count_missing_targets(iq->dp);
@@ -1670,7 +1711,7 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,
for(a = p->target_list; a; a=a->next_target) {
(void)delegpt_add_addr(iq->dp, qstate->region,
&a->addr, a->addrlen, a->bogus,
- a->lame);
+ a->lame, NULL);
}
}
iq->dp->has_parent_side_NS = 1;
@@ -1687,6 +1728,7 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,
iq->refetch_glue = 1;
iq->query_restart_count++;
iq->sent_count = 0;
+ iq->dp_target_count = 0;
if(qstate->env->cfg->qname_minimisation)
iq->minimisation_state = INIT_MINIMISE_STATE;
return next_state(iq, INIT_REQUEST_STATE);
@@ -1841,7 +1883,7 @@ processDSNSFind(struct module_qstate* qstate, struct iter_qstate* iq, int id)
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)) {
+ INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0, 0)) {
return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
@@ -1893,6 +1935,15 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
"number of sends with %d", iq->sent_count);
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
+
+ if(iq->target_count && iq->target_count[2] > MAX_TARGET_NX) {
+ verbose(VERB_QUERY, "request has exceeded the maximum "
+ " number of nxdomain nameserver lookups with %d",
+ iq->target_count[2]);
+ errinf(qstate, "exceeded the maximum nameserver nxdomains");
+ return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+ }
+
/* Make sure we have a delegation point, otherwise priming failed
* or another failure occurred */
@@ -1920,6 +1971,8 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
* generated query will immediately be discarded due to depth and
* that servfail is cached, which is not good as opportunism goes. */
if(iq->depth < ie->max_dependency_depth
+ && iq->num_target_queries == 0
+ && (!iq->target_count || iq->target_count[2]==0)
&& iq->sent_count < TARGET_FETCH_STOP) {
tf_policy = ie->target_fetch_policy[iq->depth];
}
@@ -1957,6 +2010,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
iq->num_current_queries++; /* RespState decrements it*/
iq->referral_count++; /* make sure we don't loop */
iq->sent_count = 0;
+ iq->dp_target_count = 0;
iq->state = QUERY_RESP_STATE;
return 1;
}
@@ -2041,6 +2095,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
iq->num_current_queries++; /* RespState decrements it*/
iq->referral_count++; /* make sure we don't loop */
iq->sent_count = 0;
+ iq->dp_target_count = 0;
iq->state = QUERY_RESP_STATE;
return 1;
}
@@ -2160,12 +2215,41 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
iq->qinfo_out.qtype, iq->qinfo_out.qclass,
qstate->query_flags, qstate->region,
qstate->env->scratch);
- if(msg && msg->rep->an_numrrsets == 0
- && FLAGS_GET_RCODE(msg->rep->flags) ==
+ if(msg && FLAGS_GET_RCODE(msg->rep->flags) ==
LDNS_RCODE_NOERROR)
/* no need to send query if it is already
- * cached as NOERROR/NODATA */
+ * cached as NOERROR */
return 1;
+ if(msg && FLAGS_GET_RCODE(msg->rep->flags) ==
+ LDNS_RCODE_NXDOMAIN &&
+ qstate->env->need_to_validate &&
+ qstate->env->cfg->harden_below_nxdomain) {
+ if(msg->rep->security == sec_status_secure) {
+ iq->response = msg;
+ return final_state(iq);
+ }
+ if(msg->rep->security == sec_status_unchecked) {
+ struct module_qstate* subq = NULL;
+ if(!generate_sub_request(
+ iq->qinfo_out.qname,
+ iq->qinfo_out.qname_len,
+ iq->qinfo_out.qtype,
+ iq->qinfo_out.qclass,
+ qstate, id, iq,
+ INIT_REQUEST_STATE,
+ FINISHED_STATE, &subq, 1, 1))
+ verbose(VERB_ALGO,
+ "could not validate NXDOMAIN "
+ "response");
+ }
+ }
+ if(msg && FLAGS_GET_RCODE(msg->rep->flags) ==
+ LDNS_RCODE_NXDOMAIN) {
+ /* return and add a label in the next
+ * minimisation iteration.
+ */
+ return 1;
+ }
}
}
if(iq->minimisation_state == SKIP_MINIMISE_STATE) {
@@ -2216,6 +2300,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
outbound_list_insert(&iq->outlist, outq);
iq->num_current_queries++;
iq->sent_count++;
+ iq->dp_target_count = 0;
qstate->ext_state[id] = module_wait_reply;
return 0;
@@ -2404,7 +2489,8 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
/* Make subrequest to validate intermediate
* NXDOMAIN if harden-below-nxdomain is
* enabled. */
- if(qstate->env->cfg->harden_below_nxdomain) {
+ if(qstate->env->cfg->harden_below_nxdomain &&
+ qstate->env->need_to_validate) {
struct module_qstate* subq = NULL;
log_query_info(VERB_QUERY,
"schedule NXDOMAIN validation:",
@@ -2416,7 +2502,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
iq->response->qinfo.qclass,
qstate, id, iq,
INIT_REQUEST_STATE,
- FINISHED_STATE, &subq, 1))
+ FINISHED_STATE, &subq, 1, 1))
verbose(VERB_ALGO,
"could not validate NXDOMAIN "
"response");
@@ -2499,6 +2585,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
/* Count this as a referral. */
iq->referral_count++;
iq->sent_count = 0;
+ iq->dp_target_count = 0;
/* see if the next dp is a trust anchor, or a DS was sent
* along, indicating dnssec is expected for next zone */
iq->dnssec_expected = iter_indicates_dnssec(qstate->env,
@@ -2576,6 +2663,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
/* Note the query restart. */
iq->query_restart_count++;
iq->sent_count = 0;
+ iq->dp_target_count = 0;
/* stop current outstanding queries.
* FIXME: should the outstanding queries be waited for and
@@ -2739,7 +2827,7 @@ processPrimeResponse(struct module_qstate* qstate, int id)
if(!generate_sub_request(qstate->qinfo.qname,
qstate->qinfo.qname_len, qstate->qinfo.qtype,
qstate->qinfo.qclass, qstate, id, iq,
- INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1)) {
+ INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1, 0)) {
verbose(VERB_ALGO, "could not generate prime check");
}
generate_a_aaaa_check(qstate, iq, id);
@@ -2767,6 +2855,7 @@ static void
processTargetResponse(struct module_qstate* qstate, int id,
struct module_qstate* forq)
{
+ struct iter_env* ie = (struct iter_env*)qstate->env->modinfo[id];
struct iter_qstate* iq = (struct iter_qstate*)qstate->minfo[id];
struct iter_qstate* foriq = (struct iter_qstate*)forq->minfo[id];
struct ub_packed_rrset_key* rrset;
@@ -2804,7 +2893,7 @@ processTargetResponse(struct module_qstate* qstate, int id,
log_rrset_key(VERB_ALGO, "add parentside glue to dp",
iq->pside_glue);
if(!delegpt_add_rrset(foriq->dp, forq->region,
- iq->pside_glue, 1))
+ iq->pside_glue, 1, NULL))
log_err("out of memory adding pside glue");
}
@@ -2815,6 +2904,7 @@ processTargetResponse(struct module_qstate* qstate, int id,
* response type was ANSWER. */
rrset = reply_find_answer_rrset(&iq->qchase, qstate->return_msg->rep);
if(rrset) {
+ int additions = 0;
/* if CNAMEs have been followed - add new NS to delegpt. */
/* BTW. RFC 1918 says NS should not have got CNAMEs. Robust. */
if(!delegpt_find_ns(foriq->dp, rrset->rk.dname,
@@ -2826,13 +2916,23 @@ processTargetResponse(struct module_qstate* qstate, int id,
}
/* if dpns->lame then set the address(es) lame too */
if(!delegpt_add_rrset(foriq->dp, forq->region, rrset,
- dpns->lame))
+ dpns->lame, &additions))
log_err("out of memory adding targets");
+ if(!additions) {
+ /* no new addresses, increase the nxns counter, like
+ * this could be a list of wildcards with no new
+ * addresses */
+ target_count_increase_nx(foriq, 1);
+ }
verbose(VERB_ALGO, "added target response");
delegpt_log(VERB_ALGO, foriq->dp);
} else {
verbose(VERB_ALGO, "iterator TargetResponse failed");
+ delegpt_mark_neg(dpns, qstate->qinfo.qtype);
dpns->resolved = 1; /* fail the target */
+ if((dpns->got4 == 2 || !ie->supports_ipv4) &&
+ (dpns->got6 == 2 || !ie->supports_ipv6))
+ target_count_increase_nx(foriq, 1);
}
}
@@ -3003,7 +3103,7 @@ processCollectClass(struct module_qstate* qstate, int id)
qstate->qinfo.qname_len, qstate->qinfo.qtype,
c, qstate, id, iq, INIT_REQUEST_STATE,
FINISHED_STATE, &subq,
- (int)!(qstate->query_flags&BIT_CD))) {
+ (int)!(qstate->query_flags&BIT_CD), 0)) {
return error_response(qstate, id,
LDNS_RCODE_SERVFAIL);
}
diff --git a/iterator/iterator.h b/iterator/iterator.h
index 75aafee..3534b5c 100644
--- a/iterator/iterator.h
+++ b/iterator/iterator.h
@@ -55,6 +55,11 @@ struct rbtree_type;
/** max number of targets spawned for a query and its subqueries */
#define MAX_TARGET_COUNT 64
+/** max number of target lookups per qstate, per delegation point */
+#define MAX_DP_TARGET_COUNT 16
+/** max number of nxdomains allowed for target lookups for a query and
+ * its subqueries */
+#define MAX_TARGET_NX 5
/** max number of query restarts. Determines max number of CNAME chain. */
#define MAX_RESTART_COUNT 8
/** max number of referrals. Makes sure resolver does not run away */
@@ -305,9 +310,14 @@ struct iter_qstate {
int sent_count;
/** number of target queries spawned in [1], for this query and its
- * subqueries, the malloced-array is shared, [0] refcount. */
+ * subqueries, the malloced-array is shared, [0] refcount.
+ * in [2] the number of nxdomains is counted. */
int* target_count;
+ /** number of target lookups per delegation point. Reset to 0 after
+ * receiving referral answer. Not shared with subqueries. */
+ int dp_target_count;
+
/** if true, already tested for ratelimiting and passed the test */
int ratelimit_ok;
diff --git a/services/cache/dns.c b/services/cache/dns.c
index da43c50..ce138f1 100644
--- a/services/cache/dns.c
+++ b/services/cache/dns.c
@@ -223,7 +223,7 @@ find_add_addrs(struct module_env* env, uint16_t qclass,
akey = rrset_cache_lookup(env->rrset_cache, ns->name,
ns->namelen, LDNS_RR_TYPE_A, qclass, 0, now, 0);
if(akey) {
- if(!delegpt_add_rrset_A(dp, region, akey, 0)) {
+ if(!delegpt_add_rrset_A(dp, region, akey, 0, NULL)) {
lock_rw_unlock(&akey->entry.lock);
return 0;
}
@@ -243,7 +243,7 @@ find_add_addrs(struct module_env* env, uint16_t qclass,
akey = rrset_cache_lookup(env->rrset_cache, ns->name,
ns->namelen, LDNS_RR_TYPE_AAAA, qclass, 0, now, 0);
if(akey) {
- if(!delegpt_add_rrset_AAAA(dp, region, akey, 0)) {
+ if(!delegpt_add_rrset_AAAA(dp, region, akey, 0, NULL)) {
lock_rw_unlock(&akey->entry.lock);
return 0;
}
@@ -277,7 +277,8 @@ cache_fill_missing(struct module_env* env, uint16_t qclass,
akey = rrset_cache_lookup(env->rrset_cache, ns->name,
ns->namelen, LDNS_RR_TYPE_A, qclass, 0, now, 0);
if(akey) {
- if(!delegpt_add_rrset_A(dp, region, akey, ns->lame)) {
+ if(!delegpt_add_rrset_A(dp, region, akey, ns->lame,
+ NULL)) {
lock_rw_unlock(&akey->entry.lock);
return 0;
}
@@ -297,7 +298,8 @@ cache_fill_missing(struct module_env* env, uint16_t qclass,
akey = rrset_cache_lookup(env->rrset_cache, ns->name,
ns->namelen, LDNS_RR_TYPE_AAAA, qclass, 0, now, 0);
if(akey) {
- if(!delegpt_add_rrset_AAAA(dp, region, akey, ns->lame)) {
+ if(!delegpt_add_rrset_AAAA(dp, region, akey, ns->lame,
+ NULL)) {
lock_rw_unlock(&akey->entry.lock);
return 0;
}
diff --git a/util/data/dname.c b/util/data/dname.c
index 517af28..a80a4e2 100644
--- a/util/data/dname.c
+++ b/util/data/dname.c
@@ -231,17 +231,28 @@ int
dname_pkt_compare(sldns_buffer* pkt, uint8_t* d1, uint8_t* d2)
{
uint8_t len1, len2;
+ int count1 = 0, count2 = 0;
log_assert(pkt && d1 && d2);
len1 = *d1++;
len2 = *d2++;
while( len1 != 0 || len2 != 0 ) {
/* resolve ptrs */
if(LABEL_IS_PTR(len1)) {
+ if((size_t)PTR_OFFSET(len1, *d1)
+ >= sldns_buffer_limit(pkt))
+ return -1;
+ if(count1++ > MAX_COMPRESS_PTRS)
+ return -1;
d1 = sldns_buffer_at(pkt, PTR_OFFSET(len1, *d1));
len1 = *d1++;
continue;
}
if(LABEL_IS_PTR(len2)) {
+ if((size_t)PTR_OFFSET(len2, *d2)
+ >= sldns_buffer_limit(pkt))
+ return 1;
+ if(count2++ > MAX_COMPRESS_PTRS)
+ return 1;
d2 = sldns_buffer_at(pkt, PTR_OFFSET(len2, *d2));
len2 = *d2++;
continue;
@@ -276,6 +287,7 @@ dname_query_hash(uint8_t* dname, hashvalue_type h)
uint8_t labuf[LDNS_MAX_LABELLEN+1];
uint8_t lablen;
int i;
+ int count = 0;
/* preserve case of query, make hash label by label */
lablen = *dname++;
@@ -333,6 +345,9 @@ void dname_pkt_copy(sldns_buffer* pkt, uint8_t* to, uint8_t* dname)
while(lablen) {
if(LABEL_IS_PTR(lablen)) {
/* follow pointer */
+ if((size_t)PTR_OFFSET(lablen, *dname)
+ >= sldns_buffer_limit(pkt))
+ return;
dname = sldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname));
lablen = *dname++;
continue;
@@ -357,6 +372,7 @@ void dname_pkt_copy(sldns_buffer* pkt, uint8_t* to, uint8_t* dname)
void dname_print(FILE* out, struct sldns_buffer* pkt, uint8_t* dname)
{
uint8_t lablen;
+ int count = 0;
if(!out) out = stdout;
if(!dname) return;
@@ -370,6 +386,15 @@ void dname_print(FILE* out, struct sldns_buffer* pkt, uint8_t* dname)
fputs("??compressionptr??", out);
return;
}
+ if((size_t)PTR_OFFSET(lablen, *dname)
+ >= sldns_buffer_limit(pkt)) {
+ fputs("??compressionptr??", out);
+ return;
+ }
+ if(count++ > MAX_COMPRESS_PTRS) {
+ fputs("??compressionptr??", out);
+ return;
+ }
dname = sldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname));
lablen = *dname++;
continue;
diff --git a/util/data/msgparse.c b/util/data/msgparse.c
index 2887200..783f3a7 100644
--- a/util/data/msgparse.c
+++ b/util/data/msgparse.c
@@ -55,7 +55,11 @@ smart_compare(sldns_buffer* pkt, uint8_t* dnow,
{
if(LABEL_IS_PTR(*dnow)) {
/* ptr points to a previous dname */
- uint8_t* p = sldns_buffer_at(pkt, PTR_OFFSET(dnow[0], dnow[1]));
+ uint8_t* p;
+ if((size_t)PTR_OFFSET(dnow[0], dnow[1])
+ >= sldns_buffer_limit(pkt))
+ return -1;
+ p = sldns_buffer_at(pkt, PTR_OFFSET(dnow[0], dnow[1]));
if( p == dprfirst || p == dprlast )
return 0;
/* prev dname is also a ptr, both ptrs are the same. */