aboutsummaryrefslogtreecommitdiff
path: root/contrib/unbound/edns-subnet/subnetmod.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/unbound/edns-subnet/subnetmod.c')
-rw-r--r--contrib/unbound/edns-subnet/subnetmod.c406
1 files changed, 347 insertions, 59 deletions
diff --git a/contrib/unbound/edns-subnet/subnetmod.c b/contrib/unbound/edns-subnet/subnetmod.c
index 81f0bf3ade55..88310a785d7a 100644
--- a/contrib/unbound/edns-subnet/subnetmod.c
+++ b/contrib/unbound/edns-subnet/subnetmod.c
@@ -51,11 +51,16 @@
#include "services/cache/dns.h"
#include "util/module.h"
#include "util/regional.h"
+#include "util/fptr_wlist.h"
#include "util/storage/slabhash.h"
#include "util/config_file.h"
#include "util/data/msgreply.h"
#include "sldns/sbuffer.h"
+#include "sldns/wire2str.h"
#include "iterator/iter_utils.h"
+#ifdef USE_CACHEDB
+#include "cachedb/cachedb.h"
+#endif
/** externally called */
void
@@ -93,13 +98,14 @@ subnet_new_qstate(struct module_qstate *qstate, int id)
qstate->minfo[id] = sq;
memset(sq, 0, sizeof(*sq));
sq->started_no_cache_store = qstate->no_cache_store;
+ sq->started_no_cache_lookup = qstate->no_cache_lookup;
return 1;
}
/** Add ecs struct to edns list, after parsing it to wire format. */
-static void
-ecs_opt_list_append(struct ecs_data* ecs, struct edns_option** list,
- struct module_qstate *qstate)
+void
+subnet_ecs_opt_list_append(struct ecs_data* ecs, struct edns_option** list,
+ struct module_qstate *qstate, struct regional *region)
{
size_t sn_octs, sn_octs_remainder;
sldns_buffer* buf = qstate->env->scratch_buffer;
@@ -131,7 +137,7 @@ ecs_opt_list_append(struct ecs_data* ecs, struct edns_option** list,
edns_opt_list_append(list,
qstate->env->cfg->client_subnet_opcode,
sn_octs + sn_octs_remainder + 4,
- sldns_buffer_begin(buf), qstate->region);
+ sldns_buffer_begin(buf), region);
}
}
@@ -139,7 +145,7 @@ int ecs_whitelist_check(struct query_info* qinfo,
uint16_t ATTR_UNUSED(flags), struct module_qstate* qstate,
struct sockaddr_storage* addr, socklen_t addrlen,
uint8_t* ATTR_UNUSED(zone), size_t ATTR_UNUSED(zonelen),
- struct regional* ATTR_UNUSED(region), int id, void* ATTR_UNUSED(cbargs))
+ struct regional *region, int id, void* ATTR_UNUSED(cbargs))
{
struct subnet_qstate *sq;
struct subnet_env *sn_env;
@@ -148,12 +154,29 @@ int ecs_whitelist_check(struct query_info* qinfo,
return 1;
sn_env = (struct subnet_env*)qstate->env->modinfo[id];
+ if(sq->is_subquery_nonsubnet) {
+ if(sq->is_subquery_scopezero) {
+ /* Check if the result can be stored in the global cache,
+ * this is okay if the address and name are not configured
+ * as subnet address and subnet zone. */
+ if(!ecs_is_whitelisted(sn_env->whitelist,
+ addr, addrlen, qinfo->qname, qinfo->qname_len,
+ qinfo->qclass)) {
+ verbose(VERB_ALGO, "subnet store subquery global, name and addr have no subnet treatment.");
+ qstate->no_cache_store = 0;
+ }
+ }
+ return 1;
+ }
+
/* Cache by default, might be disabled after parsing EDNS option
* received from nameserver. */
- if(!iter_stub_fwd_no_cache(qstate, &qstate->qinfo, NULL, NULL)) {
+ if(!iter_stub_fwd_no_cache(qstate, &qstate->qinfo, NULL, NULL, NULL, 0)
+ && sq->ecs_client_in.subnet_validdata) {
qstate->no_cache_store = 0;
}
+ sq->subnet_sent_no_subnet = 0;
if(sq->ecs_server_out.subnet_validdata && ((sq->subnet_downstream &&
qstate->env->cfg->client_subnet_always_forward) ||
ecs_is_whitelisted(sn_env->whitelist,
@@ -162,17 +185,29 @@ int ecs_whitelist_check(struct query_info* qinfo,
/* Address on whitelist or client query contains ECS option, we
* want to sent out ECS. Only add option if it is not already
* set. */
- if(!(sq->subnet_sent)) {
- ecs_opt_list_append(&sq->ecs_server_out,
- &qstate->edns_opts_back_out, qstate);
- sq->subnet_sent = 1;
+ if(!edns_opt_list_find(qstate->edns_opts_back_out,
+ qstate->env->cfg->client_subnet_opcode)) {
+ /* if the client is not wanting an EDNS subnet option,
+ * omit it and store that we omitted it but actually
+ * are doing EDNS subnet to the server. */
+ if(sq->ecs_server_out.subnet_source_mask == 0) {
+ sq->subnet_sent_no_subnet = 1;
+ sq->subnet_sent = 0;
+ return 1;
+ }
+ subnet_ecs_opt_list_append(&sq->ecs_server_out,
+ &qstate->edns_opts_back_out, qstate, region);
}
+ sq->subnet_sent = 1;
}
- else if(sq->subnet_sent) {
+ else {
/* Outgoing ECS option is set, but we don't want to sent it to
* this address, remove option. */
- edns_opt_list_remove(&qstate->edns_opts_back_out,
- qstate->env->cfg->client_subnet_opcode);
+ if(edns_opt_list_find(qstate->edns_opts_back_out,
+ qstate->env->cfg->client_subnet_opcode)) {
+ edns_opt_list_remove(&qstate->edns_opts_back_out,
+ qstate->env->cfg->client_subnet_opcode);
+ }
sq->subnet_sent = 0;
}
return 1;
@@ -198,18 +233,29 @@ subnetmod_init(struct module_env *env, int id)
}
alloc_init(&sn_env->alloc, NULL, 0);
env->modinfo[id] = (void*)sn_env;
+
+ /* Warn that serve-expired and prefetch do not work with the subnet
+ * module cache. */
+ if(env->cfg->serve_expired)
+ log_warn(
+ "subnetcache: serve-expired is set but not working "
+ "for data originating from the subnet module cache.");
+ if(env->cfg->prefetch)
+ log_warn(
+ "subnetcache: prefetch is set but not working "
+ "for data originating from the subnet module cache.");
/* Copy msg_cache settings */
sn_env->subnet_msg_cache = slabhash_create(env->cfg->msg_cache_slabs,
HASH_DEFAULT_STARTARRAY, env->cfg->msg_cache_size,
msg_cache_sizefunc, query_info_compare, query_entry_delete,
subnet_data_delete, NULL);
- slabhash_setmarkdel(sn_env->subnet_msg_cache, &subnet_markdel);
if(!sn_env->subnet_msg_cache) {
log_err("subnetcache: could not create cache");
free(sn_env);
env->modinfo[id] = NULL;
return 0;
}
+ slabhash_setmarkdel(sn_env->subnet_msg_cache, &subnet_markdel);
/* whitelist for edns subnet capable servers */
sn_env->whitelist = ecs_whitelist_create();
if(!sn_env->whitelist ||
@@ -227,7 +273,7 @@ subnetmod_init(struct module_env *env, int id)
env->unique_mesh = 1;
if(!edns_register_option(env->cfg->client_subnet_opcode,
env->cfg->client_subnet_always_forward /* bypass cache */,
- 0 /* no aggregation */, env)) {
+ 1 /* no aggregation */, env)) {
log_err("subnetcache: could not register opcode");
ecs_whitelist_delete(sn_env->whitelist);
slabhash_delete(sn_env->subnet_msg_cache);
@@ -284,9 +330,18 @@ delfunc(void *envptr, void *elemptr) {
static size_t
sizefunc(void *elemptr) {
struct reply_info *elem = (struct reply_info *)elemptr;
- return sizeof (struct reply_info) - sizeof (struct rrset_ref)
+ size_t s = sizeof (struct reply_info) - sizeof (struct rrset_ref)
+ elem->rrset_count * sizeof (struct rrset_ref)
+ elem->rrset_count * sizeof (struct ub_packed_rrset_key *);
+ size_t i;
+ for (i = 0; i < elem->rrset_count; i++) {
+ struct ub_packed_rrset_key *key = elem->rrsets[i];
+ struct packed_rrset_data *data = key->entry.data;
+ s += ub_rrset_sizefunc(key, data);
+ }
+ if(elem->reason_bogus_str)
+ s += strlen(elem->reason_bogus_str)+1;
+ return s;
}
/**
@@ -326,13 +381,16 @@ update_cache(struct module_qstate *qstate, int id)
struct slabhash *subnet_msg_cache = sne->subnet_msg_cache;
struct ecs_data *edns = &sq->ecs_client_in;
size_t i;
+ int only_match_scope_zero, diff_size;
- /* We already calculated hash upon lookup */
- hashvalue_type h = qstate->minfo[id] ?
- ((struct subnet_qstate*)qstate->minfo[id])->qinfo_hash :
+ /* We already calculated hash upon lookup (lookup_and_reply) if we were
+ * allowed to look in the ECS cache */
+ hashvalue_type h = qstate->minfo[id] &&
+ ((struct subnet_qstate*)qstate->minfo[id])->qinfo_hash_calculated?
+ ((struct subnet_qstate*)qstate->minfo[id])->qinfo_hash :
query_info_hash(&qstate->qinfo, qstate->query_flags);
/* Step 1, general qinfo lookup */
- struct lruhash_entry *lru_entry = slabhash_lookup(subnet_msg_cache, h,
+ struct lruhash_entry* lru_entry = slabhash_lookup(subnet_msg_cache, h,
&qstate->qinfo, 1);
int need_to_insert = (lru_entry == NULL);
if (!lru_entry) {
@@ -376,29 +434,38 @@ update_cache(struct module_qstate *qstate, int id)
log_err("subnetcache: cache insertion failed");
return;
}
-
+
/* store RRsets */
for(i=0; i<rep->rrset_count; i++) {
rep->ref[i].key = rep->rrsets[i];
rep->ref[i].id = rep->rrsets[i]->id;
}
reply_info_set_ttls(rep, *qstate->env->now);
+ reply_info_sortref(rep);
rep->flags |= (BIT_RA | BIT_QR); /* fix flags to be sensible for */
rep->flags &= ~(BIT_AA | BIT_CD);/* a reply based on the cache */
+ if(edns->subnet_source_mask == 0 && edns->subnet_scope_mask == 0)
+ only_match_scope_zero = 1;
+ else only_match_scope_zero = 0;
+ diff_size = (int)tree->size_bytes;
addrtree_insert(tree, (addrkey_t*)edns->subnet_addr,
edns->subnet_source_mask, sq->max_scope, rep,
- rep->ttl, *qstate->env->now);
+ rep->ttl, *qstate->env->now, only_match_scope_zero);
+ diff_size = (int)tree->size_bytes - diff_size;
lock_rw_unlock(&lru_entry->lock);
if (need_to_insert) {
slabhash_insert(subnet_msg_cache, h, lru_entry, lru_entry->data,
NULL);
+ } else {
+ slabhash_update_space_used(subnet_msg_cache, h, NULL,
+ diff_size);
}
}
/** Lookup in cache and reply true iff reply is sent. */
static int
-lookup_and_reply(struct module_qstate *qstate, int id, struct subnet_qstate *sq)
+lookup_and_reply(struct module_qstate *qstate, int id, struct subnet_qstate *sq, int prefetch)
{
struct lruhash_entry *e;
struct module_env *env = qstate->env;
@@ -412,7 +479,10 @@ lookup_and_reply(struct module_qstate *qstate, int id, struct subnet_qstate *sq)
memset(&sq->ecs_client_out, 0, sizeof(sq->ecs_client_out));
- if (sq) sq->qinfo_hash = h; /* Might be useful on cache miss */
+ if (sq) {
+ sq->qinfo_hash = h; /* Might be useful on cache miss */
+ sq->qinfo_hash_calculated = 1;
+ }
e = slabhash_lookup(sne->subnet_msg_cache, h, &qstate->qinfo, 1);
if (!e) return 0; /* qinfo not in cache */
data = e->data;
@@ -447,6 +517,10 @@ lookup_and_reply(struct module_qstate *qstate, int id, struct subnet_qstate *sq)
INET6_SIZE);
sq->ecs_client_out.subnet_validdata = 1;
}
+
+ if (prefetch && *qstate->env->now >= ((struct reply_info *)node->elem)->prefetch_ttl) {
+ qstate->need_refetch = 1;
+ }
return 1;
}
@@ -465,6 +539,83 @@ common_prefix(uint8_t *a, uint8_t *b, uint8_t net)
return !memcmp(a, b, n) && ((net % 8) == 0 || a[n] == b[n]);
}
+/**
+ * Create sub request that looks up the query.
+ * @param qstate: query state
+ * @param id: module id.
+ * @param sq: subnet qstate
+ * @return false on failure.
+ */
+static int
+generate_sub_request(struct module_qstate *qstate, int id, struct subnet_qstate* sq)
+{
+ struct module_qstate* subq = NULL;
+ uint16_t qflags = 0; /* OPCODE QUERY, no flags */
+ int prime = 0;
+ int valrec = 0;
+ struct query_info qinf;
+ qinf.qname = qstate->qinfo.qname;
+ qinf.qname_len = qstate->qinfo.qname_len;
+ qinf.qtype = qstate->qinfo.qtype;
+ qinf.qclass = qstate->qinfo.qclass;
+ qinf.local_alias = NULL;
+
+ qflags |= BIT_RD;
+ if((qstate->query_flags & BIT_CD)!=0) {
+ qflags |= BIT_CD;
+ valrec = 1;
+ }
+
+ fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub));
+ if(!(*qstate->env->attach_sub)(qstate, &qinf, qflags, prime, valrec,
+ &subq)) {
+ return 0;
+ }
+ if(subq) {
+ /* It is possible to access the subquery module state. */
+ struct subnet_qstate* subsq;
+ if(!subnet_new_qstate(subq, id)) {
+ verbose(VERB_ALGO, "Could not allocate new subnet qstate");
+ return 0;
+ }
+ subsq = (struct subnet_qstate*)subq->minfo[id];
+ subsq->is_subquery_nonsubnet = 1;
+
+ /* When the client asks 0.0.0.0/0 and the name is not treated
+ * as subnet, it is to be stored in the global cache.
+ * Store that the client asked for that, if so. */
+ if(sq->ecs_client_in.subnet_source_mask == 0 &&
+ edns_opt_list_find(qstate->edns_opts_front_in,
+ qstate->env->cfg->client_subnet_opcode)) {
+ subq->no_cache_store = 1;
+ subsq->is_subquery_scopezero = 1;
+ }
+ }
+ return 1;
+}
+
+/**
+ * Perform the query without subnet
+ * @param qstate: query state
+ * @param id: module id.
+ * @param sq: subnet qstate
+ * @return module state
+ */
+static enum module_ext_state
+generate_lookup_without_subnet(struct module_qstate *qstate, int id,
+ struct subnet_qstate* sq)
+{
+ verbose(VERB_ALGO, "subnetcache: make subquery to look up without subnet");
+ if(!generate_sub_request(qstate, id, sq)) {
+ verbose(VERB_ALGO, "Could not generate sub query");
+ qstate->return_rcode = LDNS_RCODE_SERVFAIL;
+ qstate->return_msg = NULL;
+ return module_finished;
+ }
+ sq->wait_subquery = 1;
+ return module_wait_subquery;
+}
+
static enum module_ext_state
eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq)
{
@@ -483,50 +634,56 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq)
* module_finished */
return module_finished;
}
-
+
/* We have not asked for subnet data */
- if (!sq->subnet_sent) {
+ if (!sq->subnet_sent && !sq->subnet_sent_no_subnet) {
if (s_in->subnet_validdata)
verbose(VERB_QUERY, "subnetcache: received spurious data");
if (sq->subnet_downstream) /* Copy back to client */
cp_edns_bad_response(c_out, c_in);
return module_finished;
}
-
+
/* subnet sent but nothing came back */
- if (!s_in->subnet_validdata) {
+ if (!s_in->subnet_validdata && !sq->subnet_sent_no_subnet) {
/* The authority indicated no support for edns subnet. As a
* consequence the answer ended up in the regular cache. It
* is still useful to put it in the edns subnet cache for
* when a client explicitly asks for subnet specific answer. */
verbose(VERB_QUERY, "subnetcache: Authority indicates no support");
- if(!sq->started_no_cache_store) {
- lock_rw_wrlock(&sne->biglock);
- update_cache(qstate, id);
- lock_rw_unlock(&sne->biglock);
- }
- if (sq->subnet_downstream)
- cp_edns_bad_response(c_out, c_in);
- return module_finished;
+ return generate_lookup_without_subnet(qstate, id, sq);
}
-
+
+ /* Purposefully there was no sent subnet, and there is consequently
+ * no subnet in the answer. If there was, use the subnet in the answer
+ * anyway. But if there is not, treat it as a prefix 0 answer. */
+ if(sq->subnet_sent_no_subnet && !s_in->subnet_validdata) {
+ /* Fill in 0.0.0.0/0 scope 0, or ::0/0 scope 0, for caching. */
+ s_in->subnet_addr_fam = s_out->subnet_addr_fam;
+ s_in->subnet_source_mask = 0;
+ s_in->subnet_scope_mask = 0;
+ memset(s_in->subnet_addr, 0, INET6_SIZE);
+ s_in->subnet_validdata = 1;
+ }
+
/* Being here means we have asked for and got a subnet specific
* answer. Also, the answer from the authority is not yet cached
* anywhere. */
-
+
/* can we accept response? */
if(s_out->subnet_addr_fam != s_in->subnet_addr_fam ||
s_out->subnet_source_mask != s_in->subnet_source_mask ||
!common_prefix(s_out->subnet_addr, s_in->subnet_addr,
s_out->subnet_source_mask))
{
- /* we can not accept, restart query without option */
+ /* we can not accept, perform query without option */
verbose(VERB_QUERY, "subnetcache: forged data");
s_out->subnet_validdata = 0;
(void)edns_opt_list_remove(&qstate->edns_opts_back_out,
qstate->env->cfg->client_subnet_opcode);
sq->subnet_sent = 0;
- return module_restart_next;
+ sq->subnet_sent_no_subnet = 0;
+ return generate_lookup_without_subnet(qstate, id, sq);
}
lock_rw_wrlock(&sne->biglock);
@@ -535,7 +692,21 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq)
}
sne->num_msg_nocache++;
lock_rw_unlock(&sne->biglock);
-
+
+ /* If there is an expired answer in the global cache, remove that,
+ * because expired answers would otherwise resurface once the ecs data
+ * expires, giving once in a while global data responses for ecs
+ * domains, with serve expired enabled. */
+ if(qstate->env->cfg->serve_expired) {
+ msg_cache_remove(qstate->env, qstate->qinfo.qname,
+ qstate->qinfo.qname_len, qstate->qinfo.qtype,
+ qstate->qinfo.qclass, 0);
+#ifdef USE_CACHEDB
+ if(qstate->env->cachedb_enabled)
+ cachedb_msg_remove(qstate);
+#endif
+ }
+
if (sq->subnet_downstream) {
/* Client wants to see the answer, echo option back
* and adjust the scope. */
@@ -598,7 +769,7 @@ parse_subnet_option(struct edns_option* ecs_option, struct ecs_data* ecs)
return 1;
}
-static void
+void
subnet_option_from_ss(struct sockaddr_storage *ss, struct ecs_data* ecs,
struct config_file* cfg)
{
@@ -646,6 +817,7 @@ ecs_query_response(struct module_qstate* qstate, struct dns_msg* response,
edns_opt_list_remove(&qstate->edns_opts_back_out,
qstate->env->cfg->client_subnet_opcode);
sq->subnet_sent = 0;
+ sq->subnet_sent_no_subnet = 0;
memset(&sq->ecs_server_out, 0, sizeof(sq->ecs_server_out));
} else if (!sq->track_max_scope &&
FLAGS_GET_RCODE(response->rep->flags) == LDNS_RCODE_NOERROR &&
@@ -664,6 +836,24 @@ ecs_query_response(struct module_qstate* qstate, struct dns_msg* response,
return 1;
}
+/** verbose print edns subnet option in pretty print */
+static void
+subnet_log_print(const char* s, struct edns_option* ecs_opt)
+{
+ if(verbosity >= VERB_ALGO) {
+ char buf[256];
+ char* str = buf;
+ size_t str_len = sizeof(buf);
+ if(!ecs_opt) {
+ verbose(VERB_ALGO, "%s (null)", s);
+ return;
+ }
+ (void)sldns_wire2str_edns_subnet_print(&str, &str_len,
+ ecs_opt->opt_data, ecs_opt->opt_len);
+ verbose(VERB_ALGO, "%s %s", s, buf);
+ }
+}
+
int
ecs_edns_back_parsed(struct module_qstate* qstate, int id,
void* ATTR_UNUSED(cbargs))
@@ -678,6 +868,7 @@ ecs_edns_back_parsed(struct module_qstate* qstate, int id,
qstate->env->cfg->client_subnet_opcode)) &&
parse_subnet_option(ecs_opt, &sq->ecs_server_in) &&
sq->subnet_sent && sq->ecs_server_in.subnet_validdata) {
+ subnet_log_print("answer has edns subnet", ecs_opt);
/* Only skip global cache store if we sent an ECS option
* and received one back. Answers from non-whitelisted
* servers will end up in global cache. Answers for
@@ -688,6 +879,12 @@ ecs_edns_back_parsed(struct module_qstate* qstate, int id,
sq->ecs_server_in.subnet_scope_mask >
sq->max_scope))
sq->max_scope = sq->ecs_server_in.subnet_scope_mask;
+ } else if(sq->subnet_sent_no_subnet) {
+ /* The answer can be stored as scope 0, not in global cache. */
+ qstate->no_cache_store = 1;
+ } else if(sq->subnet_sent) {
+ /* Need another query to be able to store in global cache. */
+ qstate->no_cache_store = 1;
}
return 1;
@@ -705,6 +902,32 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event,
strmodulevent(event));
log_query_info(VERB_QUERY, "subnetcache operate: query", &qstate->qinfo);
+ if(sq && sq->wait_subquery_done) {
+ /* The subquery lookup returned. */
+ if(sq->ecs_client_in.subnet_source_mask == 0 &&
+ edns_opt_list_find(qstate->edns_opts_front_in,
+ qstate->env->cfg->client_subnet_opcode)) {
+ if(!sq->started_no_cache_store &&
+ qstate->return_msg) {
+ lock_rw_wrlock(&sne->biglock);
+ update_cache(qstate, id);
+ lock_rw_unlock(&sne->biglock);
+ }
+ if (sq->subnet_downstream)
+ cp_edns_bad_response(&sq->ecs_client_out,
+ &sq->ecs_client_in);
+ /* It is a scope zero lookup, append edns subnet
+ * option to the querier. */
+ subnet_ecs_opt_list_append(&sq->ecs_client_out,
+ &qstate->edns_opts_front_out, qstate,
+ qstate->region);
+ }
+ sq->wait_subquery_done = 0;
+ qstate->ext_state[id] = module_finished;
+ qstate->no_cache_store = sq->started_no_cache_store;
+ qstate->no_cache_lookup = sq->started_no_cache_lookup;
+ return;
+ }
if((event == module_event_new || event == module_event_pass) &&
sq == NULL) {
struct edns_option* ecs_opt;
@@ -715,6 +938,8 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event,
}
sq = (struct subnet_qstate*)qstate->minfo[id];
+ if(sq->wait_subquery)
+ return; /* Wait for that subquery to return */
if((ecs_opt = edns_opt_list_find(
qstate->edns_opts_front_in,
@@ -726,11 +951,17 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event,
qstate->ext_state[id] = module_finished;
return;
}
+ subnet_log_print("query has edns subnet", ecs_opt);
sq->subnet_downstream = 1;
}
else if(qstate->mesh_info->reply_list) {
subnet_option_from_ss(
- &qstate->mesh_info->reply_list->query_reply.addr,
+ &qstate->mesh_info->reply_list->query_reply.client_addr,
+ &sq->ecs_client_in, qstate->env->cfg);
+ }
+ else if(qstate->client_addr.ss_family != AF_UNSPEC) {
+ subnet_option_from_ss(
+ &qstate->client_addr,
&sq->ecs_client_in, qstate->env->cfg);
}
@@ -738,6 +969,14 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event,
/* No clients are interested in result or we could not
* parse it, we don't do client subnet */
sq->ecs_server_out.subnet_validdata = 0;
+ if(edns_opt_list_find(qstate->edns_opts_front_in,
+ qstate->env->cfg->client_subnet_opcode)) {
+ /* aggregated this deaggregated state */
+ qstate->ext_state[id] =
+ generate_lookup_without_subnet(
+ qstate, id, sq);
+ return;
+ }
verbose(VERB_ALGO, "subnetcache: pass to next module");
qstate->ext_state[id] = module_wait_module;
return;
@@ -754,18 +993,38 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event,
return;
}
- lock_rw_wrlock(&sne->biglock);
- if (lookup_and_reply(qstate, id, sq)) {
- sne->num_msg_cache++;
- lock_rw_unlock(&sne->biglock);
- verbose(VERB_QUERY, "subnetcache: answered from cache");
- qstate->ext_state[id] = module_finished;
+ if(!sq->started_no_cache_lookup && !qstate->blacklist) {
+ lock_rw_wrlock(&sne->biglock);
+ if(qstate->mesh_info->reply_list &&
+ lookup_and_reply(qstate, id, sq,
+ qstate->env->cfg->prefetch)) {
+ sne->num_msg_cache++;
+ lock_rw_unlock(&sne->biglock);
+ verbose(VERB_QUERY, "subnetcache: answered from cache");
+ qstate->ext_state[id] = module_finished;
- ecs_opt_list_append(&sq->ecs_client_out,
- &qstate->edns_opts_front_out, qstate);
+ subnet_ecs_opt_list_append(&sq->ecs_client_out,
+ &qstate->edns_opts_front_out, qstate,
+ qstate->region);
+ if(verbosity >= VERB_ALGO) {
+ subnet_log_print("reply has edns subnet",
+ edns_opt_list_find(
+ qstate->edns_opts_front_out,
+ qstate->env->cfg->
+ client_subnet_opcode));
+ }
+ return;
+ }
+ lock_rw_unlock(&sne->biglock);
+ }
+ if(sq->ecs_client_in.subnet_source_mask == 0 &&
+ edns_opt_list_find(qstate->edns_opts_front_in,
+ qstate->env->cfg->client_subnet_opcode)) {
+ /* client asked for resolution without edns subnet */
+ qstate->ext_state[id] = generate_lookup_without_subnet(
+ qstate, id, sq);
return;
}
- lock_rw_unlock(&sne->biglock);
sq->ecs_server_out.subnet_addr_fam =
sq->ecs_client_in.subnet_addr_fam;
@@ -783,7 +1042,7 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event,
sq->ecs_server_out.subnet_source_mask =
qstate->env->cfg->max_client_subnet_ipv6;
/* Safe to copy completely, even if the source is limited by the
- * configuration. ecs_opt_list_append() will limit the address.
+ * configuration. subnet_ecs_opt_list_append() will limit the address.
* */
memcpy(&sq->ecs_server_out.subnet_addr,
sq->ecs_client_in.subnet_addr, INET6_SIZE);
@@ -802,15 +1061,26 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event,
qstate->ext_state[id] = module_wait_module;
return;
}
+ if(sq && sq->wait_subquery)
+ return; /* Wait for that subquery to return */
/* Query handed back by next module, we have a 'final' answer */
if(sq && event == module_event_moddone) {
qstate->ext_state[id] = eval_response(qstate, id, sq);
if(qstate->ext_state[id] == module_finished &&
qstate->return_msg) {
- ecs_opt_list_append(&sq->ecs_client_out,
- &qstate->edns_opts_front_out, qstate);
+ subnet_ecs_opt_list_append(&sq->ecs_client_out,
+ &qstate->edns_opts_front_out, qstate,
+ qstate->region);
+ if(verbosity >= VERB_ALGO) {
+ subnet_log_print("reply has edns subnet",
+ edns_opt_list_find(
+ qstate->edns_opts_front_out,
+ qstate->env->cfg->
+ client_subnet_opcode));
+ }
}
qstate->no_cache_store = sq->started_no_cache_store;
+ qstate->no_cache_lookup = sq->started_no_cache_lookup;
return;
}
if(sq && outbound) {
@@ -841,10 +1111,27 @@ subnetmod_clear(struct module_qstate *ATTR_UNUSED(qstate),
}
void
-subnetmod_inform_super(struct module_qstate *ATTR_UNUSED(qstate),
- int ATTR_UNUSED(id), struct module_qstate *ATTR_UNUSED(super))
+subnetmod_inform_super(struct module_qstate *qstate, int id,
+ struct module_qstate *super)
{
- /* Not used */
+ struct subnet_qstate* super_sq =
+ (struct subnet_qstate*)super->minfo[id];
+ log_query_info(VERB_ALGO, "subnetcache inform_super: query",
+ &super->qinfo);
+ super_sq->wait_subquery = 0;
+ super_sq->wait_subquery_done = 1;
+ if(qstate->return_rcode != LDNS_RCODE_NOERROR ||
+ !qstate->return_msg) {
+ super->return_msg = NULL;
+ super->return_rcode = LDNS_RCODE_SERVFAIL;
+ return;
+ }
+ super->return_rcode = LDNS_RCODE_NOERROR;
+ super->return_msg = dns_copy_msg(qstate->return_msg, super->region);
+ if(!super->return_msg) {
+ log_err("subnetcache: copy response, out of memory");
+ super->return_rcode = LDNS_RCODE_SERVFAIL;
+ }
}
size_t
@@ -861,7 +1148,8 @@ subnetmod_get_mem(struct module_env *env, int id)
* The module function block
*/
static struct module_func_block subnetmod_block = {
- "subnetcache", &subnetmod_init, &subnetmod_deinit, &subnetmod_operate,
+ "subnetcache",
+ NULL, NULL, &subnetmod_init, &subnetmod_deinit, &subnetmod_operate,
&subnetmod_inform_super, &subnetmod_clear, &subnetmod_get_mem
};