diff options
Diffstat (limited to 'contrib/unbound/iterator/iter_utils.c')
-rw-r--r-- | contrib/unbound/iterator/iter_utils.c | 256 |
1 files changed, 197 insertions, 59 deletions
diff --git a/contrib/unbound/iterator/iter_utils.c b/contrib/unbound/iterator/iter_utils.c index 10a8ec3eb08f..1da21896cb20 100644 --- a/contrib/unbound/iterator/iter_utils.c +++ b/contrib/unbound/iterator/iter_utils.c @@ -77,41 +77,73 @@ static const char DEFAULT_NAT64_PREFIX[] = "64:ff9b::/96"; /** fillup fetch policy array */ -static void -fetch_fill(struct iter_env* ie, const char* str) +static int +fetch_fill(int* target_fetch_policy, int max_dependency_depth, const char* str) { char* s = (char*)str, *e; int i; - for(i=0; i<ie->max_dependency_depth+1; i++) { - ie->target_fetch_policy[i] = strtol(s, &e, 10); - if(s == e) - fatal_exit("cannot parse fetch policy number %s", s); + for(i=0; i<max_dependency_depth+1; i++) { + target_fetch_policy[i] = strtol(s, &e, 10); + if(s == e) { + log_err("cannot parse fetch policy number %s", s); + return 0; + } s = e; } + return 1; } /** Read config string that represents the target fetch policy */ -static int -read_fetch_policy(struct iter_env* ie, const char* str) +int +read_fetch_policy(int** target_fetch_policy, int* max_dependency_depth, + const char* str) { int count = cfg_count_numbers(str); if(count < 1) { log_err("Cannot parse target fetch policy: \"%s\"", str); return 0; } - ie->max_dependency_depth = count - 1; - ie->target_fetch_policy = (int*)calloc( - (size_t)ie->max_dependency_depth+1, sizeof(int)); - if(!ie->target_fetch_policy) { + *max_dependency_depth = count - 1; + *target_fetch_policy = (int*)calloc( + (size_t)(*max_dependency_depth)+1, sizeof(int)); + if(!*target_fetch_policy) { log_err("alloc fetch policy: out of memory"); return 0; } - fetch_fill(ie, str); + if(!fetch_fill(*target_fetch_policy, *max_dependency_depth, str)) + return 0; return 1; } -/** apply config caps whitelist items to name tree */ -static int +struct rbtree_type* +caps_white_create(void) +{ + struct rbtree_type* caps_white = rbtree_create(name_tree_compare); + if(!caps_white) + log_err("out of memory"); + return caps_white; +} + +/** delete caps_whitelist element */ +static void +caps_free(struct rbnode_type* n, void* ATTR_UNUSED(d)) +{ + if(n) { + free(((struct name_tree_node*)n)->name); + free(n); + } +} + +void +caps_white_delete(struct rbtree_type* caps_white) +{ + if(!caps_white) + return; + traverse_postorder(caps_white, caps_free, NULL); + free(caps_white); +} + +int caps_white_apply_cfg(rbtree_type* ntree, struct config_file* cfg) { struct config_strlist* p; @@ -145,12 +177,41 @@ caps_white_apply_cfg(rbtree_type* ntree, struct config_file* cfg) } int -iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg) +nat64_apply_cfg(struct iter_nat64* nat64, struct config_file* cfg) { const char *nat64_prefix; + + nat64_prefix = cfg->nat64_prefix; + if(!nat64_prefix) + nat64_prefix = cfg->dns64_prefix; + if(!nat64_prefix) + nat64_prefix = DEFAULT_NAT64_PREFIX; + if(!netblockstrtoaddr(nat64_prefix, 0, &nat64->nat64_prefix_addr, + &nat64->nat64_prefix_addrlen, &nat64->nat64_prefix_net)) { + log_err("cannot parse nat64-prefix netblock: %s", nat64_prefix); + return 0; + } + if(!addr_is_ip6(&nat64->nat64_prefix_addr, + nat64->nat64_prefix_addrlen)) { + log_err("nat64-prefix is not IPv6: %s", cfg->nat64_prefix); + return 0; + } + if(!prefixnet_is_nat64(nat64->nat64_prefix_net)) { + log_err("nat64-prefix length it not 32, 40, 48, 56, 64 or 96: %s", + nat64_prefix); + return 0; + } + nat64->use_nat64 = cfg->do_nat64; + return 1; +} + +int +iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg) +{ int i; /* target fetch policy */ - if(!read_fetch_policy(iter_env, cfg->target_fetch_policy)) + if(!read_fetch_policy(&iter_env->target_fetch_policy, + &iter_env->max_dependency_depth, cfg->target_fetch_policy)) return 0; for(i=0; i<iter_env->max_dependency_depth+1; i++) verbose(VERB_QUERY, "target fetch policy for level %d is %d", @@ -170,7 +231,7 @@ iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg) } if(cfg->caps_whitelist) { if(!iter_env->caps_white) - iter_env->caps_white = rbtree_create(name_tree_compare); + iter_env->caps_white = caps_white_create(); if(!iter_env->caps_white || !caps_white_apply_cfg( iter_env->caps_white, cfg)) { log_err("Could not set capsforid whitelist"); @@ -179,31 +240,13 @@ iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg) } - nat64_prefix = cfg->nat64_prefix; - if(!nat64_prefix) - nat64_prefix = cfg->dns64_prefix; - if(!nat64_prefix) - nat64_prefix = DEFAULT_NAT64_PREFIX; - if(!netblockstrtoaddr(nat64_prefix, 0, &iter_env->nat64_prefix_addr, - &iter_env->nat64_prefix_addrlen, - &iter_env->nat64_prefix_net)) { - log_err("cannot parse nat64-prefix netblock: %s", nat64_prefix); - return 0; - } - if(!addr_is_ip6(&iter_env->nat64_prefix_addr, - iter_env->nat64_prefix_addrlen)) { - log_err("nat64-prefix is not IPv6: %s", cfg->nat64_prefix); - return 0; - } - if(!prefixnet_is_nat64(iter_env->nat64_prefix_net)) { - log_err("nat64-prefix length it not 32, 40, 48, 56, 64 or 96: %s", - nat64_prefix); + if(!nat64_apply_cfg(&iter_env->nat64, cfg)) { + log_err("Could not setup nat64"); return 0; } iter_env->supports_ipv6 = cfg->do_ip6; iter_env->supports_ipv4 = cfg->do_ip4; - iter_env->use_nat64 = cfg->do_nat64; iter_env->outbound_msg_retry = cfg->outbound_msg_retry; iter_env->max_sent_count = cfg->max_sent_count; iter_env->max_query_restarts = cfg->max_query_restarts; @@ -270,7 +313,7 @@ iter_filter_unsuitable(struct iter_env* iter_env, struct module_env* env, if(!iter_env->supports_ipv6 && addr_is_ip6(&a->addr, a->addrlen)) { return -1; /* there is no ip6 available */ } - if(!iter_env->supports_ipv4 && !iter_env->use_nat64 && + if(!iter_env->supports_ipv4 && !iter_env->nat64.use_nat64 && !addr_is_ip6(&a->addr, a->addrlen)) { return -1; /* there is no ip4 available */ } @@ -279,9 +322,10 @@ iter_filter_unsuitable(struct iter_env* iter_env, struct module_env* env, name, namelen, qtype, &lame, &dnsseclame, &reclame, &rtt, now)) { log_addr(VERB_ALGO, "servselect", &a->addr, a->addrlen); - verbose(VERB_ALGO, " rtt=%d%s%s%s%s", rtt, + verbose(VERB_ALGO, " rtt=%d%s%s%s%s%s", rtt, lame?" LAME":"", dnsseclame?" DNSSEC_LAME":"", + a->dnsseclame?" ADDR_DNSSEC_LAME":"", reclame?" REC_LAME":"", a->lame?" ADDR_LAME":""); if(lame) @@ -692,10 +736,11 @@ dns_copy_msg(struct dns_msg* from, struct regional* region) void iter_dns_store(struct module_env* env, struct query_info* msgqinf, struct reply_info* msgrep, int is_referral, time_t leeway, int pside, - struct regional* region, uint16_t flags, time_t qstarttime) + struct regional* region, uint16_t flags, time_t qstarttime, + int is_valrec) { if(!dns_cache_store(env, msgqinf, msgrep, is_referral, leeway, - pside, region, flags, qstarttime)) + pside, region, flags, qstarttime, is_valrec)) log_err("out of memory: cannot store data in cache"); } @@ -1284,8 +1329,17 @@ iter_get_next_root(struct iter_hints* hints, struct iter_forwards* fwd, uint16_t* c) { uint16_t c1 = *c, c2 = *c; - int r1 = hints_next_root(hints, &c1); - int r2 = forwards_next_root(fwd, &c2); + int r1, r2; + int nolock = 1; + + /* prelock both forwards and hints for atomic read. */ + lock_rw_rdlock(&fwd->lock); + lock_rw_rdlock(&hints->lock); + r1 = hints_next_root(hints, &c1, nolock); + r2 = forwards_next_root(fwd, &c2, nolock); + lock_rw_unlock(&fwd->lock); + lock_rw_unlock(&hints->lock); + if(!r1 && !r2) /* got none, end of list */ return 0; else if(!r1) /* got one, return that */ @@ -1450,15 +1504,21 @@ 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, - uint8_t** retdpname, size_t* retdpnamelen) + uint8_t** retdpname, size_t* retdpnamelen, uint8_t* dpname_storage, + size_t dpname_storage_len) { struct iter_hints_stub *stub; struct delegpt *dp; + int nolock = 1; /* Check for stub. */ + /* Lock both forwards and hints for atomic read. */ + lock_rw_rdlock(&qstate->env->fwds->lock); + lock_rw_rdlock(&qstate->env->hints->lock); stub = hints_lookup_stub(qstate->env->hints, qinf->qname, - qinf->qclass, NULL); - dp = forwards_lookup(qstate->env->fwds, qinf->qname, qinf->qclass); + qinf->qclass, NULL, nolock); + dp = forwards_lookup(qstate->env->fwds, qinf->qname, qinf->qclass, + nolock); /* see if forward or stub is more pertinent */ if(stub && stub->dp && dp) { @@ -1472,35 +1532,62 @@ iter_stub_fwd_no_cache(struct module_qstate *qstate, struct query_info *qinf, /* check stub */ if (stub != NULL && stub->dp != NULL) { - if(stub->dp->no_cache) { - char qname[255+1]; - char dpname[255+1]; + enum verbosity_value level = VERB_ALGO; + int stub_no_cache = stub->dp->no_cache; + lock_rw_unlock(&qstate->env->fwds->lock); + if(verbosity >= level && stub_no_cache) { + char qname[LDNS_MAX_DOMAINLEN]; + char dpname[LDNS_MAX_DOMAINLEN]; dname_str(qinf->qname, qname); dname_str(stub->dp->name, dpname); - verbose(VERB_ALGO, "stub for %s %s has no_cache", qname, dpname); + verbose(level, "stub for %s %s has no_cache", qname, dpname); } if(retdpname) { - *retdpname = stub->dp->name; + if(stub->dp->namelen > dpname_storage_len) { + verbose(VERB_ALGO, "no cache stub dpname too long"); + lock_rw_unlock(&qstate->env->hints->lock); + *retdpname = NULL; + *retdpnamelen = 0; + return stub_no_cache; + } + memmove(dpname_storage, stub->dp->name, + stub->dp->namelen); + *retdpname = dpname_storage; *retdpnamelen = stub->dp->namelen; } - return (stub->dp->no_cache); + lock_rw_unlock(&qstate->env->hints->lock); + return stub_no_cache; } /* Check for forward. */ if (dp) { - if(dp->no_cache) { - char qname[255+1]; - char dpname[255+1]; + enum verbosity_value level = VERB_ALGO; + int dp_no_cache = dp->no_cache; + lock_rw_unlock(&qstate->env->hints->lock); + if(verbosity >= level && dp_no_cache) { + char qname[LDNS_MAX_DOMAINLEN]; + char dpname[LDNS_MAX_DOMAINLEN]; dname_str(qinf->qname, qname); dname_str(dp->name, dpname); - verbose(VERB_ALGO, "forward for %s %s has no_cache", qname, dpname); + verbose(level, "forward for %s %s has no_cache", qname, dpname); } if(retdpname) { - *retdpname = dp->name; + if(dp->namelen > dpname_storage_len) { + verbose(VERB_ALGO, "no cache dpname too long"); + lock_rw_unlock(&qstate->env->fwds->lock); + *retdpname = NULL; + *retdpnamelen = 0; + return dp_no_cache; + } + memmove(dpname_storage, dp->name, dp->namelen); + *retdpname = dpname_storage; *retdpnamelen = dp->namelen; } - return (dp->no_cache); + lock_rw_unlock(&qstate->env->fwds->lock); + return dp_no_cache; } + lock_rw_unlock(&qstate->env->fwds->lock); + lock_rw_unlock(&qstate->env->hints->lock); if(retdpname) { *retdpname = NULL; *retdpnamelen = 0; @@ -1523,3 +1610,54 @@ void iterator_set_ip46_support(struct module_stack* mods, if(outnet->num_ip6 == 0) ie->supports_ipv6 = 0; } + +void +limit_nsec_ttl(struct dns_msg* msg) +{ + /* Limit NSEC and NSEC3 TTL in response, RFC9077 */ + size_t i; + int found = 0; + time_t soa_ttl = 0; + /* Limit the NSEC and NSEC3 TTL values to the SOA TTL and SOA minimum + * TTL. That has already been applied to the SOA record ttl. */ + for(i=0; i<msg->rep->rrset_count; i++) { + struct ub_packed_rrset_key* s = msg->rep->rrsets[i]; + if(ntohs(s->rk.type) == LDNS_RR_TYPE_SOA) { + struct packed_rrset_data* soadata = (struct packed_rrset_data*)s->entry.data; + found = 1; + soa_ttl = soadata->ttl; + break; + } + } + if(!found) + return; + for(i=0; i<msg->rep->rrset_count; i++) { + struct ub_packed_rrset_key* s = msg->rep->rrsets[i]; + if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC || + ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) { + struct packed_rrset_data* data = (struct packed_rrset_data*)s->entry.data; + /* Limit the negative TTL. */ + if(data->ttl > soa_ttl) { + if(verbosity >= VERB_ALGO) { + char buf[256]; + snprintf(buf, sizeof(buf), + "limiting TTL %d of %s record to the SOA TTL of %d for", + (int)data->ttl, ((ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC)?"NSEC":"NSEC3"), (int)soa_ttl); + log_nametypeclass(VERB_ALGO, buf, + s->rk.dname, ntohs(s->rk.type), + ntohs(s->rk.rrset_class)); + } + data->ttl = soa_ttl; + } + } + } +} + +void +iter_make_minimal(struct reply_info* rep) +{ + size_t rem = rep->ns_numrrsets + rep->ar_numrrsets; + rep->ns_numrrsets = 0; + rep->ar_numrrsets = 0; + rep->rrset_count -= rem; +} |