Blame SOURCES/unbound-1.7.3-amplifying-an-incoming-query.patch

b8c5d4
diff --git a/iterator/iter_delegpt.c b/iterator/iter_delegpt.c
b8c5d4
index f88b3e1..522e0e9 100644
b8c5d4
--- a/iterator/iter_delegpt.c
b8c5d4
+++ b/iterator/iter_delegpt.c
b8c5d4
@@ -84,7 +84,7 @@ struct delegpt* delegpt_copy(struct delegpt* dp, struct regional* region)
b8c5d4
 	}
b8c5d4
 	for(a = dp->target_list; a; a = a->next_target) {
b8c5d4
 		if(!delegpt_add_addr(copy, region, &a->addr, a->addrlen, 
b8c5d4
-			a->bogus, a->lame, a->tls_auth_name))
b8c5d4
+           a->bogus, a->lame, a->tls_auth_name, NULL))
b8c5d4
 			return NULL;
b8c5d4
 	}
b8c5d4
 	return copy;
b8c5d4
@@ -161,7 +161,7 @@ delegpt_find_addr(struct delegpt* dp, struct sockaddr_storage* addr,
b8c5d4
 int 
b8c5d4
 delegpt_add_target(struct delegpt* dp, struct regional* region, 
b8c5d4
 	uint8_t* name, size_t namelen, struct sockaddr_storage* addr, 
b8c5d4
-	socklen_t addrlen, uint8_t bogus, uint8_t lame)
b8c5d4
+   socklen_t addrlen, uint8_t bogus, uint8_t lame, int* additions)
b8c5d4
 {
b8c5d4
 	struct delegpt_ns* ns = delegpt_find_ns(dp, name, namelen);
b8c5d4
 	log_assert(!dp->dp_type_mlc);
b8c5d4
@@ -176,13 +176,14 @@ delegpt_add_target(struct delegpt* dp, struct regional* region,
b8c5d4
 		if(ns->got4 && ns->got6)
b8c5d4
 			ns->resolved = 1;
b8c5d4
 	}
b8c5d4
-	return delegpt_add_addr(dp, region, addr, addrlen, bogus, lame, NULL);
b8c5d4
+	return delegpt_add_addr(dp, region, addr, addrlen, bogus, lame, NULL,
b8c5d4
+        additions);
b8c5d4
 }
b8c5d4
 
b8c5d4
 int 
b8c5d4
 delegpt_add_addr(struct delegpt* dp, struct regional* region, 
b8c5d4
 	struct sockaddr_storage* addr, socklen_t addrlen, uint8_t bogus, 
b8c5d4
-	uint8_t lame, char* tls_auth_name)
b8c5d4
+	uint8_t lame, char* tls_auth_name, int* additions)
b8c5d4
 {
b8c5d4
 	struct delegpt_addr* a;
b8c5d4
 	log_assert(!dp->dp_type_mlc);
b8c5d4
@@ -195,6 +196,9 @@ delegpt_add_addr(struct delegpt* dp, struct regional* region,
b8c5d4
 		return 1;
b8c5d4
 	}
b8c5d4
 
b8c5d4
+    if(additions)
b8c5d4
+        *additions = 1;
b8c5d4
+
b8c5d4
 	a = (struct delegpt_addr*)regional_alloc(region,
b8c5d4
 		sizeof(struct delegpt_addr));
b8c5d4
 	if(!a)
b8c5d4
@@ -382,10 +386,10 @@ delegpt_from_message(struct dns_msg* msg, struct regional* region)
b8c5d4
 			continue;
b8c5d4
 
b8c5d4
 		if(ntohs(s->rk.type) == LDNS_RR_TYPE_A) {
b8c5d4
-			if(!delegpt_add_rrset_A(dp, region, s, 0))
b8c5d4
+			if(!delegpt_add_rrset_A(dp, region, s, 0, NULL))
b8c5d4
 				return NULL;
b8c5d4
 		} else if(ntohs(s->rk.type) == LDNS_RR_TYPE_AAAA) {
b8c5d4
-			if(!delegpt_add_rrset_AAAA(dp, region, s, 0))
b8c5d4
+			if(!delegpt_add_rrset_AAAA(dp, region, s, 0, NULL))
b8c5d4
 				return NULL;
b8c5d4
 		}
b8c5d4
 	}
b8c5d4
@@ -416,7 +420,7 @@ delegpt_rrset_add_ns(struct delegpt* dp, struct regional* region,
b8c5d4
 
b8c5d4
 int 
b8c5d4
 delegpt_add_rrset_A(struct delegpt* dp, struct regional* region,
b8c5d4
-	struct ub_packed_rrset_key* ak, uint8_t lame)
b8c5d4
+	struct ub_packed_rrset_key* ak, uint8_t lame, int* additions)
b8c5d4
 {
b8c5d4
         struct packed_rrset_data* d=(struct packed_rrset_data*)ak->entry.data;
b8c5d4
         size_t i;
b8c5d4
@@ -432,7 +436,7 @@ delegpt_add_rrset_A(struct delegpt* dp, struct regional* region,
b8c5d4
                 memmove(&sa.sin_addr, d->rr_data[i]+2, INET_SIZE);
b8c5d4
                 if(!delegpt_add_target(dp, region, ak->rk.dname,
b8c5d4
                         ak->rk.dname_len, (struct sockaddr_storage*)&sa,
b8c5d4
-                        len, (d->security==sec_status_bogus), lame))
b8c5d4
+                        len, (d->security==sec_status_bogus), lame, additions))
b8c5d4
                         return 0;
b8c5d4
         }
b8c5d4
         return 1;
b8c5d4
@@ -440,7 +444,7 @@ delegpt_add_rrset_A(struct delegpt* dp, struct regional* region,
b8c5d4
 
b8c5d4
 int 
b8c5d4
 delegpt_add_rrset_AAAA(struct delegpt* dp, struct regional* region,
b8c5d4
-	struct ub_packed_rrset_key* ak, uint8_t lame)
b8c5d4
+	struct ub_packed_rrset_key* ak, uint8_t lame, int* additions)
b8c5d4
 {
b8c5d4
         struct packed_rrset_data* d=(struct packed_rrset_data*)ak->entry.data;
b8c5d4
         size_t i;
b8c5d4
@@ -456,7 +460,7 @@ delegpt_add_rrset_AAAA(struct delegpt* dp, struct regional* region,
b8c5d4
                 memmove(&sa.sin6_addr, d->rr_data[i]+2, INET6_SIZE);
b8c5d4
                 if(!delegpt_add_target(dp, region, ak->rk.dname,
b8c5d4
                         ak->rk.dname_len, (struct sockaddr_storage*)&sa,
b8c5d4
-                        len, (d->security==sec_status_bogus), lame))
b8c5d4
+                        len, (d->security==sec_status_bogus), lame, additions))
b8c5d4
                         return 0;
b8c5d4
         }
b8c5d4
         return 1;
b8c5d4
@@ -464,20 +468,32 @@ delegpt_add_rrset_AAAA(struct delegpt* dp, struct regional* region,
b8c5d4
 
b8c5d4
 int 
b8c5d4
 delegpt_add_rrset(struct delegpt* dp, struct regional* region,
b8c5d4
-        struct ub_packed_rrset_key* rrset, uint8_t lame)
b8c5d4
+        struct ub_packed_rrset_key* rrset, uint8_t lame, int* additions)
b8c5d4
 {
b8c5d4
 	if(!rrset)
b8c5d4
 		return 1;
b8c5d4
 	if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_NS)
b8c5d4
 		return delegpt_rrset_add_ns(dp, region, rrset, lame);
b8c5d4
 	else if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_A)
b8c5d4
-		return delegpt_add_rrset_A(dp, region, rrset, lame);
b8c5d4
+		return delegpt_add_rrset_A(dp, region, rrset, lame, additions);
b8c5d4
 	else if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_AAAA)
b8c5d4
-		return delegpt_add_rrset_AAAA(dp, region, rrset, lame);
b8c5d4
+		return delegpt_add_rrset_AAAA(dp, region, rrset, lame, additions);
b8c5d4
 	log_warn("Unknown rrset type added to delegpt");
b8c5d4
 	return 1;
b8c5d4
 }
b8c5d4
 
b8c5d4
+void delegpt_mark_neg(struct delegpt_ns* ns, uint16_t qtype)
b8c5d4
+{
b8c5d4
+    if(ns) {
b8c5d4
+        if(qtype == LDNS_RR_TYPE_A)
b8c5d4
+            ns->got4 = 2;
b8c5d4
+        else if(qtype == LDNS_RR_TYPE_AAAA)
b8c5d4
+            ns->got6 = 2;
b8c5d4
+        if(ns->got4 && ns->got6)
b8c5d4
+            ns->resolved = 1;
b8c5d4
+    }
b8c5d4
+}
b8c5d4
+
b8c5d4
 void delegpt_add_neg_msg(struct delegpt* dp, struct msgreply_entry* msg)
b8c5d4
 {
b8c5d4
 	struct reply_info* rep = (struct reply_info*)msg->entry.data;
b8c5d4
@@ -487,14 +503,7 @@ void delegpt_add_neg_msg(struct delegpt* dp, struct msgreply_entry* msg)
b8c5d4
 	if(FLAGS_GET_RCODE(rep->flags) != 0 || rep->an_numrrsets == 0) {
b8c5d4
 		struct delegpt_ns* ns = delegpt_find_ns(dp, msg->key.qname, 
b8c5d4
 			msg->key.qname_len);
b8c5d4
-		if(ns) {
b8c5d4
-			if(msg->key.qtype == LDNS_RR_TYPE_A)
b8c5d4
-				ns->got4 = 1;
b8c5d4
-			else if(msg->key.qtype == LDNS_RR_TYPE_AAAA)
b8c5d4
-				ns->got6 = 1;
b8c5d4
-			if(ns->got4 && ns->got6)
b8c5d4
-				ns->resolved = 1;
b8c5d4
-		}
b8c5d4
+        delegpt_mark_neg(ns, msg->key.qtype);
b8c5d4
 	}
b8c5d4
 }
b8c5d4
 
b8c5d4
diff --git a/iterator/iter_delegpt.h b/iterator/iter_delegpt.h
b8c5d4
index 354bd61..3aded22 100644
b8c5d4
--- a/iterator/iter_delegpt.h
b8c5d4
+++ b/iterator/iter_delegpt.h
b8c5d4
@@ -104,9 +104,10 @@ struct delegpt_ns {
b8c5d4
 	 * and marked true if got4 and got6 are both true.
b8c5d4
 	 */
b8c5d4
 	int resolved;
b8c5d4
-	/** if the ipv4 address is in the delegpt */
b8c5d4
+	/** if the ipv4 address is in the delegpt, 0=not, 1=yes 2=negative,
b8c5d4
+     * negative means it was done, but no content. */
b8c5d4
 	uint8_t got4;
b8c5d4
-	/** if the ipv6 address is in the delegpt */
b8c5d4
+	/** if the ipv6 address is in the delegpt, 0=not, 1=yes 2=negative */
b8c5d4
 	uint8_t got6;
b8c5d4
 	/**
b8c5d4
 	 * If the name is parent-side only and thus dispreferred.
b8c5d4
@@ -213,11 +214,12 @@ int delegpt_rrset_add_ns(struct delegpt* dp, struct regional* regional,
b8c5d4
  * @param addrlen: the length of addr.
b8c5d4
  * @param bogus: security status for the address, pass true if bogus.
b8c5d4
  * @param lame: address is lame.
b8c5d4
+ * @param additions: will be set to 1 if a new address is added
b8c5d4
  * @return false on error.
b8c5d4
  */
b8c5d4
 int delegpt_add_target(struct delegpt* dp, struct regional* regional, 
b8c5d4
 	uint8_t* name, size_t namelen, struct sockaddr_storage* addr, 
b8c5d4
-	socklen_t addrlen, uint8_t bogus, uint8_t lame);
b8c5d4
+	socklen_t addrlen, uint8_t bogus, uint8_t lame, int* additions);
b8c5d4
 
b8c5d4
 /**
b8c5d4
  * Add A RRset to delegpt.
b8c5d4
@@ -225,10 +227,11 @@ int delegpt_add_target(struct delegpt* dp, struct regional* regional,
b8c5d4
  * @param regional: where to allocate the info.
b8c5d4
  * @param rrset: RRset A to add.
b8c5d4
  * @param lame: rrset is lame, disprefer it.
b8c5d4
+ * @param additions: will be set to 1 if a new address is added
b8c5d4
  * @return 0 on alloc error.
b8c5d4
  */
b8c5d4
 int delegpt_add_rrset_A(struct delegpt* dp, struct regional* regional, 
b8c5d4
-	struct ub_packed_rrset_key* rrset, uint8_t lame);
b8c5d4
+	struct ub_packed_rrset_key* rrset, uint8_t lame, int* additions);
b8c5d4
 
b8c5d4
 /**
b8c5d4
  * Add AAAA RRset to delegpt.
b8c5d4
@@ -236,10 +239,11 @@ int delegpt_add_rrset_A(struct delegpt* dp, struct regional* regional,
b8c5d4
  * @param regional: where to allocate the info.
b8c5d4
  * @param rrset: RRset AAAA to add.
b8c5d4
  * @param lame: rrset is lame, disprefer it.
b8c5d4
+ * @param additions: will be set to 1 if a new address is added
b8c5d4
  * @return 0 on alloc error.
b8c5d4
  */
b8c5d4
 int delegpt_add_rrset_AAAA(struct delegpt* dp, struct regional* regional, 
b8c5d4
-	struct ub_packed_rrset_key* rrset, uint8_t lame);
b8c5d4
+	struct ub_packed_rrset_key* rrset, uint8_t lame, int* additions);
b8c5d4
 
b8c5d4
 /**
b8c5d4
  * Add any RRset to delegpt.
b8c5d4
@@ -248,10 +252,11 @@ int delegpt_add_rrset_AAAA(struct delegpt* dp, struct regional* regional,
b8c5d4
  * @param regional: where to allocate the info.
b8c5d4
  * @param rrset: RRset to add, NS, A, AAAA.
b8c5d4
  * @param lame: rrset is lame, disprefer it.
b8c5d4
+ * @param additions: will be set to 1 if a new address is added
b8c5d4
  * @return 0 on alloc error.
b8c5d4
  */
b8c5d4
 int delegpt_add_rrset(struct delegpt* dp, struct regional* regional, 
b8c5d4
-	struct ub_packed_rrset_key* rrset, uint8_t lame);
b8c5d4
+	struct ub_packed_rrset_key* rrset, uint8_t lame, int* additions);
b8c5d4
 
b8c5d4
 /**
b8c5d4
  * Add address to the delegation point. No servername is associated or checked.
b8c5d4
@@ -262,11 +267,13 @@ int delegpt_add_rrset(struct delegpt* dp, struct regional* regional,
b8c5d4
  * @param bogus: if address is bogus.
b8c5d4
  * @param lame: if address is lame.
b8c5d4
  * @param tls_auth_name: TLS authentication name (or NULL).
b8c5d4
+ * @param additions: will be set to 1 if a new address is added
b8c5d4
+ * @return 0 on alloc error.
b8c5d4
  * @return false on error.
b8c5d4
  */
b8c5d4
 int delegpt_add_addr(struct delegpt* dp, struct regional* regional, 
b8c5d4
 	struct sockaddr_storage* addr, socklen_t addrlen,
b8c5d4
-	uint8_t bogus, uint8_t lame, char* tls_auth_name);
b8c5d4
+	uint8_t bogus, uint8_t lame, char* tls_auth_name, int* additions);
b8c5d4
 
b8c5d4
 /** 
b8c5d4
  * Find NS record in name list of delegation point.
b8c5d4
@@ -339,6 +346,14 @@ size_t delegpt_count_targets(struct delegpt* dp);
b8c5d4
 struct delegpt* delegpt_from_message(struct dns_msg* msg, 
b8c5d4
 	struct regional* regional);
b8c5d4
 
b8c5d4
+/**
b8c5d4
+* Mark negative return in delegation point for specific nameserver.
b8c5d4
+* sets the got4 or got6 to negative, updates the ns->resolved.
b8c5d4
+* @param ns: the nameserver in the delegpt.
b8c5d4
+* @param qtype: A or AAAA (host order).
b8c5d4
+*/
b8c5d4
+void delegpt_mark_neg(struct delegpt_ns* ns, uint16_t qtype);
b8c5d4
+
b8c5d4
 /**
b8c5d4
  * Add negative message to delegation point.
b8c5d4
  * @param dp: delegation point.
b8c5d4
diff --git a/iterator/iter_scrub.c b/iterator/iter_scrub.c
b8c5d4
index 12580dc..8230d17 100644
b8c5d4
--- a/iterator/iter_scrub.c
b8c5d4
+++ b/iterator/iter_scrub.c
b8c5d4
@@ -185,8 +185,9 @@ mark_additional_rrset(sldns_buffer* pkt, struct msg_parse* msg,
b8c5d4
 /** Get target name of a CNAME */
b8c5d4
 static int
b8c5d4
 parse_get_cname_target(struct rrset_parse* rrset, uint8_t** sname, 
b8c5d4
-	size_t* snamelen)
b8c5d4
+	size_t* snamelen, sldns_buffer* pkt)
b8c5d4
 {
b8c5d4
+    size_t oldpos, dlen;
b8c5d4
 	if(rrset->rr_count != 1) {
b8c5d4
 		struct rr_parse* sig;
b8c5d4
 		verbose(VERB_ALGO, "Found CNAME rrset with "
b8c5d4
@@ -204,6 +205,19 @@ parse_get_cname_target(struct rrset_parse* rrset, uint8_t** sname,
b8c5d4
 	*sname = rrset->rr_first->ttl_data + sizeof(uint32_t)
b8c5d4
 		+ sizeof(uint16_t); /* skip ttl, rdatalen */
b8c5d4
 	*snamelen = rrset->rr_first->size - sizeof(uint16_t);
b8c5d4
+
b8c5d4
+    if(rrset->rr_first->outside_packet) {
b8c5d4
+        if(!dname_valid(*sname, *snamelen))
b8c5d4
+            return 0;
b8c5d4
+        return 1;
b8c5d4
+    }
b8c5d4
+    oldpos = sldns_buffer_position(pkt);
b8c5d4
+    sldns_buffer_set_position(pkt, (size_t)(*sname - sldns_buffer_begin(pkt)));
b8c5d4
+    dlen = pkt_dname_len(pkt);
b8c5d4
+    sldns_buffer_set_position(pkt, oldpos);
b8c5d4
+    if(dlen == 0)
b8c5d4
+        return 0; /* parse fail on the rdata name */
b8c5d4
+    *snamelen = dlen;
b8c5d4
 	return 1;
b8c5d4
 }
b8c5d4
 
b8c5d4
@@ -215,7 +229,7 @@ synth_cname(uint8_t* qname, size_t qnamelen, struct rrset_parse* dname_rrset,
b8c5d4
 	/* we already know that sname is a strict subdomain of DNAME owner */
b8c5d4
 	uint8_t* dtarg = NULL;
b8c5d4
 	size_t dtarglen;
b8c5d4
-	if(!parse_get_cname_target(dname_rrset, &dtarg, &dtarglen))
b8c5d4
+	if(!parse_get_cname_target(dname_rrset, &dtarg, &dtarglen, pkt))
b8c5d4
 		return 0; 
b8c5d4
 	log_assert(qnamelen > dname_rrset->dname_len);
b8c5d4
 	/* DNAME from com. to net. with qname example.com. -> example.net. */
b8c5d4
@@ -372,7 +386,7 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg,
b8c5d4
 				/* check next cname */
b8c5d4
 				uint8_t* t = NULL;
b8c5d4
 				size_t tlen = 0;
b8c5d4
-				if(!parse_get_cname_target(nx, &t, &tlen))
b8c5d4
+				if(!parse_get_cname_target(nx, &t, &tlen, pkt))
b8c5d4
 					return 0;
b8c5d4
 				if(dname_pkt_compare(pkt, alias, t) == 0) {
b8c5d4
 					/* it's OK and better capitalized */
b8c5d4
@@ -423,7 +437,7 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg,
b8c5d4
 				size_t tlen = 0;
b8c5d4
 				if(synth_cname(sname, snamelen, nx, alias,
b8c5d4
 					&aliaslen, pkt) &&
b8c5d4
-					parse_get_cname_target(rrset, &t, &tlen) &&
b8c5d4
+					parse_get_cname_target(rrset, &t, &tlen, pkt) &&
b8c5d4
 			   		dname_pkt_compare(pkt, alias, t) == 0) {
b8c5d4
 					/* the synthesized CNAME equals the
b8c5d4
 					 * current CNAME.  This CNAME is the
b8c5d4
@@ -442,7 +456,7 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg,
b8c5d4
 			}
b8c5d4
 
b8c5d4
 			/* move to next name in CNAME chain */
b8c5d4
-			if(!parse_get_cname_target(rrset, &sname, &snamelen))
b8c5d4
+			if(!parse_get_cname_target(rrset, &sname, &snamelen, pkt))
b8c5d4
 				return 0;
b8c5d4
 			prev = rrset;
b8c5d4
 			rrset = rrset->rrset_all_next;
b8c5d4
diff --git a/iterator/iter_utils.c b/iterator/iter_utils.c
b8c5d4
index 0a8f770..a107bee 100644
b8c5d4
--- a/iterator/iter_utils.c
b8c5d4
+++ b/iterator/iter_utils.c
b8c5d4
@@ -1008,7 +1008,7 @@ int iter_lookup_parent_glue_from_cache(struct module_env* env,
b8c5d4
 			log_rrset_key(VERB_ALGO, "found parent-side", akey);
b8c5d4
 			ns->done_pside4 = 1;
b8c5d4
 			/* a negative-cache-element has no addresses it adds */
b8c5d4
-			if(!delegpt_add_rrset_A(dp, region, akey, 1))
b8c5d4
+			if(!delegpt_add_rrset_A(dp, region, akey, 1, NULL))
b8c5d4
 				log_err("malloc failure in lookup_parent_glue");
b8c5d4
 			lock_rw_unlock(&akey->entry.lock);
b8c5d4
 		}
b8c5d4
@@ -1020,7 +1020,7 @@ int iter_lookup_parent_glue_from_cache(struct module_env* env,
b8c5d4
 			log_rrset_key(VERB_ALGO, "found parent-side", akey);
b8c5d4
 			ns->done_pside6 = 1;
b8c5d4
 			/* a negative-cache-element has no addresses it adds */
b8c5d4
-			if(!delegpt_add_rrset_AAAA(dp, region, akey, 1))
b8c5d4
+			if(!delegpt_add_rrset_AAAA(dp, region, akey, 1, NULL))
b8c5d4
 				log_err("malloc failure in lookup_parent_glue");
b8c5d4
 			lock_rw_unlock(&akey->entry.lock);
b8c5d4
 		}
b8c5d4
diff --git a/iterator/iterator.c b/iterator/iterator.c
b8c5d4
index 58a9bff..a4ad319 100644
b8c5d4
--- a/iterator/iterator.c
b8c5d4
+++ b/iterator/iterator.c
b8c5d4
@@ -69,6 +69,8 @@
b8c5d4
 #include "sldns/parseutil.h"
b8c5d4
 #include "sldns/sbuffer.h"
b8c5d4
 
b8c5d4
+static void target_count_increase_nx(struct iter_qstate* iq, int num);
b8c5d4
+
b8c5d4
 int 
b8c5d4
 iter_init(struct module_env* env, int id)
b8c5d4
 {
b8c5d4
@@ -147,6 +149,7 @@ iter_new(struct module_qstate* qstate, int id)
b8c5d4
 	iq->sent_count = 0;
b8c5d4
 	iq->ratelimit_ok = 0;
b8c5d4
 	iq->target_count = NULL;
b8c5d4
+    iq->dp_target_count = 0;
b8c5d4
 	iq->wait_priming_stub = 0;
b8c5d4
 	iq->refetch_glue = 0;
b8c5d4
 	iq->dnssec_expected = 0;
b8c5d4
@@ -218,6 +221,7 @@ final_state(struct iter_qstate* iq)
b8c5d4
 static void
b8c5d4
 error_supers(struct module_qstate* qstate, int id, struct module_qstate* super)
b8c5d4
 {
b8c5d4
+    struct iter_env* ie = (struct iter_env*)qstate->env->modinfo[id];
b8c5d4
 	struct iter_qstate* super_iq = (struct iter_qstate*)super->minfo[id];
b8c5d4
 
b8c5d4
 	if(qstate->qinfo.qtype == LDNS_RR_TYPE_A ||
b8c5d4
@@ -242,7 +246,11 @@ error_supers(struct module_qstate* qstate, int id, struct module_qstate* super)
b8c5d4
 				super->region, super_iq->dp))
b8c5d4
 				log_err("out of memory adding missing");
b8c5d4
 		}
b8c5d4
+        delegpt_mark_neg(dpns, qstate->qinfo.qtype);
b8c5d4
 		dpns->resolved = 1; /* mark as failed */
b8c5d4
+        if((dpns->got4 == 2 || !ie->supports_ipv4) &&
b8c5d4
+            (dpns->got6 == 2 || !ie->supports_ipv6))
b8c5d4
+            target_count_increase_nx(super_iq, 1);
b8c5d4
 	}
b8c5d4
 	if(qstate->qinfo.qtype == LDNS_RR_TYPE_NS) {
b8c5d4
 		/* prime failed to get delegation */
b8c5d4
@@ -577,7 +585,7 @@ static void
b8c5d4
 target_count_create(struct iter_qstate* iq)
b8c5d4
 {
b8c5d4
 	if(!iq->target_count) {
b8c5d4
-		iq->target_count = (int*)calloc(2, sizeof(int));
b8c5d4
+		iq->target_count = (int*)calloc(3, sizeof(int));
b8c5d4
 		/* if calloc fails we simply do not track this number */
b8c5d4
 		if(iq->target_count)
b8c5d4
 			iq->target_count[0] = 1;
b8c5d4
@@ -590,6 +598,15 @@ target_count_increase(struct iter_qstate* iq, int num)
b8c5d4
 	target_count_create(iq);
b8c5d4
 	if(iq->target_count)
b8c5d4
 		iq->target_count[1] += num;
b8c5d4
+    iq->dp_target_count++;
b8c5d4
+}
b8c5d4
+
b8c5d4
+static void
b8c5d4
+target_count_increase_nx(struct iter_qstate* iq, int num)
b8c5d4
+{
b8c5d4
+   target_count_create(iq);
b8c5d4
+   if(iq->target_count)
b8c5d4
+       iq->target_count[2] += num;
b8c5d4
 }
b8c5d4
 
b8c5d4
 /**
b8c5d4
@@ -612,13 +629,15 @@ target_count_increase(struct iter_qstate* iq, int num)
b8c5d4
  * @param subq_ret: if newly allocated, the subquerystate, or NULL if it does
b8c5d4
  * 	not need initialisation.
b8c5d4
  * @param v: if true, validation is done on the subquery.
b8c5d4
+ * @param detached: true if this qstate should not attach to the subquery
b8c5d4
  * @return false on error (malloc).
b8c5d4
  */
b8c5d4
 static int
b8c5d4
 generate_sub_request(uint8_t* qname, size_t qnamelen, uint16_t qtype, 
b8c5d4
 	uint16_t qclass, struct module_qstate* qstate, int id,
b8c5d4
 	struct iter_qstate* iq, enum iter_state initial_state, 
b8c5d4
-	enum iter_state finalstate, struct module_qstate** subq_ret, int v)
b8c5d4
+	enum iter_state finalstate, struct module_qstate** subq_ret, int v,
b8c5d4
+    int detached)
b8c5d4
 {
b8c5d4
 	struct module_qstate* subq = NULL;
b8c5d4
 	struct iter_qstate* subiq = NULL;
b8c5d4
@@ -645,12 +664,24 @@ generate_sub_request(uint8_t* qname, size_t qnamelen, uint16_t qtype,
b8c5d4
 		valrec = 1;
b8c5d4
 	}
b8c5d4
 	
b8c5d4
-	/* attach subquery, lookup existing or make a new one */
b8c5d4
-	fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub));
b8c5d4
-	if(!(*qstate->env->attach_sub)(qstate, &qinf, qflags, prime, valrec,
b8c5d4
-		&subq)) {
b8c5d4
-		return 0;
b8c5d4
-	}
b8c5d4
+    if(detached) {
b8c5d4
+        struct mesh_state* sub = NULL;
b8c5d4
+        fptr_ok(fptr_whitelist_modenv_add_sub(
b8c5d4
+            qstate->env->add_sub));
b8c5d4
+        if(!(*qstate->env->add_sub)(qstate, &qinf,
b8c5d4
+            qflags, prime, valrec, &subq, &sub)){
b8c5d4
+            return 0;
b8c5d4
+        }
b8c5d4
+    }
b8c5d4
+    else {
b8c5d4
+        /* attach subquery, lookup existing or make a new one */
b8c5d4
+        fptr_ok(fptr_whitelist_modenv_attach_sub(
b8c5d4
+            qstate->env->attach_sub));
b8c5d4
+        if(!(*qstate->env->attach_sub)(qstate, &qinf, qflags, prime,
b8c5d4
+            valrec, &subq)) {
b8c5d4
+            return 0;
b8c5d4
+        }
b8c5d4
+    }
b8c5d4
 	*subq_ret = subq;
b8c5d4
 	if(subq) {
b8c5d4
 		/* initialise the new subquery */
b8c5d4
@@ -672,6 +703,7 @@ generate_sub_request(uint8_t* qname, size_t qnamelen, uint16_t qtype,
b8c5d4
 		subiq->target_count = iq->target_count;
b8c5d4
 		if(iq->target_count)
b8c5d4
 			iq->target_count[0] ++; /* extra reference */
b8c5d4
+        subiq->dp_target_count = 0;
b8c5d4
 		subiq->num_current_queries = 0;
b8c5d4
 		subiq->depth = iq->depth+1;
b8c5d4
 		outbound_list_init(&subiq->outlist);
b8c5d4
@@ -715,7 +747,7 @@ prime_root(struct module_qstate* qstate, struct iter_qstate* iq, int id,
b8c5d4
 	 * the normal INIT state logic (which would cause an infloop). */
b8c5d4
 	if(!generate_sub_request((uint8_t*)"\000", 1, LDNS_RR_TYPE_NS, 
b8c5d4
 		qclass, qstate, id, iq, QUERYTARGETS_STATE, PRIME_RESP_STATE,
b8c5d4
-		&subq, 0)) {
b8c5d4
+		&subq, 0, 0)) {
b8c5d4
 		verbose(VERB_ALGO, "could not prime root");
b8c5d4
 		return 0;
b8c5d4
 	}
b8c5d4
@@ -805,7 +837,7 @@ prime_stub(struct module_qstate* qstate, struct iter_qstate* iq, int id,
b8c5d4
 	 * redundant INIT state processing. */
b8c5d4
 	if(!generate_sub_request(stub_dp->name, stub_dp->namelen, 
b8c5d4
 		LDNS_RR_TYPE_NS, qclass, qstate, id, iq,
b8c5d4
-		QUERYTARGETS_STATE, PRIME_RESP_STATE, &subq, 0)) {
b8c5d4
+		QUERYTARGETS_STATE, PRIME_RESP_STATE, &subq, 0, 0)) {
b8c5d4
 		verbose(VERB_ALGO, "could not prime stub");
b8c5d4
 		(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
b8c5d4
 		return 1; /* return 1 to make module stop, with error */
b8c5d4
@@ -976,7 +1008,7 @@ generate_a_aaaa_check(struct module_qstate* qstate, struct iter_qstate* iq,
b8c5d4
 		if(!generate_sub_request(s->rk.dname, s->rk.dname_len, 
b8c5d4
 			ntohs(s->rk.type), ntohs(s->rk.rrset_class),
b8c5d4
 			qstate, id, iq,
b8c5d4
-			INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1)) {
b8c5d4
+			INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1, 0)) {
b8c5d4
 			verbose(VERB_ALGO, "could not generate addr check");
b8c5d4
 			return;
b8c5d4
 		}
b8c5d4
@@ -1020,7 +1052,7 @@ generate_ns_check(struct module_qstate* qstate, struct iter_qstate* iq, int id)
b8c5d4
 		iq->dp->name, LDNS_RR_TYPE_NS, iq->qchase.qclass);
b8c5d4
 	if(!generate_sub_request(iq->dp->name, iq->dp->namelen, 
b8c5d4
 		LDNS_RR_TYPE_NS, iq->qchase.qclass, qstate, id, iq,
b8c5d4
-		INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1)) {
b8c5d4
+		INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1, 0)) {
b8c5d4
 		verbose(VERB_ALGO, "could not generate ns check");
b8c5d4
 		return;
b8c5d4
 	}
b8c5d4
@@ -1077,7 +1109,7 @@ generate_dnskey_prefetch(struct module_qstate* qstate,
b8c5d4
 		iq->dp->name, LDNS_RR_TYPE_DNSKEY, iq->qchase.qclass);
b8c5d4
 	if(!generate_sub_request(iq->dp->name, iq->dp->namelen, 
b8c5d4
 		LDNS_RR_TYPE_DNSKEY, iq->qchase.qclass, qstate, id, iq,
b8c5d4
-		INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0)) {
b8c5d4
+		INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0, 0)) {
b8c5d4
 		/* we'll be slower, but it'll work */
b8c5d4
 		verbose(VERB_ALGO, "could not generate dnskey prefetch");
b8c5d4
 		return;
b8c5d4
@@ -1251,6 +1283,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
b8c5d4
 			iq->refetch_glue = 0;
b8c5d4
 			iq->query_restart_count++;
b8c5d4
 			iq->sent_count = 0;
b8c5d4
+            iq->dp_target_count = 0;
b8c5d4
 			sock_list_insert(&qstate->reply_origin, NULL, 0, qstate->region);
b8c5d4
 			if(qstate->env->cfg->qname_minimisation)
b8c5d4
 				iq->minimisation_state = INIT_MINIMISE_STATE;
b8c5d4
@@ -1613,7 +1646,7 @@ generate_parentside_target_query(struct module_qstate* qstate,
b8c5d4
 {
b8c5d4
 	struct module_qstate* subq;
b8c5d4
 	if(!generate_sub_request(name, namelen, qtype, qclass, qstate, 
b8c5d4
-		id, iq, INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0))
b8c5d4
+		id, iq, INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0, 0))
b8c5d4
 		return 0;
b8c5d4
 	if(subq) {
b8c5d4
 		struct iter_qstate* subiq = 
b8c5d4
@@ -1664,7 +1697,7 @@ generate_target_query(struct module_qstate* qstate, struct iter_qstate* iq,
b8c5d4
 {
b8c5d4
 	struct module_qstate* subq;
b8c5d4
 	if(!generate_sub_request(name, namelen, qtype, qclass, qstate, 
b8c5d4
-		id, iq, INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0))
b8c5d4
+		id, iq, INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0, 0))
b8c5d4
 		return 0;
b8c5d4
 	log_nametypeclass(VERB_QUERY, "new target", name, qtype, qclass);
b8c5d4
 	return 1;
b8c5d4
@@ -1703,6 +1736,14 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq,
b8c5d4
 			"number of glue fetches %d", s, iq->target_count[1]);
b8c5d4
 		return 0;
b8c5d4
 	}
b8c5d4
+    if(iq->dp_target_count > MAX_DP_TARGET_COUNT) {
b8c5d4
+        char s[LDNS_MAX_DOMAINLEN+1];
b8c5d4
+        dname_str(qstate->qinfo.qname, s);
b8c5d4
+        verbose(VERB_QUERY, "request %s has exceeded the maximum "
b8c5d4
+            "number of glue fetches %d to a single delegation point",
b8c5d4
+            s, iq->dp_target_count);
b8c5d4
+        return 0;
b8c5d4
+    }
b8c5d4
 
b8c5d4
 	iter_mark_cycle_targets(qstate, iq->dp);
b8c5d4
 	missing = (int)delegpt_count_missing_targets(iq->dp);
b8c5d4
@@ -1815,7 +1856,7 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,
b8c5d4
 			for(a = p->target_list; a; a=a->next_target) {
b8c5d4
 				(void)delegpt_add_addr(iq->dp, qstate->region,
b8c5d4
 					&a->addr, a->addrlen, a->bogus,
b8c5d4
-					a->lame, a->tls_auth_name);
b8c5d4
+					a->lame, a->tls_auth_name, NULL);
b8c5d4
 			}
b8c5d4
 		}
b8c5d4
 		iq->dp->has_parent_side_NS = 1;
b8c5d4
@@ -1832,6 +1873,7 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,
b8c5d4
 			iq->refetch_glue = 1;
b8c5d4
 			iq->query_restart_count++;
b8c5d4
 			iq->sent_count = 0;
b8c5d4
+            iq->dp_target_count = 0;
b8c5d4
 			if(qstate->env->cfg->qname_minimisation)
b8c5d4
 				iq->minimisation_state = INIT_MINIMISE_STATE;
b8c5d4
 			return next_state(iq, INIT_REQUEST_STATE);
b8c5d4
@@ -1986,7 +2028,7 @@ processDSNSFind(struct module_qstate* qstate, struct iter_qstate* iq, int id)
b8c5d4
 		iq->dsns_point, LDNS_RR_TYPE_NS, iq->qchase.qclass);
b8c5d4
 	if(!generate_sub_request(iq->dsns_point, iq->dsns_point_len, 
b8c5d4
 		LDNS_RR_TYPE_NS, iq->qchase.qclass, qstate, id, iq,
b8c5d4
-		INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0)) {
b8c5d4
+		INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0, 0)) {
b8c5d4
 		return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
b8c5d4
 	}
b8c5d4
 
b8c5d4
@@ -2039,7 +2081,14 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
b8c5d4
 			"number of sends with %d", iq->sent_count);
b8c5d4
 		return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
b8c5d4
 	}
b8c5d4
-	
b8c5d4
+    if(iq->target_count && iq->target_count[2] > MAX_TARGET_NX) {
b8c5d4
+        verbose(VERB_QUERY, "request has exceeded the maximum "
b8c5d4
+            " number of nxdomain nameserver lookups with %d",
b8c5d4
+            iq->target_count[2]);
b8c5d4
+        errinf(qstate, "exceeded the maximum nameserver nxdomains");
b8c5d4
+        return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
b8c5d4
+    }
b8c5d4
+
b8c5d4
 	/* Make sure we have a delegation point, otherwise priming failed
b8c5d4
 	 * or another failure occurred */
b8c5d4
 	if(!iq->dp) {
b8c5d4
@@ -2139,12 +2188,41 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
b8c5d4
 				iq->qinfo_out.qtype, iq->qinfo_out.qclass, 
b8c5d4
 				qstate->query_flags, qstate->region, 
b8c5d4
 				qstate->env->scratch, 0);
b8c5d4
-			if(msg && msg->rep->an_numrrsets == 0
b8c5d4
-				&& FLAGS_GET_RCODE(msg->rep->flags) == 
b8c5d4
+            if(msg && FLAGS_GET_RCODE(msg->rep->flags) ==
b8c5d4
 				LDNS_RCODE_NOERROR)
b8c5d4
 				/* no need to send query if it is already 
b8c5d4
-				 * cached as NOERROR/NODATA */
b8c5d4
+				 * cached as NOERROR */
b8c5d4
 				return 1;
b8c5d4
+            if(msg && FLAGS_GET_RCODE(msg->rep->flags) ==
b8c5d4
+                LDNS_RCODE_NXDOMAIN &&
b8c5d4
+                qstate->env->need_to_validate &&
b8c5d4
+                qstate->env->cfg->harden_below_nxdomain) {
b8c5d4
+                if(msg->rep->security == sec_status_secure) {
b8c5d4
+                    iq->response = msg;
b8c5d4
+                    return final_state(iq);
b8c5d4
+                }
b8c5d4
+                if(msg->rep->security == sec_status_unchecked) {
b8c5d4
+                    struct module_qstate* subq = NULL;
b8c5d4
+                    if(!generate_sub_request(
b8c5d4
+                        iq->qinfo_out.qname,
b8c5d4
+                        iq->qinfo_out.qname_len,
b8c5d4
+                        iq->qinfo_out.qtype,
b8c5d4
+                        iq->qinfo_out.qclass,
b8c5d4
+                        qstate, id, iq,
b8c5d4
+                        INIT_REQUEST_STATE,
b8c5d4
+                        FINISHED_STATE, &subq, 1, 1))
b8c5d4
+                        verbose(VERB_ALGO,
b8c5d4
+                        "could not validate NXDOMAIN "
b8c5d4
+                        "response");
b8c5d4
+                }
b8c5d4
+            }
b8c5d4
+            if(msg && FLAGS_GET_RCODE(msg->rep->flags) ==
b8c5d4
+                LDNS_RCODE_NXDOMAIN) {
b8c5d4
+                /* return and add a label in the next
b8c5d4
+                 * minimisation iteration.
b8c5d4
+                 */
b8c5d4
+                return 1;
b8c5d4
+            }
b8c5d4
 		}
b8c5d4
 	}
b8c5d4
 	if(iq->minimisation_state == SKIP_MINIMISE_STATE) {
b8c5d4
@@ -2219,6 +2297,8 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
b8c5d4
 	 * generated query will immediately be discarded due to depth and
b8c5d4
 	 * that servfail is cached, which is not good as opportunism goes. */
b8c5d4
 	if(iq->depth < ie->max_dependency_depth
b8c5d4
+        && iq->num_target_queries == 0
b8c5d4
+        && (!iq->target_count || iq->target_count[2]==0)
b8c5d4
 		&& iq->sent_count < TARGET_FETCH_STOP) {
b8c5d4
 		tf_policy = ie->target_fetch_policy[iq->depth];
b8c5d4
 	}
b8c5d4
@@ -2256,6 +2336,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
b8c5d4
 			iq->num_current_queries++; /* RespState decrements it*/
b8c5d4
 			iq->referral_count++; /* make sure we don't loop */
b8c5d4
 			iq->sent_count = 0;
b8c5d4
+            iq->dp_target_count = 0;
b8c5d4
 			iq->state = QUERY_RESP_STATE;
b8c5d4
 			return 1;
b8c5d4
 		}
b8c5d4
@@ -2341,6 +2422,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
b8c5d4
 					iq->num_current_queries++; /* RespState decrements it*/
b8c5d4
 					iq->referral_count++; /* make sure we don't loop */
b8c5d4
 					iq->sent_count = 0;
b8c5d4
+                    iq->dp_target_count = 0;
b8c5d4
 					iq->state = QUERY_RESP_STATE;
b8c5d4
 					return 1;
b8c5d4
 				}
b8c5d4
@@ -2607,7 +2689,8 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
b8c5d4
 				/* Make subrequest to validate intermediate
b8c5d4
 				 * NXDOMAIN if harden-below-nxdomain is
b8c5d4
 				 * enabled. */
b8c5d4
-				if(qstate->env->cfg->harden_below_nxdomain) {
b8c5d4
+				if(qstate->env->cfg->harden_below_nxdomain &&
b8c5d4
+                    qstate->env->need_to_validate) {
b8c5d4
 					struct module_qstate* subq = NULL;
b8c5d4
 					log_query_info(VERB_QUERY,
b8c5d4
 						"schedule NXDOMAIN validation:",
b8c5d4
@@ -2619,7 +2702,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
b8c5d4
 						iq->response->qinfo.qclass,
b8c5d4
 						qstate, id, iq,
b8c5d4
 						INIT_REQUEST_STATE,
b8c5d4
-						FINISHED_STATE, &subq, 1))
b8c5d4
+						FINISHED_STATE, &subq, 1, 1))
b8c5d4
 						verbose(VERB_ALGO,
b8c5d4
 						"could not validate NXDOMAIN "
b8c5d4
 						"response");
b8c5d4
@@ -2702,6 +2785,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
b8c5d4
 		/* Count this as a referral. */
b8c5d4
 		iq->referral_count++;
b8c5d4
 		iq->sent_count = 0;
b8c5d4
+        iq->dp_target_count = 0;
b8c5d4
 		/* see if the next dp is a trust anchor, or a DS was sent
b8c5d4
 		 * along, indicating dnssec is expected for next zone */
b8c5d4
 		iq->dnssec_expected = iter_indicates_dnssec(qstate->env, 
b8c5d4
@@ -2776,6 +2860,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
b8c5d4
 		iq->dsns_point = NULL;
b8c5d4
 		iq->auth_zone_response = 0;
b8c5d4
 		iq->sent_count = 0;
b8c5d4
+        iq->dp_target_count = 0;
b8c5d4
 		if(iq->minimisation_state != MINIMISE_STATE)
b8c5d4
 			/* Only count as query restart when it is not an extra
b8c5d4
 			 * query as result of qname minimisation. */
b8c5d4
@@ -2964,7 +3049,7 @@ processPrimeResponse(struct module_qstate* qstate, int id)
b8c5d4
 		if(!generate_sub_request(qstate->qinfo.qname, 
b8c5d4
 			qstate->qinfo.qname_len, qstate->qinfo.qtype,
b8c5d4
 			qstate->qinfo.qclass, qstate, id, iq,
b8c5d4
-			INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1)) {
b8c5d4
+			INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1, 0)) {
b8c5d4
 			verbose(VERB_ALGO, "could not generate prime check");
b8c5d4
 		}
b8c5d4
 		generate_a_aaaa_check(qstate, iq, id);
b8c5d4
@@ -2992,6 +3077,7 @@ static void
b8c5d4
 processTargetResponse(struct module_qstate* qstate, int id,
b8c5d4
 	struct module_qstate* forq)
b8c5d4
 {
b8c5d4
+    struct iter_env* ie = (struct iter_env*)qstate->env->modinfo[id];
b8c5d4
 	struct iter_qstate* iq = (struct iter_qstate*)qstate->minfo[id];
b8c5d4
 	struct iter_qstate* foriq = (struct iter_qstate*)forq->minfo[id];
b8c5d4
 	struct ub_packed_rrset_key* rrset;
b8c5d4
@@ -3029,7 +3115,7 @@ processTargetResponse(struct module_qstate* qstate, int id,
b8c5d4
 		log_rrset_key(VERB_ALGO, "add parentside glue to dp", 
b8c5d4
 			iq->pside_glue);
b8c5d4
 		if(!delegpt_add_rrset(foriq->dp, forq->region, 
b8c5d4
-			iq->pside_glue, 1))
b8c5d4
+			iq->pside_glue, 1, NULL))
b8c5d4
 			log_err("out of memory adding pside glue");
b8c5d4
 	}
b8c5d4
 
b8c5d4
@@ -3040,6 +3126,7 @@ processTargetResponse(struct module_qstate* qstate, int id,
b8c5d4
 	 * response type was ANSWER. */
b8c5d4
 	rrset = reply_find_answer_rrset(&iq->qchase, qstate->return_msg->rep);
b8c5d4
 	if(rrset) {
b8c5d4
+        int additions = 0;
b8c5d4
 		/* if CNAMEs have been followed - add new NS to delegpt. */
b8c5d4
 		/* BTW. RFC 1918 says NS should not have got CNAMEs. Robust. */
b8c5d4
 		if(!delegpt_find_ns(foriq->dp, rrset->rk.dname, 
b8c5d4
@@ -3051,13 +3138,23 @@ processTargetResponse(struct module_qstate* qstate, int id,
b8c5d4
 		}
b8c5d4
 		/* if dpns->lame then set the address(es) lame too */
b8c5d4
 		if(!delegpt_add_rrset(foriq->dp, forq->region, rrset, 
b8c5d4
-			dpns->lame))
b8c5d4
+			dpns->lame, &additions))
b8c5d4
 			log_err("out of memory adding targets");
b8c5d4
+        if(!additions) {
b8c5d4
+            /* no new addresses, increase the nxns counter, like
b8c5d4
+             * this could be a list of wildcards with no new
b8c5d4
+             * addresses */
b8c5d4
+            target_count_increase_nx(foriq, 1);
b8c5d4
+        }
b8c5d4
 		verbose(VERB_ALGO, "added target response");
b8c5d4
 		delegpt_log(VERB_ALGO, foriq->dp);
b8c5d4
 	} else {
b8c5d4
 		verbose(VERB_ALGO, "iterator TargetResponse failed");
b8c5d4
+        delegpt_mark_neg(dpns, qstate->qinfo.qtype);
b8c5d4
 		dpns->resolved = 1; /* fail the target */
b8c5d4
+        if((dpns->got4 == 2 || !ie->supports_ipv4) &&
b8c5d4
+            (dpns->got6 == 2 || !ie->supports_ipv6))
b8c5d4
+            target_count_increase_nx(foriq, 1);
b8c5d4
 	}
b8c5d4
 }
b8c5d4
 
b8c5d4
@@ -3228,7 +3325,7 @@ processCollectClass(struct module_qstate* qstate, int id)
b8c5d4
 				qstate->qinfo.qname_len, qstate->qinfo.qtype,
b8c5d4
 				c, qstate, id, iq, INIT_REQUEST_STATE,
b8c5d4
 				FINISHED_STATE, &subq, 
b8c5d4
-				(int)!(qstate->query_flags&BIT_CD))) {
b8c5d4
+				(int)!(qstate->query_flags&BIT_CD), 0)) {
b8c5d4
 				return error_response(qstate, id, 
b8c5d4
 					LDNS_RCODE_SERVFAIL);
b8c5d4
 			}
b8c5d4
diff --git a/iterator/iterator.h b/iterator/iterator.h
b8c5d4
index 67ffeb1..4b325b5 100644
b8c5d4
--- a/iterator/iterator.h
b8c5d4
+++ b/iterator/iterator.h
b8c5d4
@@ -55,6 +55,11 @@ struct rbtree_type;
b8c5d4
 
b8c5d4
 /** max number of targets spawned for a query and its subqueries */
b8c5d4
 #define MAX_TARGET_COUNT	64
b8c5d4
+/** max number of target lookups per qstate, per delegation point */
b8c5d4
+#define MAX_DP_TARGET_COUNT    16
b8c5d4
+/** max number of nxdomains allowed for target lookups for a query and
b8c5d4
+ * its subqueries */
b8c5d4
+#define MAX_TARGET_NX      5
b8c5d4
 /** max number of query restarts. Determines max number of CNAME chain. */
b8c5d4
 #define MAX_RESTART_COUNT       8
b8c5d4
 /** max number of referrals. Makes sure resolver does not run away */
b8c5d4
@@ -305,9 +310,14 @@ struct iter_qstate {
b8c5d4
 	int sent_count;
b8c5d4
 	
b8c5d4
 	/** number of target queries spawned in [1], for this query and its
b8c5d4
-	 * subqueries, the malloced-array is shared, [0] refcount. */
b8c5d4
+	 * subqueries, the malloced-array is shared, [0] refcount.
b8c5d4
+	 * in [2] the number of nxdomains is counted. */
b8c5d4
 	int* target_count;
b8c5d4
 
b8c5d4
+    /** number of target lookups per delegation point. Reset to 0 after
b8c5d4
+     * receiving referral answer. Not shared with subqueries. */
b8c5d4
+    int dp_target_count;
b8c5d4
+
b8c5d4
 	/** if true, already tested for ratelimiting and passed the test */
b8c5d4
 	int ratelimit_ok;
b8c5d4
 
b8c5d4
diff --git a/services/cache/dns.c b/services/cache/dns.c
b8c5d4
index 35adc35..23ec68e 100644
b8c5d4
--- a/services/cache/dns.c
b8c5d4
+++ b/services/cache/dns.c
b8c5d4
@@ -271,7 +271,7 @@ find_add_addrs(struct module_env* env, uint16_t qclass,
b8c5d4
 		akey = rrset_cache_lookup(env->rrset_cache, ns->name, 
b8c5d4
 			ns->namelen, LDNS_RR_TYPE_A, qclass, 0, now, 0);
b8c5d4
 		if(akey) {
b8c5d4
-			if(!delegpt_add_rrset_A(dp, region, akey, 0)) {
b8c5d4
+			if(!delegpt_add_rrset_A(dp, region, akey, 0, NULL)) {
b8c5d4
 				lock_rw_unlock(&akey->entry.lock);
b8c5d4
 				return 0;
b8c5d4
 			}
b8c5d4
@@ -291,7 +291,7 @@ find_add_addrs(struct module_env* env, uint16_t qclass,
b8c5d4
 		akey = rrset_cache_lookup(env->rrset_cache, ns->name, 
b8c5d4
 			ns->namelen, LDNS_RR_TYPE_AAAA, qclass, 0, now, 0);
b8c5d4
 		if(akey) {
b8c5d4
-			if(!delegpt_add_rrset_AAAA(dp, region, akey, 0)) {
b8c5d4
+			if(!delegpt_add_rrset_AAAA(dp, region, akey, 0, NULL)) {
b8c5d4
 				lock_rw_unlock(&akey->entry.lock);
b8c5d4
 				return 0;
b8c5d4
 			}
b8c5d4
@@ -325,7 +325,8 @@ cache_fill_missing(struct module_env* env, uint16_t qclass,
b8c5d4
 		akey = rrset_cache_lookup(env->rrset_cache, ns->name, 
b8c5d4
 			ns->namelen, LDNS_RR_TYPE_A, qclass, 0, now, 0);
b8c5d4
 		if(akey) {
b8c5d4
-			if(!delegpt_add_rrset_A(dp, region, akey, ns->lame)) {
b8c5d4
+			if(!delegpt_add_rrset_A(dp, region, akey, ns->lame,
b8c5d4
+                NULL)) {
b8c5d4
 				lock_rw_unlock(&akey->entry.lock);
b8c5d4
 				return 0;
b8c5d4
 			}
b8c5d4
@@ -345,7 +346,8 @@ cache_fill_missing(struct module_env* env, uint16_t qclass,
b8c5d4
 		akey = rrset_cache_lookup(env->rrset_cache, ns->name, 
b8c5d4
 			ns->namelen, LDNS_RR_TYPE_AAAA, qclass, 0, now, 0);
b8c5d4
 		if(akey) {
b8c5d4
-			if(!delegpt_add_rrset_AAAA(dp, region, akey, ns->lame)) {
b8c5d4
+			if(!delegpt_add_rrset_AAAA(dp, region, akey, ns->lame,
b8c5d4
+                NULL)) {
b8c5d4
 				lock_rw_unlock(&akey->entry.lock);
b8c5d4
 				return 0;
b8c5d4
 			}
b8c5d4
diff --git a/util/data/dname.c b/util/data/dname.c
b8c5d4
index c7360f7..b744f06 100644
b8c5d4
--- a/util/data/dname.c
b8c5d4
+++ b/util/data/dname.c
b8c5d4
@@ -231,17 +231,28 @@ int
b8c5d4
 dname_pkt_compare(sldns_buffer* pkt, uint8_t* d1, uint8_t* d2)
b8c5d4
 {
b8c5d4
 	uint8_t len1, len2;
b8c5d4
+    int count1 = 0, count2 = 0;
b8c5d4
 	log_assert(pkt && d1 && d2);
b8c5d4
 	len1 = *d1++;
b8c5d4
 	len2 = *d2++;
b8c5d4
 	while( len1 != 0 || len2 != 0 ) {
b8c5d4
 		/* resolve ptrs */
b8c5d4
 		if(LABEL_IS_PTR(len1)) {
b8c5d4
+            if((size_t)PTR_OFFSET(len1, *d1)
b8c5d4
+                >= sldns_buffer_limit(pkt))
b8c5d4
+                return -1;
b8c5d4
+            if(count1++ > MAX_COMPRESS_PTRS)
b8c5d4
+                return -1;
b8c5d4
 			d1 = sldns_buffer_at(pkt, PTR_OFFSET(len1, *d1));
b8c5d4
 			len1 = *d1++;
b8c5d4
 			continue;
b8c5d4
 		}
b8c5d4
 		if(LABEL_IS_PTR(len2)) {
b8c5d4
+            if((size_t)PTR_OFFSET(len2, *d2)
b8c5d4
+                >= sldns_buffer_limit(pkt))
b8c5d4
+                return 1;
b8c5d4
+            if(count2++ > MAX_COMPRESS_PTRS)
b8c5d4
+                return 1;
b8c5d4
 			d2 = sldns_buffer_at(pkt, PTR_OFFSET(len2, *d2));
b8c5d4
 			len2 = *d2++;
b8c5d4
 			continue;
b8c5d4
@@ -300,12 +311,19 @@ dname_pkt_hash(sldns_buffer* pkt, uint8_t* dname, hashvalue_type h)
b8c5d4
 	uint8_t labuf[LDNS_MAX_LABELLEN+1];
b8c5d4
 	uint8_t lablen;
b8c5d4
 	int i;
b8c5d4
+    int count = 0;
b8c5d4
 
b8c5d4
 	/* preserve case of query, make hash label by label */
b8c5d4
 	lablen = *dname++;
b8c5d4
 	while(lablen) {
b8c5d4
 		if(LABEL_IS_PTR(lablen)) {
b8c5d4
 			/* follow pointer */
b8c5d4
+            if((size_t)PTR_OFFSET(lablen, *dname)
b8c5d4
+                >= sldns_buffer_limit(pkt))
b8c5d4
+                return h;
b8c5d4
+            if(count++ > MAX_COMPRESS_PTRS)
b8c5d4
+                return h;
b8c5d4
+
b8c5d4
 			dname = sldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname));
b8c5d4
 			lablen = *dname++;
b8c5d4
 			continue;
b8c5d4
@@ -333,6 +351,9 @@ void dname_pkt_copy(sldns_buffer* pkt, uint8_t* to, uint8_t* dname)
b8c5d4
 	while(lablen) {
b8c5d4
 		if(LABEL_IS_PTR(lablen)) {
b8c5d4
 			/* follow pointer */
b8c5d4
+            if((size_t)PTR_OFFSET(lablen, *dname)
b8c5d4
+                >= sldns_buffer_limit(pkt))
b8c5d4
+                return;
b8c5d4
 			dname = sldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname));
b8c5d4
 			lablen = *dname++;
b8c5d4
 			continue;
b8c5d4
@@ -357,6 +378,7 @@ void dname_pkt_copy(sldns_buffer* pkt, uint8_t* to, uint8_t* dname)
b8c5d4
 void dname_print(FILE* out, struct sldns_buffer* pkt, uint8_t* dname)
b8c5d4
 {
b8c5d4
 	uint8_t lablen;
b8c5d4
+    int count = 0;
b8c5d4
 	if(!out) out = stdout;
b8c5d4
 	if(!dname) return;
b8c5d4
 
b8c5d4
@@ -370,6 +392,15 @@ void dname_print(FILE* out, struct sldns_buffer* pkt, uint8_t* dname)
b8c5d4
 				fputs("??compressionptr??", out);
b8c5d4
 				return;
b8c5d4
 			}
b8c5d4
+            if((size_t)PTR_OFFSET(lablen, *dname)
b8c5d4
+                >= sldns_buffer_limit(pkt)) {
b8c5d4
+                fputs("??compressionptr??", out);
b8c5d4
+                return;
b8c5d4
+            }
b8c5d4
+            if(count++ > MAX_COMPRESS_PTRS) {
b8c5d4
+                fputs("??compressionptr??", out);
b8c5d4
+                return;
b8c5d4
+            }
b8c5d4
 			dname = sldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname));
b8c5d4
 			lablen = *dname++;
b8c5d4
 			continue;
b8c5d4
diff --git a/util/data/msgparse.c b/util/data/msgparse.c
b8c5d4
index 13cad8a..c8a5384 100644
b8c5d4
--- a/util/data/msgparse.c
b8c5d4
+++ b/util/data/msgparse.c
b8c5d4
@@ -55,7 +55,11 @@ smart_compare(sldns_buffer* pkt, uint8_t* dnow,
b8c5d4
 {
b8c5d4
 	if(LABEL_IS_PTR(*dnow)) {
b8c5d4
 		/* ptr points to a previous dname */
b8c5d4
-		uint8_t* p = sldns_buffer_at(pkt, PTR_OFFSET(dnow[0], dnow[1]));
b8c5d4
+       uint8_t* p;
b8c5d4
+       if((size_t)PTR_OFFSET(dnow[0], dnow[1])
b8c5d4
+           >= sldns_buffer_limit(pkt))
b8c5d4
+           return -1;
b8c5d4
+       p = sldns_buffer_at(pkt, PTR_OFFSET(dnow[0], dnow[1]));
b8c5d4
 		if( p == dprfirst || p == dprlast )
b8c5d4
 			return 0;
b8c5d4
 		/* prev dname is also a ptr, both ptrs are the same. */