diff options
| author | Cy Schubert <cy@FreeBSD.org> | 2021-08-16 23:55:17 +0000 |
|---|---|---|
| committer | Cy Schubert <cy@FreeBSD.org> | 2021-08-16 23:57:03 +0000 |
| commit | 625f1c1312fb7defbd148c8ba121a0cf058707ef (patch) | |
| tree | 31510b9372850c8a8dd3e0a8dac37308308d8429 /iterator | |
| parent | d60fa10fd872db7e3d8cb1e161cfdae026c43b14 (diff) | |
Diffstat (limited to 'iterator')
| -rw-r--r-- | iterator/iter_scrub.c | 24 | ||||
| -rw-r--r-- | iterator/iter_utils.c | 34 | ||||
| -rw-r--r-- | iterator/iter_utils.h | 23 | ||||
| -rw-r--r-- | iterator/iterator.c | 79 | ||||
| -rw-r--r-- | iterator/iterator.h | 8 |
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; }; /** |
