diff options
| author | Dag-Erling Smørgrav <des@FreeBSD.org> | 2026-08-04 10:04:44 +0000 |
|---|---|---|
| committer | Dag-Erling Smørgrav <des@FreeBSD.org> | 2026-08-04 10:04:44 +0000 |
| commit | 84ffc29dc8ddb0c946db5cb3b3c1310bec6a9e6c (patch) | |
| tree | 93f0b0071b6b34da50dd62dae3cee8ed757676e8 /iterator | |
| parent | c68e7bcd81d62e9f5364c6da22fd9917976acf85 (diff) | |
import unbound 1.26.0vendor/unbound/1.26.0vendor/unbound
Diffstat (limited to 'iterator')
| -rw-r--r-- | iterator/iter_delegpt.c | 55 | ||||
| -rw-r--r-- | iterator/iter_delegpt.h | 6 | ||||
| -rw-r--r-- | iterator/iter_resptype.c | 25 | ||||
| -rw-r--r-- | iterator/iter_resptype.h | 6 | ||||
| -rw-r--r-- | iterator/iter_scrub.c | 76 | ||||
| -rw-r--r-- | iterator/iter_scrub.h | 7 | ||||
| -rw-r--r-- | iterator/iter_utils.c | 11 | ||||
| -rw-r--r-- | iterator/iter_utils.h | 3 | ||||
| -rw-r--r-- | iterator/iterator.c | 84 | ||||
| -rw-r--r-- | iterator/iterator.h | 18 |
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; |
