summaryrefslogtreecommitdiff
path: root/services
diff options
context:
space:
mode:
Diffstat (limited to 'services')
-rw-r--r--services/authzone.c48
-rw-r--r--services/cache/dns.c33
-rw-r--r--services/cache/infra.c27
-rw-r--r--services/cache/infra.h2
-rw-r--r--services/listen_dnsport.c2
-rw-r--r--services/localzone.c71
-rw-r--r--services/localzone.h19
-rw-r--r--services/mesh.c41
-rw-r--r--services/modstack.c78
-rw-r--r--services/modstack.h38
-rw-r--r--services/outside_network.c20
-rw-r--r--services/rpz.c41
12 files changed, 320 insertions, 100 deletions
diff --git a/services/authzone.c b/services/authzone.c
index f01a6d9e0e0e..580a681f57ce 100644
--- a/services/authzone.c
+++ b/services/authzone.c
@@ -7778,7 +7778,8 @@ static void auth_zone_log(uint8_t* name, enum verbosity_value level,
static int zonemd_dnssec_verify_rrset(struct auth_zone* z,
struct module_env* env, struct module_stack* mods,
struct ub_packed_rrset_key* dnskey, struct auth_data* node,
- struct auth_rrset* rrset, char** why_bogus, uint8_t* sigalg)
+ struct auth_rrset* rrset, char** why_bogus, uint8_t* sigalg,
+ char* reasonbuf, size_t reasonlen)
{
struct ub_packed_rrset_key pk;
enum sec_status sec;
@@ -7808,7 +7809,7 @@ static int zonemd_dnssec_verify_rrset(struct auth_zone* z,
"zonemd: verify %s RRset with DNSKEY", typestr);
}
sec = dnskeyset_verify_rrset(env, ve, &pk, dnskey, sigalg, why_bogus, NULL,
- LDNS_SECTION_ANSWER, NULL, &verified);
+ LDNS_SECTION_ANSWER, NULL, &verified, reasonbuf, reasonlen);
if(sec == sec_status_secure) {
return 1;
}
@@ -7851,7 +7852,8 @@ static int nsec3_of_param_has_type(struct auth_rrset* nsec3, int algo,
static int zonemd_check_dnssec_absence(struct auth_zone* z,
struct module_env* env, struct module_stack* mods,
struct ub_packed_rrset_key* dnskey, struct auth_data* apex,
- char** reason, char** why_bogus, uint8_t* sigalg)
+ char** reason, char** why_bogus, uint8_t* sigalg, char* reasonbuf,
+ size_t reasonlen)
{
struct auth_rrset* nsec = NULL;
if(!apex) {
@@ -7863,7 +7865,7 @@ static int zonemd_check_dnssec_absence(struct auth_zone* z,
struct ub_packed_rrset_key pk;
/* dnssec verify the NSEC */
if(!zonemd_dnssec_verify_rrset(z, env, mods, dnskey, apex,
- nsec, why_bogus, sigalg)) {
+ nsec, why_bogus, sigalg, reasonbuf, reasonlen)) {
*reason = "DNSSEC verify failed for NSEC RRset";
return 0;
}
@@ -7906,7 +7908,7 @@ static int zonemd_check_dnssec_absence(struct auth_zone* z,
}
/* dnssec verify the NSEC3 */
if(!zonemd_dnssec_verify_rrset(z, env, mods, dnskey, match,
- nsec3, why_bogus, sigalg)) {
+ nsec3, why_bogus, sigalg, reasonbuf, reasonlen)) {
*reason = "DNSSEC verify failed for NSEC3 RRset";
return 0;
}
@@ -7928,7 +7930,7 @@ static int zonemd_check_dnssec_soazonemd(struct auth_zone* z,
struct module_env* env, struct module_stack* mods,
struct ub_packed_rrset_key* dnskey, struct auth_data* apex,
struct auth_rrset* zonemd_rrset, char** reason, char** why_bogus,
- uint8_t* sigalg)
+ uint8_t* sigalg, char* reasonbuf, size_t reasonlen)
{
struct auth_rrset* soa;
if(!apex) {
@@ -7941,12 +7943,12 @@ static int zonemd_check_dnssec_soazonemd(struct auth_zone* z,
return 0;
}
if(!zonemd_dnssec_verify_rrset(z, env, mods, dnskey, apex, soa,
- why_bogus, sigalg)) {
+ why_bogus, sigalg, reasonbuf, reasonlen)) {
*reason = "DNSSEC verify failed for SOA RRset";
return 0;
}
if(!zonemd_dnssec_verify_rrset(z, env, mods, dnskey, apex,
- zonemd_rrset, why_bogus, sigalg)) {
+ zonemd_rrset, why_bogus, sigalg, reasonbuf, reasonlen)) {
*reason = "DNSSEC verify failed for ZONEMD RRset";
return 0;
}
@@ -8014,6 +8016,7 @@ auth_zone_verify_zonemd_with_key(struct auth_zone* z, struct module_env* env,
struct module_stack* mods, struct ub_packed_rrset_key* dnskey,
int is_insecure, char** result, uint8_t* sigalg)
{
+ char reasonbuf[256];
char* reason = NULL, *why_bogus = NULL;
struct auth_data* apex = NULL;
struct auth_rrset* zonemd_rrset = NULL;
@@ -8042,7 +8045,8 @@ auth_zone_verify_zonemd_with_key(struct auth_zone* z, struct module_env* env,
} else if(!zonemd_rrset && dnskey && !is_insecure) {
/* fetch, DNSSEC verify, and check NSEC/NSEC3 */
if(!zonemd_check_dnssec_absence(z, env, mods, dnskey, apex,
- &reason, &why_bogus, sigalg)) {
+ &reason, &why_bogus, sigalg, reasonbuf,
+ sizeof(reasonbuf))) {
auth_zone_zonemd_fail(z, env, reason, why_bogus, result);
return;
}
@@ -8050,7 +8054,8 @@ auth_zone_verify_zonemd_with_key(struct auth_zone* z, struct module_env* env,
} else if(zonemd_rrset && dnskey && !is_insecure) {
/* check DNSSEC verify of SOA and ZONEMD */
if(!zonemd_check_dnssec_soazonemd(z, env, mods, dnskey, apex,
- zonemd_rrset, &reason, &why_bogus, sigalg)) {
+ zonemd_rrset, &reason, &why_bogus, sigalg, reasonbuf,
+ sizeof(reasonbuf))) {
auth_zone_zonemd_fail(z, env, reason, why_bogus, result);
return;
}
@@ -8107,6 +8112,8 @@ auth_zone_verify_zonemd_with_key(struct auth_zone* z, struct module_env* env,
* @param why_bogus: if the routine fails, returns the failure reason.
* @param keystorage: where to store the ub_packed_rrset_key that is created
* on success. A pointer to it is returned on success.
+ * @param reasonbuf: buffer to use for fail reason string print.
+ * @param reasonlen: length of reasonbuf.
* @return the dnskey RRset, reference to zone data and keystorage, or
* NULL on failure.
*/
@@ -8114,7 +8121,8 @@ static struct ub_packed_rrset_key*
zonemd_get_dnskey_from_anchor(struct auth_zone* z, struct module_env* env,
struct module_stack* mods, struct trust_anchor* anchor,
int* is_insecure, char** why_bogus,
- struct ub_packed_rrset_key* keystorage)
+ struct ub_packed_rrset_key* keystorage, char* reasonbuf,
+ size_t reasonlen)
{
struct auth_data* apex;
struct auth_rrset* dnskey_rrset;
@@ -8150,7 +8158,8 @@ zonemd_get_dnskey_from_anchor(struct auth_zone* z, struct module_env* env,
auth_zone_log(z->name, VERB_QUERY,
"zonemd: verify DNSKEY RRset with trust anchor");
sec = val_verify_DNSKEY_with_TA(env, ve, keystorage, anchor->ds_rrset,
- anchor->dnskey_rrset, NULL, why_bogus, NULL, NULL);
+ anchor->dnskey_rrset, NULL, why_bogus, NULL, NULL, reasonbuf,
+ reasonlen);
regional_free_all(env->scratch);
if(sec == sec_status_secure) {
/* success */
@@ -8173,7 +8182,8 @@ static struct ub_packed_rrset_key*
auth_zone_verify_zonemd_key_with_ds(struct auth_zone* z,
struct module_env* env, struct module_stack* mods,
struct ub_packed_rrset_key* ds, int* is_insecure, char** why_bogus,
- struct ub_packed_rrset_key* keystorage, uint8_t* sigalg)
+ struct ub_packed_rrset_key* keystorage, uint8_t* sigalg,
+ char* reasonbuf, size_t reasonlen)
{
struct auth_data* apex;
struct auth_rrset* dnskey_rrset;
@@ -8209,7 +8219,7 @@ auth_zone_verify_zonemd_key_with_ds(struct auth_zone* z,
keystorage->rk.rrset_class = htons(z->dclass);
auth_zone_log(z->name, VERB_QUERY, "zonemd: verify zone DNSKEY with DS");
sec = val_verify_DNSKEY_with_DS(env, ve, keystorage, ds, sigalg,
- why_bogus, NULL, NULL);
+ why_bogus, NULL, NULL, reasonbuf, reasonlen);
regional_free_all(env->scratch);
if(sec == sec_status_secure) {
/* success */
@@ -8235,6 +8245,7 @@ void auth_zonemd_dnskey_lookup_callback(void* arg, int rcode, sldns_buffer* buf,
{
struct auth_zone* z = (struct auth_zone*)arg;
struct module_env* env;
+ char reasonbuf[256];
char* reason = NULL, *ds_bogus = NULL, *typestr="DNSKEY";
struct ub_packed_rrset_key* dnskey = NULL, *ds = NULL;
int is_insecure = 0, downprot;
@@ -8346,7 +8357,8 @@ void auth_zonemd_dnskey_lookup_callback(void* arg, int rcode, sldns_buffer* buf,
if(!reason && !is_insecure && !dnskey && ds) {
dnskey = auth_zone_verify_zonemd_key_with_ds(z, env,
&env->mesh->mods, ds, &is_insecure, &ds_bogus,
- &keystorage, downprot?sigalg:NULL);
+ &keystorage, downprot?sigalg:NULL, reasonbuf,
+ sizeof(reasonbuf));
if(!dnskey && !is_insecure && !reason)
reason = "DNSKEY verify with DS failed";
}
@@ -8354,6 +8366,7 @@ void auth_zonemd_dnskey_lookup_callback(void* arg, int rcode, sldns_buffer* buf,
if(reason) {
auth_zone_zonemd_fail(z, env, reason, ds_bogus, NULL);
lock_rw_unlock(&z->lock);
+ regional_free_all(env->scratch);
return;
}
@@ -8438,6 +8451,7 @@ zonemd_lookup_dnskey(struct auth_zone* z, struct module_env* env)
void auth_zone_verify_zonemd(struct auth_zone* z, struct module_env* env,
struct module_stack* mods, char** result, int offline, int only_online)
{
+ char reasonbuf[256];
char* reason = NULL, *why_bogus = NULL;
struct trust_anchor* anchor = NULL;
struct ub_packed_rrset_key* dnskey = NULL;
@@ -8472,7 +8486,8 @@ void auth_zone_verify_zonemd(struct auth_zone* z, struct module_env* env,
}
/* equal to trustanchor, no need for online lookups */
dnskey = zonemd_get_dnskey_from_anchor(z, env, mods, anchor,
- &is_insecure, &why_bogus, &keystorage);
+ &is_insecure, &why_bogus, &keystorage, reasonbuf,
+ sizeof(reasonbuf));
lock_basic_unlock(&anchor->lock);
if(!dnskey && !reason && !is_insecure) {
reason = "verify DNSKEY RRset with trust anchor failed";
@@ -8498,6 +8513,7 @@ void auth_zone_verify_zonemd(struct auth_zone* z, struct module_env* env,
if(reason) {
auth_zone_zonemd_fail(z, env, reason, why_bogus, result);
+ regional_free_all(env->scratch);
return;
}
diff --git a/services/cache/dns.c b/services/cache/dns.c
index 632ed79ace49..5e74c31693b3 100644
--- a/services/cache/dns.c
+++ b/services/cache/dns.c
@@ -96,7 +96,8 @@ store_rrsets(struct module_env* env, struct reply_info* rep, time_t now,
struct ub_packed_rrset_key* ck;
lock_rw_rdlock(&rep->ref[i].key->entry.lock);
/* if deleted rrset, do not copy it */
- if(rep->ref[i].key->id == 0)
+ if(rep->ref[i].key->id == 0 ||
+ rep->ref[i].id != rep->ref[i].key->id)
ck = NULL;
else ck = packed_rrset_copy_region(
rep->ref[i].key, region, now);
@@ -109,14 +110,22 @@ store_rrsets(struct module_env* env, struct reply_info* rep, time_t now,
/* no break: also copy key item */
/* the line below is matched by gcc regex and silences
* the fallthrough warning */
+ ATTR_FALLTHROUGH
/* fallthrough */
case 1: /* ref updated, item inserted */
rep->rrsets[i] = rep->ref[i].key;
+ /* ref was updated; make sure the message ttl is
+ * updated to the minimum of the current rrsets. */
+ lock_rw_rdlock(&rep->ref[i].key->entry.lock);
+ /* if deleted, skip ttl update. */
+ if(rep->ref[i].key->id != 0 &&
+ rep->ref[i].id == rep->ref[i].key->id) {
+ ttl = ((struct packed_rrset_data*)
+ rep->rrsets[i]->entry.data)->ttl;
+ if(ttl < min_ttl) min_ttl = ttl;
+ }
+ lock_rw_unlock(&rep->ref[i].key->entry.lock);
}
- /* if ref was updated make sure the message ttl is updated to
- * the minimum of the current rrsets. */
- ttl = ((struct packed_rrset_data*)rep->rrsets[i]->entry.data)->ttl;
- if(ttl < min_ttl) min_ttl = ttl;
}
if(min_ttl < rep->ttl) {
rep->ttl = min_ttl;
@@ -337,6 +346,13 @@ find_add_addrs(struct module_env* env, uint16_t qclass,
* not use dns64 translation */
neg = msg_cache_lookup(env, ns->name, ns->namelen,
LDNS_RR_TYPE_AAAA, qclass, 0, now, 0);
+ /* Because recursion for lookup uses BIT_CD, check
+ * for that so it stops the recursion lookup, if a
+ * negative answer is cached. Because the cache uses
+ * the CD flag for type AAAA. */
+ if(!neg)
+ neg = msg_cache_lookup(env, ns->name, ns->namelen,
+ LDNS_RR_TYPE_AAAA, qclass, BIT_CD, now, 0);
if(neg) {
delegpt_add_neg_msg(dp, neg);
lock_rw_unlock(&neg->entry.lock);
@@ -396,6 +412,13 @@ cache_fill_missing(struct module_env* env, uint16_t qclass,
* not use dns64 translation */
neg = msg_cache_lookup(env, ns->name, ns->namelen,
LDNS_RR_TYPE_AAAA, qclass, 0, now, 0);
+ /* Because recursion for lookup uses BIT_CD, check
+ * for that so it stops the recursion lookup, if a
+ * negative answer is cached. Because the cache uses
+ * the CD flag for type AAAA. */
+ if(!neg)
+ neg = msg_cache_lookup(env, ns->name, ns->namelen,
+ LDNS_RR_TYPE_AAAA, qclass, BIT_CD, now, 0);
if(neg) {
delegpt_add_neg_msg(dp, neg);
lock_rw_unlock(&neg->entry.lock);
diff --git a/services/cache/infra.c b/services/cache/infra.c
index 457685ab5985..66b17c1218ea 100644
--- a/services/cache/infra.c
+++ b/services/cache/infra.c
@@ -60,6 +60,16 @@
* can do this number of packets (until those all timeout too) */
#define TIMEOUT_COUNT_MAX 3
+/** Minus 1000 because that is outside of the RTTBAND, so
+ * blacklisted servers stay blacklisted if this is chosen.
+ * If USEFUL_SERVER_TOP_TIMEOUT is below 1000 (configured via RTT_MAX_TIMEOUT,
+ * infra-cache-max-rtt) change it to just above the RTT_BAND. */
+#define STILL_USEFUL_TIMEOUT ( \
+ USEFUL_SERVER_TOP_TIMEOUT < 1000 || \
+ USEFUL_SERVER_TOP_TIMEOUT - 1000 <= RTT_BAND \
+ ?RTT_BAND + 1 \
+ :USEFUL_SERVER_TOP_TIMEOUT - 1000)
+
/** ratelimit value for delegation point */
int infra_dp_ratelimit = 0;
@@ -347,6 +357,7 @@ infra_create(struct config_file* cfg)
return NULL;
}
infra_ip_ratelimit = cfg->ip_ratelimit;
+ infra_ip_ratelimit_cookie = cfg->ip_ratelimit_cookie;
infra->client_ip_rates = slabhash_create(cfg->ip_ratelimit_slabs,
INFRA_HOST_STARTSIZE, cfg->ip_ratelimit_size, &ip_rate_sizefunc,
&ip_rate_compfunc, &ip_rate_delkeyfunc, &ip_rate_deldatafunc, NULL);
@@ -398,6 +409,7 @@ infra_adjust(struct infra_cache* infra, struct config_file* cfg)
infra->infra_keep_probing = cfg->infra_keep_probing;
infra_dp_ratelimit = cfg->ratelimit;
infra_ip_ratelimit = cfg->ip_ratelimit;
+ infra_ip_ratelimit_cookie = cfg->ip_ratelimit_cookie;
maxmem = cfg->infra_cache_numhosts * (sizeof(struct infra_key)+
sizeof(struct infra_data)+INFRA_BYTES_NAME);
/* divide cachesize by slabs and multiply by slabs, because if the
@@ -656,7 +668,7 @@ infra_update_tcp_works(struct infra_cache* infra,
if(data->rtt.rto >= RTT_MAX_TIMEOUT)
/* do not disqualify this server altogether, it is better
* than nothing */
- data->rtt.rto = RTT_MAX_TIMEOUT-1000;
+ data->rtt.rto = STILL_USEFUL_TIMEOUT;
lock_rw_unlock(&e->lock);
}
@@ -796,7 +808,7 @@ infra_get_lame_rtt(struct infra_cache* infra,
&& infra->infra_keep_probing) {
/* single probe, keep probing */
if(*rtt >= USEFUL_SERVER_TOP_TIMEOUT)
- *rtt = USEFUL_SERVER_TOP_TIMEOUT-1000;
+ *rtt = STILL_USEFUL_TIMEOUT;
} else if(host->rtt.rto >= PROBE_MAXRTO && timenow < host->probedelay
&& rtt_notimeout(&host->rtt)*4 <= host->rtt.rto) {
/* single probe for this domain, and we are not probing */
@@ -804,26 +816,23 @@ infra_get_lame_rtt(struct infra_cache* infra,
if(qtype == LDNS_RR_TYPE_A) {
if(host->timeout_A >= TIMEOUT_COUNT_MAX)
*rtt = USEFUL_SERVER_TOP_TIMEOUT;
- else *rtt = USEFUL_SERVER_TOP_TIMEOUT-1000;
+ else *rtt = STILL_USEFUL_TIMEOUT;
} else if(qtype == LDNS_RR_TYPE_AAAA) {
if(host->timeout_AAAA >= TIMEOUT_COUNT_MAX)
*rtt = USEFUL_SERVER_TOP_TIMEOUT;
- else *rtt = USEFUL_SERVER_TOP_TIMEOUT-1000;
+ else *rtt = STILL_USEFUL_TIMEOUT;
} else {
if(host->timeout_other >= TIMEOUT_COUNT_MAX)
*rtt = USEFUL_SERVER_TOP_TIMEOUT;
- else *rtt = USEFUL_SERVER_TOP_TIMEOUT-1000;
+ else *rtt = STILL_USEFUL_TIMEOUT;
}
}
/* expired entry */
if(timenow > host->ttl) {
-
/* see if this can be a re-probe of an unresponsive server */
- /* minus 1000 because that is outside of the RTTBAND, so
- * blacklisted servers stay blacklisted if this is chosen */
if(host->rtt.rto >= USEFUL_SERVER_TOP_TIMEOUT) {
lock_rw_unlock(&e->lock);
- *rtt = USEFUL_SERVER_TOP_TIMEOUT-1000;
+ *rtt = STILL_USEFUL_TIMEOUT;
*lame = 0;
*dnsseclame = 0;
*reclame = 0;
diff --git a/services/cache/infra.h b/services/cache/infra.h
index ee6f384de345..1a88bbb94da8 100644
--- a/services/cache/infra.h
+++ b/services/cache/infra.h
@@ -234,7 +234,7 @@ struct infra_cache* infra_adjust(struct infra_cache* infra,
struct config_file* cfg);
/**
- * Plain find infra data function (used by the the other functions)
+ * Plain find infra data function (used by the other functions)
* @param infra: infrastructure cache.
* @param addr: host address.
* @param addrlen: length of addr.
diff --git a/services/listen_dnsport.c b/services/listen_dnsport.c
index 7eb59a1618a1..6c0691f2a73c 100644
--- a/services/listen_dnsport.c
+++ b/services/listen_dnsport.c
@@ -675,7 +675,7 @@ create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto,
int* reuseport, int transparent, int mss, int nodelay, int freebind,
int use_systemd, int dscp)
{
- int s;
+ int s = -1;
char* err;
#if defined(SO_REUSEADDR) || defined(SO_REUSEPORT) || defined(IPV6_V6ONLY) || defined(IP_TRANSPARENT) || defined(IP_BINDANY) || defined(IP_FREEBIND) || defined(SO_BINDANY)
int on = 1;
diff --git a/services/localzone.c b/services/localzone.c
index 51056c8ffef4..d21e0c48aedb 100644
--- a/services/localzone.c
+++ b/services/localzone.c
@@ -242,7 +242,7 @@ lz_enter_zone_dname(struct local_zones* zones, uint8_t* nm, size_t len,
}
/** enter a new zone */
-static struct local_zone*
+struct local_zone*
lz_enter_zone(struct local_zones* zones, const char* name, const char* type,
uint16_t dclass)
{
@@ -983,40 +983,43 @@ lz_enter_overrides(struct local_zones* zones, struct config_file* cfg)
return 1;
}
+/* return closest parent in the tree, NULL if none */
+static struct local_zone* find_closest_parent(struct local_zone* curr,
+ struct local_zone* prev)
+{
+ struct local_zone* p;
+ int m;
+ if(!prev || prev->dclass != curr->dclass) return NULL;
+ (void)dname_lab_cmp(prev->name, prev->namelabs, curr->name,
+ curr->namelabs, &m); /* we know prev is smaller */
+ /* sort order like: . com. bla.com. zwb.com. net. */
+ /* find the previous, or parent-parent-parent */
+ for(p = prev; p; p = p->parent) {
+ /* looking for name with few labels, a parent */
+ if(p->namelabs <= m) {
+ /* ==: since prev matched m, this is closest*/
+ /* <: prev matches more, but is not a parent,
+ * this one is a (grand)parent */
+ return p;
+ }
+ }
+ return NULL;
+}
+
/** setup parent pointers, so that a lookup can be done for closest match */
-static void
-init_parents(struct local_zones* zones)
+void
+lz_init_parents(struct local_zones* zones)
{
- struct local_zone* node, *prev = NULL, *p;
- int m;
+ struct local_zone* node, *prev = NULL;
lock_rw_wrlock(&zones->lock);
- RBTREE_FOR(node, struct local_zone*, &zones->ztree) {
+ RBTREE_FOR(node, struct local_zone*, &zones->ztree) {
lock_rw_wrlock(&node->lock);
- node->parent = NULL;
- if(!prev || prev->dclass != node->dclass) {
- prev = node;
- lock_rw_unlock(&node->lock);
- continue;
- }
- (void)dname_lab_cmp(prev->name, prev->namelabs, node->name,
- node->namelabs, &m); /* we know prev is smaller */
- /* sort order like: . com. bla.com. zwb.com. net. */
- /* find the previous, or parent-parent-parent */
- for(p = prev; p; p = p->parent)
- /* looking for name with few labels, a parent */
- if(p->namelabs <= m) {
- /* ==: since prev matched m, this is closest*/
- /* <: prev matches more, but is not a parent,
- * this one is a (grand)parent */
- node->parent = p;
- break;
- }
- prev = node;
-
+ node->parent = find_closest_parent(node, prev);
+ prev = node;
if(node->override_tree)
addr_tree_init_parents(node->override_tree);
lock_rw_unlock(&node->lock);
- }
+ }
lock_rw_unlock(&zones->lock);
}
@@ -1036,7 +1039,7 @@ lz_setup_implicit(struct local_zones* zones, struct config_file* cfg)
int nmlabs = 0;
int match = 0; /* number of labels match count */
- init_parents(zones); /* to enable local_zones_lookup() */
+ lz_init_parents(zones); /* to enable local_zones_lookup() */
for(p = cfg->local_data; p; p = p->next) {
uint8_t* rr_name;
uint16_t rr_class, rr_type;
@@ -1202,7 +1205,7 @@ local_zones_apply_cfg(struct local_zones* zones, struct config_file* cfg)
}
/* setup parent ptrs for lookup during data entry */
- init_parents(zones);
+ lz_init_parents(zones);
/* insert local zone tags */
if(!lz_enter_zone_tags(zones, cfg)) {
return 0;
@@ -2028,7 +2031,9 @@ struct local_zone* local_zones_add_zone(struct local_zones* zones,
uint8_t* name, size_t len, int labs, uint16_t dclass,
enum localzone_type tp)
{
+ int exact;
/* create */
+ struct local_zone *prev;
struct local_zone* z = local_zone_create(name, len, labs, tp, dclass);
if(!z) {
free(name);
@@ -2037,10 +2042,12 @@ struct local_zone* local_zones_add_zone(struct local_zones* zones,
lock_rw_wrlock(&z->lock);
/* find the closest parent */
- z->parent = local_zones_find(zones, name, len, labs, dclass);
+ prev = local_zones_find_le(zones, name, len, labs, dclass, &exact);
+ if(!exact)
+ z->parent = find_closest_parent(z, prev);
/* insert into the tree */
- if(!rbtree_insert(&zones->ztree, &z->node)) {
+ if(exact||!rbtree_insert(&zones->ztree, &z->node)) {
/* duplicate entry! */
lock_rw_unlock(&z->lock);
local_zone_delete(z);
diff --git a/services/localzone.h b/services/localzone.h
index 4456893ee112..6f0f28b12422 100644
--- a/services/localzone.h
+++ b/services/localzone.h
@@ -641,4 +641,23 @@ local_zone_enter_rr(struct local_zone* z, uint8_t* nm, size_t nmlen,
*/
struct local_data*
local_zone_find_data(struct local_zone* z, uint8_t* nm, size_t nmlen, int nmlabs);
+
+/** Enter a new zone; returns with WRlock
+ * Made public for unit testing
+ * @param zones: the local zones tree
+ * @param name: name of the zone
+ * @param type: type of the zone
+ * @param dclass: class of the zone
+ * @return local_zone (or duplicate), NULL on parse and malloc failures
+ */
+struct local_zone*
+lz_enter_zone(struct local_zones* zones, const char* name, const char* type,
+ uint16_t dclass);
+
+/** Setup parent pointers, so that a lookup can be done for closest match
+ * Made public for unit testing
+ * @param zones: the local zones tree
+ */
+void
+lz_init_parents(struct local_zones* zones);
#endif /* SERVICES_LOCALZONE_H */
diff --git a/services/mesh.c b/services/mesh.c
index e886c4b92c84..522118844b44 100644
--- a/services/mesh.c
+++ b/services/mesh.c
@@ -413,6 +413,7 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
int timeout = mesh->env->cfg->serve_expired?
mesh->env->cfg->serve_expired_client_timeout:0;
struct sldns_buffer* r_buffer = rep->c->buffer;
+ uint16_t mesh_flags = qflags&(BIT_RD|BIT_CD);
if(rep->c->tcp_req_info) {
r_buffer = rep->c->tcp_req_info->spool_buffer;
}
@@ -425,7 +426,7 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
return;
}
if(!unique)
- s = mesh_area_find(mesh, cinfo, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0);
+ s = mesh_area_find(mesh, cinfo, qinfo, mesh_flags, 0, 0);
/* does this create a new reply state? */
if(!s || s->list_select == mesh_no_list) {
if(!mesh_make_new_space(mesh, rep->c->buffer)) {
@@ -453,7 +454,7 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
struct rbnode_type* n;
#endif
s = mesh_state_create(mesh->env, qinfo, cinfo,
- qflags&(BIT_RD|BIT_CD), 0, 0);
+ mesh_flags, 0, 0);
if(!s) {
log_err("mesh_state_create: out of memory; SERVFAIL");
if(!inplace_cb_reply_servfail_call(mesh->env, qinfo, NULL, NULL,
@@ -565,6 +566,8 @@ servfail_mem:
edns->opt_list_inplace_cb_out = NULL;
error_encode(r_buffer, LDNS_RCODE_SERVFAIL,
qinfo, qid, qflags, edns);
+ if(rep->c->use_h2)
+ http2_stream_remove_mesh_state(rep->c->h2_stream);
comm_point_send_reply(rep);
if(added)
mesh_state_delete(&s->s);
@@ -583,8 +586,9 @@ mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
int was_detached = 0;
int was_noreply = 0;
int added = 0;
+ uint16_t mesh_flags = qflags&(BIT_RD|BIT_CD);
if(!unique)
- s = mesh_area_find(mesh, NULL, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0);
+ s = mesh_area_find(mesh, NULL, qinfo, mesh_flags, 0, 0);
/* there are no limits on the number of callbacks */
@@ -594,7 +598,7 @@ mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
struct rbnode_type* n;
#endif
s = mesh_state_create(mesh->env, qinfo, NULL,
- qflags&(BIT_RD|BIT_CD), 0, 0);
+ mesh_flags, 0, 0);
if(!s) {
return 0;
}
@@ -673,8 +677,12 @@ static void mesh_schedule_prefetch(struct mesh_area* mesh,
struct query_info* qinfo, uint16_t qflags, time_t leeway, int run,
int rpz_passthru)
{
+ /* Explicitly set the BIT_RD regardless of the client's flags. This is
+ * for a prefetch query (no client attached) but it needs to be treated
+ * as a recursion query. */
+ uint16_t mesh_flags = BIT_RD|(qflags&BIT_CD);
struct mesh_state* s = mesh_area_find(mesh, NULL, qinfo,
- qflags&(BIT_RD|BIT_CD), 0, 0);
+ mesh_flags, 0, 0);
#ifdef UNBOUND_DEBUG
struct rbnode_type* n;
#endif
@@ -694,8 +702,7 @@ static void mesh_schedule_prefetch(struct mesh_area* mesh,
return;
}
- s = mesh_state_create(mesh->env, qinfo, NULL,
- qflags&(BIT_RD|BIT_CD), 0, 0);
+ s = mesh_state_create(mesh->env, qinfo, NULL, mesh_flags, 0, 0);
if(!s) {
log_err("prefetch mesh_state_create: out of memory");
return;
@@ -756,14 +763,17 @@ static void mesh_schedule_prefetch_subnet(struct mesh_area* mesh,
#ifdef UNBOUND_DEBUG
struct rbnode_type* n;
#endif
+ /* Explicitly set the BIT_RD regardless of the client's flags. This is
+ * for a prefetch query (no client attached) but it needs to be treated
+ * as a recursion query. */
+ uint16_t mesh_flags = BIT_RD|(qflags&BIT_CD);
if(!mesh_make_new_space(mesh, NULL)) {
verbose(VERB_ALGO, "Too many queries. dropped prefetch.");
mesh->stats_dropped ++;
return;
}
- s = mesh_state_create(mesh->env, qinfo, NULL,
- qflags&(BIT_RD|BIT_CD), 0, 0);
+ s = mesh_state_create(mesh->env, qinfo, NULL, mesh_flags, 0, 0);
if(!s) {
log_err("prefetch_subnet mesh_state_create: out of memory");
return;
@@ -966,6 +976,8 @@ mesh_state_cleanup(struct mesh_state* mstate)
for(; rep; rep=rep->next) {
infra_wait_limit_dec(mesh->env->infra_cache,
&rep->query_reply, mesh->env->cfg);
+ if(rep->query_reply.c->use_h2)
+ http2_stream_remove_mesh_state(rep->h2_stream);
comm_point_drop_reply(&rep->query_reply);
log_assert(mesh->num_reply_addrs > 0);
mesh->num_reply_addrs--;
@@ -1522,6 +1534,8 @@ void mesh_query_done(struct mesh_state* mstate)
infra_wait_limit_dec(mstate->s.env->infra_cache,
&r->query_reply, mstate->s.env->cfg);
mstate->reply_list = NULL;
+ if(r->query_reply.c->use_h2)
+ http2_stream_remove_mesh_state(r->h2_stream);
comm_point_drop_reply(&r->query_reply);
mstate->reply_list = reply_list;
mstate->s.env->mesh->stats_dropped++;
@@ -1554,6 +1568,9 @@ void mesh_query_done(struct mesh_state* mstate)
infra_wait_limit_dec(mstate->s.env->infra_cache,
&r->query_reply, mstate->s.env->cfg);
mstate->reply_list = NULL;
+ if(r->query_reply.c->use_h2) {
+ http2_stream_remove_mesh_state(r->h2_stream);
+ }
comm_point_drop_reply(&r->query_reply);
mstate->reply_list = reply_list;
} else {
@@ -1568,6 +1585,8 @@ void mesh_query_done(struct mesh_state* mstate)
tcp_req_info_remove_mesh_state(r->query_reply.c->tcp_req_info, mstate);
r_buffer = NULL;
}
+ /* mesh_send_reply removed mesh state from
+ * http2_stream. */
prev = r;
prev_buffer = r_buffer;
}
@@ -1720,6 +1739,7 @@ int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns,
return 0;
if(rep->c->use_h2)
r->h2_stream = rep->c->h2_stream;
+ else r->h2_stream = NULL;
/* Data related to local alias stored in 'qinfo' (if any) is ephemeral
* and can be different for different original queries (even if the
@@ -2243,6 +2263,8 @@ mesh_serve_expired_callback(void* arg)
infra_wait_limit_dec(mstate->s.env->infra_cache,
&r->query_reply, mstate->s.env->cfg);
mstate->reply_list = NULL;
+ if(r->query_reply.c->use_h2)
+ http2_stream_remove_mesh_state(r->h2_stream);
comm_point_drop_reply(&r->query_reply);
mstate->reply_list = reply_list;
mstate->s.env->mesh->stats_dropped++;
@@ -2276,6 +2298,7 @@ mesh_serve_expired_callback(void* arg)
r, r_buffer, prev, prev_buffer);
if(r->query_reply.c->tcp_req_info)
tcp_req_info_remove_mesh_state(r->query_reply.c->tcp_req_info, mstate);
+ /* mesh_send_reply removed mesh state from http2_stream. */
infra_wait_limit_dec(mstate->s.env->infra_cache,
&r->query_reply, mstate->s.env->cfg);
prev = r;
diff --git a/services/modstack.c b/services/modstack.c
index a90d7178c410..6c8af0505b69 100644
--- a/services/modstack.c
+++ b/services/modstack.c
@@ -95,6 +95,16 @@ modstack_init(struct module_stack* stack)
stack->mod = NULL;
}
+void
+modstack_free(struct module_stack* stack)
+{
+ if(!stack)
+ return;
+ stack->num = 0;
+ free(stack->mod);
+ stack->mod = NULL;
+}
+
int
modstack_config(struct module_stack* stack, const char* module_conf)
{
@@ -222,19 +232,60 @@ module_func_block* module_factory(const char** str)
return NULL;
}
-int
-modstack_setup(struct module_stack* stack, const char* module_conf,
+int
+modstack_call_startup(struct module_stack* stack, const char* module_conf,
struct module_env* env)
{
int i;
if(stack->num != 0)
- modstack_desetup(stack, env);
+ fatal_exit("unexpected already initialised modules");
/* fixed setup of the modules */
if(!modstack_config(stack, module_conf)) {
return 0;
}
+ for(i=0; i<stack->num; i++) {
+ if(stack->mod[i]->startup == NULL)
+ continue;
+ verbose(VERB_OPS, "startup module %d: %s",
+ i, stack->mod[i]->name);
+ fptr_ok(fptr_whitelist_mod_startup(stack->mod[i]->startup));
+ if(!(*stack->mod[i]->startup)(env, i)) {
+ log_err("module startup for module %s failed",
+ stack->mod[i]->name);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+int
+modstack_call_init(struct module_stack* stack, const char* module_conf,
+ struct module_env* env)
+{
+ int i, changed = 0;
env->need_to_validate = 0; /* set by module init below */
for(i=0; i<stack->num; i++) {
+ while(*module_conf && isspace(*module_conf))
+ module_conf++;
+ if(strncmp(stack->mod[i]->name, module_conf,
+ strlen(stack->mod[i]->name))) {
+ if(stack->mod[i]->startup || stack->mod[i]->destartup) {
+ log_err("changed module ordering during reload not supported, for module that needs startup");
+ return 0;
+ } else {
+ changed = 1;
+ }
+ }
+ module_conf += strlen(stack->mod[i]->name);
+ }
+ if(changed) {
+ modstack_free(stack);
+ if(!modstack_config(stack, module_conf)) {
+ return 0;
+ }
+ }
+
+ for(i=0; i<stack->num; i++) {
verbose(VERB_OPS, "init module %d: %s",
i, stack->mod[i]->name);
fptr_ok(fptr_whitelist_mod_init(stack->mod[i]->init));
@@ -247,20 +298,29 @@ modstack_setup(struct module_stack* stack, const char* module_conf,
return 1;
}
-void
-modstack_desetup(struct module_stack* stack, struct module_env* env)
+void
+modstack_call_deinit(struct module_stack* stack, struct module_env* env)
{
int i;
for(i=0; i<stack->num; i++) {
fptr_ok(fptr_whitelist_mod_deinit(stack->mod[i]->deinit));
(*stack->mod[i]->deinit)(env, i);
}
- stack->num = 0;
- free(stack->mod);
- stack->mod = NULL;
}
-int
+void
+modstack_call_destartup(struct module_stack* stack, struct module_env* env)
+{
+ int i;
+ for(i=0; i<stack->num; i++) {
+ if(stack->mod[i]->destartup == NULL)
+ continue;
+ fptr_ok(fptr_whitelist_mod_destartup(stack->mod[i]->destartup));
+ (*stack->mod[i]->destartup)(env, i);
+ }
+}
+
+int
modstack_find(struct module_stack* stack, const char* name)
{
int i;
diff --git a/services/modstack.h b/services/modstack.h
index 3ff01b54d938..5674aefdd018 100644
--- a/services/modstack.h
+++ b/services/modstack.h
@@ -61,6 +61,23 @@ struct module_stack {
void modstack_init(struct module_stack* stack);
/**
+ * Free the stack of modules
+ * @param stack: stack that frees up memory.
+ */
+void modstack_free(struct module_stack* stack);
+
+/**
+ * Initialises modules and assignes ids. Calls module_startup().
+ * @param stack: Expected empty, filled according to module_conf
+ * @param module_conf: string what modules to initialize
+ * @param env: module environment which is inited by the modules.
+ * environment should have a superalloc, cfg,
+ * @return on false a module init failed.
+ */
+int modstack_call_startup(struct module_stack* stack, const char* module_conf,
+ struct module_env* env);
+
+/**
* Read config file module settings and set up the modfunc block
* @param stack: the stack of modules (empty before call).
* @param module_conf: string what modules to insert.
@@ -83,24 +100,31 @@ struct module_func_block* module_factory(const char** str);
const char** module_list_avail(void);
/**
- * Setup modules. Assigns ids and calls module_init.
- * @param stack: if not empty beforehand, it will be desetup()ed.
- * It is then modstack_configged().
- * @param module_conf: string what modules to insert.
+ * Init modules. Calls module_init().
+ * @param stack: It is modstack_setupped().
+ * @param module_conf: module ordering to check against the ordering in stack.
+ * fails on changed ordering.
* @param env: module environment which is inited by the modules.
* environment should have a superalloc, cfg,
* env.need_to_validate is set by the modules.
* @return on false a module init failed.
*/
-int modstack_setup(struct module_stack* stack, const char* module_conf,
+int modstack_call_init(struct module_stack* stack, const char* module_conf,
struct module_env* env);
/**
- * Desetup the modules, deinit, delete.
+ * Deinit the modules.
* @param stack: made empty.
* @param env: module env for module deinit() calls.
*/
-void modstack_desetup(struct module_stack* stack, struct module_env* env);
+void modstack_call_deinit(struct module_stack* stack, struct module_env* env);
+
+/**
+ * Destartup the modules, close, delete.
+ * @param stack: made empty.
+ * @param env: module env for module destartup() calls.
+ */
+void modstack_call_destartup(struct module_stack* stack, struct module_env* env);
/**
* Find index of module by name.
diff --git a/services/outside_network.c b/services/outside_network.c
index 1f89740da360..58f1e6d586aa 100644
--- a/services/outside_network.c
+++ b/services/outside_network.c
@@ -2051,7 +2051,8 @@ select_id(struct outside_network* outnet, struct pending* pend,
}
/** return true is UDP connect error needs to be logged */
-static int udp_connect_needs_log(int err)
+static int udp_connect_needs_log(int err, struct sockaddr_storage* addr,
+ socklen_t addrlen)
{
switch(err) {
case ECONNREFUSED:
@@ -2075,6 +2076,15 @@ static int udp_connect_needs_log(int err)
if(verbosity >= VERB_ALGO)
return 1;
return 0;
+ case EINVAL:
+ /* Stop 'Invalid argument for fe80::/10' addresses appearing
+ * in the logs, at low verbosity. They cannot be sent to. */
+ if(addr_is_ip6linklocal(addr, addrlen)) {
+ if(verbosity >= VERB_ALGO)
+ return 1;
+ return 0;
+ }
+ break;
default:
break;
}
@@ -2141,7 +2151,8 @@ select_ifport(struct outside_network* outnet, struct pending* pend,
/* connect() to the destination */
if(connect(fd, (struct sockaddr*)&pend->addr,
pend->addrlen) < 0) {
- if(udp_connect_needs_log(errno)) {
+ if(udp_connect_needs_log(errno,
+ &pend->addr, pend->addrlen)) {
log_err_addr("udp connect failed",
strerror(errno), &pend->addr,
pend->addrlen);
@@ -3455,7 +3466,10 @@ outnet_serviced_query(struct outside_network* outnet,
timenow = *env->now;
if(!infra_ratelimit_inc(env->infra_cache, zone,
zonelen, timenow, env->cfg->ratelimit_backoff,
- &qstate->qinfo, qstate->reply)) {
+ &qstate->qinfo,
+ qstate->mesh_info->reply_list
+ ?&qstate->mesh_info->reply_list->query_reply
+ :NULL)) {
/* Can we pass through with slip factor? */
if(env->cfg->ratelimit_factor == 0 ||
ub_random_max(env->rnd,
diff --git a/services/rpz.c b/services/rpz.c
index f036cc5fd649..d8999a8a55eb 100644
--- a/services/rpz.c
+++ b/services/rpz.c
@@ -242,10 +242,14 @@ rpz_action_to_localzone_type(enum rpz_action a)
case RPZ_NODATA_ACTION: return local_zone_always_nodata;
case RPZ_DROP_ACTION: return local_zone_always_deny;
case RPZ_PASSTHRU_ACTION: return local_zone_always_transparent;
- case RPZ_LOCAL_DATA_ACTION: /* fallthrough */
+ case RPZ_LOCAL_DATA_ACTION:
+ ATTR_FALLTHROUGH
+ /* fallthrough */
case RPZ_CNAME_OVERRIDE_ACTION: return local_zone_redirect;
case RPZ_TCP_ONLY_ACTION: return local_zone_truncate;
- case RPZ_INVALID_ACTION: /* fallthrough */
+ case RPZ_INVALID_ACTION:
+ ATTR_FALLTHROUGH
+ /* fallthrough */
default: return local_zone_invalid;
}
}
@@ -258,10 +262,14 @@ rpz_action_to_respip_action(enum rpz_action a)
case RPZ_NODATA_ACTION: return respip_always_nodata;
case RPZ_DROP_ACTION: return respip_always_deny;
case RPZ_PASSTHRU_ACTION: return respip_always_transparent;
- case RPZ_LOCAL_DATA_ACTION: /* fallthrough */
+ case RPZ_LOCAL_DATA_ACTION:
+ ATTR_FALLTHROUGH
+ /* fallthrough */
case RPZ_CNAME_OVERRIDE_ACTION: return respip_redirect;
case RPZ_TCP_ONLY_ACTION: return respip_truncate;
- case RPZ_INVALID_ACTION: /* fallthrough */
+ case RPZ_INVALID_ACTION:
+ ATTR_FALLTHROUGH
+ /* fallthrough */
default: return respip_invalid;
}
}
@@ -276,7 +284,9 @@ localzone_type_to_rpz_action(enum localzone_type lzt)
case local_zone_always_transparent: return RPZ_PASSTHRU_ACTION;
case local_zone_redirect: return RPZ_LOCAL_DATA_ACTION;
case local_zone_truncate: return RPZ_TCP_ONLY_ACTION;
- case local_zone_invalid: /* fallthrough */
+ case local_zone_invalid:
+ ATTR_FALLTHROUGH
+ /* fallthrough */
default: return RPZ_INVALID_ACTION;
}
}
@@ -291,7 +301,9 @@ respip_action_to_rpz_action(enum respip_action a)
case respip_always_transparent: return RPZ_PASSTHRU_ACTION;
case respip_redirect: return RPZ_LOCAL_DATA_ACTION;
case respip_truncate: return RPZ_TCP_ONLY_ACTION;
- case respip_invalid: /* fallthrough */
+ case respip_invalid:
+ ATTR_FALLTHROUGH
+ /* fallthrough */
default: return RPZ_INVALID_ACTION;
}
}
@@ -2435,11 +2447,10 @@ rpz_callback_from_iterator_module(struct module_qstate* ms, struct iter_qstate*
if(ms->env == NULL || ms->env->auth_zones == NULL) { return 0; }
az = ms->env->auth_zones;
+ lock_rw_rdlock(&az->rpz_lock);
verbose(VERB_ALGO, "rpz: iterator module callback: have_rpz=%d", az->rpz_first != NULL);
- lock_rw_rdlock(&az->rpz_lock);
-
/* precedence of RPZ works, loosely, like this:
* CNAMEs in order of the CNAME chain. rpzs in the order they are
* configured. In an RPZ: first client-IP addr, then QNAME, then
@@ -2454,6 +2465,13 @@ rpz_callback_from_iterator_module(struct module_qstate* ms, struct iter_qstate*
lock_rw_unlock(&a->lock);
continue;
}
+ if(r->taglist && (!ms->client_info ||
+ !taglist_intersect(r->taglist, r->taglistlen,
+ ms->client_info->taglist,
+ ms->client_info->taglen))) {
+ lock_rw_unlock(&a->lock);
+ continue;
+ }
/* the nsdname has precedence over the nsip triggers */
z = rpz_delegation_point_zone_lookup(is->dp, r->nsdname_zones,
@@ -2512,6 +2530,13 @@ struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* ms,
lock_rw_unlock(&a->lock);
continue;
}
+ if(r->taglist && (!ms->client_info ||
+ !taglist_intersect(r->taglist, r->taglistlen,
+ ms->client_info->taglist,
+ ms->client_info->taglen))) {
+ lock_rw_unlock(&a->lock);
+ continue;
+ }
z = rpz_find_zone(r->local_zones, is->qchase.qname,
is->qchase.qname_len, is->qchase.qclass, 0, 0, 0);
if(z && r->action_override == RPZ_DISABLED_ACTION) {