aboutsummaryrefslogtreecommitdiff
path: root/iterator
diff options
context:
space:
mode:
authorCy Schubert <cy@FreeBSD.org>2021-08-16 23:55:17 +0000
committerCy Schubert <cy@FreeBSD.org>2021-08-16 23:57:03 +0000
commit625f1c1312fb7defbd148c8ba121a0cf058707ef (patch)
tree31510b9372850c8a8dd3e0a8dac37308308d8429 /iterator
parentd60fa10fd872db7e3d8cb1e161cfdae026c43b14 (diff)
Diffstat (limited to 'iterator')
-rw-r--r--iterator/iter_scrub.c24
-rw-r--r--iterator/iter_utils.c34
-rw-r--r--iterator/iter_utils.h23
-rw-r--r--iterator/iterator.c79
-rw-r--r--iterator/iterator.h8
5 files changed, 151 insertions, 17 deletions
diff --git a/iterator/iter_scrub.c b/iterator/iter_scrub.c
index aae934dd44fe..f093c1bf999a 100644
--- a/iterator/iter_scrub.c
+++ b/iterator/iter_scrub.c
@@ -640,25 +640,37 @@ store_rrset(sldns_buffer* pkt, struct msg_parse* msg, struct module_env* env,
/**
* Check if right hand name in NSEC is within zone
+ * @param pkt: the packet buffer for decompression.
* @param rrset: the NSEC rrset
* @param zonename: the zone name.
* @return true if BAD.
*/
-static int sanitize_nsec_is_overreach(struct rrset_parse* rrset,
- uint8_t* zonename)
+static int sanitize_nsec_is_overreach(sldns_buffer* pkt,
+ struct rrset_parse* rrset, uint8_t* zonename)
{
struct rr_parse* rr;
uint8_t* rhs;
size_t len;
log_assert(rrset->type == LDNS_RR_TYPE_NSEC);
for(rr = rrset->rr_first; rr; rr = rr->next) {
+ size_t pos = sldns_buffer_position(pkt);
+ size_t rhspos;
rhs = rr->ttl_data+4+2;
len = sldns_read_uint16(rr->ttl_data+4);
- if(!dname_valid(rhs, len)) {
- /* malformed domain name in rdata */
+ rhspos = rhs-sldns_buffer_begin(pkt);
+ sldns_buffer_set_position(pkt, rhspos);
+ if(pkt_dname_len(pkt) == 0) {
+ /* malformed */
+ sldns_buffer_set_position(pkt, pos);
return 1;
}
- if(!dname_subdomain_c(rhs, zonename)) {
+ if(sldns_buffer_position(pkt)-rhspos > len) {
+ /* outside of rdata boundaries */
+ sldns_buffer_set_position(pkt, pos);
+ return 1;
+ }
+ sldns_buffer_set_position(pkt, pos);
+ if(!pkt_sub(pkt, rhs, zonename)) {
/* overreaching */
return 1;
}
@@ -791,7 +803,7 @@ scrub_sanitize(sldns_buffer* pkt, struct msg_parse* msg,
}
/* check if right hand side of NSEC is within zone */
if(rrset->type == LDNS_RR_TYPE_NSEC &&
- sanitize_nsec_is_overreach(rrset, zonename)) {
+ sanitize_nsec_is_overreach(pkt, rrset, zonename)) {
remove_rrset("sanitize: removing overreaching NSEC "
"RRset:", pkt, msg, prev, &rrset);
continue;
diff --git a/iterator/iter_utils.c b/iterator/iter_utils.c
index 7bc67da69b2e..668f898eb0ff 100644
--- a/iterator/iter_utils.c
+++ b/iterator/iter_utils.c
@@ -50,6 +50,7 @@
#include "services/cache/infra.h"
#include "services/cache/dns.h"
#include "services/cache/rrset.h"
+#include "services/outside_network.h"
#include "util/net_help.h"
#include "util/module.h"
#include "util/log.h"
@@ -439,6 +440,7 @@ iter_filter_order(struct iter_env* iter_env, struct module_env* env,
prev = NULL;
a = dp->result_list;
for(i = 0; i < got_num; i++) {
+ if(!a) break; /* robustness */
swap_to_front = 0;
if(a->addr.ss_family != AF_INET6 && attempt == -1) {
/* if we only have ip4 at low attempt count,
@@ -496,6 +498,7 @@ iter_filter_order(struct iter_env* iter_env, struct module_env* env,
prev = NULL;
a = dp->result_list;
for(i = 0; i < got_num; i++) {
+ if(!a) break; /* robustness */
swap_to_front = 0;
if(a->addr.ss_family != AF_INET && attempt == -1) {
/* if we only have ip6 at low attempt count,
@@ -1390,7 +1393,8 @@ int iter_dp_cangodown(struct query_info* qinfo, struct delegpt* dp)
}
int
-iter_stub_fwd_no_cache(struct module_qstate *qstate, struct query_info *qinf)
+iter_stub_fwd_no_cache(struct module_qstate *qstate, struct query_info *qinf,
+ uint8_t** retdpname, size_t* retdpnamelen)
{
struct iter_hints_stub *stub;
struct delegpt *dp;
@@ -1419,6 +1423,10 @@ iter_stub_fwd_no_cache(struct module_qstate *qstate, struct query_info *qinf)
dname_str(stub->dp->name, dpname);
verbose(VERB_ALGO, "stub for %s %s has no_cache", qname, dpname);
}
+ if(retdpname) {
+ *retdpname = stub->dp->name;
+ *retdpnamelen = stub->dp->namelen;
+ }
return (stub->dp->no_cache);
}
@@ -1431,7 +1439,31 @@ iter_stub_fwd_no_cache(struct module_qstate *qstate, struct query_info *qinf)
dname_str(dp->name, dpname);
verbose(VERB_ALGO, "forward for %s %s has no_cache", qname, dpname);
}
+ if(retdpname) {
+ *retdpname = dp->name;
+ *retdpnamelen = dp->namelen;
+ }
return (dp->no_cache);
}
+ if(retdpname) {
+ *retdpname = NULL;
+ *retdpnamelen = 0;
+ }
return 0;
}
+
+void iterator_set_ip46_support(struct module_stack* mods,
+ struct module_env* env, struct outside_network* outnet)
+{
+ int m = modstack_find(mods, "iterator");
+ struct iter_env* ie = NULL;
+ if(m == -1)
+ return;
+ ie = (struct iter_env*)env->modinfo[m];
+ if(outnet->pending == NULL)
+ return; /* we are in testbound, no rbtree for UDP */
+ if(outnet->num_ip4 == 0)
+ ie->supports_ipv4 = 0;
+ if(outnet->num_ip6 == 0)
+ ie->supports_ipv6 = 0;
+}
diff --git a/iterator/iter_utils.h b/iterator/iter_utils.h
index f771930bba2b..509d2921e306 100644
--- a/iterator/iter_utils.h
+++ b/iterator/iter_utils.h
@@ -59,6 +59,8 @@ struct reply_info;
struct module_qstate;
struct sock_list;
struct ub_packed_rrset_key;
+struct module_stack;
+struct outside_network;
/**
* Process config options and set iterator module state.
@@ -130,7 +132,7 @@ struct dns_msg* dns_copy_msg(struct dns_msg* from, struct regional* regional);
* can be prefetch-updates.
* @param region: to copy modified (cache is better) rrs back to.
* @param flags: with BIT_CD for dns64 AAAA translated queries.
- * @return void, because we are not interested in alloc errors,
+ * return void, because we are not interested in alloc errors,
* the iterator and validator can operate on the results in their
* scratch space (the qstate.region) and are not dependent on the cache.
* It is useful to log the alloc failure (for the server operator),
@@ -380,9 +382,26 @@ int iter_dp_cangodown(struct query_info* qinfo, struct delegpt* dp);
* Lookup if no_cache is set in stub or fwd.
* @param qstate: query state with env with hints and fwds.
* @param qinf: query name to lookup for.
+ * @param retdpname: returns NULL or the deepest enclosing name of fwd or stub.
+ * This is the name under which the closest lookup is going to happen.
+ * Used for NXDOMAIN checks, above that it is an nxdomain from a
+ * different server and zone. You can pass NULL to not get it.
+ * @param retdpnamelen: returns the length of the dpname.
* @return true if no_cache is set in stub or fwd.
*/
int iter_stub_fwd_no_cache(struct module_qstate *qstate,
- struct query_info *qinf);
+ struct query_info *qinf, uint8_t** retdpname, size_t* retdpnamelen);
+
+/**
+ * Set support for IP4 and IP6 depending on outgoing interfaces
+ * in the outside network. If none, no support, so no use to lookup
+ * the AAAA and then attempt to use it if there is no outgoing-interface
+ * for it.
+ * @param mods: modstack to find iterator module in.
+ * @param env: module env, find iterator module (if one) in there.
+ * @param outnet: outside network structure.
+ */
+void iterator_set_ip46_support(struct module_stack* mods,
+ struct module_env* env, struct outside_network* outnet);
#endif /* ITERATOR_ITER_UTILS_H */
diff --git a/iterator/iterator.c b/iterator/iterator.c
index 99d020117842..f0105ad4b085 100644
--- a/iterator/iterator.c
+++ b/iterator/iterator.c
@@ -585,6 +585,60 @@ handle_cname_response(struct module_qstate* qstate, struct iter_qstate* iq,
return 1;
}
+/** add response specific error information for log servfail */
+static void
+errinf_reply(struct module_qstate* qstate, struct iter_qstate* iq)
+{
+ if(qstate->env->cfg->val_log_level < 2 && !qstate->env->cfg->log_servfail)
+ return;
+ if((qstate->reply && qstate->reply->addrlen != 0) ||
+ (iq->fail_reply && iq->fail_reply->addrlen != 0)) {
+ char from[256], frm[512];
+ if(qstate->reply && qstate->reply->addrlen != 0)
+ addr_to_str(&qstate->reply->addr, qstate->reply->addrlen,
+ from, sizeof(from));
+ else
+ addr_to_str(&iq->fail_reply->addr, iq->fail_reply->addrlen,
+ from, sizeof(from));
+ snprintf(frm, sizeof(frm), "from %s", from);
+ errinf(qstate, frm);
+ }
+ if(iq->scrub_failures || iq->parse_failures) {
+ if(iq->scrub_failures)
+ errinf(qstate, "upstream response failed scrub");
+ if(iq->parse_failures)
+ errinf(qstate, "could not parse upstream response");
+ } else if(iq->response == NULL && iq->timeout_count != 0) {
+ errinf(qstate, "upstream server timeout");
+ } else if(iq->response == NULL) {
+ errinf(qstate, "no server to query");
+ if(iq->dp) {
+ if(iq->dp->target_list == NULL)
+ errinf(qstate, "no addresses for nameservers");
+ else errinf(qstate, "nameserver addresses not usable");
+ if(iq->dp->nslist == NULL)
+ errinf(qstate, "have no nameserver names");
+ if(iq->dp->bogus)
+ errinf(qstate, "NS record was dnssec bogus");
+ }
+ }
+ if(iq->response && iq->response->rep) {
+ if(FLAGS_GET_RCODE(iq->response->rep->flags) != 0) {
+ char rcode[256], rc[32];
+ (void)sldns_wire2str_rcode_buf(
+ FLAGS_GET_RCODE(iq->response->rep->flags),
+ rc, sizeof(rc));
+ snprintf(rcode, sizeof(rcode), "got %s", rc);
+ errinf(qstate, rcode);
+ } else {
+ /* rcode NOERROR */
+ if(iq->response->rep->an_numrrsets == 0) {
+ errinf(qstate, "nodata answer");
+ }
+ }
+ }
+}
+
/** see if last resort is possible - does config allow queries to parent */
static int
can_have_last_resort(struct module_env* env, uint8_t* nm, size_t nmlen,
@@ -1228,8 +1282,8 @@ static int
processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
struct iter_env* ie, int id)
{
- uint8_t* delname;
- size_t delnamelen;
+ uint8_t* delname, *dpname=NULL;
+ size_t delnamelen, dpnamelen=0;
struct dns_msg* msg = NULL;
log_query_info(VERB_DETAIL, "resolving", &qstate->qinfo);
@@ -1283,7 +1337,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
/* This either results in a query restart (CNAME cache response), a
* terminating response (ANSWER), or a cache miss (null). */
- if (iter_stub_fwd_no_cache(qstate, &iq->qchase)) {
+ if (iter_stub_fwd_no_cache(qstate, &iq->qchase, &dpname, &dpnamelen)) {
/* Asked to not query cache. */
verbose(VERB_ALGO, "no-cache set, going to the network");
qstate->no_cache_lookup = 1;
@@ -1298,7 +1352,8 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
msg = dns_cache_lookup(qstate->env, iq->qchase.qname,
iq->qchase.qname_len, iq->qchase.qtype,
iq->qchase.qclass, qstate->query_flags,
- qstate->region, qstate->env->scratch, 0);
+ qstate->region, qstate->env->scratch, 0, dpname,
+ dpnamelen);
if(!msg && qstate->env->neg_cache &&
iter_qname_indicates_dnssec(qstate->env, &iq->qchase)) {
/* lookup in negative cache; may result in
@@ -1921,6 +1976,7 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,
* of a response. */
errinf(qstate, "all the configured stub or forward servers failed,");
errinf_dname(qstate, "at zone", iq->dp->name);
+ errinf_reply(qstate, iq);
verbose(VERB_QUERY, "configured stub or forward servers failed -- returning SERVFAIL");
return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
@@ -2067,6 +2123,7 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,
errinf(qstate, "all servers for this domain failed,");
errinf_dname(qstate, "at zone", iq->dp->name);
+ errinf_reply(qstate, iq);
verbose(VERB_QUERY, "out of query targets -- returning SERVFAIL");
/* fail -- no more targets, no more hope of targets, no hope
* of a response. */
@@ -2288,7 +2345,8 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
iq->qinfo_out.qname, iq->qinfo_out.qname_len,
iq->qinfo_out.qtype, iq->qinfo_out.qclass,
qstate->query_flags, qstate->region,
- qstate->env->scratch, 0);
+ qstate->env->scratch, 0, iq->dp->name,
+ iq->dp->namelen);
if(msg && FLAGS_GET_RCODE(msg->rep->flags) ==
LDNS_RCODE_NOERROR)
/* no need to send query if it is already
@@ -2611,7 +2669,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
(iq->dp->ssl_upstream || qstate->env->cfg->ssl_upstream),
target->tls_auth_name, qstate);
if(!outq) {
- log_addr(VERB_DETAIL, "error sending query to auth server",
+ log_addr(VERB_QUERY, "error sending query to auth server",
&target->addr, target->addrlen);
if(!(iq->chase_flags & BIT_RD) && !iq->ratelimit_ok)
infra_ratelimit_dec(qstate->env->infra_cache, iq->dp->name,
@@ -2957,6 +3015,8 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
qstate->env->detach_subs));
(*qstate->env->detach_subs)(qstate);
iq->num_target_queries = 0;
+ iq->response = NULL;
+ iq->fail_reply = NULL;
verbose(VERB_ALGO, "cleared outbound list for next round");
return next_state(iq, QUERYTARGETS_STATE);
} else if(type == RESPONSE_TYPE_CNAME) {
@@ -3720,6 +3780,7 @@ process_response(struct module_qstate* qstate, struct iter_qstate* iq,
}
/* parse message */
+ iq->fail_reply = qstate->reply;
prs = (struct msg_parse*)regional_alloc(qstate->env->scratch,
sizeof(struct msg_parse));
if(!prs) {
@@ -3733,12 +3794,15 @@ process_response(struct module_qstate* qstate, struct iter_qstate* iq,
sldns_buffer_set_position(pkt, 0);
if(parse_packet(pkt, prs, qstate->env->scratch) != LDNS_RCODE_NOERROR) {
verbose(VERB_ALGO, "parse error on reply packet");
+ iq->parse_failures++;
goto handle_it;
}
/* edns is not examined, but removed from message to help cache */
if(parse_extract_edns(prs, &edns, qstate->env->scratch) !=
- LDNS_RCODE_NOERROR)
+ LDNS_RCODE_NOERROR) {
+ iq->parse_failures++;
goto handle_it;
+ }
/* Copy the edns options we may got from the back end */
if(edns.opt_list) {
@@ -3772,6 +3836,7 @@ process_response(struct module_qstate* qstate, struct iter_qstate* iq,
iq->num_current_queries--;
verbose(VERB_DETAIL, "Capsforid: scrub failed, starting fallback with no response");
}
+ iq->scrub_failures++;
goto handle_it;
}
diff --git a/iterator/iterator.h b/iterator/iterator.h
index 342ac207e826..dc5e57527d87 100644
--- a/iterator/iterator.h
+++ b/iterator/iterator.h
@@ -61,7 +61,7 @@ struct rbtree_type;
* its subqueries */
#define MAX_TARGET_NX 5
/** max number of query restarts. Determines max number of CNAME chain. */
-#define MAX_RESTART_COUNT 8
+#define MAX_RESTART_COUNT 11
/** max number of referrals. Makes sure resolver does not run away */
#define MAX_REFERRAL_COUNT 130
/** max number of queries-sent-out. Make sure large NS set does not loop */
@@ -406,6 +406,12 @@ struct iter_qstate {
int auth_zone_response;
/** True if the auth_zones should not be consulted for the query */
int auth_zone_avoid;
+ /** true if there have been scrubbing failures of reply packets */
+ int scrub_failures;
+ /** true if there have been parse failures of reply packets */
+ int parse_failures;
+ /** a failure printout address for last received answer */
+ struct comm_reply* fail_reply;
};
/**