165b390aaSDag-Erling Smørgrav /*
265b390aaSDag-Erling Smørgrav  * edns-subnet/subnetmod.c - edns subnet module. Must be called before validator
365b390aaSDag-Erling Smørgrav  * and iterator.
465b390aaSDag-Erling Smørgrav  *
565b390aaSDag-Erling Smørgrav  * Copyright (c) 2013, NLnet Labs. All rights reserved.
665b390aaSDag-Erling Smørgrav  *
765b390aaSDag-Erling Smørgrav  * This software is open source.
865b390aaSDag-Erling Smørgrav  *
965b390aaSDag-Erling Smørgrav  * Redistribution and use in source and binary forms, with or without
1065b390aaSDag-Erling Smørgrav  * modification, are permitted provided that the following conditions
1165b390aaSDag-Erling Smørgrav  * are met:
1265b390aaSDag-Erling Smørgrav  *
1365b390aaSDag-Erling Smørgrav  * Redistributions of source code must retain the above copyright notice,
1465b390aaSDag-Erling Smørgrav  * this list of conditions and the following disclaimer.
1565b390aaSDag-Erling Smørgrav  *
1665b390aaSDag-Erling Smørgrav  * Redistributions in binary form must reproduce the above copyright notice,
1765b390aaSDag-Erling Smørgrav  * this list of conditions and the following disclaimer in the documentation
1865b390aaSDag-Erling Smørgrav  * and/or other materials provided with the distribution.
1965b390aaSDag-Erling Smørgrav  *
2065b390aaSDag-Erling Smørgrav  * Neither the name of the NLNET LABS nor the names of its contributors may
2165b390aaSDag-Erling Smørgrav  * be used to endorse or promote products derived from this software without
2265b390aaSDag-Erling Smørgrav  * specific prior written permission.
2365b390aaSDag-Erling Smørgrav  *
2465b390aaSDag-Erling Smørgrav  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2565b390aaSDag-Erling Smørgrav  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2665b390aaSDag-Erling Smørgrav  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2765b390aaSDag-Erling Smørgrav  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2865b390aaSDag-Erling Smørgrav  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2965b390aaSDag-Erling Smørgrav  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
3065b390aaSDag-Erling Smørgrav  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
3165b390aaSDag-Erling Smørgrav  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
3265b390aaSDag-Erling Smørgrav  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
3365b390aaSDag-Erling Smørgrav  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
3465b390aaSDag-Erling Smørgrav  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3565b390aaSDag-Erling Smørgrav  */
3665b390aaSDag-Erling Smørgrav  /**
3765b390aaSDag-Erling Smørgrav  * \file
3865b390aaSDag-Erling Smørgrav  * subnet module for unbound.
3965b390aaSDag-Erling Smørgrav  */
4065b390aaSDag-Erling Smørgrav 
4165b390aaSDag-Erling Smørgrav #include "config.h"
4265b390aaSDag-Erling Smørgrav 
4365b390aaSDag-Erling Smørgrav #ifdef CLIENT_SUBNET /* keeps splint happy */
4465b390aaSDag-Erling Smørgrav 
4565b390aaSDag-Erling Smørgrav #include "edns-subnet/subnetmod.h"
4665b390aaSDag-Erling Smørgrav #include "edns-subnet/edns-subnet.h"
4765b390aaSDag-Erling Smørgrav #include "edns-subnet/addrtree.h"
4865b390aaSDag-Erling Smørgrav #include "edns-subnet/subnet-whitelist.h"
4965b390aaSDag-Erling Smørgrav 
5065b390aaSDag-Erling Smørgrav #include "services/mesh.h"
5165b390aaSDag-Erling Smørgrav #include "services/cache/dns.h"
5265b390aaSDag-Erling Smørgrav #include "util/module.h"
5365b390aaSDag-Erling Smørgrav #include "util/regional.h"
5465b390aaSDag-Erling Smørgrav #include "util/storage/slabhash.h"
5565b390aaSDag-Erling Smørgrav #include "util/config_file.h"
5665b390aaSDag-Erling Smørgrav #include "util/data/msgreply.h"
5765b390aaSDag-Erling Smørgrav #include "sldns/sbuffer.h"
58865f46b2SCy Schubert #include "sldns/wire2str.h"
59e86b9096SDag-Erling Smørgrav #include "iterator/iter_utils.h"
60*335c7cdaSCy Schubert #ifdef USE_CACHEDB
61*335c7cdaSCy Schubert #include "cachedb/cachedb.h"
62*335c7cdaSCy Schubert #endif
6365b390aaSDag-Erling Smørgrav 
6465b390aaSDag-Erling Smørgrav /** externally called */
6565b390aaSDag-Erling Smørgrav void
subnet_data_delete(void * d,void * ATTR_UNUSED (arg))6665b390aaSDag-Erling Smørgrav subnet_data_delete(void *d, void *ATTR_UNUSED(arg))
6765b390aaSDag-Erling Smørgrav {
6865b390aaSDag-Erling Smørgrav 	struct subnet_msg_cache_data *r;
6965b390aaSDag-Erling Smørgrav 	r = (struct subnet_msg_cache_data*)d;
7065b390aaSDag-Erling Smørgrav 	addrtree_delete(r->tree4);
7165b390aaSDag-Erling Smørgrav 	addrtree_delete(r->tree6);
7265b390aaSDag-Erling Smørgrav 	free(r);
7365b390aaSDag-Erling Smørgrav }
7465b390aaSDag-Erling Smørgrav 
7565b390aaSDag-Erling Smørgrav /** externally called */
7665b390aaSDag-Erling Smørgrav size_t
msg_cache_sizefunc(void * k,void * d)7765b390aaSDag-Erling Smørgrav msg_cache_sizefunc(void *k, void *d)
7865b390aaSDag-Erling Smørgrav {
7965b390aaSDag-Erling Smørgrav 	struct msgreply_entry *q = (struct msgreply_entry*)k;
8065b390aaSDag-Erling Smørgrav 	struct subnet_msg_cache_data *r = (struct subnet_msg_cache_data*)d;
8165b390aaSDag-Erling Smørgrav 	size_t s = sizeof(struct msgreply_entry)
8265b390aaSDag-Erling Smørgrav 		+ sizeof(struct subnet_msg_cache_data)
8365b390aaSDag-Erling Smørgrav 		+ q->key.qname_len + lock_get_mem(&q->entry.lock);
8465b390aaSDag-Erling Smørgrav 	s += addrtree_size(r->tree4);
8565b390aaSDag-Erling Smørgrav 	s += addrtree_size(r->tree6);
8665b390aaSDag-Erling Smørgrav 	return s;
8765b390aaSDag-Erling Smørgrav }
8865b390aaSDag-Erling Smørgrav 
8965b390aaSDag-Erling Smørgrav /** new query for ecs module */
9065b390aaSDag-Erling Smørgrav static int
subnet_new_qstate(struct module_qstate * qstate,int id)9165b390aaSDag-Erling Smørgrav subnet_new_qstate(struct module_qstate *qstate, int id)
9265b390aaSDag-Erling Smørgrav {
9365b390aaSDag-Erling Smørgrav 	struct subnet_qstate *sq = (struct subnet_qstate*)regional_alloc(
9465b390aaSDag-Erling Smørgrav 		qstate->region, sizeof(struct subnet_qstate));
9565b390aaSDag-Erling Smørgrav 	if(!sq)
9665b390aaSDag-Erling Smørgrav 		return 0;
9765b390aaSDag-Erling Smørgrav 	qstate->minfo[id] = sq;
9865b390aaSDag-Erling Smørgrav 	memset(sq, 0, sizeof(*sq));
99e86b9096SDag-Erling Smørgrav 	sq->started_no_cache_store = qstate->no_cache_store;
1000a92a9fcSCy Schubert 	sq->started_no_cache_lookup = qstate->no_cache_lookup;
10165b390aaSDag-Erling Smørgrav 	return 1;
10265b390aaSDag-Erling Smørgrav }
10365b390aaSDag-Erling Smørgrav 
10465b390aaSDag-Erling Smørgrav /** Add ecs struct to edns list, after parsing it to wire format. */
105a39a5a69SCy Schubert void
subnet_ecs_opt_list_append(struct ecs_data * ecs,struct edns_option ** list,struct module_qstate * qstate,struct regional * region)106a39a5a69SCy Schubert subnet_ecs_opt_list_append(struct ecs_data* ecs, struct edns_option** list,
1070a92a9fcSCy Schubert 	struct module_qstate *qstate, struct regional *region)
10865b390aaSDag-Erling Smørgrav {
10965b390aaSDag-Erling Smørgrav 	size_t sn_octs, sn_octs_remainder;
11065b390aaSDag-Erling Smørgrav 	sldns_buffer* buf = qstate->env->scratch_buffer;
11165b390aaSDag-Erling Smørgrav 
11265b390aaSDag-Erling Smørgrav 	if(ecs->subnet_validdata) {
11365b390aaSDag-Erling Smørgrav 		log_assert(ecs->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4 ||
11465b390aaSDag-Erling Smørgrav 			ecs->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP6);
11565b390aaSDag-Erling Smørgrav 		log_assert(ecs->subnet_addr_fam != EDNSSUBNET_ADDRFAM_IP4 ||
11665b390aaSDag-Erling Smørgrav 			ecs->subnet_source_mask <=  INET_SIZE*8);
11765b390aaSDag-Erling Smørgrav 		log_assert(ecs->subnet_addr_fam != EDNSSUBNET_ADDRFAM_IP6 ||
11865b390aaSDag-Erling Smørgrav 			ecs->subnet_source_mask <= INET6_SIZE*8);
11965b390aaSDag-Erling Smørgrav 
12065b390aaSDag-Erling Smørgrav 		sn_octs = ecs->subnet_source_mask / 8;
12165b390aaSDag-Erling Smørgrav 		sn_octs_remainder =
12265b390aaSDag-Erling Smørgrav 			(size_t)((ecs->subnet_source_mask % 8)>0?1:0);
12365b390aaSDag-Erling Smørgrav 
12465b390aaSDag-Erling Smørgrav 		log_assert(sn_octs + sn_octs_remainder <= INET6_SIZE);
12565b390aaSDag-Erling Smørgrav 
12665b390aaSDag-Erling Smørgrav 		sldns_buffer_clear(buf);
12765b390aaSDag-Erling Smørgrav 		sldns_buffer_write_u16(buf, ecs->subnet_addr_fam);
12865b390aaSDag-Erling Smørgrav 		sldns_buffer_write_u8(buf, ecs->subnet_source_mask);
12965b390aaSDag-Erling Smørgrav 		sldns_buffer_write_u8(buf, ecs->subnet_scope_mask);
13065b390aaSDag-Erling Smørgrav 		sldns_buffer_write(buf, ecs->subnet_addr, sn_octs);
13165b390aaSDag-Erling Smørgrav 		if(sn_octs_remainder)
13265b390aaSDag-Erling Smørgrav 			sldns_buffer_write_u8(buf, ecs->subnet_addr[sn_octs] &
13365b390aaSDag-Erling Smørgrav 				~(0xFF >> (ecs->subnet_source_mask % 8)));
13465b390aaSDag-Erling Smørgrav 		sldns_buffer_flip(buf);
13565b390aaSDag-Erling Smørgrav 
13665b390aaSDag-Erling Smørgrav 		edns_opt_list_append(list,
13765b390aaSDag-Erling Smørgrav 				qstate->env->cfg->client_subnet_opcode,
13865b390aaSDag-Erling Smørgrav 				sn_octs + sn_octs_remainder + 4,
1390a92a9fcSCy Schubert 				sldns_buffer_begin(buf), region);
14065b390aaSDag-Erling Smørgrav 	}
14165b390aaSDag-Erling Smørgrav }
14265b390aaSDag-Erling Smørgrav 
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 * region,int id,void * ATTR_UNUSED (cbargs))143c7f4d7adSDag-Erling Smørgrav int ecs_whitelist_check(struct query_info* qinfo,
14465b390aaSDag-Erling Smørgrav 	uint16_t ATTR_UNUSED(flags), struct module_qstate* qstate,
14565b390aaSDag-Erling Smørgrav 	struct sockaddr_storage* addr, socklen_t addrlen,
14665b390aaSDag-Erling Smørgrav 	uint8_t* ATTR_UNUSED(zone), size_t ATTR_UNUSED(zonelen),
1470a92a9fcSCy Schubert 	struct regional *region, int id, void* ATTR_UNUSED(cbargs))
14865b390aaSDag-Erling Smørgrav {
14965b390aaSDag-Erling Smørgrav 	struct subnet_qstate *sq;
15065b390aaSDag-Erling Smørgrav 	struct subnet_env *sn_env;
15165b390aaSDag-Erling Smørgrav 
15265b390aaSDag-Erling Smørgrav 	if(!(sq=(struct subnet_qstate*)qstate->minfo[id]))
15365b390aaSDag-Erling Smørgrav 		return 1;
15465b390aaSDag-Erling Smørgrav 	sn_env = (struct subnet_env*)qstate->env->modinfo[id];
15565b390aaSDag-Erling Smørgrav 
15665b390aaSDag-Erling Smørgrav 	/* Cache by default, might be disabled after parsing EDNS option
15765b390aaSDag-Erling Smørgrav 	 * received from nameserver. */
158*335c7cdaSCy Schubert 	if(!iter_stub_fwd_no_cache(qstate, &qstate->qinfo, NULL, NULL, NULL, 0)) {
15965b390aaSDag-Erling Smørgrav 		qstate->no_cache_store = 0;
160e86b9096SDag-Erling Smørgrav 	}
16165b390aaSDag-Erling Smørgrav 
162103ba509SCy Schubert 	sq->subnet_sent_no_subnet = 0;
16365b390aaSDag-Erling Smørgrav 	if(sq->ecs_server_out.subnet_validdata && ((sq->subnet_downstream &&
16465b390aaSDag-Erling Smørgrav 		qstate->env->cfg->client_subnet_always_forward) ||
165c7f4d7adSDag-Erling Smørgrav 		ecs_is_whitelisted(sn_env->whitelist,
166c7f4d7adSDag-Erling Smørgrav 		addr, addrlen, qinfo->qname, qinfo->qname_len,
167c7f4d7adSDag-Erling Smørgrav 		qinfo->qclass))) {
16865b390aaSDag-Erling Smørgrav 		/* Address on whitelist or client query contains ECS option, we
16965b390aaSDag-Erling Smørgrav 		 * want to sent out ECS. Only add option if it is not already
17065b390aaSDag-Erling Smørgrav 		 * set. */
171a39a5a69SCy Schubert 		if(!edns_opt_list_find(qstate->edns_opts_back_out,
172a39a5a69SCy Schubert 			qstate->env->cfg->client_subnet_opcode)) {
173103ba509SCy Schubert 			/* if the client is not wanting an EDNS subnet option,
174103ba509SCy Schubert 			 * omit it and store that we omitted it but actually
175103ba509SCy Schubert 			 * are doing EDNS subnet to the server. */
176103ba509SCy Schubert 			if(sq->ecs_server_out.subnet_source_mask == 0) {
177103ba509SCy Schubert 				sq->subnet_sent_no_subnet = 1;
178103ba509SCy Schubert 				sq->subnet_sent = 0;
179103ba509SCy Schubert 				return 1;
180103ba509SCy Schubert 			}
181a39a5a69SCy Schubert 			subnet_ecs_opt_list_append(&sq->ecs_server_out,
1820a92a9fcSCy Schubert 				&qstate->edns_opts_back_out, qstate, region);
183a39a5a69SCy Schubert 		}
18465b390aaSDag-Erling Smørgrav 		sq->subnet_sent = 1;
18565b390aaSDag-Erling Smørgrav 	}
186a39a5a69SCy Schubert 	else {
18765b390aaSDag-Erling Smørgrav 		/* Outgoing ECS option is set, but we don't want to sent it to
18865b390aaSDag-Erling Smørgrav 		 * this address, remove option. */
189a39a5a69SCy Schubert 		if(edns_opt_list_find(qstate->edns_opts_back_out,
190a39a5a69SCy Schubert 			qstate->env->cfg->client_subnet_opcode)) {
19165b390aaSDag-Erling Smørgrav 			edns_opt_list_remove(&qstate->edns_opts_back_out,
19265b390aaSDag-Erling Smørgrav 				qstate->env->cfg->client_subnet_opcode);
193a39a5a69SCy Schubert 		}
19465b390aaSDag-Erling Smørgrav 		sq->subnet_sent = 0;
19565b390aaSDag-Erling Smørgrav 	}
19665b390aaSDag-Erling Smørgrav 	return 1;
19765b390aaSDag-Erling Smørgrav }
19865b390aaSDag-Erling Smørgrav 
19965b390aaSDag-Erling Smørgrav 
200e86b9096SDag-Erling Smørgrav void
subnet_markdel(void * key)201e86b9096SDag-Erling Smørgrav subnet_markdel(void* key)
202e86b9096SDag-Erling Smørgrav {
203e86b9096SDag-Erling Smørgrav 	struct msgreply_entry *e = (struct msgreply_entry*)key;
204e86b9096SDag-Erling Smørgrav 	e->key.qtype = 0;
205e86b9096SDag-Erling Smørgrav 	e->key.qclass = 0;
206e86b9096SDag-Erling Smørgrav }
207e86b9096SDag-Erling Smørgrav 
20865b390aaSDag-Erling Smørgrav int
subnetmod_init(struct module_env * env,int id)20965b390aaSDag-Erling Smørgrav subnetmod_init(struct module_env *env, int id)
21065b390aaSDag-Erling Smørgrav {
21165b390aaSDag-Erling Smørgrav 	struct subnet_env *sn_env = (struct subnet_env*)calloc(1,
21265b390aaSDag-Erling Smørgrav 		sizeof(struct subnet_env));
21365b390aaSDag-Erling Smørgrav 	if(!sn_env) {
21465b390aaSDag-Erling Smørgrav 		log_err("malloc failure");
21565b390aaSDag-Erling Smørgrav 		return 0;
21665b390aaSDag-Erling Smørgrav 	}
21765b390aaSDag-Erling Smørgrav 	alloc_init(&sn_env->alloc, NULL, 0);
21865b390aaSDag-Erling Smørgrav 	env->modinfo[id] = (void*)sn_env;
2191838dec3SCy Schubert 
2201838dec3SCy Schubert 	/* Warn that serve-expired and prefetch do not work with the subnet
2211838dec3SCy Schubert 	 * module cache. */
2221838dec3SCy Schubert 	if(env->cfg->serve_expired)
2231838dec3SCy Schubert 		log_warn(
2241838dec3SCy Schubert 			"subnetcache: serve-expired is set but not working "
2251838dec3SCy Schubert 			"for data originating from the subnet module cache.");
2261838dec3SCy Schubert 	if(env->cfg->prefetch)
2271838dec3SCy Schubert 		log_warn(
2281838dec3SCy Schubert 			"subnetcache: prefetch is set but not working "
2291838dec3SCy Schubert 			"for data originating from the subnet module cache.");
23065b390aaSDag-Erling Smørgrav 	/* Copy msg_cache settings */
23165b390aaSDag-Erling Smørgrav 	sn_env->subnet_msg_cache = slabhash_create(env->cfg->msg_cache_slabs,
23265b390aaSDag-Erling Smørgrav 		HASH_DEFAULT_STARTARRAY, env->cfg->msg_cache_size,
23365b390aaSDag-Erling Smørgrav 		msg_cache_sizefunc, query_info_compare, query_entry_delete,
23465b390aaSDag-Erling Smørgrav 		subnet_data_delete, NULL);
235e86b9096SDag-Erling Smørgrav 	slabhash_setmarkdel(sn_env->subnet_msg_cache, &subnet_markdel);
23665b390aaSDag-Erling Smørgrav 	if(!sn_env->subnet_msg_cache) {
2375469a995SCy Schubert 		log_err("subnetcache: could not create cache");
23865b390aaSDag-Erling Smørgrav 		free(sn_env);
23965b390aaSDag-Erling Smørgrav 		env->modinfo[id] = NULL;
24065b390aaSDag-Erling Smørgrav 		return 0;
24165b390aaSDag-Erling Smørgrav 	}
24265b390aaSDag-Erling Smørgrav 	/* whitelist for edns subnet capable servers */
243c7f4d7adSDag-Erling Smørgrav 	sn_env->whitelist = ecs_whitelist_create();
244c7f4d7adSDag-Erling Smørgrav 	if(!sn_env->whitelist ||
245c7f4d7adSDag-Erling Smørgrav 		!ecs_whitelist_apply_cfg(sn_env->whitelist, env->cfg)) {
2465469a995SCy Schubert 		log_err("subnetcache: could not create ECS whitelist");
24765b390aaSDag-Erling Smørgrav 		slabhash_delete(sn_env->subnet_msg_cache);
24865b390aaSDag-Erling Smørgrav 		free(sn_env);
24965b390aaSDag-Erling Smørgrav 		env->modinfo[id] = NULL;
25065b390aaSDag-Erling Smørgrav 		return 0;
25165b390aaSDag-Erling Smørgrav 	}
25265b390aaSDag-Erling Smørgrav 
2535469a995SCy Schubert 	verbose(VERB_QUERY, "subnetcache: option registered (%d)",
25465b390aaSDag-Erling Smørgrav 		env->cfg->client_subnet_opcode);
25565b390aaSDag-Erling Smørgrav 	/* Create new mesh state for all queries. */
25665b390aaSDag-Erling Smørgrav 	env->unique_mesh = 1;
25765b390aaSDag-Erling Smørgrav 	if(!edns_register_option(env->cfg->client_subnet_opcode,
25865b390aaSDag-Erling Smørgrav 		env->cfg->client_subnet_always_forward /* bypass cache */,
259a39a5a69SCy Schubert 		1 /* no aggregation */, env)) {
2605469a995SCy Schubert 		log_err("subnetcache: could not register opcode");
261c7f4d7adSDag-Erling Smørgrav 		ecs_whitelist_delete(sn_env->whitelist);
26265b390aaSDag-Erling Smørgrav 		slabhash_delete(sn_env->subnet_msg_cache);
26365b390aaSDag-Erling Smørgrav 		free(sn_env);
26465b390aaSDag-Erling Smørgrav 		env->modinfo[id] = NULL;
26565b390aaSDag-Erling Smørgrav 		return 0;
26665b390aaSDag-Erling Smørgrav 	}
26765b390aaSDag-Erling Smørgrav 	inplace_cb_register((void*)ecs_whitelist_check, inplace_cb_query, NULL,
26865b390aaSDag-Erling Smørgrav 		env, id);
26965b390aaSDag-Erling Smørgrav 	inplace_cb_register((void*)ecs_edns_back_parsed,
27065b390aaSDag-Erling Smørgrav 		inplace_cb_edns_back_parsed, NULL, env, id);
27165b390aaSDag-Erling Smørgrav 	inplace_cb_register((void*)ecs_query_response,
27265b390aaSDag-Erling Smørgrav 		inplace_cb_query_response, NULL, env, id);
27365b390aaSDag-Erling Smørgrav 	lock_rw_init(&sn_env->biglock);
27465b390aaSDag-Erling Smørgrav 	return 1;
27565b390aaSDag-Erling Smørgrav }
27665b390aaSDag-Erling Smørgrav 
27765b390aaSDag-Erling Smørgrav void
subnetmod_deinit(struct module_env * env,int id)27865b390aaSDag-Erling Smørgrav subnetmod_deinit(struct module_env *env, int id)
27965b390aaSDag-Erling Smørgrav {
28065b390aaSDag-Erling Smørgrav 	struct subnet_env *sn_env;
28165b390aaSDag-Erling Smørgrav 	if(!env || !env->modinfo[id])
28265b390aaSDag-Erling Smørgrav 		return;
28365b390aaSDag-Erling Smørgrav 	sn_env = (struct subnet_env*)env->modinfo[id];
28465b390aaSDag-Erling Smørgrav 	lock_rw_destroy(&sn_env->biglock);
28565b390aaSDag-Erling Smørgrav 	inplace_cb_delete(env, inplace_cb_edns_back_parsed, id);
28665b390aaSDag-Erling Smørgrav 	inplace_cb_delete(env, inplace_cb_query, id);
287c7f4d7adSDag-Erling Smørgrav 	inplace_cb_delete(env, inplace_cb_query_response, id);
288c7f4d7adSDag-Erling Smørgrav 	ecs_whitelist_delete(sn_env->whitelist);
28965b390aaSDag-Erling Smørgrav 	slabhash_delete(sn_env->subnet_msg_cache);
29065b390aaSDag-Erling Smørgrav 	alloc_clear(&sn_env->alloc);
29165b390aaSDag-Erling Smørgrav 	free(sn_env);
29265b390aaSDag-Erling Smørgrav 	env->modinfo[id] = NULL;
29365b390aaSDag-Erling Smørgrav }
29465b390aaSDag-Erling Smørgrav 
29565b390aaSDag-Erling Smørgrav /** Tells client that upstream has no/improper support */
29665b390aaSDag-Erling Smørgrav static void
cp_edns_bad_response(struct ecs_data * target,struct ecs_data * source)29765b390aaSDag-Erling Smørgrav cp_edns_bad_response(struct ecs_data *target, struct ecs_data *source)
29865b390aaSDag-Erling Smørgrav {
29965b390aaSDag-Erling Smørgrav 	target->subnet_scope_mask  = 0;
30065b390aaSDag-Erling Smørgrav 	target->subnet_source_mask = source->subnet_source_mask;
30165b390aaSDag-Erling Smørgrav 	target->subnet_addr_fam    = source->subnet_addr_fam;
30265b390aaSDag-Erling Smørgrav 	memcpy(target->subnet_addr, source->subnet_addr, INET6_SIZE);
30365b390aaSDag-Erling Smørgrav 	target->subnet_validdata = 1;
30465b390aaSDag-Erling Smørgrav }
30565b390aaSDag-Erling Smørgrav 
30665b390aaSDag-Erling Smørgrav static void
delfunc(void * envptr,void * elemptr)30765b390aaSDag-Erling Smørgrav delfunc(void *envptr, void *elemptr) {
30865b390aaSDag-Erling Smørgrav 	struct reply_info *elem = (struct reply_info *)elemptr;
30965b390aaSDag-Erling Smørgrav 	struct subnet_env *env = (struct subnet_env *)envptr;
31065b390aaSDag-Erling Smørgrav 	reply_info_parsedelete(elem, &env->alloc);
31165b390aaSDag-Erling Smørgrav }
31265b390aaSDag-Erling Smørgrav 
31365b390aaSDag-Erling Smørgrav static size_t
sizefunc(void * elemptr)31465b390aaSDag-Erling Smørgrav sizefunc(void *elemptr) {
31565b390aaSDag-Erling Smørgrav 	struct reply_info *elem  = (struct reply_info *)elemptr;
316*335c7cdaSCy Schubert 	size_t s = sizeof (struct reply_info) - sizeof (struct rrset_ref)
31765b390aaSDag-Erling Smørgrav 		+ elem->rrset_count * sizeof (struct rrset_ref)
31865b390aaSDag-Erling Smørgrav 		+ elem->rrset_count * sizeof (struct ub_packed_rrset_key *);
319*335c7cdaSCy Schubert 	size_t i;
320*335c7cdaSCy Schubert 	for (i = 0; i < elem->rrset_count; i++) {
321*335c7cdaSCy Schubert 		struct ub_packed_rrset_key *key = elem->rrsets[i];
322*335c7cdaSCy Schubert 		struct packed_rrset_data *data = key->entry.data;
323*335c7cdaSCy Schubert 		s += ub_rrset_sizefunc(key, data);
324*335c7cdaSCy Schubert 	}
325*335c7cdaSCy Schubert 	if(elem->reason_bogus_str)
326*335c7cdaSCy Schubert 		s += strlen(elem->reason_bogus_str)+1;
327*335c7cdaSCy Schubert 	return s;
32865b390aaSDag-Erling Smørgrav }
32965b390aaSDag-Erling Smørgrav 
33065b390aaSDag-Erling Smørgrav /**
33165b390aaSDag-Erling Smørgrav  * Select tree from cache entry based on edns data.
33265b390aaSDag-Erling Smørgrav  * If for address family not present it will create a new one.
33365b390aaSDag-Erling Smørgrav  * NULL on failure to create. */
33465b390aaSDag-Erling Smørgrav static struct addrtree*
get_tree(struct subnet_msg_cache_data * data,struct ecs_data * edns,struct subnet_env * env,struct config_file * cfg)33565b390aaSDag-Erling Smørgrav get_tree(struct subnet_msg_cache_data *data, struct ecs_data *edns,
33665b390aaSDag-Erling Smørgrav 	struct subnet_env *env, struct config_file* cfg)
33765b390aaSDag-Erling Smørgrav {
33865b390aaSDag-Erling Smørgrav 	struct addrtree *tree;
33965b390aaSDag-Erling Smørgrav 	if (edns->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4) {
34065b390aaSDag-Erling Smørgrav 		if (!data->tree4)
34165b390aaSDag-Erling Smørgrav 			data->tree4 = addrtree_create(
34265b390aaSDag-Erling Smørgrav 				cfg->max_client_subnet_ipv4, &delfunc,
343e86b9096SDag-Erling Smørgrav 				&sizefunc, env, cfg->max_ecs_tree_size_ipv4);
34465b390aaSDag-Erling Smørgrav 		tree = data->tree4;
34565b390aaSDag-Erling Smørgrav 	} else {
34665b390aaSDag-Erling Smørgrav 		if (!data->tree6)
34765b390aaSDag-Erling Smørgrav 			data->tree6 = addrtree_create(
34865b390aaSDag-Erling Smørgrav 				cfg->max_client_subnet_ipv6, &delfunc,
349e86b9096SDag-Erling Smørgrav 				&sizefunc, env, cfg->max_ecs_tree_size_ipv6);
35065b390aaSDag-Erling Smørgrav 		tree = data->tree6;
35165b390aaSDag-Erling Smørgrav 	}
35265b390aaSDag-Erling Smørgrav 	return tree;
35365b390aaSDag-Erling Smørgrav }
35465b390aaSDag-Erling Smørgrav 
35565b390aaSDag-Erling Smørgrav static void
update_cache(struct module_qstate * qstate,int id)35665b390aaSDag-Erling Smørgrav update_cache(struct module_qstate *qstate, int id)
35765b390aaSDag-Erling Smørgrav {
35865b390aaSDag-Erling Smørgrav 	struct msgreply_entry *mrep_entry;
35965b390aaSDag-Erling Smørgrav 	struct addrtree *tree;
36065b390aaSDag-Erling Smørgrav 	struct reply_info *rep;
36165b390aaSDag-Erling Smørgrav 	struct query_info qinf;
36265b390aaSDag-Erling Smørgrav 	struct subnet_env *sne = qstate->env->modinfo[id];
36365b390aaSDag-Erling Smørgrav 	struct subnet_qstate *sq = (struct subnet_qstate*)qstate->minfo[id];
36465b390aaSDag-Erling Smørgrav 	struct slabhash *subnet_msg_cache = sne->subnet_msg_cache;
36565b390aaSDag-Erling Smørgrav 	struct ecs_data *edns = &sq->ecs_client_in;
36665b390aaSDag-Erling Smørgrav 	size_t i;
367*335c7cdaSCy Schubert 	int only_match_scope_zero, diff_size;
36865b390aaSDag-Erling Smørgrav 
3690a92a9fcSCy Schubert 	/* We already calculated hash upon lookup (lookup_and_reply) if we were
3700a92a9fcSCy Schubert 	 * allowed to look in the ECS cache */
3710a92a9fcSCy Schubert 	hashvalue_type h = qstate->minfo[id] &&
3720a92a9fcSCy Schubert 		((struct subnet_qstate*)qstate->minfo[id])->qinfo_hash_calculated?
37365b390aaSDag-Erling Smørgrav 		((struct subnet_qstate*)qstate->minfo[id])->qinfo_hash :
37465b390aaSDag-Erling Smørgrav 		query_info_hash(&qstate->qinfo, qstate->query_flags);
37565b390aaSDag-Erling Smørgrav 	/* Step 1, general qinfo lookup */
37665b390aaSDag-Erling Smørgrav 	struct lruhash_entry* lru_entry = slabhash_lookup(subnet_msg_cache, h,
37765b390aaSDag-Erling Smørgrav 		&qstate->qinfo, 1);
378e86b9096SDag-Erling Smørgrav 	int need_to_insert = (lru_entry == NULL);
37965b390aaSDag-Erling Smørgrav 	if (!lru_entry) {
380e86b9096SDag-Erling Smørgrav 		void* data = calloc(1,
381e86b9096SDag-Erling Smørgrav 			sizeof(struct subnet_msg_cache_data));
382e86b9096SDag-Erling Smørgrav 		if(!data) {
383e86b9096SDag-Erling Smørgrav 			log_err("malloc failed");
384e86b9096SDag-Erling Smørgrav 			return;
385e86b9096SDag-Erling Smørgrav 		}
38665b390aaSDag-Erling Smørgrav 		qinf = qstate->qinfo;
38765b390aaSDag-Erling Smørgrav 		qinf.qname = memdup(qstate->qinfo.qname,
38865b390aaSDag-Erling Smørgrav 			qstate->qinfo.qname_len);
38965b390aaSDag-Erling Smørgrav 		if(!qinf.qname) {
390e86b9096SDag-Erling Smørgrav 			free(data);
39165b390aaSDag-Erling Smørgrav 			log_err("memdup failed");
39265b390aaSDag-Erling Smørgrav 			return;
39365b390aaSDag-Erling Smørgrav 		}
394e86b9096SDag-Erling Smørgrav 		mrep_entry = query_info_entrysetup(&qinf, data, h);
39565b390aaSDag-Erling Smørgrav 		free(qinf.qname); /* if qname 'consumed', it is set to NULL */
39665b390aaSDag-Erling Smørgrav 		if (!mrep_entry) {
397e86b9096SDag-Erling Smørgrav 			free(data);
39865b390aaSDag-Erling Smørgrav 			log_err("query_info_entrysetup failed");
39965b390aaSDag-Erling Smørgrav 			return;
40065b390aaSDag-Erling Smørgrav 		}
40165b390aaSDag-Erling Smørgrav 		lru_entry = &mrep_entry->entry;
40257bddd21SDag-Erling Smørgrav 		lock_rw_wrlock(&lru_entry->lock);
40365b390aaSDag-Erling Smørgrav 	}
404e86b9096SDag-Erling Smørgrav 	/* lru_entry->lock is locked regardless of how we got here,
405e86b9096SDag-Erling Smørgrav 	 * either from the slabhash_lookup, or above in the new allocated */
40665b390aaSDag-Erling Smørgrav 	/* Step 2, find the correct tree */
40765b390aaSDag-Erling Smørgrav 	if (!(tree = get_tree(lru_entry->data, edns, sne, qstate->env->cfg))) {
408e86b9096SDag-Erling Smørgrav 		lock_rw_unlock(&lru_entry->lock);
4095469a995SCy Schubert 		log_err("subnetcache: cache insertion failed");
41065b390aaSDag-Erling Smørgrav 		return;
41165b390aaSDag-Erling Smørgrav 	}
41257bddd21SDag-Erling Smørgrav 	lock_quick_lock(&sne->alloc.lock);
41365b390aaSDag-Erling Smørgrav 	rep = reply_info_copy(qstate->return_msg->rep, &sne->alloc, NULL);
41457bddd21SDag-Erling Smørgrav 	lock_quick_unlock(&sne->alloc.lock);
41565b390aaSDag-Erling Smørgrav 	if (!rep) {
416e86b9096SDag-Erling Smørgrav 		lock_rw_unlock(&lru_entry->lock);
4175469a995SCy Schubert 		log_err("subnetcache: cache insertion failed");
41865b390aaSDag-Erling Smørgrav 		return;
41965b390aaSDag-Erling Smørgrav 	}
42065b390aaSDag-Erling Smørgrav 
42165b390aaSDag-Erling Smørgrav 	/* store RRsets */
42265b390aaSDag-Erling Smørgrav 	for(i=0; i<rep->rrset_count; i++) {
42365b390aaSDag-Erling Smørgrav 		rep->ref[i].key = rep->rrsets[i];
42465b390aaSDag-Erling Smørgrav 		rep->ref[i].id = rep->rrsets[i]->id;
42565b390aaSDag-Erling Smørgrav 	}
42665b390aaSDag-Erling Smørgrav 	reply_info_set_ttls(rep, *qstate->env->now);
427*335c7cdaSCy Schubert 	reply_info_sortref(rep);
42865b390aaSDag-Erling Smørgrav 	rep->flags |= (BIT_RA | BIT_QR); /* fix flags to be sensible for */
42965b390aaSDag-Erling Smørgrav 	rep->flags &= ~(BIT_AA | BIT_CD);/* a reply based on the cache   */
430865f46b2SCy Schubert 	if(edns->subnet_source_mask == 0 && edns->subnet_scope_mask == 0)
431865f46b2SCy Schubert 		only_match_scope_zero = 1;
432865f46b2SCy Schubert 	else only_match_scope_zero = 0;
433*335c7cdaSCy Schubert 	diff_size = (int)tree->size_bytes;
43465b390aaSDag-Erling Smørgrav 	addrtree_insert(tree, (addrkey_t*)edns->subnet_addr,
43525039b37SCy Schubert 		edns->subnet_source_mask, sq->max_scope, rep,
436865f46b2SCy Schubert 		rep->ttl, *qstate->env->now, only_match_scope_zero);
437*335c7cdaSCy Schubert 	diff_size = (int)tree->size_bytes - diff_size;
438e86b9096SDag-Erling Smørgrav 
43965b390aaSDag-Erling Smørgrav 	lock_rw_unlock(&lru_entry->lock);
440e86b9096SDag-Erling Smørgrav 	if (need_to_insert) {
44165b390aaSDag-Erling Smørgrav 		slabhash_insert(subnet_msg_cache, h, lru_entry, lru_entry->data,
44265b390aaSDag-Erling Smørgrav 			NULL);
443*335c7cdaSCy Schubert 	} else {
444*335c7cdaSCy Schubert 		slabhash_update_space_used(subnet_msg_cache, h, NULL,
445*335c7cdaSCy Schubert 			diff_size);
44665b390aaSDag-Erling Smørgrav 	}
44765b390aaSDag-Erling Smørgrav }
44865b390aaSDag-Erling Smørgrav 
44965b390aaSDag-Erling Smørgrav /** Lookup in cache and reply true iff reply is sent. */
45065b390aaSDag-Erling Smørgrav static int
lookup_and_reply(struct module_qstate * qstate,int id,struct subnet_qstate * sq,int prefetch)4518f76bb7dSCy Schubert lookup_and_reply(struct module_qstate *qstate, int id, struct subnet_qstate *sq, int prefetch)
45265b390aaSDag-Erling Smørgrav {
45365b390aaSDag-Erling Smørgrav 	struct lruhash_entry *e;
45465b390aaSDag-Erling Smørgrav 	struct module_env *env = qstate->env;
45565b390aaSDag-Erling Smørgrav 	struct subnet_env *sne = (struct subnet_env*)env->modinfo[id];
45665b390aaSDag-Erling Smørgrav 	hashvalue_type h = query_info_hash(&qstate->qinfo, qstate->query_flags);
45765b390aaSDag-Erling Smørgrav 	struct subnet_msg_cache_data *data;
45865b390aaSDag-Erling Smørgrav 	struct ecs_data *ecs = &sq->ecs_client_in;
45965b390aaSDag-Erling Smørgrav 	struct addrtree *tree;
46065b390aaSDag-Erling Smørgrav 	struct addrnode *node;
46165b390aaSDag-Erling Smørgrav 	uint8_t scope;
46265b390aaSDag-Erling Smørgrav 
46365b390aaSDag-Erling Smørgrav 	memset(&sq->ecs_client_out, 0, sizeof(sq->ecs_client_out));
46465b390aaSDag-Erling Smørgrav 
4650a92a9fcSCy Schubert 	if (sq) {
4660a92a9fcSCy Schubert 		sq->qinfo_hash = h; /* Might be useful on cache miss */
4670a92a9fcSCy Schubert 		sq->qinfo_hash_calculated = 1;
4680a92a9fcSCy Schubert 	}
46965b390aaSDag-Erling Smørgrav 	e = slabhash_lookup(sne->subnet_msg_cache, h, &qstate->qinfo, 1);
47065b390aaSDag-Erling Smørgrav 	if (!e) return 0; /* qinfo not in cache */
47165b390aaSDag-Erling Smørgrav 	data = e->data;
47265b390aaSDag-Erling Smørgrav 	tree = (ecs->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4)?
47365b390aaSDag-Erling Smørgrav 		data->tree4 : data->tree6;
47465b390aaSDag-Erling Smørgrav 	if (!tree) { /* qinfo in cache but not for this family */
47565b390aaSDag-Erling Smørgrav 		lock_rw_unlock(&e->lock);
47665b390aaSDag-Erling Smørgrav 		return 0;
47765b390aaSDag-Erling Smørgrav 	}
47865b390aaSDag-Erling Smørgrav 	node = addrtree_find(tree, (addrkey_t*)ecs->subnet_addr,
47965b390aaSDag-Erling Smørgrav 		ecs->subnet_source_mask, *env->now);
48065b390aaSDag-Erling Smørgrav 	if (!node) { /* plain old cache miss */
48165b390aaSDag-Erling Smørgrav 		lock_rw_unlock(&e->lock);
48265b390aaSDag-Erling Smørgrav 		return 0;
48365b390aaSDag-Erling Smørgrav 	}
48465b390aaSDag-Erling Smørgrav 
48565b390aaSDag-Erling Smørgrav 	qstate->return_msg = tomsg(NULL, &qstate->qinfo,
486091e9e46SCy Schubert 		(struct reply_info *)node->elem, qstate->region, *env->now, 0,
48765b390aaSDag-Erling Smørgrav 		env->scratch);
48865b390aaSDag-Erling Smørgrav 	scope = (uint8_t)node->scope;
48965b390aaSDag-Erling Smørgrav 	lock_rw_unlock(&e->lock);
49065b390aaSDag-Erling Smørgrav 
49165b390aaSDag-Erling Smørgrav 	if (!qstate->return_msg) { /* Failed allocation or expired TTL */
49265b390aaSDag-Erling Smørgrav 		return 0;
49365b390aaSDag-Erling Smørgrav 	}
49465b390aaSDag-Erling Smørgrav 
49565b390aaSDag-Erling Smørgrav 	if (sq->subnet_downstream) { /* relay to interested client */
49665b390aaSDag-Erling Smørgrav 		sq->ecs_client_out.subnet_scope_mask = scope;
49765b390aaSDag-Erling Smørgrav 		sq->ecs_client_out.subnet_addr_fam = ecs->subnet_addr_fam;
49865b390aaSDag-Erling Smørgrav 		sq->ecs_client_out.subnet_source_mask = ecs->subnet_source_mask;
49965b390aaSDag-Erling Smørgrav 		memcpy(&sq->ecs_client_out.subnet_addr, &ecs->subnet_addr,
50065b390aaSDag-Erling Smørgrav 			INET6_SIZE);
50165b390aaSDag-Erling Smørgrav 		sq->ecs_client_out.subnet_validdata = 1;
50265b390aaSDag-Erling Smørgrav 	}
5038f76bb7dSCy Schubert 
5048f76bb7dSCy Schubert 	if (prefetch && *qstate->env->now >= ((struct reply_info *)node->elem)->prefetch_ttl) {
5058f76bb7dSCy Schubert 		qstate->need_refetch = 1;
5068f76bb7dSCy Schubert 	}
50765b390aaSDag-Erling Smørgrav 	return 1;
50865b390aaSDag-Erling Smørgrav }
50965b390aaSDag-Erling Smørgrav 
51065b390aaSDag-Erling Smørgrav /**
51165b390aaSDag-Erling Smørgrav  * Test first bits of addresses for equality. Caller is responsible
51265b390aaSDag-Erling Smørgrav  * for making sure that both a and b are at least net/8 octets long.
51365b390aaSDag-Erling Smørgrav  * @param a: first address.
51465b390aaSDag-Erling Smørgrav  * @param a: seconds address.
51565b390aaSDag-Erling Smørgrav  * @param net: Number of bits to test.
51665b390aaSDag-Erling Smørgrav  * @return: 1 if equal, 0 otherwise.
51765b390aaSDag-Erling Smørgrav  */
51865b390aaSDag-Erling Smørgrav static int
common_prefix(uint8_t * a,uint8_t * b,uint8_t net)51965b390aaSDag-Erling Smørgrav common_prefix(uint8_t *a, uint8_t *b, uint8_t net)
52065b390aaSDag-Erling Smørgrav {
52165b390aaSDag-Erling Smørgrav 	size_t n = (size_t)net / 8;
52265b390aaSDag-Erling Smørgrav 	return !memcmp(a, b, n) && ((net % 8) == 0 || a[n] == b[n]);
52365b390aaSDag-Erling Smørgrav }
52465b390aaSDag-Erling Smørgrav 
52565b390aaSDag-Erling Smørgrav static enum module_ext_state
eval_response(struct module_qstate * qstate,int id,struct subnet_qstate * sq)52665b390aaSDag-Erling Smørgrav eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq)
52765b390aaSDag-Erling Smørgrav {
52865b390aaSDag-Erling Smørgrav 	struct subnet_env *sne = qstate->env->modinfo[id];
52965b390aaSDag-Erling Smørgrav 
53065b390aaSDag-Erling Smørgrav 	struct ecs_data *c_in  = &sq->ecs_client_in; /* rcvd from client */
53165b390aaSDag-Erling Smørgrav 	struct ecs_data *c_out = &sq->ecs_client_out;/* will send to client */
53265b390aaSDag-Erling Smørgrav 	struct ecs_data *s_in  = &sq->ecs_server_in; /* rcvd from auth */
53365b390aaSDag-Erling Smørgrav 	struct ecs_data *s_out = &sq->ecs_server_out;/* sent to auth */
53465b390aaSDag-Erling Smørgrav 
53565b390aaSDag-Erling Smørgrav 	memset(c_out, 0, sizeof(*c_out));
53665b390aaSDag-Erling Smørgrav 
5374c75e3aaSDag-Erling Smørgrav 	if (!qstate->return_msg) {
5384c75e3aaSDag-Erling Smørgrav 		/* already an answer and its not a message, but retain
5394c75e3aaSDag-Erling Smørgrav 		 * the actual rcode, instead of module_error, so send
5404c75e3aaSDag-Erling Smørgrav 		 * module_finished */
5414c75e3aaSDag-Erling Smørgrav 		return module_finished;
5424c75e3aaSDag-Erling Smørgrav 	}
54365b390aaSDag-Erling Smørgrav 
54465b390aaSDag-Erling Smørgrav 	/* We have not asked for subnet data */
545103ba509SCy Schubert 	if (!sq->subnet_sent && !sq->subnet_sent_no_subnet) {
54665b390aaSDag-Erling Smørgrav 		if (s_in->subnet_validdata)
5475469a995SCy Schubert 			verbose(VERB_QUERY, "subnetcache: received spurious data");
54865b390aaSDag-Erling Smørgrav 		if (sq->subnet_downstream) /* Copy back to client */
54965b390aaSDag-Erling Smørgrav 			cp_edns_bad_response(c_out, c_in);
55065b390aaSDag-Erling Smørgrav 		return module_finished;
55165b390aaSDag-Erling Smørgrav 	}
55265b390aaSDag-Erling Smørgrav 
55365b390aaSDag-Erling Smørgrav 	/* subnet sent but nothing came back */
554103ba509SCy Schubert 	if (!s_in->subnet_validdata && !sq->subnet_sent_no_subnet) {
55565b390aaSDag-Erling Smørgrav 		/* The authority indicated no support for edns subnet. As a
55665b390aaSDag-Erling Smørgrav 		 * consequence the answer ended up in the regular cache. It
55724e36522SCy Schubert 		 * is still useful to put it in the edns subnet cache for
55865b390aaSDag-Erling Smørgrav 		 * when a client explicitly asks for subnet specific answer. */
5595469a995SCy Schubert 		verbose(VERB_QUERY, "subnetcache: Authority indicates no support");
560e86b9096SDag-Erling Smørgrav 		if(!sq->started_no_cache_store) {
56165b390aaSDag-Erling Smørgrav 			lock_rw_wrlock(&sne->biglock);
56265b390aaSDag-Erling Smørgrav 			update_cache(qstate, id);
56365b390aaSDag-Erling Smørgrav 			lock_rw_unlock(&sne->biglock);
564e86b9096SDag-Erling Smørgrav 		}
56565b390aaSDag-Erling Smørgrav 		if (sq->subnet_downstream)
56665b390aaSDag-Erling Smørgrav 			cp_edns_bad_response(c_out, c_in);
56765b390aaSDag-Erling Smørgrav 		return module_finished;
56865b390aaSDag-Erling Smørgrav 	}
56965b390aaSDag-Erling Smørgrav 
570103ba509SCy Schubert 	/* Purposefully there was no sent subnet, and there is consequently
571103ba509SCy Schubert 	 * no subnet in the answer. If there was, use the subnet in the answer
572103ba509SCy Schubert 	 * anyway. But if there is not, treat it as a prefix 0 answer. */
573103ba509SCy Schubert 	if(sq->subnet_sent_no_subnet && !s_in->subnet_validdata) {
574103ba509SCy Schubert 		/* Fill in 0.0.0.0/0 scope 0, or ::0/0 scope 0, for caching. */
575103ba509SCy Schubert 		s_in->subnet_addr_fam = s_out->subnet_addr_fam;
576103ba509SCy Schubert 		s_in->subnet_source_mask = 0;
577103ba509SCy Schubert 		s_in->subnet_scope_mask = 0;
578103ba509SCy Schubert 		memset(s_in->subnet_addr, 0, INET6_SIZE);
579103ba509SCy Schubert 		s_in->subnet_validdata = 1;
580103ba509SCy Schubert 	}
581103ba509SCy Schubert 
58265b390aaSDag-Erling Smørgrav 	/* Being here means we have asked for and got a subnet specific
58365b390aaSDag-Erling Smørgrav 	 * answer. Also, the answer from the authority is not yet cached
58465b390aaSDag-Erling Smørgrav 	 * anywhere. */
58565b390aaSDag-Erling Smørgrav 
58665b390aaSDag-Erling Smørgrav 	/* can we accept response? */
58765b390aaSDag-Erling Smørgrav 	if(s_out->subnet_addr_fam != s_in->subnet_addr_fam ||
58865b390aaSDag-Erling Smørgrav 		s_out->subnet_source_mask != s_in->subnet_source_mask ||
58965b390aaSDag-Erling Smørgrav 		!common_prefix(s_out->subnet_addr, s_in->subnet_addr,
59065b390aaSDag-Erling Smørgrav 			s_out->subnet_source_mask))
59165b390aaSDag-Erling Smørgrav 	{
59265b390aaSDag-Erling Smørgrav 		/* we can not accept, restart query without option */
5935469a995SCy Schubert 		verbose(VERB_QUERY, "subnetcache: forged data");
59465b390aaSDag-Erling Smørgrav 		s_out->subnet_validdata = 0;
59565b390aaSDag-Erling Smørgrav 		(void)edns_opt_list_remove(&qstate->edns_opts_back_out,
59665b390aaSDag-Erling Smørgrav 			qstate->env->cfg->client_subnet_opcode);
59765b390aaSDag-Erling Smørgrav 		sq->subnet_sent = 0;
598103ba509SCy Schubert 		sq->subnet_sent_no_subnet = 0;
59965b390aaSDag-Erling Smørgrav 		return module_restart_next;
60065b390aaSDag-Erling Smørgrav 	}
60165b390aaSDag-Erling Smørgrav 
60265b390aaSDag-Erling Smørgrav 	lock_rw_wrlock(&sne->biglock);
603e86b9096SDag-Erling Smørgrav 	if(!sq->started_no_cache_store) {
60465b390aaSDag-Erling Smørgrav 		update_cache(qstate, id);
605e86b9096SDag-Erling Smørgrav 	}
6064c75e3aaSDag-Erling Smørgrav 	sne->num_msg_nocache++;
60765b390aaSDag-Erling Smørgrav 	lock_rw_unlock(&sne->biglock);
60865b390aaSDag-Erling Smørgrav 
609*335c7cdaSCy Schubert 	/* If there is an expired answer in the global cache, remove that,
610*335c7cdaSCy Schubert 	 * because expired answers would otherwise resurface once the ecs data
611*335c7cdaSCy Schubert 	 * expires, giving once in a while global data responses for ecs
612*335c7cdaSCy Schubert 	 * domains, with serve expired enabled. */
613*335c7cdaSCy Schubert 	if(qstate->env->cfg->serve_expired) {
614*335c7cdaSCy Schubert 		msg_cache_remove(qstate->env, qstate->qinfo.qname,
615*335c7cdaSCy Schubert 			qstate->qinfo.qname_len, qstate->qinfo.qtype,
616*335c7cdaSCy Schubert 			qstate->qinfo.qclass, 0);
617*335c7cdaSCy Schubert #ifdef USE_CACHEDB
618*335c7cdaSCy Schubert 		if(qstate->env->cachedb_enabled)
619*335c7cdaSCy Schubert 			cachedb_msg_remove(qstate);
620*335c7cdaSCy Schubert #endif
621*335c7cdaSCy Schubert 	}
622*335c7cdaSCy Schubert 
62365b390aaSDag-Erling Smørgrav 	if (sq->subnet_downstream) {
62465b390aaSDag-Erling Smørgrav 		/* Client wants to see the answer, echo option back
62565b390aaSDag-Erling Smørgrav 		 * and adjust the scope. */
62665b390aaSDag-Erling Smørgrav 		c_out->subnet_addr_fam = c_in->subnet_addr_fam;
62765b390aaSDag-Erling Smørgrav 		c_out->subnet_source_mask = c_in->subnet_source_mask;
62865b390aaSDag-Erling Smørgrav 		memcpy(&c_out->subnet_addr, &c_in->subnet_addr, INET6_SIZE);
62925039b37SCy Schubert 		c_out->subnet_scope_mask = sq->max_scope;
630e86b9096SDag-Erling Smørgrav 		/* Limit scope returned to client to scope used for caching. */
631e86b9096SDag-Erling Smørgrav 		if(c_out->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4) {
632e86b9096SDag-Erling Smørgrav 			if(c_out->subnet_scope_mask >
633e86b9096SDag-Erling Smørgrav 				qstate->env->cfg->max_client_subnet_ipv4) {
634e86b9096SDag-Erling Smørgrav 				c_out->subnet_scope_mask =
635e86b9096SDag-Erling Smørgrav 					qstate->env->cfg->max_client_subnet_ipv4;
636e86b9096SDag-Erling Smørgrav 			}
637e86b9096SDag-Erling Smørgrav 		}
638e86b9096SDag-Erling Smørgrav 		else if(c_out->subnet_scope_mask >
639e86b9096SDag-Erling Smørgrav 				qstate->env->cfg->max_client_subnet_ipv6) {
640e86b9096SDag-Erling Smørgrav 				c_out->subnet_scope_mask =
641e86b9096SDag-Erling Smørgrav 					qstate->env->cfg->max_client_subnet_ipv6;
642e86b9096SDag-Erling Smørgrav 		}
64365b390aaSDag-Erling Smørgrav 		c_out->subnet_validdata = 1;
64465b390aaSDag-Erling Smørgrav 	}
64565b390aaSDag-Erling Smørgrav 	return module_finished;
64665b390aaSDag-Erling Smørgrav }
64765b390aaSDag-Erling Smørgrav 
64865b390aaSDag-Erling Smørgrav /** Parse EDNS opt data containing ECS */
64965b390aaSDag-Erling Smørgrav static int
parse_subnet_option(struct edns_option * ecs_option,struct ecs_data * ecs)65065b390aaSDag-Erling Smørgrav parse_subnet_option(struct edns_option* ecs_option, struct ecs_data* ecs)
65165b390aaSDag-Erling Smørgrav {
65265b390aaSDag-Erling Smørgrav 	memset(ecs, 0, sizeof(*ecs));
65365b390aaSDag-Erling Smørgrav 	if (ecs_option->opt_len < 4)
65465b390aaSDag-Erling Smørgrav 		return 0;
65565b390aaSDag-Erling Smørgrav 
65665b390aaSDag-Erling Smørgrav 	ecs->subnet_addr_fam = sldns_read_uint16(ecs_option->opt_data);
65765b390aaSDag-Erling Smørgrav 	ecs->subnet_source_mask = ecs_option->opt_data[2];
65865b390aaSDag-Erling Smørgrav 	ecs->subnet_scope_mask = ecs_option->opt_data[3];
6598a384985SDag-Erling Smørgrav 	/* remaining bytes indicate address */
66065b390aaSDag-Erling Smørgrav 
66165b390aaSDag-Erling Smørgrav 	/* validate input*/
66265b390aaSDag-Erling Smørgrav 	/* option length matches calculated length? */
66365b390aaSDag-Erling Smørgrav 	if (ecs_option->opt_len != (size_t)((ecs->subnet_source_mask+7)/8 + 4))
66465b390aaSDag-Erling Smørgrav 		return 0;
66565b390aaSDag-Erling Smørgrav 	if (ecs_option->opt_len - 4 > INET6_SIZE || ecs_option->opt_len == 0)
66665b390aaSDag-Erling Smørgrav 		return 0;
66765b390aaSDag-Erling Smørgrav 	if (ecs->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4) {
66865b390aaSDag-Erling Smørgrav 		if (ecs->subnet_source_mask > 32 || ecs->subnet_scope_mask > 32)
66965b390aaSDag-Erling Smørgrav 			return 0;
67065b390aaSDag-Erling Smørgrav 	} else if (ecs->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP6) {
67165b390aaSDag-Erling Smørgrav 		if (ecs->subnet_source_mask > 128 ||
67265b390aaSDag-Erling Smørgrav 			ecs->subnet_scope_mask > 128)
67365b390aaSDag-Erling Smørgrav 			return 0;
67465b390aaSDag-Erling Smørgrav 	} else
67565b390aaSDag-Erling Smørgrav 		return 0;
67665b390aaSDag-Erling Smørgrav 
67765b390aaSDag-Erling Smørgrav 	/* valid ECS data, write to ecs_data */
67865b390aaSDag-Erling Smørgrav 	if (copy_clear(ecs->subnet_addr, INET6_SIZE, ecs_option->opt_data + 4,
67965b390aaSDag-Erling Smørgrav 		ecs_option->opt_len - 4, ecs->subnet_source_mask))
68065b390aaSDag-Erling Smørgrav 		return 0;
68165b390aaSDag-Erling Smørgrav 	ecs->subnet_validdata = 1;
68265b390aaSDag-Erling Smørgrav 	return 1;
68365b390aaSDag-Erling Smørgrav }
68465b390aaSDag-Erling Smørgrav 
685a39a5a69SCy Schubert void
subnet_option_from_ss(struct sockaddr_storage * ss,struct ecs_data * ecs,struct config_file * cfg)68665b390aaSDag-Erling Smørgrav subnet_option_from_ss(struct sockaddr_storage *ss, struct ecs_data* ecs,
68765b390aaSDag-Erling Smørgrav 	struct config_file* cfg)
68865b390aaSDag-Erling Smørgrav {
68965b390aaSDag-Erling Smørgrav 	void* sinaddr;
69065b390aaSDag-Erling Smørgrav 
69165b390aaSDag-Erling Smørgrav 	/* Construct subnet option from original query */
69265b390aaSDag-Erling Smørgrav 	if(((struct sockaddr_in*)ss)->sin_family == AF_INET) {
69365b390aaSDag-Erling Smørgrav 		ecs->subnet_source_mask = cfg->max_client_subnet_ipv4;
69465b390aaSDag-Erling Smørgrav 		ecs->subnet_addr_fam = EDNSSUBNET_ADDRFAM_IP4;
69565b390aaSDag-Erling Smørgrav 		sinaddr = &((struct sockaddr_in*)ss)->sin_addr;
69665b390aaSDag-Erling Smørgrav 		if (!copy_clear( ecs->subnet_addr, INET6_SIZE,
69765b390aaSDag-Erling Smørgrav 			(uint8_t *)sinaddr, INET_SIZE,
69865b390aaSDag-Erling Smørgrav 			ecs->subnet_source_mask)) {
69965b390aaSDag-Erling Smørgrav 			ecs->subnet_validdata = 1;
70065b390aaSDag-Erling Smørgrav 		}
70165b390aaSDag-Erling Smørgrav 	}
70265b390aaSDag-Erling Smørgrav #ifdef INET6
70365b390aaSDag-Erling Smørgrav 	else {
70465b390aaSDag-Erling Smørgrav 		ecs->subnet_source_mask = cfg->max_client_subnet_ipv6;
70565b390aaSDag-Erling Smørgrav 		ecs->subnet_addr_fam = EDNSSUBNET_ADDRFAM_IP6;
70665b390aaSDag-Erling Smørgrav 		sinaddr = &((struct sockaddr_in6*)ss)->sin6_addr;
70765b390aaSDag-Erling Smørgrav 		if (!copy_clear( ecs->subnet_addr, INET6_SIZE,
70865b390aaSDag-Erling Smørgrav 			(uint8_t *)sinaddr, INET6_SIZE,
70965b390aaSDag-Erling Smørgrav 			ecs->subnet_source_mask)) {
71065b390aaSDag-Erling Smørgrav 			ecs->subnet_validdata = 1;
71165b390aaSDag-Erling Smørgrav 		}
71265b390aaSDag-Erling Smørgrav 	}
71365b390aaSDag-Erling Smørgrav #else
71465b390aaSDag-Erling Smørgrav 			/* We don't know how to handle ip6, just pass */
71565b390aaSDag-Erling Smørgrav #endif /* INET6 */
71665b390aaSDag-Erling Smørgrav }
71765b390aaSDag-Erling Smørgrav 
71865b390aaSDag-Erling Smørgrav int
ecs_query_response(struct module_qstate * qstate,struct dns_msg * response,int id,void * ATTR_UNUSED (cbargs))71965b390aaSDag-Erling Smørgrav ecs_query_response(struct module_qstate* qstate, struct dns_msg* response,
72065b390aaSDag-Erling Smørgrav 	int id, void* ATTR_UNUSED(cbargs))
72165b390aaSDag-Erling Smørgrav {
72265b390aaSDag-Erling Smørgrav 	struct subnet_qstate *sq;
72365b390aaSDag-Erling Smørgrav 
72465b390aaSDag-Erling Smørgrav 	if(!response || !(sq=(struct subnet_qstate*)qstate->minfo[id]))
72565b390aaSDag-Erling Smørgrav 		return 1;
72665b390aaSDag-Erling Smørgrav 
72765b390aaSDag-Erling Smørgrav 	if(sq->subnet_sent &&
72865b390aaSDag-Erling Smørgrav 		FLAGS_GET_RCODE(response->rep->flags) == LDNS_RCODE_REFUSED) {
7298a384985SDag-Erling Smørgrav 		/* REFUSED response to ECS query, remove ECS option. */
73065b390aaSDag-Erling Smørgrav 		edns_opt_list_remove(&qstate->edns_opts_back_out,
73165b390aaSDag-Erling Smørgrav 			qstate->env->cfg->client_subnet_opcode);
73265b390aaSDag-Erling Smørgrav 		sq->subnet_sent = 0;
733103ba509SCy Schubert 		sq->subnet_sent_no_subnet = 0;
73465b390aaSDag-Erling Smørgrav 		memset(&sq->ecs_server_out, 0, sizeof(sq->ecs_server_out));
73525039b37SCy Schubert 	} else if (!sq->track_max_scope &&
73625039b37SCy Schubert 		FLAGS_GET_RCODE(response->rep->flags) == LDNS_RCODE_NOERROR &&
73725039b37SCy Schubert 		response->rep->an_numrrsets > 0
73825039b37SCy Schubert 		) {
73925039b37SCy Schubert 		struct ub_packed_rrset_key* s = response->rep->rrsets[0];
74025039b37SCy Schubert 		if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME &&
74125039b37SCy Schubert 			query_dname_compare(qstate->qinfo.qname,
74225039b37SCy Schubert 			s->rk.dname) == 0) {
74325039b37SCy Schubert 			/* CNAME response for QNAME. From now on keep track of
74425039b37SCy Schubert 			 * longest received ECS prefix for all queries on this
74525039b37SCy Schubert 			 * qstate. */
74625039b37SCy Schubert 			sq->track_max_scope = 1;
74725039b37SCy Schubert 		}
74865b390aaSDag-Erling Smørgrav 	}
74965b390aaSDag-Erling Smørgrav 	return 1;
75065b390aaSDag-Erling Smørgrav }
75165b390aaSDag-Erling Smørgrav 
752865f46b2SCy Schubert /** verbose print edns subnet option in pretty print */
753865f46b2SCy Schubert static void
subnet_log_print(const char * s,struct edns_option * ecs_opt)754865f46b2SCy Schubert subnet_log_print(const char* s, struct edns_option* ecs_opt)
755865f46b2SCy Schubert {
756865f46b2SCy Schubert 	if(verbosity >= VERB_ALGO) {
757865f46b2SCy Schubert 		char buf[256];
758865f46b2SCy Schubert 		char* str = buf;
759865f46b2SCy Schubert 		size_t str_len = sizeof(buf);
760865f46b2SCy Schubert 		if(!ecs_opt) {
761865f46b2SCy Schubert 			verbose(VERB_ALGO, "%s (null)", s);
762865f46b2SCy Schubert 			return;
763865f46b2SCy Schubert 		}
764865f46b2SCy Schubert 		(void)sldns_wire2str_edns_subnet_print(&str, &str_len,
765865f46b2SCy Schubert 			ecs_opt->opt_data, ecs_opt->opt_len);
766865f46b2SCy Schubert 		verbose(VERB_ALGO, "%s %s", s, buf);
767865f46b2SCy Schubert 	}
768865f46b2SCy Schubert }
769865f46b2SCy Schubert 
77065b390aaSDag-Erling Smørgrav int
ecs_edns_back_parsed(struct module_qstate * qstate,int id,void * ATTR_UNUSED (cbargs))77165b390aaSDag-Erling Smørgrav ecs_edns_back_parsed(struct module_qstate* qstate, int id,
77265b390aaSDag-Erling Smørgrav 	void* ATTR_UNUSED(cbargs))
77365b390aaSDag-Erling Smørgrav {
77465b390aaSDag-Erling Smørgrav 	struct subnet_qstate *sq;
77565b390aaSDag-Erling Smørgrav 	struct edns_option* ecs_opt;
77665b390aaSDag-Erling Smørgrav 
77765b390aaSDag-Erling Smørgrav 	if(!(sq=(struct subnet_qstate*)qstate->minfo[id]))
77865b390aaSDag-Erling Smørgrav 		return 1;
77965b390aaSDag-Erling Smørgrav 	if((ecs_opt = edns_opt_list_find(
78065b390aaSDag-Erling Smørgrav 		qstate->edns_opts_back_in,
78125039b37SCy Schubert 		qstate->env->cfg->client_subnet_opcode)) &&
78225039b37SCy Schubert 		parse_subnet_option(ecs_opt, &sq->ecs_server_in) &&
78325039b37SCy Schubert 		sq->subnet_sent && sq->ecs_server_in.subnet_validdata) {
784865f46b2SCy Schubert 			subnet_log_print("answer has edns subnet", ecs_opt);
78565b390aaSDag-Erling Smørgrav 			/* Only skip global cache store if we sent an ECS option
78665b390aaSDag-Erling Smørgrav 			 * and received one back. Answers from non-whitelisted
7878a384985SDag-Erling Smørgrav 			 * servers will end up in global cache. Answers for
78865b390aaSDag-Erling Smørgrav 			 * queries with 0 source will not (unless nameserver
78965b390aaSDag-Erling Smørgrav 			 * does not support ECS). */
79065b390aaSDag-Erling Smørgrav 			qstate->no_cache_store = 1;
79125039b37SCy Schubert 			if(!sq->track_max_scope || (sq->track_max_scope &&
79225039b37SCy Schubert 				sq->ecs_server_in.subnet_scope_mask >
79325039b37SCy Schubert 				sq->max_scope))
79425039b37SCy Schubert 				sq->max_scope = sq->ecs_server_in.subnet_scope_mask;
795103ba509SCy Schubert 	} else if(sq->subnet_sent_no_subnet) {
796103ba509SCy Schubert 		/* The answer can be stored as scope 0, not in global cache. */
797103ba509SCy Schubert 		qstate->no_cache_store = 1;
79865b390aaSDag-Erling Smørgrav 	}
79965b390aaSDag-Erling Smørgrav 
80065b390aaSDag-Erling Smørgrav 	return 1;
80165b390aaSDag-Erling Smørgrav }
80265b390aaSDag-Erling Smørgrav 
80365b390aaSDag-Erling Smørgrav void
subnetmod_operate(struct module_qstate * qstate,enum module_ev event,int id,struct outbound_entry * outbound)80465b390aaSDag-Erling Smørgrav subnetmod_operate(struct module_qstate *qstate, enum module_ev event,
80565b390aaSDag-Erling Smørgrav 	int id, struct outbound_entry* outbound)
80665b390aaSDag-Erling Smørgrav {
80765b390aaSDag-Erling Smørgrav 	struct subnet_env *sne = qstate->env->modinfo[id];
80865b390aaSDag-Erling Smørgrav 	struct subnet_qstate *sq = (struct subnet_qstate*)qstate->minfo[id];
80965b390aaSDag-Erling Smørgrav 
8105469a995SCy Schubert 	verbose(VERB_QUERY, "subnetcache[module %d] operate: extstate:%s "
81165b390aaSDag-Erling Smørgrav 		"event:%s", id, strextstate(qstate->ext_state[id]),
81265b390aaSDag-Erling Smørgrav 		strmodulevent(event));
8135469a995SCy Schubert 	log_query_info(VERB_QUERY, "subnetcache operate: query", &qstate->qinfo);
81465b390aaSDag-Erling Smørgrav 
81565b390aaSDag-Erling Smørgrav 	if((event == module_event_new || event == module_event_pass) &&
81665b390aaSDag-Erling Smørgrav 		sq == NULL) {
81765b390aaSDag-Erling Smørgrav 		struct edns_option* ecs_opt;
81865b390aaSDag-Erling Smørgrav 		if(!subnet_new_qstate(qstate, id)) {
81965b390aaSDag-Erling Smørgrav 			qstate->return_msg = NULL;
82065b390aaSDag-Erling Smørgrav 			qstate->ext_state[id] = module_finished;
82165b390aaSDag-Erling Smørgrav 			return;
82265b390aaSDag-Erling Smørgrav 		}
82365b390aaSDag-Erling Smørgrav 
82465b390aaSDag-Erling Smørgrav 		sq = (struct subnet_qstate*)qstate->minfo[id];
82565b390aaSDag-Erling Smørgrav 
82665b390aaSDag-Erling Smørgrav 		if((ecs_opt = edns_opt_list_find(
82765b390aaSDag-Erling Smørgrav 			qstate->edns_opts_front_in,
82865b390aaSDag-Erling Smørgrav 			qstate->env->cfg->client_subnet_opcode))) {
82965b390aaSDag-Erling Smørgrav 			if(!parse_subnet_option(ecs_opt, &sq->ecs_client_in)) {
83065b390aaSDag-Erling Smørgrav 				/* Wrongly formatted ECS option. RFC mandates to
83165b390aaSDag-Erling Smørgrav 				 * return FORMERROR. */
83265b390aaSDag-Erling Smørgrav 				qstate->return_rcode = LDNS_RCODE_FORMERR;
83365b390aaSDag-Erling Smørgrav 				qstate->ext_state[id] = module_finished;
83465b390aaSDag-Erling Smørgrav 				return;
83565b390aaSDag-Erling Smørgrav 			}
836865f46b2SCy Schubert 			subnet_log_print("query has edns subnet", ecs_opt);
83765b390aaSDag-Erling Smørgrav 			sq->subnet_downstream = 1;
83865b390aaSDag-Erling Smørgrav 		}
83965b390aaSDag-Erling Smørgrav 		else if(qstate->mesh_info->reply_list) {
84065b390aaSDag-Erling Smørgrav 			subnet_option_from_ss(
841865f46b2SCy Schubert 				&qstate->mesh_info->reply_list->query_reply.client_addr,
84265b390aaSDag-Erling Smørgrav 				&sq->ecs_client_in, qstate->env->cfg);
84365b390aaSDag-Erling Smørgrav 		}
8448f76bb7dSCy Schubert 		else if(qstate->client_addr.ss_family != AF_UNSPEC) {
8458f76bb7dSCy Schubert 			subnet_option_from_ss(
8468f76bb7dSCy Schubert 				&qstate->client_addr,
8478f76bb7dSCy Schubert 				&sq->ecs_client_in, qstate->env->cfg);
8488f76bb7dSCy Schubert 		}
84965b390aaSDag-Erling Smørgrav 
85065b390aaSDag-Erling Smørgrav 		if(sq->ecs_client_in.subnet_validdata == 0) {
85165b390aaSDag-Erling Smørgrav 			/* No clients are interested in result or we could not
85265b390aaSDag-Erling Smørgrav 			 * parse it, we don't do client subnet */
85365b390aaSDag-Erling Smørgrav 			sq->ecs_server_out.subnet_validdata = 0;
8545469a995SCy Schubert 			verbose(VERB_ALGO, "subnetcache: pass to next module");
85565b390aaSDag-Erling Smørgrav 			qstate->ext_state[id] = module_wait_module;
85665b390aaSDag-Erling Smørgrav 			return;
85765b390aaSDag-Erling Smørgrav 		}
85865b390aaSDag-Erling Smørgrav 
859e86b9096SDag-Erling Smørgrav 		/* Limit to minimum allowed source mask */
860e86b9096SDag-Erling Smørgrav 		if(sq->ecs_client_in.subnet_source_mask != 0 && (
861e86b9096SDag-Erling Smørgrav 			(sq->ecs_client_in.subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4 &&
862e86b9096SDag-Erling Smørgrav 			 sq->ecs_client_in.subnet_source_mask < qstate->env->cfg->min_client_subnet_ipv4) ||
863e86b9096SDag-Erling Smørgrav 			(sq->ecs_client_in.subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP6 &&
864e86b9096SDag-Erling Smørgrav 			 sq->ecs_client_in.subnet_source_mask < qstate->env->cfg->min_client_subnet_ipv6))) {
865e86b9096SDag-Erling Smørgrav 				qstate->return_rcode = LDNS_RCODE_REFUSED;
866e86b9096SDag-Erling Smørgrav 				qstate->ext_state[id] = module_finished;
867e86b9096SDag-Erling Smørgrav 				return;
868e86b9096SDag-Erling Smørgrav 		}
869e86b9096SDag-Erling Smørgrav 
8700a92a9fcSCy Schubert 		if(!sq->started_no_cache_lookup && !qstate->blacklist) {
87165b390aaSDag-Erling Smørgrav 			lock_rw_wrlock(&sne->biglock);
8728f76bb7dSCy Schubert 			if(qstate->mesh_info->reply_list &&
8738f76bb7dSCy Schubert 				lookup_and_reply(qstate, id, sq,
8748f76bb7dSCy Schubert 				qstate->env->cfg->prefetch)) {
8754c75e3aaSDag-Erling Smørgrav 				sne->num_msg_cache++;
87665b390aaSDag-Erling Smørgrav 				lock_rw_unlock(&sne->biglock);
8775469a995SCy Schubert 				verbose(VERB_QUERY, "subnetcache: answered from cache");
87865b390aaSDag-Erling Smørgrav 				qstate->ext_state[id] = module_finished;
87965b390aaSDag-Erling Smørgrav 
880a39a5a69SCy Schubert 				subnet_ecs_opt_list_append(&sq->ecs_client_out,
8810a92a9fcSCy Schubert 					&qstate->edns_opts_front_out, qstate,
8820a92a9fcSCy Schubert 					qstate->region);
883865f46b2SCy Schubert 				if(verbosity >= VERB_ALGO) {
884865f46b2SCy Schubert 					subnet_log_print("reply has edns subnet",
885865f46b2SCy Schubert 						edns_opt_list_find(
886865f46b2SCy Schubert 						qstate->edns_opts_front_out,
887865f46b2SCy Schubert 						qstate->env->cfg->
888865f46b2SCy Schubert 						client_subnet_opcode));
889865f46b2SCy Schubert 				}
89065b390aaSDag-Erling Smørgrav 				return;
89165b390aaSDag-Erling Smørgrav 			}
89265b390aaSDag-Erling Smørgrav 			lock_rw_unlock(&sne->biglock);
8930a92a9fcSCy Schubert 		}
89465b390aaSDag-Erling Smørgrav 
89565b390aaSDag-Erling Smørgrav 		sq->ecs_server_out.subnet_addr_fam =
89665b390aaSDag-Erling Smørgrav 			sq->ecs_client_in.subnet_addr_fam;
89765b390aaSDag-Erling Smørgrav 		sq->ecs_server_out.subnet_source_mask =
89865b390aaSDag-Erling Smørgrav 			sq->ecs_client_in.subnet_source_mask;
89965b390aaSDag-Erling Smørgrav 		/* Limit source prefix to configured maximum */
90065b390aaSDag-Erling Smørgrav 		if(sq->ecs_server_out.subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4
90165b390aaSDag-Erling Smørgrav 			&& sq->ecs_server_out.subnet_source_mask >
90265b390aaSDag-Erling Smørgrav 			qstate->env->cfg->max_client_subnet_ipv4)
90365b390aaSDag-Erling Smørgrav 			sq->ecs_server_out.subnet_source_mask =
90465b390aaSDag-Erling Smørgrav 				qstate->env->cfg->max_client_subnet_ipv4;
90565b390aaSDag-Erling Smørgrav 		else if(sq->ecs_server_out.subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP6
90665b390aaSDag-Erling Smørgrav 			&& sq->ecs_server_out.subnet_source_mask >
90765b390aaSDag-Erling Smørgrav 			qstate->env->cfg->max_client_subnet_ipv6)
90865b390aaSDag-Erling Smørgrav 			sq->ecs_server_out.subnet_source_mask =
90965b390aaSDag-Erling Smørgrav 				qstate->env->cfg->max_client_subnet_ipv6;
91065b390aaSDag-Erling Smørgrav 		/* Safe to copy completely, even if the source is limited by the
911a39a5a69SCy Schubert 		 * configuration. subnet_ecs_opt_list_append() will limit the address.
91265b390aaSDag-Erling Smørgrav 		 * */
91365b390aaSDag-Erling Smørgrav 		memcpy(&sq->ecs_server_out.subnet_addr,
91465b390aaSDag-Erling Smørgrav 			sq->ecs_client_in.subnet_addr, INET6_SIZE);
91565b390aaSDag-Erling Smørgrav 		sq->ecs_server_out.subnet_scope_mask = 0;
91665b390aaSDag-Erling Smørgrav 		sq->ecs_server_out.subnet_validdata = 1;
91765b390aaSDag-Erling Smørgrav 		if(sq->ecs_server_out.subnet_source_mask != 0 &&
9188a384985SDag-Erling Smørgrav 			qstate->env->cfg->client_subnet_always_forward &&
91965b390aaSDag-Erling Smørgrav 			sq->subnet_downstream)
92065b390aaSDag-Erling Smørgrav 			/* ECS specific data required, do not look at the global
92165b390aaSDag-Erling Smørgrav 			 * cache in other modules. */
92265b390aaSDag-Erling Smørgrav 			qstate->no_cache_lookup = 1;
92365b390aaSDag-Erling Smørgrav 
92465b390aaSDag-Erling Smørgrav 		/* pass request to next module */
92565b390aaSDag-Erling Smørgrav 		verbose(VERB_ALGO,
9265469a995SCy Schubert 			"subnetcache: not found in cache. pass to next module");
92765b390aaSDag-Erling Smørgrav 		qstate->ext_state[id] = module_wait_module;
92865b390aaSDag-Erling Smørgrav 		return;
92965b390aaSDag-Erling Smørgrav 	}
93065b390aaSDag-Erling Smørgrav 	/* Query handed back by next module, we have a 'final' answer */
93165b390aaSDag-Erling Smørgrav 	if(sq && event == module_event_moddone) {
93265b390aaSDag-Erling Smørgrav 		qstate->ext_state[id] = eval_response(qstate, id, sq);
9334c75e3aaSDag-Erling Smørgrav 		if(qstate->ext_state[id] == module_finished &&
9344c75e3aaSDag-Erling Smørgrav 			qstate->return_msg) {
935a39a5a69SCy Schubert 			subnet_ecs_opt_list_append(&sq->ecs_client_out,
9360a92a9fcSCy Schubert 				&qstate->edns_opts_front_out, qstate,
9370a92a9fcSCy Schubert 				qstate->region);
938865f46b2SCy Schubert 			if(verbosity >= VERB_ALGO) {
939865f46b2SCy Schubert 				subnet_log_print("reply has edns subnet",
940865f46b2SCy Schubert 					edns_opt_list_find(
941865f46b2SCy Schubert 					qstate->edns_opts_front_out,
942865f46b2SCy Schubert 					qstate->env->cfg->
943865f46b2SCy Schubert 					client_subnet_opcode));
944865f46b2SCy Schubert 			}
94565b390aaSDag-Erling Smørgrav 		}
946e86b9096SDag-Erling Smørgrav 		qstate->no_cache_store = sq->started_no_cache_store;
9470a92a9fcSCy Schubert 		qstate->no_cache_lookup = sq->started_no_cache_lookup;
94865b390aaSDag-Erling Smørgrav 		return;
94965b390aaSDag-Erling Smørgrav 	}
95065b390aaSDag-Erling Smørgrav 	if(sq && outbound) {
95165b390aaSDag-Erling Smørgrav 		return;
95265b390aaSDag-Erling Smørgrav 	}
95365b390aaSDag-Erling Smørgrav 	/* We are being revisited */
95465b390aaSDag-Erling Smørgrav 	if(event == module_event_pass || event == module_event_new) {
95565b390aaSDag-Erling Smørgrav 		/* Just pass it on, we already did the work */
9565469a995SCy Schubert 		verbose(VERB_ALGO, "subnetcache: pass to next module");
95765b390aaSDag-Erling Smørgrav 		qstate->ext_state[id] = module_wait_module;
95865b390aaSDag-Erling Smørgrav 		return;
95965b390aaSDag-Erling Smørgrav 	}
96065b390aaSDag-Erling Smørgrav 	if(!sq && (event == module_event_moddone)) {
96165b390aaSDag-Erling Smørgrav 		/* during priming, module done but we never started */
96265b390aaSDag-Erling Smørgrav 		qstate->ext_state[id] = module_finished;
96365b390aaSDag-Erling Smørgrav 		return;
96465b390aaSDag-Erling Smørgrav 	}
9655469a995SCy Schubert 	log_err("subnetcache: bad event %s", strmodulevent(event));
96665b390aaSDag-Erling Smørgrav 	qstate->ext_state[id] = module_error;
96765b390aaSDag-Erling Smørgrav 	return;
96865b390aaSDag-Erling Smørgrav }
96965b390aaSDag-Erling Smørgrav 
97065b390aaSDag-Erling Smørgrav void
subnetmod_clear(struct module_qstate * ATTR_UNUSED (qstate),int ATTR_UNUSED (id))97165b390aaSDag-Erling Smørgrav subnetmod_clear(struct module_qstate *ATTR_UNUSED(qstate),
97265b390aaSDag-Erling Smørgrav 	int ATTR_UNUSED(id))
97365b390aaSDag-Erling Smørgrav {
97465b390aaSDag-Erling Smørgrav 	/* qstate has no data outside region */
97565b390aaSDag-Erling Smørgrav }
97665b390aaSDag-Erling Smørgrav 
97765b390aaSDag-Erling Smørgrav void
subnetmod_inform_super(struct module_qstate * ATTR_UNUSED (qstate),int ATTR_UNUSED (id),struct module_qstate * ATTR_UNUSED (super))97865b390aaSDag-Erling Smørgrav subnetmod_inform_super(struct module_qstate *ATTR_UNUSED(qstate),
97965b390aaSDag-Erling Smørgrav 	int ATTR_UNUSED(id), struct module_qstate *ATTR_UNUSED(super))
98065b390aaSDag-Erling Smørgrav {
98165b390aaSDag-Erling Smørgrav 	/* Not used */
98265b390aaSDag-Erling Smørgrav }
98365b390aaSDag-Erling Smørgrav 
98465b390aaSDag-Erling Smørgrav size_t
subnetmod_get_mem(struct module_env * env,int id)98565b390aaSDag-Erling Smørgrav subnetmod_get_mem(struct module_env *env, int id)
98665b390aaSDag-Erling Smørgrav {
98765b390aaSDag-Erling Smørgrav 	struct subnet_env *sn_env = env->modinfo[id];
98865b390aaSDag-Erling Smørgrav 	if (!sn_env) return 0;
98965b390aaSDag-Erling Smørgrav 	return sizeof(*sn_env) +
99065b390aaSDag-Erling Smørgrav 		slabhash_get_mem(sn_env->subnet_msg_cache) +
991c7f4d7adSDag-Erling Smørgrav 		ecs_whitelist_get_mem(sn_env->whitelist);
99265b390aaSDag-Erling Smørgrav }
99365b390aaSDag-Erling Smørgrav 
99465b390aaSDag-Erling Smørgrav /**
99565b390aaSDag-Erling Smørgrav  * The module function block
99665b390aaSDag-Erling Smørgrav  */
99765b390aaSDag-Erling Smørgrav static struct module_func_block subnetmod_block = {
9985469a995SCy Schubert 	"subnetcache", &subnetmod_init, &subnetmod_deinit, &subnetmod_operate,
99965b390aaSDag-Erling Smørgrav 	&subnetmod_inform_super, &subnetmod_clear, &subnetmod_get_mem
100065b390aaSDag-Erling Smørgrav };
100165b390aaSDag-Erling Smørgrav 
100265b390aaSDag-Erling Smørgrav struct module_func_block*
subnetmod_get_funcblock(void)100365b390aaSDag-Erling Smørgrav subnetmod_get_funcblock(void)
100465b390aaSDag-Erling Smørgrav {
100565b390aaSDag-Erling Smørgrav 	return &subnetmod_block;
100665b390aaSDag-Erling Smørgrav }
100765b390aaSDag-Erling Smørgrav 
100865b390aaSDag-Erling Smørgrav /** Wrappers for static functions to unit test */
100965b390aaSDag-Erling Smørgrav size_t
unittest_wrapper_subnetmod_sizefunc(void * elemptr)101065b390aaSDag-Erling Smørgrav unittest_wrapper_subnetmod_sizefunc(void *elemptr)
101165b390aaSDag-Erling Smørgrav {
101265b390aaSDag-Erling Smørgrav 	return sizefunc(elemptr);
101365b390aaSDag-Erling Smørgrav }
101465b390aaSDag-Erling Smørgrav 
101565b390aaSDag-Erling Smørgrav #endif  /* CLIENT_SUBNET */
1016