diff options
author | Erik de Castro Lopo <erikd@mega-nerd.com> | 2017-06-16 20:16:05 +1000 |
---|---|---|
committer | Erik de Castro Lopo <erikd@mega-nerd.com> | 2017-06-17 23:04:00 +1000 |
commit | a85b5759f34c0c4110a479a8b5fa606f15ed9b23 (patch) | |
tree | 518cb8346249a42fd2aa8a78c09c3631e14db6aa /external/unbound/iterator | |
parent | Merge pull request #2059 (diff) | |
download | monero-a85b5759f34c0c4110a479a8b5fa606f15ed9b23.tar.xz |
Upgrade unbound library
These files were pulled from the 1.6.3 release tarball.
This new version builds against OpenSSL version 1.1 which will be
the default in the new Debian Stable which is due to be released
RealSoonNow (tm).
Diffstat (limited to 'external/unbound/iterator')
-rw-r--r-- | external/unbound/iterator/iter_delegpt.c | 1 | ||||
-rw-r--r-- | external/unbound/iterator/iter_delegpt.h | 4 | ||||
-rw-r--r-- | external/unbound/iterator/iter_donotq.h | 2 | ||||
-rw-r--r-- | external/unbound/iterator/iter_fwd.c | 11 | ||||
-rw-r--r-- | external/unbound/iterator/iter_fwd.h | 4 | ||||
-rw-r--r-- | external/unbound/iterator/iter_hints.c | 8 | ||||
-rw-r--r-- | external/unbound/iterator/iter_hints.h | 2 | ||||
-rw-r--r-- | external/unbound/iterator/iter_priv.h | 4 | ||||
-rw-r--r-- | external/unbound/iterator/iter_scrub.c | 4 | ||||
-rw-r--r-- | external/unbound/iterator/iter_utils.c | 57 | ||||
-rw-r--r-- | external/unbound/iterator/iter_utils.h | 12 | ||||
-rw-r--r-- | external/unbound/iterator/iterator.c | 364 | ||||
-rw-r--r-- | external/unbound/iterator/iterator.h | 34 |
13 files changed, 370 insertions, 137 deletions
diff --git a/external/unbound/iterator/iter_delegpt.c b/external/unbound/iterator/iter_delegpt.c index 0e251ff58..ecf88b293 100644 --- a/external/unbound/iterator/iter_delegpt.c +++ b/external/unbound/iterator/iter_delegpt.c @@ -72,6 +72,7 @@ struct delegpt* delegpt_copy(struct delegpt* dp, struct regional* region) return NULL; copy->bogus = dp->bogus; copy->has_parent_side_NS = dp->has_parent_side_NS; + copy->ssl_upstream = dp->ssl_upstream; for(ns = dp->nslist; ns; ns = ns->next) { if(!delegpt_add_ns(copy, region, ns->name, ns->lame)) return NULL; diff --git a/external/unbound/iterator/iter_delegpt.h b/external/unbound/iterator/iter_delegpt.h index eb771359c..4bd79c81a 100644 --- a/external/unbound/iterator/iter_delegpt.h +++ b/external/unbound/iterator/iter_delegpt.h @@ -81,6 +81,8 @@ struct delegpt { uint8_t has_parent_side_NS; /** for assertions on type of delegpt */ uint8_t dp_type_mlc; + /** use SSL for upstream query */ + uint8_t ssl_upstream; }; /** @@ -355,7 +357,7 @@ void delegpt_no_ipv4(struct delegpt* dp); /** * create malloced delegation point, with the given name - * @param name: uncompressed wireformat of degegpt name. + * @param name: uncompressed wireformat of delegpt name. * @return NULL on alloc failure */ struct delegpt* delegpt_create_mlc(uint8_t* name); diff --git a/external/unbound/iterator/iter_donotq.h b/external/unbound/iterator/iter_donotq.h index 429e5a3dd..14105073a 100644 --- a/external/unbound/iterator/iter_donotq.h +++ b/external/unbound/iterator/iter_donotq.h @@ -58,7 +58,7 @@ struct iter_donotq { * contents of type addr_tree_node. Each node is an address span * that must not be used to send queries to. */ - rbtree_t tree; + rbtree_type tree; }; /** diff --git a/external/unbound/iterator/iter_fwd.c b/external/unbound/iterator/iter_fwd.c index 0feee032c..0ba6c6ddf 100644 --- a/external/unbound/iterator/iter_fwd.c +++ b/external/unbound/iterator/iter_fwd.c @@ -82,7 +82,7 @@ static void fwd_zone_free(struct iter_forward_zone* n) free(n); } -static void delfwdnode(rbnode_t* n, void* ATTR_UNUSED(arg)) +static void delfwdnode(rbnode_type* n, void* ATTR_UNUSED(arg)) { struct iter_forward_zone* node = (struct iter_forward_zone*)n; fwd_zone_free(node); @@ -265,6 +265,8 @@ read_forwards(struct iter_forwards* fwd, struct config_file* cfg) * last resort will ask for parent-side NS record and thus * fallback to the internet name servers on a failure */ dp->has_parent_side_NS = (uint8_t)!s->isfirst; + /* use SSL for queries to this forwarder */ + dp->ssl_upstream = (uint8_t)s->ssl_upstream; verbose(VERB_QUERY, "Forward zone server list:"); delegpt_log(VERB_QUERY, dp); if(!forwards_insert(fwd, LDNS_RR_CLASS_IN, dp)) @@ -294,6 +296,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); @@ -329,7 +332,7 @@ forwards_apply_cfg(struct iter_forwards* fwd, struct config_file* cfg) struct delegpt* forwards_find(struct iter_forwards* fwd, uint8_t* qname, uint16_t qclass) { - rbnode_t* res = NULL; + rbnode_type* res = NULL; struct iter_forward_zone key; key.node.key = &key; key.dclass = qclass; @@ -344,7 +347,7 @@ struct delegpt* forwards_lookup(struct iter_forwards* fwd, uint8_t* qname, uint16_t qclass) { /* lookup the forward zone in the tree */ - rbnode_t* res = NULL; + rbnode_type* res = NULL; struct iter_forward_zone *result; struct iter_forward_zone key; key.node.key = &key; @@ -385,7 +388,7 @@ int forwards_next_root(struct iter_forwards* fwd, uint16_t* dclass) { struct iter_forward_zone key; - rbnode_t* n; + rbnode_type* n; struct iter_forward_zone* p; if(*dclass == 0) { /* first root item is first item in tree */ diff --git a/external/unbound/iterator/iter_fwd.h b/external/unbound/iterator/iter_fwd.h index 20113a395..e90b74c16 100644 --- a/external/unbound/iterator/iter_fwd.h +++ b/external/unbound/iterator/iter_fwd.h @@ -57,7 +57,7 @@ struct iter_forwards { * match which gives the ancestor needed. * contents of type iter_forward_zone. */ - rbtree_t* tree; + rbtree_type* tree; }; /** @@ -65,7 +65,7 @@ struct iter_forwards { */ struct iter_forward_zone { /** redblacktree node, key is this structure: class and name */ - rbnode_t node; + rbnode_type node; /** name */ uint8_t* name; /** length of name */ diff --git a/external/unbound/iterator/iter_hints.c b/external/unbound/iterator/iter_hints.c index d7f8158d1..74869d355 100644 --- a/external/unbound/iterator/iter_hints.c +++ b/external/unbound/iterator/iter_hints.c @@ -67,7 +67,7 @@ static void hints_stub_free(struct iter_hints_stub* s) free(s); } -static void delhintnode(rbnode_t* n, void* ATTR_UNUSED(arg)) +static void delhintnode(rbnode_type* n, void* ATTR_UNUSED(arg)) { struct iter_hints_stub* node = (struct iter_hints_stub*)n; hints_stub_free(node); @@ -147,12 +147,14 @@ compile_time_root_prime(int do_ip4, int do_ip6) if(!ah(dp, "B.ROOT-SERVERS.NET.", "2001:500:84::b")) goto failed; if(!ah(dp, "C.ROOT-SERVERS.NET.", "2001:500:2::c")) goto failed; if(!ah(dp, "D.ROOT-SERVERS.NET.", "2001:500:2d::d")) goto failed; + if(!ah(dp, "E.ROOT-SERVERS.NET.", "2001:500:a8::e")) goto failed; if(!ah(dp, "F.ROOT-SERVERS.NET.", "2001:500:2f::f")) goto failed; + if(!ah(dp, "G.ROOT-SERVERS.NET.", "2001:500:12::d0d")) goto failed; if(!ah(dp, "H.ROOT-SERVERS.NET.", "2001:500:1::53")) goto failed; if(!ah(dp, "I.ROOT-SERVERS.NET.", "2001:7fe::53")) goto failed; if(!ah(dp, "J.ROOT-SERVERS.NET.", "2001:503:c27::2:30")) goto failed; if(!ah(dp, "K.ROOT-SERVERS.NET.", "2001:7fd::1")) goto failed; - if(!ah(dp, "L.ROOT-SERVERS.NET.", "2001:500:3::42")) goto failed; + if(!ah(dp, "L.ROOT-SERVERS.NET.", "2001:500:9f::42")) goto failed; if(!ah(dp, "M.ROOT-SERVERS.NET.", "2001:dc3::35")) goto failed; } return dp; @@ -274,6 +276,8 @@ read_stubs(struct iter_hints* hints, struct config_file* cfg) * last resort will ask for parent-side NS record and thus * fallback to the internet name servers on a failure */ dp->has_parent_side_NS = (uint8_t)!s->isfirst; + /* ssl_upstream */ + dp->ssl_upstream = (uint8_t)s->ssl_upstream; delegpt_log(VERB_QUERY, dp); if(!hints_insert(hints, LDNS_RR_CLASS_IN, dp, !s->isprime)) return 0; diff --git a/external/unbound/iterator/iter_hints.h b/external/unbound/iterator/iter_hints.h index 715ec9f41..06b4b9667 100644 --- a/external/unbound/iterator/iter_hints.h +++ b/external/unbound/iterator/iter_hints.h @@ -59,7 +59,7 @@ struct iter_hints { * contents of type iter_hints_stub. The class IN root is in here. * uses name_tree_node from dnstree.h. */ - rbtree_t tree; + rbtree_type tree; }; /** diff --git a/external/unbound/iterator/iter_priv.h b/external/unbound/iterator/iter_priv.h index 6fa84900b..0430d57e3 100644 --- a/external/unbound/iterator/iter_priv.h +++ b/external/unbound/iterator/iter_priv.h @@ -60,14 +60,14 @@ struct iter_priv { * contents of type addr_tree_node. * No further data need, only presence or absence. */ - rbtree_t a; + rbtree_type a; /** * Tree of the domains spans that are allowed to contain * the blocked address spans. * contents of type name_tree_node. * No further data need, only presence or absence. */ - rbtree_t n; + rbtree_type n; }; /** diff --git a/external/unbound/iterator/iter_scrub.c b/external/unbound/iterator/iter_scrub.c index 8a3fc170c..1bee85c0b 100644 --- a/external/unbound/iterator/iter_scrub.c +++ b/external/unbound/iterator/iter_scrub.c @@ -161,8 +161,8 @@ mark_additional_rrset(sldns_buffer* pkt, struct msg_parse* msg, for(rr = rrset->rr_first; rr; rr = rr->next) { if(get_additional_name(rrset, rr, &nm, &nmlen, pkt)) { /* mark A */ - hashvalue_t h = pkt_hash_rrset(pkt, nm, LDNS_RR_TYPE_A, - rrset->rrset_class, 0); + hashvalue_type h = pkt_hash_rrset(pkt, nm, + LDNS_RR_TYPE_A, rrset->rrset_class, 0); struct rrset_parse* r = msgparse_hashtable_lookup( msg, pkt, h, 0, nm, nmlen, LDNS_RR_TYPE_A, rrset->rrset_class); diff --git a/external/unbound/iterator/iter_utils.c b/external/unbound/iterator/iter_utils.c index 58e62fbeb..0b1b45611 100644 --- a/external/unbound/iterator/iter_utils.c +++ b/external/unbound/iterator/iter_utils.c @@ -108,7 +108,7 @@ read_fetch_policy(struct iter_env* ie, const char* str) /** apply config caps whitelist items to name tree */ static int -caps_white_apply_cfg(rbtree_t* ntree, struct config_file* cfg) +caps_white_apply_cfg(rbtree_type* ntree, struct config_file* cfg) { struct config_strlist* p; for(p=cfg->caps_whitelist; p; p=p->next) { @@ -360,6 +360,39 @@ iter_filter_order(struct iter_env* iter_env, struct module_env* env, } } *selected_rtt = low_rtt; + + if (env->cfg->prefer_ip6) { + int got_num6 = 0; + int low_rtt6 = 0; + int i; + prev = NULL; + a = dp->result_list; + for(i = 0; i < got_num; i++) { + swap_to_front = 0; + if(a->addr.ss_family == AF_INET6) { + got_num6++; + swap_to_front = 1; + if(low_rtt6 == 0 || a->sel_rtt < low_rtt6) { + low_rtt6 = a->sel_rtt; + } + } + /* swap to front if IPv6, or move to next result */ + if(swap_to_front && prev) { + n = a->next_result; + prev->next_result = n; + a->next_result = dp->result_list; + dp->result_list = a; + a = n; + } else { + prev = a; + a = a->next_result; + } + } + if(got_num6 > 0) { + got_num = got_num6; + *selected_rtt = low_rtt6; + } + } return got_num; } @@ -499,6 +532,7 @@ causes_cycle(struct module_qstate* qstate, uint8_t* name, size_t namelen, qinf.qname_len = namelen; qinf.qtype = t; qinf.qclass = c; + qinf.local_alias = NULL; fptr_ok(fptr_whitelist_modenv_detect_cycle( qstate->env->detect_cycle)); return (*qstate->env->detect_cycle)(qstate, &qinf, @@ -590,6 +624,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/external/unbound/iterator/iter_utils.h b/external/unbound/iterator/iter_utils.h index 3a4df3e45..50c5fc093 100644 --- a/external/unbound/iterator/iter_utils.h +++ b/external/unbound/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/external/unbound/iterator/iterator.c b/external/unbound/iterator/iterator.c index b1bf902d5..43b3d30c3 100644 --- a/external/unbound/iterator/iterator.c +++ b/external/unbound/iterator/iterator.c @@ -82,27 +82,13 @@ 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; } /** delete caps_whitelist element */ static void -caps_free(struct rbnode_t* n, void* ATTR_UNUSED(d)) +caps_free(struct rbnode_type* n, void* ATTR_UNUSED(d)) { if(n) { free(((struct name_tree_node*)n)->name); @@ -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,8 @@ 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; + iq->minimise_timeout_count = 0; if (qstate->env->cfg->qname_minimisation) iq->minimisation_state = INIT_MINIMISE_STATE; else @@ -229,6 +216,7 @@ error_supers(struct module_qstate* qstate, int id, struct module_qstate* super) qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA) { /* mark address as failed. */ struct delegpt_ns* dpns = NULL; + super_iq->num_target_queries--; if(super_iq->dp) dpns = delegpt_find_ns(super_iq->dp, qstate->qinfo.qname, qstate->qinfo.qname_len); @@ -242,13 +230,11 @@ error_supers(struct module_qstate* qstate, int id, struct module_qstate* super) return; } else { /* see if the failure did get (parent-lame) info */ - if(!cache_fill_missing(super->env, - super_iq->qchase.qclass, super->region, - super_iq->dp)) + if(!cache_fill_missing(super->env, super_iq->qchase.qclass, + super->region, super_iq->dp)) log_err("out of memory adding missing"); } dpns->resolved = 1; /* mark as failed */ - super_iq->num_target_queries--; } if(qstate->qinfo.qtype == LDNS_RR_TYPE_NS) { /* prime failed to get delegation */ @@ -291,27 +277,29 @@ error_response(struct module_qstate* qstate, int id, int rcode) static int error_response_cache(struct module_qstate* qstate, int id, int rcode) { - /* store in cache */ - struct reply_info err; - if(qstate->prefetch_leeway > NORR_TTL) { - verbose(VERB_ALGO, "error response for prefetch in cache"); - /* attempt to adjust the cache entry prefetch */ - if(dns_cache_prefetch_adjust(qstate->env, &qstate->qinfo, - NORR_TTL, qstate->query_flags)) - return error_response(qstate, id, rcode); - /* if that fails (not in cache), fall through to store err */ - } - memset(&err, 0, sizeof(err)); - err.flags = (uint16_t)(BIT_QR | BIT_RA); - FLAGS_SET_RCODE(err.flags, rcode); - err.qdcount = 1; - err.ttl = NORR_TTL; - err.prefetch_ttl = PREFETCH_TTL_CALC(err.ttl); - /* do not waste time trying to validate this servfail */ - err.security = sec_status_indeterminate; - verbose(VERB_ALGO, "store error response in message cache"); - iter_dns_store(qstate->env, &qstate->qinfo, &err, 0, 0, 0, NULL, - qstate->query_flags); + if(!qstate->no_cache_store) { + /* store in cache */ + struct reply_info err; + if(qstate->prefetch_leeway > NORR_TTL) { + verbose(VERB_ALGO, "error response for prefetch in cache"); + /* attempt to adjust the cache entry prefetch */ + if(dns_cache_prefetch_adjust(qstate->env, &qstate->qinfo, + NORR_TTL, qstate->query_flags)) + return error_response(qstate, id, rcode); + /* if that fails (not in cache), fall through to store err */ + } + memset(&err, 0, sizeof(err)); + err.flags = (uint16_t)(BIT_QR | BIT_RA); + FLAGS_SET_RCODE(err.flags, rcode); + err.qdcount = 1; + err.ttl = NORR_TTL; + err.prefetch_ttl = PREFETCH_TTL_CALC(err.ttl); + /* do not waste time trying to validate this servfail */ + err.security = sec_status_indeterminate; + verbose(VERB_ALGO, "store error response in message cache"); + iter_dns_store(qstate->env, &qstate->qinfo, &err, 0, 0, 0, NULL, + qstate->query_flags); + } return error_response(qstate, id, rcode); } @@ -385,6 +373,29 @@ iter_prepend(struct iter_qstate* iq, struct dns_msg* msg, } /** + * Find rrset in ANSWER prepend list. + * to avoid duplicate DNAMEs when a DNAME is traversed twice. + * @param iq: iterator query state. + * @param rrset: rrset to add. + * @return false if not found + */ +static int +iter_find_rrset_in_prepend_answer(struct iter_qstate* iq, + struct ub_packed_rrset_key* rrset) +{ + struct iter_prep_list* p = iq->an_prepend_list; + while(p) { + if(ub_rrset_compare(p->rrset, rrset) == 0 && + rrsetdata_equal((struct packed_rrset_data*)p->rrset + ->entry.data, (struct packed_rrset_data*)rrset + ->entry.data)) + return 1; + p = p->next; + } + return 0; +} + +/** * Add rrset to ANSWER prepend list * @param qstate: query state. * @param iq: iterator query state. @@ -466,14 +477,16 @@ handle_cname_response(struct module_qstate* qstate, struct iter_qstate* iq, * by this DNAME following, so we don't process the DNAME * directly. */ if(ntohs(r->rk.type) == LDNS_RR_TYPE_DNAME && - dname_strict_subdomain_c(*mname, r->rk.dname)) { + dname_strict_subdomain_c(*mname, r->rk.dname) && + !iter_find_rrset_in_prepend_answer(iq, r)) { if(!iter_add_prepend_answer(qstate, iq, r)) return 0; continue; } if(ntohs(r->rk.type) == LDNS_RR_TYPE_CNAME && - query_dname_compare(*mname, r->rk.dname) == 0) { + query_dname_compare(*mname, r->rk.dname) == 0 && + !iter_find_rrset_in_prepend_answer(iq, r)) { /* Add this relevant CNAME rrset to the prepend list.*/ if(!iter_add_prepend_answer(qstate, iq, r)) return 0; @@ -564,6 +577,7 @@ generate_sub_request(uint8_t* qname, size_t qnamelen, uint16_t qtype, qinf.qname_len = qnamelen; qinf.qtype = qtype; qinf.qclass = qclass; + qinf.local_alias = NULL; /* RD should be set only when sending the query back through the INIT * state. */ @@ -981,7 +995,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, { uint8_t* delname; size_t delnamelen; - struct dns_msg* msg; + struct dns_msg* msg = NULL; log_query_info(VERB_DETAIL, "resolving", &qstate->qinfo); /* check effort */ @@ -1021,13 +1035,13 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, * getting older results from cache is a bad idea, no cache */ verbose(VERB_ALGO, "cache blacklisted, going to the network"); msg = NULL; - } else { + } else if(!qstate->no_cache_lookup) { 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); if(!msg && qstate->env->neg_cache) { - /* lookup in negative cache; may result in + /* lookup in negative cache; may result in * NOERROR/NODATA or NXDOMAIN answers that need validation */ msg = val_neg_getmsg(qstate->env->neg_cache, &iq->qchase, qstate->region, qstate->env->rrset_cache, @@ -1337,7 +1351,7 @@ processInitRequest3(struct module_qstate* qstate, struct iter_qstate* iq, /* If the RD flag wasn't set, then we just finish with the * cached referral as the response. */ - if(!(qstate->query_flags & BIT_RD)) { + if(!(qstate->query_flags & BIT_RD) && iq->deleg_msg) { iq->response = iq->deleg_msg; if(verbosity >= VERB_ALGO && iq->response) log_dns_msg("no RD requested, using delegation msg", @@ -1713,10 +1727,11 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq, /* if this was a parent-side glue query itself, then store that * failure in cache. */ - if(iq->query_for_pside_glue && !iq->pside_glue) - iter_store_parentside_neg(qstate->env, &qstate->qinfo, - iq->deleg_msg?iq->deleg_msg->rep: - (iq->response?iq->response->rep:NULL)); + if(!qstate->no_cache_store && iq->query_for_pside_glue + && !iq->pside_glue) + iter_store_parentside_neg(qstate->env, &qstate->qinfo, + iq->deleg_msg?iq->deleg_msg->rep: + (iq->response?iq->response->rep:NULL)); verbose(VERB_QUERY, "out of query targets -- returning SERVFAIL"); /* fail -- no more targets, no more hope of targets, no hope @@ -2009,9 +2024,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) @@ -2019,30 +2035,53 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, iq->dp->name))) { iq->qinfo_out.qname = iq->dp->name; iq->qinfo_out.qname_len = iq->dp->namelen; - iq->qinfo_out.qtype = LDNS_RR_TYPE_NS; + iq->qinfo_out.qtype = LDNS_RR_TYPE_A; iq->qinfo_out.qclass = iq->qchase.qclass; + iq->qinfo_out.local_alias = NULL; + 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++; + iq->minimise_timeout_count = 0; - /* 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; + iter_dec_attempts(iq->dp, 1); + + /* 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) { @@ -2051,11 +2090,12 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, &iq->qinfo_out.qname_len, labdiff-1); } - if(labdiff < 1 || - (labdiff < 2 && iq->qchase.qtype == LDNS_RR_TYPE_DS)) + if(labdiff < 1 || (labdiff < 2 + && (iq->qchase.qtype == LDNS_RR_TYPE_DS + || iq->qchase.qtype == LDNS_RR_TYPE_A))) /* Stop minimising this query, resolve "as usual" */ iq->minimisation_state = DONOT_MINIMISE_STATE; - else { + else if(!qstate->no_cache_lookup) { struct dns_msg* msg = dns_cache_lookup(qstate->env, iq->qinfo_out.qname, iq->qinfo_out.qname_len, iq->qinfo_out.qtype, iq->qinfo_out.qclass, @@ -2068,12 +2108,18 @@ 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 - * iteration */ - iq->minimisation_state = MINIMISE_STATE; + if(iq->minimisation_state == SKIP_MINIMISE_STATE) { + iq->minimise_timeout_count++; + if(iq->minimise_timeout_count < MAX_MINIMISE_TIMEOUT_COUNT) + /* Do not increment qname, continue incrementing next + * iteration */ + iq->minimisation_state = MINIMISE_STATE; + else if(!qstate->env->cfg->qname_minimisation_strict) + /* Too many time-outs detected for this QNAME and QTYPE. + * We give up, disable QNAME minimisation. */ + iq->minimisation_state = DONOT_MINIMISE_STATE; + } if(iq->minimisation_state == DONOT_MINIMISE_STATE) iq->qinfo_out = iq->qchase; @@ -2087,13 +2133,18 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, iq->dnssec_lame_query?" but lame_query anyway": ""); } fptr_ok(fptr_whitelist_modenv_send_query(qstate->env->send_query)); - 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, + outq = (*qstate->env->send_query)(&iq->qinfo_out, + 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), &target->addr, target->addrlen, + iq->dp->name, iq->dp->namelen, + (iq->dp->ssl_upstream || qstate->env->cfg->ssl_upstream), qstate); if(!outq) { log_addr(VERB_DETAIL, "error sending query to auth server", &target->addr, target->addrlen); @@ -2143,9 +2194,13 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, int dnsseclame = 0; enum response_type type; iq->num_current_queries--; + + if(!inplace_cb_query_response_call(qstate->env, qstate, iq->response)) + log_err("unable to call query_response callback"); + if(iq->response == NULL) { /* Don't increment qname when QNAME minimisation is enabled */ - if (qstate->env->cfg->qname_minimisation) + if(qstate->env->cfg->qname_minimisation) iq->minimisation_state = SKIP_MINIMISE_STATE; iq->chase_to_rd = 0; iq->dnssec_lame_query = 0; @@ -2161,8 +2216,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 @@ -2205,6 +2262,22 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, } else iter_scrub_ds(iq->response, ns, iq->dp->name); } else iter_scrub_ds(iq->response, NULL, NULL); + if(type == RESPONSE_TYPE_THROWAWAY && + FLAGS_GET_RCODE(iq->response->rep->flags) == LDNS_RCODE_YXDOMAIN) { + /* YXDOMAIN is a permanent error, no need to retry */ + type = RESPONSE_TYPE_ANSWER; + } + if(type == RESPONSE_TYPE_CNAME && iq->response->rep->an_numrrsets >= 1 + && ntohs(iq->response->rep->rrsets[0]->rk.type) == LDNS_RR_TYPE_DNAME) { + uint8_t* sname = NULL; + size_t snamelen = 0; + get_cname_target(iq->response->rep->rrsets[0], &sname, + &snamelen); + if(snamelen && dname_subdomain_c(sname, iq->response->rep->rrsets[0]->rk.dname)) { + /* DNAME to a subdomain loop; do not recurse */ + type = RESPONSE_TYPE_ANSWER; + } + } /* handle each of the type cases */ if(type == RESPONSE_TYPE_ANSWER) { @@ -2232,10 +2305,11 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, iq->num_target_queries = 0; return processDSNSFind(qstate, iq, id); } - iter_dns_store(qstate->env, &iq->response->qinfo, - iq->response->rep, 0, qstate->prefetch_leeway, - iq->dp&&iq->dp->has_parent_side_NS, - qstate->region, qstate->query_flags); + if(!qstate->no_cache_store) + iter_dns_store(qstate->env, &iq->response->qinfo, + iq->response->rep, 0, qstate->prefetch_leeway, + iq->dp&&iq->dp->has_parent_side_NS, + qstate->region, qstate->query_flags); /* close down outstanding requests to be discarded */ outbound_list_clear(&iq->outlist); iq->num_current_queries = 0; @@ -2248,12 +2322,44 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, &qstate->reply->addr, qstate->reply->addrlen, qstate->region); if(iq->minimisation_state != DONOT_MINIMISE_STATE) { - /* Best effort qname-minimisation. - * Stop minimising and send full query when RCODE - * is not NOERROR */ if(FLAGS_GET_RCODE(iq->response->rep->flags) != - LDNS_RCODE_NOERROR) + LDNS_RCODE_NOERROR) { + if(qstate->env->cfg->qname_minimisation_strict) + return final_state(iq); + /* Best effort qname-minimisation. + * Stop minimising and send full query when + * RCODE is not 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); @@ -2271,7 +2377,8 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, } /* if hardened, only store referral if we asked for it */ - if(!qstate->env->cfg->harden_referral_path || + if(!qstate->no_cache_store && + (!qstate->env->cfg->harden_referral_path || ( qstate->qinfo.qtype == LDNS_RR_TYPE_NS && (qstate->query_flags&BIT_RD) && !(qstate->query_flags&BIT_CD) @@ -2286,7 +2393,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, iq->qchase.qname, iq->qchase.qname_len, LDNS_RR_TYPE_NS, iq->qchase.qclass) ) - )) { + ))) { /* Store the referral under the current query */ /* no prefetch-leeway, since its not the answer */ iter_dns_store(qstate->env, &iq->response->qinfo, @@ -2299,16 +2406,17 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, iq->response->rep, iq->dp->name); } /* store parent-side-in-zone-glue, if directly queried for */ - if(iq->query_for_pside_glue && !iq->pside_glue) { - iq->pside_glue = reply_find_rrset(iq->response->rep, - iq->qchase.qname, iq->qchase.qname_len, - iq->qchase.qtype, iq->qchase.qclass); - if(iq->pside_glue) { - log_rrset_key(VERB_ALGO, "found parent-side " - "glue", iq->pside_glue); - iter_store_parentside_rrset(qstate->env, - iq->pside_glue); - } + if(!qstate->no_cache_store && iq->query_for_pside_glue + && !iq->pside_glue) { + iq->pside_glue = reply_find_rrset(iq->response->rep, + iq->qchase.qname, iq->qchase.qname_len, + iq->qchase.qtype, iq->qchase.qclass); + if(iq->pside_glue) { + log_rrset_key(VERB_ALGO, "found parent-side " + "glue", iq->pside_glue); + iter_store_parentside_rrset(qstate->env, + iq->pside_glue); + } } /* Reset the event state, setting the current delegation @@ -2389,10 +2497,11 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, /* NOTE : set referral=1, so that rrsets get stored but not * the partial query answer (CNAME only). */ /* prefetchleeway applied because this updates answer parts */ - iter_dns_store(qstate->env, &iq->response->qinfo, - iq->response->rep, 1, qstate->prefetch_leeway, - iq->dp&&iq->dp->has_parent_side_NS, NULL, - qstate->query_flags); + if(!qstate->no_cache_store) + iter_dns_store(qstate->env, &iq->response->qinfo, + iq->response->rep, 1, qstate->prefetch_leeway, + iq->dp&&iq->dp->has_parent_side_NS, NULL, + qstate->query_flags); /* set the current request's qname to the new value. */ iq->qchase.qname = sname; iq->qchase.qname_len = snamelen; @@ -2471,7 +2580,8 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, /* LAME, THROWAWAY and "unknown" all end up here. * Recycle to the QUERYTARGETS state to hopefully try a * different target. */ - if (qstate->env->cfg->qname_minimisation) + if (qstate->env->cfg->qname_minimisation && + !qstate->env->cfg->qname_minimisation_strict) iq->minimisation_state = DONOT_MINIMISE_STATE; return next_state(iq, QUERYTARGETS_STATE); } @@ -2605,6 +2715,10 @@ processTargetResponse(struct module_qstate* qstate, int id, log_query_info(VERB_ALGO, "processTargetResponse", &qstate->qinfo); log_query_info(VERB_ALGO, "processTargetResponse super", &forq->qinfo); + /* Tell the originating event that this target query has finished + * (regardless if it succeeded or not). */ + foriq->num_target_queries--; + /* check to see if parent event is still interested (in orig name). */ if(!foriq->dp) { verbose(VERB_ALGO, "subq: parent not interested, was reset"); @@ -2620,10 +2734,6 @@ processTargetResponse(struct module_qstate* qstate, int id, return; } - /* Tell the originating event that this target query has finished - * (regardless if it succeeded or not). */ - foriq->num_target_queries--; - /* if iq->query_for_pside_glue then add the pside_glue (marked lame) */ if(iq->pside_glue) { /* if the pside_glue is NULL, then it could not be found, @@ -2871,10 +2981,11 @@ processFinished(struct module_qstate* qstate, struct iter_qstate* iq, &qstate->qinfo); /* store negative cache element for parent side glue. */ - if(iq->query_for_pside_glue && !iq->pside_glue) - iter_store_parentside_neg(qstate->env, &qstate->qinfo, - iq->deleg_msg?iq->deleg_msg->rep: - (iq->response?iq->response->rep:NULL)); + if(!qstate->no_cache_store && iq->query_for_pside_glue + && !iq->pside_glue) + iter_store_parentside_neg(qstate->env, &qstate->qinfo, + iq->deleg_msg?iq->deleg_msg->rep: + (iq->response?iq->response->rep:NULL)); if(!iq->response) { verbose(VERB_ALGO, "No response is set, servfail"); return error_response(qstate, id, LDNS_RCODE_SERVFAIL); @@ -2910,7 +3021,7 @@ processFinished(struct module_qstate* qstate, struct iter_qstate* iq, /* store message with the finished prepended items, * but only if we did recursion. The nonrecursion referral * from cache does not need to be stored in the msg cache. */ - if(qstate->query_flags&BIT_RD) { + if(!qstate->no_cache_store && qstate->query_flags&BIT_RD) { iter_dns_store(qstate->env, &qstate->qinfo, iq->response->rep, 0, qstate->prefetch_leeway, iq->dp&&iq->dp->has_parent_side_NS, @@ -3082,8 +3193,25 @@ 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; + + /* Copy the edns options we may got from the back end */ + if(edns.opt_list) { + qstate->edns_opts_back_in = edns_opt_copy_region(edns.opt_list, + qstate->region); + if(!qstate->edns_opts_back_in) { + log_err("out of memory on incoming message"); + /* like packet got dropped */ + goto handle_it; + } + if(!inplace_cb_edns_back_parsed_call(qstate->env, qstate)) { + log_err("unable to call edns_back_parsed callback"); + goto handle_it; + } + } + /* remove CD-bit, we asked for in case we handle validation ourself */ prs->flags &= ~BIT_CD; diff --git a/external/unbound/iterator/iterator.h b/external/unbound/iterator/iterator.h index b7aa82ebe..37b0ab0dc 100644 --- a/external/unbound/iterator/iterator.h +++ b/external/unbound/iterator/iterator.h @@ -51,7 +51,7 @@ struct iter_forwards; struct iter_donotq; struct iter_prep_list; struct iter_priv; -struct rbtree_t; +struct rbtree_type; /** max number of targets spawned for a query and its subqueries */ #define MAX_TARGET_COUNT 64 @@ -61,6 +61,23 @@ 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 +/* max number of time-outs for minimised query. Prevents resolving failures + * when the QNAME minimisation QTYPE is blocked. */ +#define MAX_MINIMISE_TIMEOUT_COUNT 3 +/** + * 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 @@ -98,7 +115,7 @@ struct iter_env { struct iter_priv* priv; /** whitelist for capsforid names */ - struct rbtree_t* caps_white; + struct rbtree_type* caps_white; /** The maximum dependency depth that this resolver will pursue. */ int max_dependency_depth; @@ -349,7 +366,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 +374,17 @@ 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; + + /** + * Count number of time-outs. Used to prevent resolving failures when + * the QNAME minimisation QTYPE is blocked. */ + int minimise_timeout_count; }; /** |