aboutsummaryrefslogtreecommitdiff
path: root/contrib/unbound/iterator/iter_utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/unbound/iterator/iter_utils.c')
-rw-r--r--contrib/unbound/iterator/iter_utils.c256
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;
+}