aboutsummaryrefslogtreecommitdiff
path: root/iterator
diff options
context:
space:
mode:
authorDag-Erling Smørgrav <des@FreeBSD.org>2026-08-04 10:04:44 +0000
committerDag-Erling Smørgrav <des@FreeBSD.org>2026-08-04 10:04:44 +0000
commit84ffc29dc8ddb0c946db5cb3b3c1310bec6a9e6c (patch)
tree93f0b0071b6b34da50dd62dae3cee8ed757676e8 /iterator
parentc68e7bcd81d62e9f5364c6da22fd9917976acf85 (diff)
Diffstat (limited to 'iterator')
-rw-r--r--iterator/iter_delegpt.c55
-rw-r--r--iterator/iter_delegpt.h6
-rw-r--r--iterator/iter_resptype.c25
-rw-r--r--iterator/iter_resptype.h6
-rw-r--r--iterator/iter_scrub.c76
-rw-r--r--iterator/iter_scrub.h7
-rw-r--r--iterator/iter_utils.c11
-rw-r--r--iterator/iter_utils.h3
-rw-r--r--iterator/iterator.c84
-rw-r--r--iterator/iterator.h18
10 files changed, 234 insertions, 57 deletions
diff --git a/iterator/iter_delegpt.c b/iterator/iter_delegpt.c
index be5bf482d673..f0f94cadd219 100644
--- a/iterator/iter_delegpt.c
+++ b/iterator/iter_delegpt.c
@@ -118,10 +118,10 @@ delegpt_add_ns(struct delegpt* dp, struct regional* region, uint8_t* name,
sizeof(struct delegpt_ns));
if(!ns)
return 0;
- ns->next = dp->nslist;
ns->namelen = len;
- dp->nslist = ns;
ns->name = regional_alloc_init(region, name, ns->namelen);
+ if(!ns->name)
+ return 0;
ns->cache_lookup_count = 0;
ns->resolved = 0;
ns->got4 = 0;
@@ -137,7 +137,9 @@ delegpt_add_ns(struct delegpt* dp, struct regional* region, uint8_t* name,
} else {
ns->tls_auth_name = NULL;
}
- return ns->name != 0;
+ ns->next = dp->nslist;
+ dp->nslist = ns;
+ return 1;
}
struct delegpt_ns*
@@ -223,11 +225,7 @@ delegpt_add_addr(struct delegpt* dp, struct regional* region,
sizeof(struct delegpt_addr));
if(!a)
return 0;
- a->next_target = dp->target_list;
- dp->target_list = a;
a->next_result = 0;
- a->next_usable = dp->usable_list;
- dp->usable_list = a;
memcpy(&a->addr, addr, addrlen);
a->addrlen = addrlen;
a->attempts = 0;
@@ -241,6 +239,10 @@ delegpt_add_addr(struct delegpt* dp, struct regional* region,
} else {
a->tls_auth_name = NULL;
}
+ a->next_target = dp->target_list;
+ dp->target_list = a;
+ a->next_usable = dp->usable_list;
+ dp->usable_list = a;
return 1;
}
@@ -398,30 +400,33 @@ delegpt_count_missing_targets(struct delegpt* dp, int* alllame)
/** find NS rrset in given list */
static struct ub_packed_rrset_key*
-find_NS(struct reply_info* rep, size_t from, size_t to)
+find_NS(struct reply_info* rep, size_t from, size_t to, uint16_t qclass)
{
size_t i;
for(i=from; i<to; i++) {
- if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NS)
+ if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NS &&
+ ntohs(rep->rrsets[i]->rk.rrset_class) == qclass)
return rep->rrsets[i];
}
return NULL;
}
struct delegpt*
-delegpt_from_message(struct dns_msg* msg, struct regional* region)
+delegpt_from_message(struct dns_msg* msg, struct regional* region, int port)
{
struct ub_packed_rrset_key* ns_rrset = NULL;
struct delegpt* dp;
size_t i;
/* look for NS records in the authority section... */
ns_rrset = find_NS(msg->rep, msg->rep->an_numrrsets,
- msg->rep->an_numrrsets+msg->rep->ns_numrrsets);
+ msg->rep->an_numrrsets+msg->rep->ns_numrrsets,
+ msg->qinfo.qclass);
/* In some cases (even legitimate, perfectly legal cases), the
* NS set for the "referral" might be in the answer section. */
if(!ns_rrset)
- ns_rrset = find_NS(msg->rep, 0, msg->rep->an_numrrsets);
+ ns_rrset = find_NS(msg->rep, 0, msg->rep->an_numrrsets,
+ msg->qinfo.qclass);
/* If there was no NS rrset in the authority section, then this
* wasn't a referral message. (It might not actually be a
@@ -436,7 +441,7 @@ delegpt_from_message(struct dns_msg* msg, struct regional* region)
dp->has_parent_side_NS = 1; /* created from message */
if(!delegpt_set_name(dp, region, ns_rrset->rk.dname))
return NULL;
- if(!delegpt_rrset_add_ns(dp, region, ns_rrset, 0))
+ if(!delegpt_rrset_add_ns(dp, region, ns_rrset, 0, port))
return NULL;
/* add glue, A and AAAA in answer and additional section */
@@ -447,10 +452,12 @@ delegpt_from_message(struct dns_msg* msg, struct regional* region)
i < (msg->rep->an_numrrsets+msg->rep->ns_numrrsets))
continue;
- if(ntohs(s->rk.type) == LDNS_RR_TYPE_A) {
+ if(ntohs(s->rk.type) == LDNS_RR_TYPE_A &&
+ ntohs(s->rk.rrset_class) == msg->qinfo.qclass) {
if(!delegpt_add_rrset_A(dp, region, s, 0, NULL))
return NULL;
- } else if(ntohs(s->rk.type) == LDNS_RR_TYPE_AAAA) {
+ } else if(ntohs(s->rk.type) == LDNS_RR_TYPE_AAAA &&
+ ntohs(s->rk.rrset_class) == msg->qinfo.qclass) {
if(!delegpt_add_rrset_AAAA(dp, region, s, 0, NULL))
return NULL;
}
@@ -460,7 +467,7 @@ delegpt_from_message(struct dns_msg* msg, struct regional* region)
int
delegpt_rrset_add_ns(struct delegpt* dp, struct regional* region,
- struct ub_packed_rrset_key* ns_rrset, uint8_t lame)
+ struct ub_packed_rrset_key* ns_rrset, uint8_t lame, int port)
{
struct packed_rrset_data* nsdata = (struct packed_rrset_data*)
ns_rrset->entry.data;
@@ -475,7 +482,7 @@ delegpt_rrset_add_ns(struct delegpt* dp, struct regional* region,
continue; /* bad format */
/* add rdata of NS (= wirefmt dname), skip rdatalen bytes */
if(!delegpt_add_ns(dp, region, nsdata->rr_data[i]+2, lame,
- NULL, UNBOUND_DNS_PORT))
+ NULL, (port==-1?UNBOUND_DNS_PORT:port)))
return 0;
}
return 1;
@@ -534,7 +541,7 @@ delegpt_add_rrset(struct delegpt* dp, struct regional* region,
if(!rrset)
return 1;
if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_NS)
- return delegpt_rrset_add_ns(dp, region, rrset, lame);
+ return delegpt_rrset_add_ns(dp, region, rrset, lame, -1);
else if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_A)
return delegpt_add_rrset_A(dp, region, rrset, lame, additions);
else if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_AAAA)
@@ -659,8 +666,6 @@ int delegpt_add_ns_mlc(struct delegpt* dp, uint8_t* name, uint8_t lame,
free(ns);
return 0;
}
- ns->next = dp->nslist;
- dp->nslist = ns;
ns->cache_lookup_count = 0;
ns->resolved = 0;
ns->got4 = 0;
@@ -679,6 +684,8 @@ int delegpt_add_ns_mlc(struct delegpt* dp, uint8_t* name, uint8_t lame,
} else {
ns->tls_auth_name = NULL;
}
+ ns->next = dp->nslist;
+ dp->nslist = ns;
return 1;
}
@@ -704,11 +711,7 @@ int delegpt_add_addr_mlc(struct delegpt* dp, struct sockaddr_storage* addr,
a = (struct delegpt_addr*)malloc(sizeof(struct delegpt_addr));
if(!a)
return 0;
- a->next_target = dp->target_list;
- dp->target_list = a;
a->next_result = 0;
- a->next_usable = dp->usable_list;
- dp->usable_list = a;
memcpy(&a->addr, addr, addrlen);
a->addrlen = addrlen;
a->attempts = 0;
@@ -724,6 +727,10 @@ int delegpt_add_addr_mlc(struct delegpt* dp, struct sockaddr_storage* addr,
} else {
a->tls_auth_name = NULL;
}
+ a->next_target = dp->target_list;
+ dp->target_list = a;
+ a->next_usable = dp->usable_list;
+ dp->usable_list = a;
return 1;
}
diff --git a/iterator/iter_delegpt.h b/iterator/iter_delegpt.h
index 287bf92134d2..db15277fca18 100644
--- a/iterator/iter_delegpt.h
+++ b/iterator/iter_delegpt.h
@@ -221,10 +221,11 @@ int delegpt_add_ns(struct delegpt* dp, struct regional* regional,
* @param regional: where to allocate the info.
* @param ns_rrset: NS rrset.
* @param lame: rrset is lame, disprefer it.
+ * @param port: port or -1 if not set.
* @return 0 on alloc error.
*/
int delegpt_rrset_add_ns(struct delegpt* dp, struct regional* regional,
- struct ub_packed_rrset_key* ns_rrset, uint8_t lame);
+ struct ub_packed_rrset_key* ns_rrset, uint8_t lame, int port);
/**
* Add target address to the delegation point.
@@ -365,11 +366,12 @@ size_t delegpt_count_targets(struct delegpt* dp);
*
* @param msg: the dns message, referral.
* @param regional: where to allocate delegation point.
+ * @param port: if not -1 specifies a port number.
* @return new delegation point or NULL on alloc error, or if the
* message was not appropriate.
*/
struct delegpt* delegpt_from_message(struct dns_msg* msg,
- struct regional* regional);
+ struct regional* regional, int port);
/**
* Mark negative return in delegation point for specific nameserver.
diff --git a/iterator/iter_resptype.c b/iterator/iter_resptype.c
index 38e186e79048..f16520b3eaad 100644
--- a/iterator/iter_resptype.c
+++ b/iterator/iter_resptype.c
@@ -107,7 +107,7 @@ response_type_from_cache(struct dns_msg* msg,
enum response_type
response_type_from_server(int rdset,
struct dns_msg* msg, struct query_info* request, struct delegpt* dp,
- int* empty_nodata_found)
+ int* empty_nodata_found, int msg_lame_empty, int msg_lame_referral)
{
uint8_t* origzone = (uint8_t*)"\000"; /* the default */
struct ub_packed_rrset_key* s;
@@ -122,6 +122,10 @@ response_type_from_server(int rdset,
/* If the message is NXDOMAIN, then it answers the question. */
if(FLAGS_GET_RCODE(msg->rep->flags) == LDNS_RCODE_NXDOMAIN) {
+ if(msg->rep->an_numrrsets == 0 &&
+ msg->rep->ns_numrrsets == 0 &&
+ msg_lame_empty)
+ return RESPONSE_TYPE_LAME;
/* make sure its not recursive when we don't want it to */
if( (msg->rep->flags&BIT_RA) &&
!(msg->rep->flags&BIT_AA) && !rdset)
@@ -143,6 +147,10 @@ response_type_from_server(int rdset,
if(FLAGS_GET_RCODE(msg->rep->flags) != LDNS_RCODE_NOERROR)
return RESPONSE_TYPE_THROWAWAY;
+ if(msg->rep->an_numrrsets == 0 && msg->rep->ns_numrrsets == 0 &&
+ msg_lame_empty)
+ return RESPONSE_TYPE_LAME;
+
/* Note: TC bit has already been handled */
if(dp) {
@@ -249,13 +257,16 @@ response_type_from_server(int rdset,
* which gives ns==zone delegation from cache
* without AA bit as well, with nodata nosoa*/
/* real answer must be +AA and SOA RFC(2308),
- * so this is wrong, and we SERVFAIL it if
- * this is the only possible reply, if it
- * is misdeployed the THROWAWAY makes us pick
- * the next server from the selection */
- if(msg->rep->an_numrrsets==0 &&
+ * this is picked up as lame_referral by the
+ * sanitize step, so it can spot if there
+ * was data in the answer section before
+ * removal. If such data is then removed we
+ * do not want to turn that answer into lame.
+ * But if it was not there, it can be lame. */
+ if(msg_lame_referral &&
+ msg->rep->an_numrrsets==0 &&
!(msg->rep->flags&BIT_AA) && !rdset)
- return RESPONSE_TYPE_THROWAWAY;
+ return RESPONSE_TYPE_LAME;
return RESPONSE_TYPE_ANSWER;
}
/* If we are getting a referral upwards (or to
diff --git a/iterator/iter_resptype.h b/iterator/iter_resptype.h
index bfd4b664f621..73719abda875 100644
--- a/iterator/iter_resptype.h
+++ b/iterator/iter_resptype.h
@@ -120,10 +120,14 @@ enum response_type response_type_from_cache(struct dns_msg* msg,
* @param dp: The delegation point that was being queried
* when the response was returned.
* @param empty_nodata_found: flag to keep track of empty nodata detection.
+ * @param msg_lame_empty: The scrubber indicates that this empty message
+ * is lame, before it became empty.
+ * @param msg_lame_referral: returned true if the reply has a referral before
+ * scrub.
* @return the response type (CNAME or ANSWER).
*/
enum response_type response_type_from_server(int rdset,
struct dns_msg* msg, struct query_info* request, struct delegpt* dp,
- int* empty_nodata_found);
+ int* empty_nodata_found, int msg_lame_empty, int msg_lame_referral);
#endif /* ITERATOR_ITER_RESPTYPE_H */
diff --git a/iterator/iter_scrub.c b/iterator/iter_scrub.c
index 74a2586402cb..f2f20a5c1ad3 100644
--- a/iterator/iter_scrub.c
+++ b/iterator/iter_scrub.c
@@ -316,6 +316,20 @@ synth_cname_rrset(uint8_t** sname, size_t* snamelen, uint8_t* alias,
return cn;
}
+/** Check if the packet has type NS in answer or authority section */
+static int
+pkt_contains_ns(struct msg_parse* msg)
+{
+ struct rrset_parse* rrset;
+ for(rrset = msg->rrset_first; rrset; rrset = rrset->rrset_all_next) {
+ if(rrset->type == LDNS_RR_TYPE_NS &&
+ (rrset->section == LDNS_SECTION_ANSWER ||
+ rrset->section == LDNS_SECTION_AUTHORITY))
+ return 1;
+ }
+ return 0;
+}
+
/** check if DNAME applies to a name */
static int
pkt_strict_sub(sldns_buffer* pkt, uint8_t* sname, uint8_t* dr)
@@ -394,6 +408,8 @@ shorten_rrset(sldns_buffer* pkt, struct rrset_parse* rrset, int count)
struct rr_parse* rr = rrset->rr_first, *prev = NULL;
if(!rr)
return;
+ if(count < 1)
+ return; /* cannot leave a still-linked rrset_parse with rr_count == 0 */
for(i=0; i<count; i++) {
prev = rr;
rr = rr->next;
@@ -478,6 +494,7 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg,
size_t snamelen = qinfo->qname_len;
struct rrset_parse* rrset, *prev, *nsset=NULL;
int cname_length = 0; /* number of CNAMEs, or DNAMEs */
+ int has_answer = 0; /* if answer section contains nonCNAME,nonDNAME */
if(FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NOERROR &&
FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NXDOMAIN &&
@@ -519,6 +536,11 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg,
(unsigned)rrset->rr_count);
return 0;
}
+ if(has_answer) {
+ remove_rrset("normalize: removing DNAME redirection after answer:",
+ pkt, msg, prev, &rrset);
+ continue;
+ }
if(!synth_cname(sname, snamelen, rrset, alias,
&aliaslen, pkt)) {
verbose(VERB_ALGO, "synthesized CNAME "
@@ -569,6 +591,11 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg,
if(rrset->type == LDNS_RR_TYPE_CNAME) {
struct rrset_parse* nx = rrset->rrset_all_next;
uint8_t* oldsname = sname;
+ if(has_answer) {
+ remove_rrset("normalize: removing redirection after answer:",
+ pkt, msg, prev, &rrset);
+ continue;
+ }
cname_length++;
/* see if the next one is a DNAME, if so, swap them */
if(nx && nx->section == LDNS_SECTION_ANSWER &&
@@ -647,6 +674,7 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg,
* will be removed by sanitize, so no additional for them */
if(dname_pkt_compare(pkt, qinfo->qname, rrset->dname) == 0)
mark_additional_rrset(pkt, msg, rrset);
+ has_answer = 1;
prev = rrset;
rrset = rrset->rrset_all_next;
@@ -732,6 +760,11 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg,
"RRset:", pkt, msg, prev, &rrset);
continue;
}
+ if(ntohs(rrset->rrset_class) != qinfo->qclass) {
+ remove_rrset("normalize: removing other class "
+ "RRset:", pkt, msg, prev, &rrset);
+ continue;
+ }
if(nsset == NULL) {
nsset = rrset;
} else {
@@ -968,12 +1001,20 @@ scrub_sanitize_rr_length(sldns_buffer* pkt, struct msg_parse* msg,
* @param env: module environment with config and cache.
* @param ie: iterator environment with private address data.
* @param qstate: for setting errinf for EDE error messages.
+ * @param pkt_before_NS: if the packet had type NS before scrub. If that
+ * is removed now, that indicates this may have been lame.
+ * @param msg_lame_empty: returned true if the empty packet is lame.
+ * @param msg_lame_referral: returned true if the reply has a referral before
+ * scrub.
+ * @param rdset: if RD bit was sent in query sent by unbound.
* @return 0 on error.
*/
static int
scrub_sanitize(sldns_buffer* pkt, struct msg_parse* msg,
struct query_info* qinfo, uint8_t* zonename, struct module_env* env,
- struct iter_env* ie, struct module_qstate* qstate)
+ struct iter_env* ie, struct module_qstate* qstate,
+ int pkt_before_NS, int* msg_lame_empty, int* msg_lame_referral,
+ int rdset)
{
int del_addi = 0; /* if additional-holding rrsets are deleted, we
do not trust the normalized additional-A-AAAA any more */
@@ -1130,6 +1171,21 @@ scrub_sanitize(sldns_buffer* pkt, struct msg_parse* msg,
prev = rrset;
rrset = rrset->rrset_all_next;
}
+
+ /* If the packet is empty now, but it was not before. And there
+ * was type NS in authority, then that indicates the answer is lame. */
+ if(msg->rrset_first == NULL && pkt_before_NS) {
+ *msg_lame_empty = 1;
+ verbose(VERB_ALGO, "sanitize: empty message had referral to NS before, marked as lame");
+ } else if(pkt_before_NS && msg->an_rrsets==0 &&
+ !(msg->flags&BIT_AA) && !rdset) {
+ /* If the packet is now a referral, not really a nodata,
+ * then if it was also with an empty answer section before,
+ * it is also lame. */
+ *msg_lame_referral = 1;
+ verbose(VERB_ALGO, "sanitize: message has referral not answer, marked as lame");
+ }
+
return 1;
}
@@ -1137,11 +1193,15 @@ int
scrub_message(sldns_buffer* pkt, struct msg_parse* msg,
struct query_info* qinfo, uint8_t* zonename, struct regional* region,
struct module_env* env, struct module_qstate* qstate,
- struct iter_env* ie)
+ struct iter_env* ie, int* msg_lame_empty, int* msg_lame_referral,
+ int rdset)
{
+ int pkt_before_NS;
/* basic sanity checks */
log_nametypeclass(VERB_ALGO, "scrub for", zonename, LDNS_RR_TYPE_NS,
qinfo->qclass);
+ *msg_lame_empty = 0;
+ *msg_lame_referral = 0;
if(msg->qdcount > 1)
return 0;
if( !(msg->flags&BIT_QR) )
@@ -1166,11 +1226,21 @@ scrub_message(sldns_buffer* pkt, struct msg_parse* msg,
return 0;
}
+ /* If the packet contains type NS in authority before scrub,
+ * like a self referral. With the answer section empty, it
+ * was not AA, the query was not sent with RD, with NS in auth,
+ * and no SOA in auth. For a negative answer, type SOA is present.
+ * This detects certain lameness if after has removed that. */
+ pkt_before_NS = msg->an_rrsets == 0 &&
+ !(msg->flags&BIT_AA) && !rdset &&
+ pkt_contains_ns(msg) && !soa_in_auth(msg);
+
/* normalize the response, this cleans up the additional. */
if(!scrub_normalize(pkt, msg, qinfo, region, env, zonename))
return 0;
/* delete all out-of-zone information */
- if(!scrub_sanitize(pkt, msg, qinfo, zonename, env, ie, qstate))
+ if(!scrub_sanitize(pkt, msg, qinfo, zonename, env, ie, qstate,
+ pkt_before_NS, msg_lame_empty, msg_lame_referral, rdset))
return 0;
return 1;
}
diff --git a/iterator/iter_scrub.h b/iterator/iter_scrub.h
index 4d6ce7166516..6b40e6c574c9 100644
--- a/iterator/iter_scrub.h
+++ b/iterator/iter_scrub.h
@@ -62,11 +62,16 @@ struct module_qstate;
* @param env: module environment with config settings and cache.
* @param qstate: for setting errinf for EDE error messages.
* @param ie: iterator module environment data.
+ * @param msg_lame_empty: returned true if the empty packet is lame.
+ * @param msg_lame_referral: returned true if the reply has a referral before
+ * scrub.
+ * @param rdset: if RD bit was sent in query sent by unbound.
* @return: false if the message is total waste. true if scrubbed with success.
*/
int scrub_message(struct sldns_buffer* pkt, struct msg_parse* msg,
struct query_info* qinfo, uint8_t* zonename, struct regional* regional,
struct module_env* env, struct module_qstate* qstate,
- struct iter_env* ie);
+ struct iter_env* ie, int* msg_lame_empty, int* msg_lame_referral,
+ int rdset);
#endif /* ITERATOR_ITER_SCRUB_H */
diff --git a/iterator/iter_utils.c b/iterator/iter_utils.c
index cc09fa52461e..e848e83ba812 100644
--- a/iterator/iter_utils.c
+++ b/iterator/iter_utils.c
@@ -1313,7 +1313,8 @@ iter_lookup_parent_NS_from_cache(struct module_env* env, struct delegpt* dp,
log_rrset_key(VERB_ALGO, "found parent-side NS in cache", akey);
dp->has_parent_side_NS = 1;
/* and mark the new names as lame */
- if(!delegpt_rrset_add_ns(dp, region, akey, 1)) {
+ if(!delegpt_rrset_add_ns(dp, region, akey, 1,
+ deleg_port_number(env))) {
lock_rw_unlock(&akey->entry.lock);
return 0;
}
@@ -1703,3 +1704,11 @@ iter_make_minimal(struct reply_info* rep)
rep->ar_numrrsets = 0;
rep->rrset_count -= rem;
}
+
+int
+deleg_port_number(struct module_env* env)
+{
+ if(env->cfg->ssl_upstream)
+ return env->cfg->ssl_port;
+ return -1;
+}
diff --git a/iterator/iter_utils.h b/iterator/iter_utils.h
index f7f374742803..9fb361ab5298 100644
--- a/iterator/iter_utils.h
+++ b/iterator/iter_utils.h
@@ -483,4 +483,7 @@ void limit_nsec_ttl(struct dns_msg* msg);
*/
void iter_make_minimal(struct reply_info* rep);
+/** See if we need a different port number */
+int deleg_port_number(struct module_env* env);
+
#endif /* ITERATOR_ITER_UTILS_H */
diff --git a/iterator/iterator.c b/iterator/iterator.c
index 78cd9485bad4..cadb95ebd0ab 100644
--- a/iterator/iterator.c
+++ b/iterator/iterator.c
@@ -1511,6 +1511,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
verbose(VERB_ALGO, "no-cache set, going to the network");
qstate->no_cache_lookup = 1;
qstate->no_cache_store = 1;
+ qstate->fwd_stub_no_cache = 1;
msg = NULL;
} else if(qstate->blacklist) {
/* if cache, or anything else, was blacklisted then
@@ -1530,7 +1531,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
msg = val_neg_getmsg(qstate->env->neg_cache, &iq->qchase,
qstate->region, qstate->env->rrset_cache,
qstate->env->scratch_buffer,
- *qstate->env->now, 1/*add SOA*/, NULL,
+ *qstate->env->now, 1/*add SOA*/, dpname,
qstate->env->cfg);
}
/* item taken from cache does not match our query name, thus
@@ -2391,6 +2392,12 @@ processDSNSFind(struct module_qstate* qstate, struct iter_qstate* iq, int id)
/* go up one (more) step, until we hit the dp, if so, end */
dname_remove_label(&iq->dsns_point, &iq->dsns_point_len);
+ if(++iq->dsns_count > MAX_DSNS_FIND_COUNT) {
+ verbose(VERB_QUERY, "DS NS search exceeded %d labels",
+ MAX_DSNS_FIND_COUNT);
+ errinf(qstate, "DS NS search exceeded label limit");
+ return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
+ }
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
@@ -3073,7 +3080,9 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
/* Do not check ratelimit for forwarding queries or if we already got a
* pass. */
- sq_check_ratelimit = (!(iq->chase_flags & BIT_RD) && !iq->ratelimit_ok);
+ sq_check_ratelimit = ((!(iq->chase_flags & BIT_RD) &&
+ !iq->ratelimit_ok));
+ iq->ratelimit_incremented = 0;
/* We have a valid target. */
if(verbosity >= VERB_QUERY) {
log_query_info(VERB_QUERY, "sending query:", &iq->qinfo_out);
@@ -3099,7 +3108,8 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
iq->dp->name, iq->dp->namelen,
(iq->dp->tcp_upstream || qstate->env->cfg->tcp_upstream),
(iq->dp->ssl_upstream || qstate->env->cfg->ssl_upstream),
- target->tls_auth_name, qstate, &sq_was_ratelimited);
+ target->tls_auth_name, qstate, &sq_was_ratelimited,
+ &iq->ratelimit_incremented);
if(!outq) {
if(sq_was_ratelimited) {
lock_basic_lock(&ie->queries_ratelimit_lock);
@@ -3137,7 +3147,6 @@ find_NS(struct reply_info* rep, size_t from, size_t to)
return NULL;
}
-
/**
* Process the query response. All queries end up at this state first. This
* process generally consists of analyzing the response and routing the
@@ -3179,7 +3188,8 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
orig_empty_nodata_found = iq->empty_nodata_found;
type = response_type_from_server(
(int)((iq->chase_flags&BIT_RD) || iq->chase_to_rd),
- iq->response, &iq->qinfo_out, iq->dp, &iq->empty_nodata_found);
+ iq->response, &iq->qinfo_out, iq->dp, &iq->empty_nodata_found,
+ iq->msg_lame_empty, iq->msg_lame_referral);
iq->chase_to_rd = 0;
/* remove TC flag, if this is erroneously set by TCP upstream */
iq->response->rep->flags &= ~BIT_TC;
@@ -3457,7 +3467,14 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
iq->deleg_msg = iq->response;
/* Keep current delegation point for label comparison */
old_dp = iq->dp;
- iq->dp = delegpt_from_message(iq->response, qstate->region);
+ /* A referral reply is "pleasant", refund the
+ * parent dp's rate charge before descending to the child. */
+ if(iq->ratelimit_incremented)
+ infra_ratelimit_dec(qstate->env->infra_cache,
+ old_dp->name, old_dp->namelen,
+ *qstate->env->now);
+ iq->dp = delegpt_from_message(iq->response, qstate->region,
+ deleg_port_number(qstate->env));
if (qstate->env->cfg->qname_minimisation)
iq->minimisation_state = INIT_MINIMISE_STATE;
if(!iq->dp) {
@@ -3734,7 +3751,8 @@ prime_supers(struct module_qstate* qstate, int id, struct module_qstate* forq)
log_assert(qstate->is_priming || foriq->wait_priming_stub);
log_assert(qstate->return_rcode == LDNS_RCODE_NOERROR);
/* Convert our response to a delegation point */
- dp = delegpt_from_message(qstate->return_msg, forq->region);
+ dp = delegpt_from_message(qstate->return_msg, forq->region,
+ deleg_port_number(forq->env));
if(!dp) {
/* if there is no convertible delegation point, then
* the ANSWER type was (presumably) a negative answer. */
@@ -3785,7 +3803,8 @@ processPrimeResponse(struct module_qstate* qstate, int id)
iq->response->rep->flags &= ~(BIT_RD|BIT_RA); /* ignore rec-lame */
type = response_type_from_server(
(int)((iq->chase_flags&BIT_RD) || iq->chase_to_rd),
- iq->response, &iq->qchase, iq->dp, NULL);
+ iq->response, &iq->qchase, iq->dp, NULL, iq->msg_lame_empty,
+ iq->msg_lame_referral);
if(type == RESPONSE_TYPE_ANSWER) {
qstate->return_rcode = LDNS_RCODE_NOERROR;
qstate->return_msg = iq->response;
@@ -3949,7 +3968,8 @@ processDSNSResponse(struct module_qstate* qstate, int id,
/* else, store as DP and continue at querytargets */
foriq->state = QUERYTARGETS_STATE;
- foriq->dp = delegpt_from_message(qstate->return_msg, forq->region);
+ foriq->dp = delegpt_from_message(qstate->return_msg, forq->region,
+ deleg_port_number(forq->env));
if(!foriq->dp) {
log_err("out of memory in dsns dp alloc");
errinf(qstate, "malloc failure, in DS search");
@@ -3998,7 +4018,7 @@ processClassResponse(struct module_qstate* qstate, int id,
/* if there are records, copy RCODE */
/* lower sec_state if this message is lower */
if(from->rep->rrset_count != 0) {
- size_t n = from->rep->rrset_count+to->rep->rrset_count;
+ size_t i, n = from->rep->rrset_count+to->rep->rrset_count;
struct ub_packed_rrset_key** dest, **d;
/* copy appropriate rcode */
to->rep->flags = from->rep->flags;
@@ -4020,24 +4040,49 @@ processClassResponse(struct module_qstate* qstate, int id,
memcpy(dest, to->rep->rrsets, to->rep->an_numrrsets
* sizeof(dest[0]));
dest += to->rep->an_numrrsets;
- memcpy(dest, from->rep->rrsets, from->rep->an_numrrsets
- * sizeof(dest[0]));
+ for(i=0; i<from->rep->an_numrrsets; i++) {
+ dest[i] = packed_rrset_copy_region(
+ from->rep->rrsets[i], forq->region, 0);
+ if(!dest[i]) {
+ log_err("malloc failed in collect ANY");
+ foriq->state = FINISHED_STATE;
+ return;
+ }
+ }
dest += from->rep->an_numrrsets;
/* copy NS */
memcpy(dest, to->rep->rrsets+to->rep->an_numrrsets,
to->rep->ns_numrrsets * sizeof(dest[0]));
dest += to->rep->ns_numrrsets;
- memcpy(dest, from->rep->rrsets+from->rep->an_numrrsets,
- from->rep->ns_numrrsets * sizeof(dest[0]));
+ for(i=0; i<from->rep->ns_numrrsets; i++) {
+ dest[i] = packed_rrset_copy_region(
+ from->rep->rrsets[
+ from->rep->an_numrrsets+i],
+ forq->region, 0);
+ if(!dest[i]) {
+ log_err("malloc failed in collect ANY");
+ foriq->state = FINISHED_STATE;
+ return;
+ }
+ }
dest += from->rep->ns_numrrsets;
/* copy AR */
memcpy(dest, to->rep->rrsets+to->rep->an_numrrsets+
to->rep->ns_numrrsets,
to->rep->ar_numrrsets * sizeof(dest[0]));
dest += to->rep->ar_numrrsets;
- memcpy(dest, from->rep->rrsets+from->rep->an_numrrsets+
- from->rep->ns_numrrsets,
- from->rep->ar_numrrsets * sizeof(dest[0]));
+ for(i=0; i<from->rep->ar_numrrsets; i++) {
+ dest[i] = packed_rrset_copy_region(
+ from->rep->rrsets[
+ from->rep->an_numrrsets+
+ from->rep->ns_numrrsets+i],
+ forq->region, 0);
+ if(!dest[i]) {
+ log_err("malloc failed in collect ANY");
+ foriq->state = FINISHED_STATE;
+ return;
+ }
+ }
/* update counts */
to->rep->rrsets = d;
to->rep->an_numrrsets += from->rep->an_numrrsets;
@@ -4395,7 +4440,10 @@ process_response(struct module_qstate* qstate, struct iter_qstate* iq,
/* normalize and sanitize: easy to delete items from linked lists */
if(!scrub_message(pkt, prs, &iq->qinfo_out, iq->dp->name,
- qstate->env->scratch, qstate->env, qstate, ie)) {
+ qstate->env->scratch, qstate->env, qstate, ie,
+ &iq->msg_lame_empty, &iq->msg_lame_referral,
+ (int)((iq->chase_flags&BIT_RD) || iq->chase_to_rd)
+ )) {
/* if 0x20 enabled, start fallback, but we have no message */
if(event == module_event_capsfail && !iq->caps_fallback) {
iq->caps_fallback = 1;
diff --git a/iterator/iterator.h b/iterator/iterator.h
index ae4b4e45170a..df5449c52533 100644
--- a/iterator/iterator.h
+++ b/iterator/iterator.h
@@ -104,6 +104,11 @@ extern int BLACKLIST_PENALTY;
#define RTT_BAND 400
/** Number of retries for empty nodata packets before it is accepted. */
#define EMPTY_NODATA_RETRY_COUNT 2
+/** max label-strip iterations in DSNS_FIND_STATE (RFC 4035 4.2 parent-NS
+ * search) before giving up; bounds upstream NS sends per client DS.
+ * Means the max number of labels in grandchild to the grandparent zone that
+ * are co-hosted. */
+#define MAX_DSNS_FIND_COUNT 20
/**
* Iterator global state for nat64.
@@ -375,6 +380,10 @@ struct iter_qstate {
/** if true, already tested for ratelimiting and passed the test */
int ratelimit_ok;
+ /** If the last query, that may be a referral, incremented the
+ * ratelimit counter. */
+ int ratelimit_incremented;
+
/**
* The query must store NS records from referrals as parentside RRs
* Enabled once it hits resolution problems, to throttle retries.
@@ -399,6 +408,8 @@ struct iter_qstate {
uint8_t* dsns_point;
/** length of the dname in dsns_point */
size_t dsns_point_len;
+ /** number of label-strip iterations performed in DSNS_FIND_STATE */
+ int dsns_count;
/**
* expected dnssec information for this iteration step.
@@ -434,6 +445,13 @@ struct iter_qstate {
* already so that it is accepted later. */
int empty_nodata_found;
+ /** Store if the answer was empty, but lame, before it became empty.*/
+ int msg_lame_empty;
+
+ /** Store if the answer was a referral, to self, before scrub. So the
+ * it is not some sort of answer. */
+ int msg_lame_referral;
+
/** list of pending queries to authoritative servers. */
struct outbound_list outlist;