diff options
Diffstat (limited to 'iterator')
| -rw-r--r-- | iterator/iter_fwd.c | 1 | ||||
| -rw-r--r-- | iterator/iter_utils.c | 21 | ||||
| -rw-r--r-- | iterator/iter_utils.h | 12 | ||||
| -rw-r--r-- | iterator/iterator.c | 119 | ||||
| -rw-r--r-- | iterator/iterator.h | 22 |
5 files changed, 137 insertions, 38 deletions
diff --git a/iterator/iter_fwd.c b/iterator/iter_fwd.c index 0feee032c960..3e580ca35513 100644 --- a/iterator/iter_fwd.c +++ b/iterator/iter_fwd.c @@ -294,6 +294,7 @@ make_stub_holes(struct iter_forwards* fwd, struct config_file* cfg) uint8_t* dname; size_t dname_len; for(s = cfg->stubs; s; s = s->next) { + if(!s->name) continue; dname = sldns_str2wire_dname(s->name, &dname_len); if(!dname) { log_err("cannot parse stub name '%s'", s->name); diff --git a/iterator/iter_utils.c b/iterator/iter_utils.c index 58e62fbeb27b..a5aefa9602c2 100644 --- a/iterator/iter_utils.c +++ b/iterator/iter_utils.c @@ -590,6 +590,27 @@ iter_dp_is_useless(struct query_info* qinfo, uint16_t qflags, return 1; } +int +iter_indicates_dnssec_fwd(struct module_env* env, struct query_info *qinfo) +{ + struct trust_anchor* a; + if(!env || !env->anchors || !qinfo || !qinfo->qname) + return 0; + /* a trust anchor exists above the name? */ + if((a=anchors_lookup(env->anchors, qinfo->qname, qinfo->qname_len, + qinfo->qclass))) { + if(a->numDS == 0 && a->numDNSKEY == 0) { + /* insecure trust point */ + lock_basic_unlock(&a->lock); + return 0; + } + lock_basic_unlock(&a->lock); + return 1; + } + /* no trust anchor above it. */ + return 0; +} + int iter_indicates_dnssec(struct module_env* env, struct delegpt* dp, struct dns_msg* msg, uint16_t dclass) diff --git a/iterator/iter_utils.h b/iterator/iter_utils.h index 3a4df3e45968..50c5fc093f6c 100644 --- a/iterator/iter_utils.h +++ b/iterator/iter_utils.h @@ -174,6 +174,18 @@ int iter_dp_is_useless(struct query_info* qinfo, uint16_t qflags, struct delegpt* dp); /** + * See if qname has DNSSEC needs in the forwarding case. This is true if + * there is a trust anchor above it. Whether there is an insecure delegation + * to the data is unknown, but CD-retry is needed. + * @param env: environment with anchors. + * @param qinfo: query name and class. + * @return true if trust anchor above qname, false if no anchor or insecure + * point above qname. + */ +int iter_indicates_dnssec_fwd(struct module_env* env, + struct query_info *qinfo); + +/** * See if delegation is expected to have DNSSEC information (RRSIGs) in * its answers, or not. Inspects delegation point (name), trust anchors, * and delegation message (DS RRset) to determine this. diff --git a/iterator/iterator.c b/iterator/iterator.c index b1bf902d583d..139cae4bae0a 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -82,20 +82,6 @@ iter_init(struct module_env* env, int id) log_err("iterator: could not apply configuration settings."); return 0; } - if(env->cfg->qname_minimisation) { - uint8_t dname[LDNS_MAX_DOMAINLEN+1]; - size_t len = sizeof(dname); - if(sldns_str2wire_dname_buf("ip6.arpa.", dname, &len) != 0) { - log_err("ip6.arpa. parse error"); - return 0; - } - iter_env->ip6arpa_dname = (uint8_t*)malloc(len); - if(!iter_env->ip6arpa_dname) { - log_err("malloc failure"); - return 0; - } - memcpy(iter_env->ip6arpa_dname, dname, len); - } return 1; } @@ -117,7 +103,6 @@ iter_deinit(struct module_env* env, int id) if(!env || !env->modinfo[id]) return; iter_env = (struct iter_env*)env->modinfo[id]; - free(iter_env->ip6arpa_dname); free(iter_env->target_fetch_policy); priv_delete(iter_env->priv); donotq_delete(iter_env->donotq); @@ -162,6 +147,7 @@ iter_new(struct module_qstate* qstate, int id) /* Start with the (current) qname. */ iq->qchase = qstate->qinfo; outbound_list_init(&iq->outlist); + iq->minimise_count = 0; if (qstate->env->cfg->qname_minimisation) iq->minimisation_state = INIT_MINIMISE_STATE; else @@ -1800,6 +1786,8 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, int tf_policy; struct delegpt_addr* target; struct outbound_entry* outq; + /* EDNS options to set on outgoing packet */ + struct edns_option* opt_list = NULL; /* NOTE: a request will encounter this state for each target it * needs to send a query to. That is, at least one per referral, @@ -2009,9 +1997,10 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, } if(iq->minimisation_state == INIT_MINIMISE_STATE) { - /* (Re)set qinfo_out to (new) delegation point, except - * when qinfo_out is already a subdomain of dp. This happens - * when resolving ip6.arpa dnames. */ + /* (Re)set qinfo_out to (new) delegation point, except when + * qinfo_out is already a subdomain of dp. This happens when + * increasing by more than one label at once (QNAMEs with more + * than MAX_MINIMISE_COUNT labels). */ if(!(iq->qinfo_out.qname_len && dname_subdomain_c(iq->qchase.qname, iq->qinfo_out.qname) @@ -2021,28 +2010,47 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, iq->qinfo_out.qname_len = iq->dp->namelen; iq->qinfo_out.qtype = LDNS_RR_TYPE_NS; iq->qinfo_out.qclass = iq->qchase.qclass; + iq->minimise_count = 0; } iq->minimisation_state = MINIMISE_STATE; } if(iq->minimisation_state == MINIMISE_STATE) { - int labdiff = dname_count_labels(iq->qchase.qname) - + int qchaselabs = dname_count_labels(iq->qchase.qname); + int labdiff = qchaselabs - dname_count_labels(iq->qinfo_out.qname); iq->qinfo_out.qname = iq->qchase.qname; iq->qinfo_out.qname_len = iq->qchase.qname_len; + iq->minimise_count++; - /* Special treatment for ip6.arpa lookups. - * Reverse IPv6 dname has 34 labels, increment the IP part - * (usually first 32 labels) by 8 labels (7 more than the - * default 1 label increment). */ - if(labdiff <= 32 && - dname_subdomain_c(iq->qchase.qname, ie->ip6arpa_dname)) { - labdiff -= 7; - /* Small chance of zone cut after first label. Stop - * minimising */ - if(labdiff <= 1) - labdiff = 0; + /* Limit number of iterations for QNAMEs with more + * than MAX_MINIMISE_COUNT labels. Send first MINIMISE_ONE_LAB + * labels of QNAME always individually. + */ + if(qchaselabs > MAX_MINIMISE_COUNT && labdiff > 1 && + iq->minimise_count > MINIMISE_ONE_LAB) { + if(iq->minimise_count < MAX_MINIMISE_COUNT) { + int multilabs = qchaselabs - 1 - + MINIMISE_ONE_LAB; + int extralabs = multilabs / + MINIMISE_MULTIPLE_LABS; + + if (MAX_MINIMISE_COUNT - iq->minimise_count >= + multilabs % MINIMISE_MULTIPLE_LABS) + /* Default behaviour is to add 1 label + * every iteration. Therefore, decrement + * the extralabs by 1 */ + extralabs--; + if (extralabs < labdiff) + labdiff -= extralabs; + else + labdiff = 1; + } + /* Last minimised iteration, send all labels with + * QTYPE=NS */ + else + labdiff = 1; } if(labdiff > 1) { @@ -2068,7 +2076,6 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, * cached as NOERROR/NODATA */ return 1; } - } if(iq->minimisation_state == SKIP_MINIMISE_STATE) /* Do not increment qname, continue incrementing next @@ -2090,10 +2097,16 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, outq = (*qstate->env->send_query)( iq->qinfo_out.qname, iq->qinfo_out.qname_len, iq->qinfo_out.qtype, iq->qinfo_out.qclass, - iq->chase_flags | (iq->chase_to_rd?BIT_RD:0), EDNS_DO|BIT_CD, + iq->chase_flags | (iq->chase_to_rd?BIT_RD:0), + /* unset CD if to forwarder(RD set) and not dnssec retry + * (blacklist nonempty) and no trust-anchors are configured + * above the qname or on the first attempt when dnssec is on */ + EDNS_DO| ((iq->chase_to_rd||(iq->chase_flags&BIT_RD)!=0)&& + !qstate->blacklist&&(!iter_indicates_dnssec_fwd(qstate->env, + &iq->qinfo_out)||target->attempts==1)?0:BIT_CD), iq->dnssec_expected, iq->caps_fallback || is_caps_whitelisted( - ie, iq), &target->addr, target->addrlen, iq->dp->name, - iq->dp->namelen, qstate); + ie, iq), opt_list, &target->addr, target->addrlen, + iq->dp->name, iq->dp->namelen, qstate); if(!outq) { log_addr(VERB_DETAIL, "error sending query to auth server", &target->addr, target->addrlen); @@ -2161,8 +2174,10 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, * differently. No queries should be sent elsewhere */ type = RESPONSE_TYPE_ANSWER; } - if(iq->dnssec_expected && !iq->dnssec_lame_query && + if(!qstate->env->cfg->disable_dnssec_lame_check && iq->dnssec_expected + && !iq->dnssec_lame_query && !(iq->chase_flags&BIT_RD) + && iq->sent_count < DNSSEC_LAME_DETECT_COUNT && type != RESPONSE_TYPE_LAME && type != RESPONSE_TYPE_REC_LAME && type != RESPONSE_TYPE_THROWAWAY @@ -2250,10 +2265,39 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, if(iq->minimisation_state != DONOT_MINIMISE_STATE) { /* Best effort qname-minimisation. * Stop minimising and send full query when RCODE - * is not NOERROR */ + * is not NOERROR. */ if(FLAGS_GET_RCODE(iq->response->rep->flags) != LDNS_RCODE_NOERROR) iq->minimisation_state = DONOT_MINIMISE_STATE; + if(FLAGS_GET_RCODE(iq->response->rep->flags) == + LDNS_RCODE_NXDOMAIN) { + /* Stop resolving when NXDOMAIN is DNSSEC + * signed. Based on assumption that namservers + * serving signed zones do not return NXDOMAIN + * for empty-non-terminals. */ + if(iq->dnssec_expected) + return final_state(iq); + /* Make subrequest to validate intermediate + * NXDOMAIN if harden-below-nxdomain is + * enabled. */ + if(qstate->env->cfg->harden_below_nxdomain) { + struct module_qstate* subq = NULL; + log_query_info(VERB_QUERY, + "schedule NXDOMAIN validation:", + &iq->response->qinfo); + if(!generate_sub_request( + iq->response->qinfo.qname, + iq->response->qinfo.qname_len, + iq->response->qinfo.qtype, + iq->response->qinfo.qclass, + qstate, id, iq, + INIT_REQUEST_STATE, + FINISHED_STATE, &subq, 1)) + verbose(VERB_ALGO, + "could not validate NXDOMAIN " + "response"); + } + } return next_state(iq, QUERYTARGETS_STATE); } return final_state(iq); @@ -3082,7 +3126,8 @@ process_response(struct module_qstate* qstate, struct iter_qstate* iq, goto handle_it; } /* edns is not examined, but removed from message to help cache */ - if(parse_extract_edns(prs, &edns) != LDNS_RCODE_NOERROR) + if(parse_extract_edns(prs, &edns, qstate->env->scratch) != + LDNS_RCODE_NOERROR) goto handle_it; /* remove CD-bit, we asked for in case we handle validation ourself */ prs->flags &= ~BIT_CD; diff --git a/iterator/iterator.h b/iterator/iterator.h index b7aa82ebe4ed..7c32a74f800b 100644 --- a/iterator/iterator.h +++ b/iterator/iterator.h @@ -61,6 +61,20 @@ struct rbtree_t; #define MAX_REFERRAL_COUNT 130 /** max number of queries-sent-out. Make sure large NS set does not loop */ #define MAX_SENT_COUNT 32 +/** max number of queries for which to perform dnsseclameness detection, + * (rrsigs misssing detection) after that, just pick up that response */ +#define DNSSEC_LAME_DETECT_COUNT 4 +/** + * max number of QNAME minimisation iterations. Limits number of queries for + * QNAMEs with a lot of labels. +*/ +#define MAX_MINIMISE_COUNT 10 +/** + * number of labels from QNAME that are always send individually when using + * QNAME minimisation, even when the number of labels of the QNAME is bigger + * tham MAX_MINIMISE_COUNT */ +#define MINIMISE_ONE_LAB 4 +#define MINIMISE_MULTIPLE_LABS (MAX_MINIMISE_COUNT - MINIMISE_ONE_LAB) /** at what query-sent-count to stop target fetch policy */ #define TARGET_FETCH_STOP 3 /** how nice is a server without further information, in msec @@ -349,7 +363,7 @@ struct iter_qstate { /** list of pending queries to authoritative servers. */ struct outbound_list outlist; - /** QNAME minimisation state */ + /** QNAME minimisation state, RFC7816 */ enum minimisation_state minimisation_state; /** @@ -357,6 +371,12 @@ struct iter_qstate { * when qname minimisation is enabled. */ struct query_info qinfo_out; + + /** + * Count number of QNAME minisation iterations. Used to limit number of + * outgoing queries when QNAME minimisation is enabled. + */ + int minimise_count; }; /** |
