xref: /openbsd-src/usr.sbin/unbound/edns-subnet/subnetmod.c (revision 98bc733b08604094f4138174a0ee0bb9faaca4bd)
12be9e038Ssthen /*
22be9e038Ssthen  * edns-subnet/subnetmod.c - edns subnet module. Must be called before validator
32be9e038Ssthen  * and iterator.
42be9e038Ssthen  *
52be9e038Ssthen  * Copyright (c) 2013, NLnet Labs. All rights reserved.
62be9e038Ssthen  *
72be9e038Ssthen  * This software is open source.
82be9e038Ssthen  *
92be9e038Ssthen  * Redistribution and use in source and binary forms, with or without
102be9e038Ssthen  * modification, are permitted provided that the following conditions
112be9e038Ssthen  * are met:
122be9e038Ssthen  *
132be9e038Ssthen  * Redistributions of source code must retain the above copyright notice,
142be9e038Ssthen  * this list of conditions and the following disclaimer.
152be9e038Ssthen  *
162be9e038Ssthen  * Redistributions in binary form must reproduce the above copyright notice,
172be9e038Ssthen  * this list of conditions and the following disclaimer in the documentation
182be9e038Ssthen  * and/or other materials provided with the distribution.
192be9e038Ssthen  *
202be9e038Ssthen  * Neither the name of the NLNET LABS nor the names of its contributors may
212be9e038Ssthen  * be used to endorse or promote products derived from this software without
222be9e038Ssthen  * specific prior written permission.
232be9e038Ssthen  *
242be9e038Ssthen  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
252be9e038Ssthen  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
262be9e038Ssthen  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
272be9e038Ssthen  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
282be9e038Ssthen  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
292be9e038Ssthen  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
302be9e038Ssthen  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
312be9e038Ssthen  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
322be9e038Ssthen  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
332be9e038Ssthen  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
342be9e038Ssthen  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
352be9e038Ssthen  */
362be9e038Ssthen  /**
372be9e038Ssthen  * \file
382be9e038Ssthen  * subnet module for unbound.
392be9e038Ssthen  */
402be9e038Ssthen 
412be9e038Ssthen #include "config.h"
422be9e038Ssthen 
432be9e038Ssthen #ifdef CLIENT_SUBNET /* keeps splint happy */
442be9e038Ssthen 
452be9e038Ssthen #include "edns-subnet/subnetmod.h"
462be9e038Ssthen #include "edns-subnet/edns-subnet.h"
472be9e038Ssthen #include "edns-subnet/addrtree.h"
482be9e038Ssthen #include "edns-subnet/subnet-whitelist.h"
492be9e038Ssthen 
502be9e038Ssthen #include "services/mesh.h"
512be9e038Ssthen #include "services/cache/dns.h"
522be9e038Ssthen #include "util/module.h"
532be9e038Ssthen #include "util/regional.h"
542be9e038Ssthen #include "util/storage/slabhash.h"
552be9e038Ssthen #include "util/config_file.h"
562be9e038Ssthen #include "util/data/msgreply.h"
572be9e038Ssthen #include "sldns/sbuffer.h"
5845872187Ssthen #include "sldns/wire2str.h"
593150e5f6Ssthen #include "iterator/iter_utils.h"
602bdc0ed1Ssthen #ifdef USE_CACHEDB
612bdc0ed1Ssthen #include "cachedb/cachedb.h"
622bdc0ed1Ssthen #endif
632be9e038Ssthen 
642be9e038Ssthen /** externally called */
652be9e038Ssthen void
662be9e038Ssthen subnet_data_delete(void *d, void *ATTR_UNUSED(arg))
672be9e038Ssthen {
682be9e038Ssthen 	struct subnet_msg_cache_data *r;
692be9e038Ssthen 	r = (struct subnet_msg_cache_data*)d;
702be9e038Ssthen 	addrtree_delete(r->tree4);
712be9e038Ssthen 	addrtree_delete(r->tree6);
722be9e038Ssthen 	free(r);
732be9e038Ssthen }
742be9e038Ssthen 
752be9e038Ssthen /** externally called */
762be9e038Ssthen size_t
772be9e038Ssthen msg_cache_sizefunc(void *k, void *d)
782be9e038Ssthen {
792be9e038Ssthen 	struct msgreply_entry *q = (struct msgreply_entry*)k;
802be9e038Ssthen 	struct subnet_msg_cache_data *r = (struct subnet_msg_cache_data*)d;
812be9e038Ssthen 	size_t s = sizeof(struct msgreply_entry)
822be9e038Ssthen 		+ sizeof(struct subnet_msg_cache_data)
832be9e038Ssthen 		+ q->key.qname_len + lock_get_mem(&q->entry.lock);
842be9e038Ssthen 	s += addrtree_size(r->tree4);
852be9e038Ssthen 	s += addrtree_size(r->tree6);
862be9e038Ssthen 	return s;
872be9e038Ssthen }
882be9e038Ssthen 
892be9e038Ssthen /** new query for ecs module */
902be9e038Ssthen static int
912be9e038Ssthen subnet_new_qstate(struct module_qstate *qstate, int id)
922be9e038Ssthen {
932be9e038Ssthen 	struct subnet_qstate *sq = (struct subnet_qstate*)regional_alloc(
942be9e038Ssthen 		qstate->region, sizeof(struct subnet_qstate));
952be9e038Ssthen 	if(!sq)
962be9e038Ssthen 		return 0;
972be9e038Ssthen 	qstate->minfo[id] = sq;
982be9e038Ssthen 	memset(sq, 0, sizeof(*sq));
993150e5f6Ssthen 	sq->started_no_cache_store = qstate->no_cache_store;
100d1e2768aSsthen 	sq->started_no_cache_lookup = qstate->no_cache_lookup;
1012be9e038Ssthen 	return 1;
1022be9e038Ssthen }
1032be9e038Ssthen 
1042be9e038Ssthen /** Add ecs struct to edns list, after parsing it to wire format. */
1050bdb4f62Ssthen void
1060bdb4f62Ssthen subnet_ecs_opt_list_append(struct ecs_data* ecs, struct edns_option** list,
107d1e2768aSsthen 	struct module_qstate *qstate, struct regional *region)
1082be9e038Ssthen {
1092be9e038Ssthen 	size_t sn_octs, sn_octs_remainder;
1102be9e038Ssthen 	sldns_buffer* buf = qstate->env->scratch_buffer;
1112be9e038Ssthen 
1122be9e038Ssthen 	if(ecs->subnet_validdata) {
1132be9e038Ssthen 		log_assert(ecs->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4 ||
1142be9e038Ssthen 			ecs->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP6);
1152be9e038Ssthen 		log_assert(ecs->subnet_addr_fam != EDNSSUBNET_ADDRFAM_IP4 ||
1162be9e038Ssthen 			ecs->subnet_source_mask <=  INET_SIZE*8);
1172be9e038Ssthen 		log_assert(ecs->subnet_addr_fam != EDNSSUBNET_ADDRFAM_IP6 ||
1182be9e038Ssthen 			ecs->subnet_source_mask <= INET6_SIZE*8);
1192be9e038Ssthen 
1202be9e038Ssthen 		sn_octs = ecs->subnet_source_mask / 8;
1212be9e038Ssthen 		sn_octs_remainder =
1222be9e038Ssthen 			(size_t)((ecs->subnet_source_mask % 8)>0?1:0);
1232be9e038Ssthen 
1242be9e038Ssthen 		log_assert(sn_octs + sn_octs_remainder <= INET6_SIZE);
1252be9e038Ssthen 
1262be9e038Ssthen 		sldns_buffer_clear(buf);
1272be9e038Ssthen 		sldns_buffer_write_u16(buf, ecs->subnet_addr_fam);
1282be9e038Ssthen 		sldns_buffer_write_u8(buf, ecs->subnet_source_mask);
1292be9e038Ssthen 		sldns_buffer_write_u8(buf, ecs->subnet_scope_mask);
1302be9e038Ssthen 		sldns_buffer_write(buf, ecs->subnet_addr, sn_octs);
1312be9e038Ssthen 		if(sn_octs_remainder)
1322be9e038Ssthen 			sldns_buffer_write_u8(buf, ecs->subnet_addr[sn_octs] &
1332be9e038Ssthen 				~(0xFF >> (ecs->subnet_source_mask % 8)));
1342be9e038Ssthen 		sldns_buffer_flip(buf);
1352be9e038Ssthen 
1362be9e038Ssthen 		edns_opt_list_append(list,
1372be9e038Ssthen 				qstate->env->cfg->client_subnet_opcode,
1382be9e038Ssthen 				sn_octs + sn_octs_remainder + 4,
139d1e2768aSsthen 				sldns_buffer_begin(buf), region);
1402be9e038Ssthen 	}
1412be9e038Ssthen }
1422be9e038Ssthen 
1432be9e038Ssthen int ecs_whitelist_check(struct query_info* qinfo,
1442be9e038Ssthen 	uint16_t ATTR_UNUSED(flags), struct module_qstate* qstate,
1452be9e038Ssthen 	struct sockaddr_storage* addr, socklen_t addrlen,
1462be9e038Ssthen 	uint8_t* ATTR_UNUSED(zone), size_t ATTR_UNUSED(zonelen),
147d1e2768aSsthen 	struct regional *region, int id, void* ATTR_UNUSED(cbargs))
1482be9e038Ssthen {
1492be9e038Ssthen 	struct subnet_qstate *sq;
1502be9e038Ssthen 	struct subnet_env *sn_env;
1512be9e038Ssthen 
1522be9e038Ssthen 	if(!(sq=(struct subnet_qstate*)qstate->minfo[id]))
1532be9e038Ssthen 		return 1;
1542be9e038Ssthen 	sn_env = (struct subnet_env*)qstate->env->modinfo[id];
1552be9e038Ssthen 
1562be9e038Ssthen 	/* Cache by default, might be disabled after parsing EDNS option
1572be9e038Ssthen 	 * received from nameserver. */
1582bdc0ed1Ssthen 	if(!iter_stub_fwd_no_cache(qstate, &qstate->qinfo, NULL, NULL, NULL, 0)) {
1592be9e038Ssthen 		qstate->no_cache_store = 0;
1603150e5f6Ssthen 	}
1612be9e038Ssthen 
162d896b962Ssthen 	sq->subnet_sent_no_subnet = 0;
1632be9e038Ssthen 	if(sq->ecs_server_out.subnet_validdata && ((sq->subnet_downstream &&
1642be9e038Ssthen 		qstate->env->cfg->client_subnet_always_forward) ||
1652be9e038Ssthen 		ecs_is_whitelisted(sn_env->whitelist,
1662be9e038Ssthen 		addr, addrlen, qinfo->qname, qinfo->qname_len,
1672be9e038Ssthen 		qinfo->qclass))) {
1682be9e038Ssthen 		/* Address on whitelist or client query contains ECS option, we
1692be9e038Ssthen 		 * want to sent out ECS. Only add option if it is not already
1702be9e038Ssthen 		 * set. */
1710bdb4f62Ssthen 		if(!edns_opt_list_find(qstate->edns_opts_back_out,
1720bdb4f62Ssthen 			qstate->env->cfg->client_subnet_opcode)) {
173d896b962Ssthen 			/* if the client is not wanting an EDNS subnet option,
174d896b962Ssthen 			 * omit it and store that we omitted it but actually
175d896b962Ssthen 			 * are doing EDNS subnet to the server. */
176d896b962Ssthen 			if(sq->ecs_server_out.subnet_source_mask == 0) {
177d896b962Ssthen 				sq->subnet_sent_no_subnet = 1;
178d896b962Ssthen 				sq->subnet_sent = 0;
179d896b962Ssthen 				return 1;
180d896b962Ssthen 			}
1810bdb4f62Ssthen 			subnet_ecs_opt_list_append(&sq->ecs_server_out,
182d1e2768aSsthen 				&qstate->edns_opts_back_out, qstate, region);
1830bdb4f62Ssthen 		}
1842be9e038Ssthen 		sq->subnet_sent = 1;
1852be9e038Ssthen 	}
1860bdb4f62Ssthen 	else {
1872be9e038Ssthen 		/* Outgoing ECS option is set, but we don't want to sent it to
1882be9e038Ssthen 		 * this address, remove option. */
1890bdb4f62Ssthen 		if(edns_opt_list_find(qstate->edns_opts_back_out,
1900bdb4f62Ssthen 			qstate->env->cfg->client_subnet_opcode)) {
1912be9e038Ssthen 			edns_opt_list_remove(&qstate->edns_opts_back_out,
1922be9e038Ssthen 				qstate->env->cfg->client_subnet_opcode);
1930bdb4f62Ssthen 		}
1942be9e038Ssthen 		sq->subnet_sent = 0;
1952be9e038Ssthen 	}
1962be9e038Ssthen 	return 1;
1972be9e038Ssthen }
1982be9e038Ssthen 
1992be9e038Ssthen 
2003150e5f6Ssthen void
2013150e5f6Ssthen subnet_markdel(void* key)
2023150e5f6Ssthen {
2033150e5f6Ssthen 	struct msgreply_entry *e = (struct msgreply_entry*)key;
2043150e5f6Ssthen 	e->key.qtype = 0;
2053150e5f6Ssthen 	e->key.qclass = 0;
2063150e5f6Ssthen }
2073150e5f6Ssthen 
2082be9e038Ssthen int
2092be9e038Ssthen subnetmod_init(struct module_env *env, int id)
2102be9e038Ssthen {
2112be9e038Ssthen 	struct subnet_env *sn_env = (struct subnet_env*)calloc(1,
2122be9e038Ssthen 		sizeof(struct subnet_env));
2132be9e038Ssthen 	if(!sn_env) {
2142be9e038Ssthen 		log_err("malloc failure");
2152be9e038Ssthen 		return 0;
2162be9e038Ssthen 	}
2172be9e038Ssthen 	alloc_init(&sn_env->alloc, NULL, 0);
2182be9e038Ssthen 	env->modinfo[id] = (void*)sn_env;
2198b7325afSsthen 
2208b7325afSsthen 	/* Warn that serve-expired and prefetch do not work with the subnet
2218b7325afSsthen 	 * module cache. */
2228b7325afSsthen 	if(env->cfg->serve_expired)
2238b7325afSsthen 		log_warn(
2248b7325afSsthen 			"subnetcache: serve-expired is set but not working "
2258b7325afSsthen 			"for data originating from the subnet module cache.");
2268b7325afSsthen 	if(env->cfg->prefetch)
2278b7325afSsthen 		log_warn(
2288b7325afSsthen 			"subnetcache: prefetch is set but not working "
2298b7325afSsthen 			"for data originating from the subnet module cache.");
2302be9e038Ssthen 	/* Copy msg_cache settings */
2312be9e038Ssthen 	sn_env->subnet_msg_cache = slabhash_create(env->cfg->msg_cache_slabs,
2322be9e038Ssthen 		HASH_DEFAULT_STARTARRAY, env->cfg->msg_cache_size,
2332be9e038Ssthen 		msg_cache_sizefunc, query_info_compare, query_entry_delete,
2342be9e038Ssthen 		subnet_data_delete, NULL);
2353150e5f6Ssthen 	slabhash_setmarkdel(sn_env->subnet_msg_cache, &subnet_markdel);
2362be9e038Ssthen 	if(!sn_env->subnet_msg_cache) {
237191f22c6Ssthen 		log_err("subnetcache: could not create cache");
2382be9e038Ssthen 		free(sn_env);
2392be9e038Ssthen 		env->modinfo[id] = NULL;
2402be9e038Ssthen 		return 0;
2412be9e038Ssthen 	}
2422be9e038Ssthen 	/* whitelist for edns subnet capable servers */
2432be9e038Ssthen 	sn_env->whitelist = ecs_whitelist_create();
2442be9e038Ssthen 	if(!sn_env->whitelist ||
2452be9e038Ssthen 		!ecs_whitelist_apply_cfg(sn_env->whitelist, env->cfg)) {
246191f22c6Ssthen 		log_err("subnetcache: could not create ECS whitelist");
2472be9e038Ssthen 		slabhash_delete(sn_env->subnet_msg_cache);
2482be9e038Ssthen 		free(sn_env);
2492be9e038Ssthen 		env->modinfo[id] = NULL;
2502be9e038Ssthen 		return 0;
2512be9e038Ssthen 	}
2522be9e038Ssthen 
253191f22c6Ssthen 	verbose(VERB_QUERY, "subnetcache: option registered (%d)",
2542be9e038Ssthen 		env->cfg->client_subnet_opcode);
2552be9e038Ssthen 	/* Create new mesh state for all queries. */
2562be9e038Ssthen 	env->unique_mesh = 1;
2572be9e038Ssthen 	if(!edns_register_option(env->cfg->client_subnet_opcode,
2582be9e038Ssthen 		env->cfg->client_subnet_always_forward /* bypass cache */,
2590bdb4f62Ssthen 		1 /* no aggregation */, env)) {
260191f22c6Ssthen 		log_err("subnetcache: could not register opcode");
2612be9e038Ssthen 		ecs_whitelist_delete(sn_env->whitelist);
2622be9e038Ssthen 		slabhash_delete(sn_env->subnet_msg_cache);
2632be9e038Ssthen 		free(sn_env);
2642be9e038Ssthen 		env->modinfo[id] = NULL;
2652be9e038Ssthen 		return 0;
2662be9e038Ssthen 	}
2672be9e038Ssthen 	inplace_cb_register((void*)ecs_whitelist_check, inplace_cb_query, NULL,
2682be9e038Ssthen 		env, id);
2692be9e038Ssthen 	inplace_cb_register((void*)ecs_edns_back_parsed,
2702be9e038Ssthen 		inplace_cb_edns_back_parsed, NULL, env, id);
2712be9e038Ssthen 	inplace_cb_register((void*)ecs_query_response,
2722be9e038Ssthen 		inplace_cb_query_response, NULL, env, id);
2732be9e038Ssthen 	lock_rw_init(&sn_env->biglock);
2742be9e038Ssthen 	return 1;
2752be9e038Ssthen }
2762be9e038Ssthen 
2772be9e038Ssthen void
2782be9e038Ssthen subnetmod_deinit(struct module_env *env, int id)
2792be9e038Ssthen {
2802be9e038Ssthen 	struct subnet_env *sn_env;
2812be9e038Ssthen 	if(!env || !env->modinfo[id])
2822be9e038Ssthen 		return;
2832be9e038Ssthen 	sn_env = (struct subnet_env*)env->modinfo[id];
2842be9e038Ssthen 	lock_rw_destroy(&sn_env->biglock);
2852be9e038Ssthen 	inplace_cb_delete(env, inplace_cb_edns_back_parsed, id);
2862be9e038Ssthen 	inplace_cb_delete(env, inplace_cb_query, id);
2872be9e038Ssthen 	inplace_cb_delete(env, inplace_cb_query_response, id);
2882be9e038Ssthen 	ecs_whitelist_delete(sn_env->whitelist);
2892be9e038Ssthen 	slabhash_delete(sn_env->subnet_msg_cache);
2902be9e038Ssthen 	alloc_clear(&sn_env->alloc);
2912be9e038Ssthen 	free(sn_env);
2922be9e038Ssthen 	env->modinfo[id] = NULL;
2932be9e038Ssthen }
2942be9e038Ssthen 
2952be9e038Ssthen /** Tells client that upstream has no/improper support */
2962be9e038Ssthen static void
2972be9e038Ssthen cp_edns_bad_response(struct ecs_data *target, struct ecs_data *source)
2982be9e038Ssthen {
2992be9e038Ssthen 	target->subnet_scope_mask  = 0;
3002be9e038Ssthen 	target->subnet_source_mask = source->subnet_source_mask;
3012be9e038Ssthen 	target->subnet_addr_fam    = source->subnet_addr_fam;
3022be9e038Ssthen 	memcpy(target->subnet_addr, source->subnet_addr, INET6_SIZE);
3032be9e038Ssthen 	target->subnet_validdata = 1;
3042be9e038Ssthen }
3052be9e038Ssthen 
3062be9e038Ssthen static void
3072be9e038Ssthen delfunc(void *envptr, void *elemptr) {
3082be9e038Ssthen 	struct reply_info *elem = (struct reply_info *)elemptr;
3092be9e038Ssthen 	struct subnet_env *env = (struct subnet_env *)envptr;
3102be9e038Ssthen 	reply_info_parsedelete(elem, &env->alloc);
3112be9e038Ssthen }
3122be9e038Ssthen 
3132be9e038Ssthen static size_t
3142be9e038Ssthen sizefunc(void *elemptr) {
3152be9e038Ssthen 	struct reply_info *elem  = (struct reply_info *)elemptr;
3162bdc0ed1Ssthen 	size_t s = sizeof (struct reply_info) - sizeof (struct rrset_ref)
3172be9e038Ssthen 		+ elem->rrset_count * sizeof (struct rrset_ref)
3182be9e038Ssthen 		+ elem->rrset_count * sizeof (struct ub_packed_rrset_key *);
3192bdc0ed1Ssthen 	size_t i;
3202bdc0ed1Ssthen 	for (i = 0; i < elem->rrset_count; i++) {
3212bdc0ed1Ssthen 		struct ub_packed_rrset_key *key = elem->rrsets[i];
3222bdc0ed1Ssthen 		struct packed_rrset_data *data = key->entry.data;
3232bdc0ed1Ssthen 		s += ub_rrset_sizefunc(key, data);
3242bdc0ed1Ssthen 	}
3252bdc0ed1Ssthen 	if(elem->reason_bogus_str)
3262bdc0ed1Ssthen 		s += strlen(elem->reason_bogus_str)+1;
3272bdc0ed1Ssthen 	return s;
3282be9e038Ssthen }
3292be9e038Ssthen 
3302be9e038Ssthen /**
3312be9e038Ssthen  * Select tree from cache entry based on edns data.
3322be9e038Ssthen  * If for address family not present it will create a new one.
3332be9e038Ssthen  * NULL on failure to create. */
3342be9e038Ssthen static struct addrtree*
3352be9e038Ssthen get_tree(struct subnet_msg_cache_data *data, struct ecs_data *edns,
3362be9e038Ssthen 	struct subnet_env *env, struct config_file* cfg)
3372be9e038Ssthen {
3382be9e038Ssthen 	struct addrtree *tree;
3392be9e038Ssthen 	if (edns->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4) {
3402be9e038Ssthen 		if (!data->tree4)
3412be9e038Ssthen 			data->tree4 = addrtree_create(
3422be9e038Ssthen 				cfg->max_client_subnet_ipv4, &delfunc,
3433150e5f6Ssthen 				&sizefunc, env, cfg->max_ecs_tree_size_ipv4);
3442be9e038Ssthen 		tree = data->tree4;
3452be9e038Ssthen 	} else {
3462be9e038Ssthen 		if (!data->tree6)
3472be9e038Ssthen 			data->tree6 = addrtree_create(
3482be9e038Ssthen 				cfg->max_client_subnet_ipv6, &delfunc,
3493150e5f6Ssthen 				&sizefunc, env, cfg->max_ecs_tree_size_ipv6);
3502be9e038Ssthen 		tree = data->tree6;
3512be9e038Ssthen 	}
3522be9e038Ssthen 	return tree;
3532be9e038Ssthen }
3542be9e038Ssthen 
3552be9e038Ssthen static void
3562be9e038Ssthen update_cache(struct module_qstate *qstate, int id)
3572be9e038Ssthen {
3582be9e038Ssthen 	struct msgreply_entry *mrep_entry;
3592be9e038Ssthen 	struct addrtree *tree;
3602be9e038Ssthen 	struct reply_info *rep;
3612be9e038Ssthen 	struct query_info qinf;
3622be9e038Ssthen 	struct subnet_env *sne = qstate->env->modinfo[id];
3632be9e038Ssthen 	struct subnet_qstate *sq = (struct subnet_qstate*)qstate->minfo[id];
3642be9e038Ssthen 	struct slabhash *subnet_msg_cache = sne->subnet_msg_cache;
3652be9e038Ssthen 	struct ecs_data *edns = &sq->ecs_client_in;
3662be9e038Ssthen 	size_t i;
3672bdc0ed1Ssthen 	int only_match_scope_zero, diff_size;
3682be9e038Ssthen 
369d1e2768aSsthen 	/* We already calculated hash upon lookup (lookup_and_reply) if we were
370d1e2768aSsthen 	 * allowed to look in the ECS cache */
371d1e2768aSsthen 	hashvalue_type h = qstate->minfo[id] &&
372d1e2768aSsthen 		((struct subnet_qstate*)qstate->minfo[id])->qinfo_hash_calculated?
3732be9e038Ssthen 		((struct subnet_qstate*)qstate->minfo[id])->qinfo_hash :
3742be9e038Ssthen 		query_info_hash(&qstate->qinfo, qstate->query_flags);
3752be9e038Ssthen 	/* Step 1, general qinfo lookup */
3762be9e038Ssthen 	struct lruhash_entry* lru_entry = slabhash_lookup(subnet_msg_cache, h,
3772be9e038Ssthen 		&qstate->qinfo, 1);
378550cf4a9Ssthen 	int need_to_insert = (lru_entry == NULL);
3792be9e038Ssthen 	if (!lru_entry) {
380550cf4a9Ssthen 		void* data = calloc(1,
381550cf4a9Ssthen 			sizeof(struct subnet_msg_cache_data));
382550cf4a9Ssthen 		if(!data) {
383550cf4a9Ssthen 			log_err("malloc failed");
384550cf4a9Ssthen 			return;
385550cf4a9Ssthen 		}
3862be9e038Ssthen 		qinf = qstate->qinfo;
3872be9e038Ssthen 		qinf.qname = memdup(qstate->qinfo.qname,
3882be9e038Ssthen 			qstate->qinfo.qname_len);
3892be9e038Ssthen 		if(!qinf.qname) {
390550cf4a9Ssthen 			free(data);
3912be9e038Ssthen 			log_err("memdup failed");
3922be9e038Ssthen 			return;
3932be9e038Ssthen 		}
394550cf4a9Ssthen 		mrep_entry = query_info_entrysetup(&qinf, data, h);
3952be9e038Ssthen 		free(qinf.qname); /* if qname 'consumed', it is set to NULL */
3962be9e038Ssthen 		if (!mrep_entry) {
397550cf4a9Ssthen 			free(data);
3982be9e038Ssthen 			log_err("query_info_entrysetup failed");
3992be9e038Ssthen 			return;
4002be9e038Ssthen 		}
4012be9e038Ssthen 		lru_entry = &mrep_entry->entry;
402938a3a5eSflorian 		lock_rw_wrlock(&lru_entry->lock);
4032be9e038Ssthen 	}
404550cf4a9Ssthen 	/* lru_entry->lock is locked regardless of how we got here,
405550cf4a9Ssthen 	 * either from the slabhash_lookup, or above in the new allocated */
4062be9e038Ssthen 	/* Step 2, find the correct tree */
4072be9e038Ssthen 	if (!(tree = get_tree(lru_entry->data, edns, sne, qstate->env->cfg))) {
408550cf4a9Ssthen 		lock_rw_unlock(&lru_entry->lock);
409191f22c6Ssthen 		log_err("subnetcache: cache insertion failed");
4102be9e038Ssthen 		return;
4112be9e038Ssthen 	}
412938a3a5eSflorian 	lock_quick_lock(&sne->alloc.lock);
4132be9e038Ssthen 	rep = reply_info_copy(qstate->return_msg->rep, &sne->alloc, NULL);
414938a3a5eSflorian 	lock_quick_unlock(&sne->alloc.lock);
4152be9e038Ssthen 	if (!rep) {
416550cf4a9Ssthen 		lock_rw_unlock(&lru_entry->lock);
417191f22c6Ssthen 		log_err("subnetcache: cache insertion failed");
4182be9e038Ssthen 		return;
4192be9e038Ssthen 	}
4202be9e038Ssthen 
4212be9e038Ssthen 	/* store RRsets */
4222be9e038Ssthen 	for(i=0; i<rep->rrset_count; i++) {
4232be9e038Ssthen 		rep->ref[i].key = rep->rrsets[i];
4242be9e038Ssthen 		rep->ref[i].id = rep->rrsets[i]->id;
4252be9e038Ssthen 	}
4262be9e038Ssthen 	reply_info_set_ttls(rep, *qstate->env->now);
4272bdc0ed1Ssthen 	reply_info_sortref(rep);
4282be9e038Ssthen 	rep->flags |= (BIT_RA | BIT_QR); /* fix flags to be sensible for */
4292be9e038Ssthen 	rep->flags &= ~(BIT_AA | BIT_CD);/* a reply based on the cache   */
43045872187Ssthen 	if(edns->subnet_source_mask == 0 && edns->subnet_scope_mask == 0)
43145872187Ssthen 		only_match_scope_zero = 1;
43245872187Ssthen 	else only_match_scope_zero = 0;
4332bdc0ed1Ssthen 	diff_size = (int)tree->size_bytes;
4342be9e038Ssthen 	addrtree_insert(tree, (addrkey_t*)edns->subnet_addr,
435a3167c07Ssthen 		edns->subnet_source_mask, sq->max_scope, rep,
43645872187Ssthen 		rep->ttl, *qstate->env->now, only_match_scope_zero);
4372bdc0ed1Ssthen 	diff_size = (int)tree->size_bytes - diff_size;
438550cf4a9Ssthen 
4392be9e038Ssthen 	lock_rw_unlock(&lru_entry->lock);
440550cf4a9Ssthen 	if (need_to_insert) {
4412be9e038Ssthen 		slabhash_insert(subnet_msg_cache, h, lru_entry, lru_entry->data,
4422be9e038Ssthen 			NULL);
4432bdc0ed1Ssthen 	} else {
4442bdc0ed1Ssthen 		slabhash_update_space_used(subnet_msg_cache, h, NULL,
4452bdc0ed1Ssthen 			diff_size);
4462be9e038Ssthen 	}
4472be9e038Ssthen }
4482be9e038Ssthen 
4492be9e038Ssthen /** Lookup in cache and reply true iff reply is sent. */
4502be9e038Ssthen static int
4518b7325afSsthen lookup_and_reply(struct module_qstate *qstate, int id, struct subnet_qstate *sq, int prefetch)
4522be9e038Ssthen {
4532be9e038Ssthen 	struct lruhash_entry *e;
4542be9e038Ssthen 	struct module_env *env = qstate->env;
4552be9e038Ssthen 	struct subnet_env *sne = (struct subnet_env*)env->modinfo[id];
4562be9e038Ssthen 	hashvalue_type h = query_info_hash(&qstate->qinfo, qstate->query_flags);
4572be9e038Ssthen 	struct subnet_msg_cache_data *data;
4582be9e038Ssthen 	struct ecs_data *ecs = &sq->ecs_client_in;
4592be9e038Ssthen 	struct addrtree *tree;
4602be9e038Ssthen 	struct addrnode *node;
4612be9e038Ssthen 	uint8_t scope;
4622be9e038Ssthen 
4632be9e038Ssthen 	memset(&sq->ecs_client_out, 0, sizeof(sq->ecs_client_out));
4642be9e038Ssthen 
465d1e2768aSsthen 	if (sq) {
466d1e2768aSsthen 		sq->qinfo_hash = h; /* Might be useful on cache miss */
467d1e2768aSsthen 		sq->qinfo_hash_calculated = 1;
468d1e2768aSsthen 	}
4692be9e038Ssthen 	e = slabhash_lookup(sne->subnet_msg_cache, h, &qstate->qinfo, 1);
4702be9e038Ssthen 	if (!e) return 0; /* qinfo not in cache */
4712be9e038Ssthen 	data = e->data;
4722be9e038Ssthen 	tree = (ecs->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4)?
4732be9e038Ssthen 		data->tree4 : data->tree6;
4742be9e038Ssthen 	if (!tree) { /* qinfo in cache but not for this family */
4752be9e038Ssthen 		lock_rw_unlock(&e->lock);
4762be9e038Ssthen 		return 0;
4772be9e038Ssthen 	}
4782be9e038Ssthen 	node = addrtree_find(tree, (addrkey_t*)ecs->subnet_addr,
4792be9e038Ssthen 		ecs->subnet_source_mask, *env->now);
4802be9e038Ssthen 	if (!node) { /* plain old cache miss */
4812be9e038Ssthen 		lock_rw_unlock(&e->lock);
4822be9e038Ssthen 		return 0;
4832be9e038Ssthen 	}
4842be9e038Ssthen 
4852be9e038Ssthen 	qstate->return_msg = tomsg(NULL, &qstate->qinfo,
486eaf2578eSsthen 		(struct reply_info *)node->elem, qstate->region, *env->now, 0,
4872be9e038Ssthen 		env->scratch);
4882be9e038Ssthen 	scope = (uint8_t)node->scope;
4892be9e038Ssthen 	lock_rw_unlock(&e->lock);
4902be9e038Ssthen 
4912be9e038Ssthen 	if (!qstate->return_msg) { /* Failed allocation or expired TTL */
4922be9e038Ssthen 		return 0;
4932be9e038Ssthen 	}
4942be9e038Ssthen 
4952be9e038Ssthen 	if (sq->subnet_downstream) { /* relay to interested client */
4962be9e038Ssthen 		sq->ecs_client_out.subnet_scope_mask = scope;
4972be9e038Ssthen 		sq->ecs_client_out.subnet_addr_fam = ecs->subnet_addr_fam;
4982be9e038Ssthen 		sq->ecs_client_out.subnet_source_mask = ecs->subnet_source_mask;
4992be9e038Ssthen 		memcpy(&sq->ecs_client_out.subnet_addr, &ecs->subnet_addr,
5002be9e038Ssthen 			INET6_SIZE);
5012be9e038Ssthen 		sq->ecs_client_out.subnet_validdata = 1;
5022be9e038Ssthen 	}
5038b7325afSsthen 
5048b7325afSsthen 	if (prefetch && *qstate->env->now >= ((struct reply_info *)node->elem)->prefetch_ttl) {
5058b7325afSsthen 		qstate->need_refetch = 1;
5068b7325afSsthen 	}
5072be9e038Ssthen 	return 1;
5082be9e038Ssthen }
5092be9e038Ssthen 
5102be9e038Ssthen /**
5112be9e038Ssthen  * Test first bits of addresses for equality. Caller is responsible
5122be9e038Ssthen  * for making sure that both a and b are at least net/8 octets long.
5132be9e038Ssthen  * @param a: first address.
5142be9e038Ssthen  * @param a: seconds address.
5152be9e038Ssthen  * @param net: Number of bits to test.
5162be9e038Ssthen  * @return: 1 if equal, 0 otherwise.
5172be9e038Ssthen  */
5182be9e038Ssthen static int
5192be9e038Ssthen common_prefix(uint8_t *a, uint8_t *b, uint8_t net)
5202be9e038Ssthen {
5212be9e038Ssthen 	size_t n = (size_t)net / 8;
5222be9e038Ssthen 	return !memcmp(a, b, n) && ((net % 8) == 0 || a[n] == b[n]);
5232be9e038Ssthen }
5242be9e038Ssthen 
5252be9e038Ssthen static enum module_ext_state
5262be9e038Ssthen eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq)
5272be9e038Ssthen {
5282be9e038Ssthen 	struct subnet_env *sne = qstate->env->modinfo[id];
5292be9e038Ssthen 
5302be9e038Ssthen 	struct ecs_data *c_in  = &sq->ecs_client_in; /* rcvd from client */
5312be9e038Ssthen 	struct ecs_data *c_out = &sq->ecs_client_out;/* will send to client */
5322be9e038Ssthen 	struct ecs_data *s_in  = &sq->ecs_server_in; /* rcvd from auth */
5332be9e038Ssthen 	struct ecs_data *s_out = &sq->ecs_server_out;/* sent to auth */
5342be9e038Ssthen 
5352be9e038Ssthen 	memset(c_out, 0, sizeof(*c_out));
5362be9e038Ssthen 
5372308e98cSsthen 	if (!qstate->return_msg) {
5382308e98cSsthen 		/* already an answer and its not a message, but retain
5392308e98cSsthen 		 * the actual rcode, instead of module_error, so send
5402308e98cSsthen 		 * module_finished */
5412308e98cSsthen 		return module_finished;
5422308e98cSsthen 	}
5432be9e038Ssthen 
5442be9e038Ssthen 	/* We have not asked for subnet data */
545d896b962Ssthen 	if (!sq->subnet_sent && !sq->subnet_sent_no_subnet) {
5462be9e038Ssthen 		if (s_in->subnet_validdata)
547191f22c6Ssthen 			verbose(VERB_QUERY, "subnetcache: received spurious data");
5482be9e038Ssthen 		if (sq->subnet_downstream) /* Copy back to client */
5492be9e038Ssthen 			cp_edns_bad_response(c_out, c_in);
5502be9e038Ssthen 		return module_finished;
5512be9e038Ssthen 	}
5522be9e038Ssthen 
5532be9e038Ssthen 	/* subnet sent but nothing came back */
554d896b962Ssthen 	if (!s_in->subnet_validdata && !sq->subnet_sent_no_subnet) {
5552be9e038Ssthen 		/* The authority indicated no support for edns subnet. As a
5562be9e038Ssthen 		 * consequence the answer ended up in the regular cache. It
557e21c60efSsthen 		 * is still useful to put it in the edns subnet cache for
5582be9e038Ssthen 		 * when a client explicitly asks for subnet specific answer. */
559191f22c6Ssthen 		verbose(VERB_QUERY, "subnetcache: Authority indicates no support");
5603150e5f6Ssthen 		if(!sq->started_no_cache_store) {
5612be9e038Ssthen 			lock_rw_wrlock(&sne->biglock);
5622be9e038Ssthen 			update_cache(qstate, id);
5632be9e038Ssthen 			lock_rw_unlock(&sne->biglock);
5643150e5f6Ssthen 		}
5652be9e038Ssthen 		if (sq->subnet_downstream)
5662be9e038Ssthen 			cp_edns_bad_response(c_out, c_in);
5672be9e038Ssthen 		return module_finished;
5682be9e038Ssthen 	}
5692be9e038Ssthen 
570d896b962Ssthen 	/* Purposefully there was no sent subnet, and there is consequently
571d896b962Ssthen 	 * no subnet in the answer. If there was, use the subnet in the answer
572d896b962Ssthen 	 * anyway. But if there is not, treat it as a prefix 0 answer. */
573d896b962Ssthen 	if(sq->subnet_sent_no_subnet && !s_in->subnet_validdata) {
574d896b962Ssthen 		/* Fill in 0.0.0.0/0 scope 0, or ::0/0 scope 0, for caching. */
575d896b962Ssthen 		s_in->subnet_addr_fam = s_out->subnet_addr_fam;
576d896b962Ssthen 		s_in->subnet_source_mask = 0;
577d896b962Ssthen 		s_in->subnet_scope_mask = 0;
578d896b962Ssthen 		memset(s_in->subnet_addr, 0, INET6_SIZE);
579d896b962Ssthen 		s_in->subnet_validdata = 1;
580d896b962Ssthen 	}
581d896b962Ssthen 
5822be9e038Ssthen 	/* Being here means we have asked for and got a subnet specific
5832be9e038Ssthen 	 * answer. Also, the answer from the authority is not yet cached
5842be9e038Ssthen 	 * anywhere. */
5852be9e038Ssthen 
5862be9e038Ssthen 	/* can we accept response? */
5872be9e038Ssthen 	if(s_out->subnet_addr_fam != s_in->subnet_addr_fam ||
5882be9e038Ssthen 		s_out->subnet_source_mask != s_in->subnet_source_mask ||
5892be9e038Ssthen 		!common_prefix(s_out->subnet_addr, s_in->subnet_addr,
5902be9e038Ssthen 			s_out->subnet_source_mask))
5912be9e038Ssthen 	{
5922be9e038Ssthen 		/* we can not accept, restart query without option */
593191f22c6Ssthen 		verbose(VERB_QUERY, "subnetcache: forged data");
5942be9e038Ssthen 		s_out->subnet_validdata = 0;
5952be9e038Ssthen 		(void)edns_opt_list_remove(&qstate->edns_opts_back_out,
5962be9e038Ssthen 			qstate->env->cfg->client_subnet_opcode);
5972be9e038Ssthen 		sq->subnet_sent = 0;
598d896b962Ssthen 		sq->subnet_sent_no_subnet = 0;
5992be9e038Ssthen 		return module_restart_next;
6002be9e038Ssthen 	}
6012be9e038Ssthen 
6022be9e038Ssthen 	lock_rw_wrlock(&sne->biglock);
6033150e5f6Ssthen 	if(!sq->started_no_cache_store) {
6042be9e038Ssthen 		update_cache(qstate, id);
6053150e5f6Ssthen 	}
6062308e98cSsthen 	sne->num_msg_nocache++;
6072be9e038Ssthen 	lock_rw_unlock(&sne->biglock);
6082be9e038Ssthen 
6092bdc0ed1Ssthen 	/* If there is an expired answer in the global cache, remove that,
6102bdc0ed1Ssthen 	 * because expired answers would otherwise resurface once the ecs data
6112bdc0ed1Ssthen 	 * expires, giving once in a while global data responses for ecs
6122bdc0ed1Ssthen 	 * domains, with serve expired enabled. */
6132bdc0ed1Ssthen 	if(qstate->env->cfg->serve_expired) {
6142bdc0ed1Ssthen 		msg_cache_remove(qstate->env, qstate->qinfo.qname,
6152bdc0ed1Ssthen 			qstate->qinfo.qname_len, qstate->qinfo.qtype,
6162bdc0ed1Ssthen 			qstate->qinfo.qclass, 0);
6172bdc0ed1Ssthen #ifdef USE_CACHEDB
6182bdc0ed1Ssthen 		if(qstate->env->cachedb_enabled)
6192bdc0ed1Ssthen 			cachedb_msg_remove(qstate);
6202bdc0ed1Ssthen #endif
6212bdc0ed1Ssthen 	}
6222bdc0ed1Ssthen 
6232be9e038Ssthen 	if (sq->subnet_downstream) {
6242be9e038Ssthen 		/* Client wants to see the answer, echo option back
6252be9e038Ssthen 		 * and adjust the scope. */
6262be9e038Ssthen 		c_out->subnet_addr_fam = c_in->subnet_addr_fam;
6272be9e038Ssthen 		c_out->subnet_source_mask = c_in->subnet_source_mask;
6282be9e038Ssthen 		memcpy(&c_out->subnet_addr, &c_in->subnet_addr, INET6_SIZE);
629a3167c07Ssthen 		c_out->subnet_scope_mask = sq->max_scope;
6303150e5f6Ssthen 		/* Limit scope returned to client to scope used for caching. */
6313150e5f6Ssthen 		if(c_out->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4) {
6323150e5f6Ssthen 			if(c_out->subnet_scope_mask >
6333150e5f6Ssthen 				qstate->env->cfg->max_client_subnet_ipv4) {
6343150e5f6Ssthen 				c_out->subnet_scope_mask =
6353150e5f6Ssthen 					qstate->env->cfg->max_client_subnet_ipv4;
6363150e5f6Ssthen 			}
6373150e5f6Ssthen 		}
6383150e5f6Ssthen 		else if(c_out->subnet_scope_mask >
6393150e5f6Ssthen 				qstate->env->cfg->max_client_subnet_ipv6) {
6403150e5f6Ssthen 				c_out->subnet_scope_mask =
6413150e5f6Ssthen 					qstate->env->cfg->max_client_subnet_ipv6;
6423150e5f6Ssthen 		}
6432be9e038Ssthen 		c_out->subnet_validdata = 1;
6442be9e038Ssthen 	}
6452be9e038Ssthen 	return module_finished;
6462be9e038Ssthen }
6472be9e038Ssthen 
6482be9e038Ssthen /** Parse EDNS opt data containing ECS */
6492be9e038Ssthen static int
6502be9e038Ssthen parse_subnet_option(struct edns_option* ecs_option, struct ecs_data* ecs)
6512be9e038Ssthen {
6522be9e038Ssthen 	memset(ecs, 0, sizeof(*ecs));
6532be9e038Ssthen 	if (ecs_option->opt_len < 4)
6542be9e038Ssthen 		return 0;
6552be9e038Ssthen 
6562be9e038Ssthen 	ecs->subnet_addr_fam = sldns_read_uint16(ecs_option->opt_data);
6572be9e038Ssthen 	ecs->subnet_source_mask = ecs_option->opt_data[2];
6582be9e038Ssthen 	ecs->subnet_scope_mask = ecs_option->opt_data[3];
659bdfc4d55Sflorian 	/* remaining bytes indicate address */
6602be9e038Ssthen 
6612be9e038Ssthen 	/* validate input*/
6622be9e038Ssthen 	/* option length matches calculated length? */
6632be9e038Ssthen 	if (ecs_option->opt_len != (size_t)((ecs->subnet_source_mask+7)/8 + 4))
6642be9e038Ssthen 		return 0;
6652be9e038Ssthen 	if (ecs_option->opt_len - 4 > INET6_SIZE || ecs_option->opt_len == 0)
6662be9e038Ssthen 		return 0;
6672be9e038Ssthen 	if (ecs->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4) {
6682be9e038Ssthen 		if (ecs->subnet_source_mask > 32 || ecs->subnet_scope_mask > 32)
6692be9e038Ssthen 			return 0;
6702be9e038Ssthen 	} else if (ecs->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP6) {
6712be9e038Ssthen 		if (ecs->subnet_source_mask > 128 ||
6722be9e038Ssthen 			ecs->subnet_scope_mask > 128)
6732be9e038Ssthen 			return 0;
6742be9e038Ssthen 	} else
6752be9e038Ssthen 		return 0;
6762be9e038Ssthen 
6772be9e038Ssthen 	/* valid ECS data, write to ecs_data */
6782be9e038Ssthen 	if (copy_clear(ecs->subnet_addr, INET6_SIZE, ecs_option->opt_data + 4,
6792be9e038Ssthen 		ecs_option->opt_len - 4, ecs->subnet_source_mask))
6802be9e038Ssthen 		return 0;
6812be9e038Ssthen 	ecs->subnet_validdata = 1;
6822be9e038Ssthen 	return 1;
6832be9e038Ssthen }
6842be9e038Ssthen 
6850bdb4f62Ssthen void
6862be9e038Ssthen subnet_option_from_ss(struct sockaddr_storage *ss, struct ecs_data* ecs,
6872be9e038Ssthen 	struct config_file* cfg)
6882be9e038Ssthen {
6892be9e038Ssthen 	void* sinaddr;
6902be9e038Ssthen 
6912be9e038Ssthen 	/* Construct subnet option from original query */
6922be9e038Ssthen 	if(((struct sockaddr_in*)ss)->sin_family == AF_INET) {
6932be9e038Ssthen 		ecs->subnet_source_mask = cfg->max_client_subnet_ipv4;
6942be9e038Ssthen 		ecs->subnet_addr_fam = EDNSSUBNET_ADDRFAM_IP4;
6952be9e038Ssthen 		sinaddr = &((struct sockaddr_in*)ss)->sin_addr;
6962be9e038Ssthen 		if (!copy_clear( ecs->subnet_addr, INET6_SIZE,
6972be9e038Ssthen 			(uint8_t *)sinaddr, INET_SIZE,
6982be9e038Ssthen 			ecs->subnet_source_mask)) {
6992be9e038Ssthen 			ecs->subnet_validdata = 1;
7002be9e038Ssthen 		}
7012be9e038Ssthen 	}
7022be9e038Ssthen #ifdef INET6
7032be9e038Ssthen 	else {
7042be9e038Ssthen 		ecs->subnet_source_mask = cfg->max_client_subnet_ipv6;
7052be9e038Ssthen 		ecs->subnet_addr_fam = EDNSSUBNET_ADDRFAM_IP6;
7062be9e038Ssthen 		sinaddr = &((struct sockaddr_in6*)ss)->sin6_addr;
7072be9e038Ssthen 		if (!copy_clear( ecs->subnet_addr, INET6_SIZE,
7082be9e038Ssthen 			(uint8_t *)sinaddr, INET6_SIZE,
7092be9e038Ssthen 			ecs->subnet_source_mask)) {
7102be9e038Ssthen 			ecs->subnet_validdata = 1;
7112be9e038Ssthen 		}
7122be9e038Ssthen 	}
7132be9e038Ssthen #else
7142be9e038Ssthen 			/* We don't know how to handle ip6, just pass */
7152be9e038Ssthen #endif /* INET6 */
7162be9e038Ssthen }
7172be9e038Ssthen 
7182be9e038Ssthen int
7192be9e038Ssthen ecs_query_response(struct module_qstate* qstate, struct dns_msg* response,
7202be9e038Ssthen 	int id, void* ATTR_UNUSED(cbargs))
7212be9e038Ssthen {
7222be9e038Ssthen 	struct subnet_qstate *sq;
7232be9e038Ssthen 
7242be9e038Ssthen 	if(!response || !(sq=(struct subnet_qstate*)qstate->minfo[id]))
7252be9e038Ssthen 		return 1;
7262be9e038Ssthen 
7272be9e038Ssthen 	if(sq->subnet_sent &&
7282be9e038Ssthen 		FLAGS_GET_RCODE(response->rep->flags) == LDNS_RCODE_REFUSED) {
729bdfc4d55Sflorian 		/* REFUSED response to ECS query, remove ECS option. */
7302be9e038Ssthen 		edns_opt_list_remove(&qstate->edns_opts_back_out,
7312be9e038Ssthen 			qstate->env->cfg->client_subnet_opcode);
7322be9e038Ssthen 		sq->subnet_sent = 0;
733d896b962Ssthen 		sq->subnet_sent_no_subnet = 0;
7342be9e038Ssthen 		memset(&sq->ecs_server_out, 0, sizeof(sq->ecs_server_out));
735a3167c07Ssthen 	} else if (!sq->track_max_scope &&
736a3167c07Ssthen 		FLAGS_GET_RCODE(response->rep->flags) == LDNS_RCODE_NOERROR &&
737a3167c07Ssthen 		response->rep->an_numrrsets > 0
738a3167c07Ssthen 		) {
739a3167c07Ssthen 		struct ub_packed_rrset_key* s = response->rep->rrsets[0];
740a3167c07Ssthen 		if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME &&
741a3167c07Ssthen 			query_dname_compare(qstate->qinfo.qname,
742a3167c07Ssthen 			s->rk.dname) == 0) {
743a3167c07Ssthen 			/* CNAME response for QNAME. From now on keep track of
744a3167c07Ssthen 			 * longest received ECS prefix for all queries on this
745a3167c07Ssthen 			 * qstate. */
746a3167c07Ssthen 			sq->track_max_scope = 1;
747a3167c07Ssthen 		}
7482be9e038Ssthen 	}
7492be9e038Ssthen 	return 1;
7502be9e038Ssthen }
7512be9e038Ssthen 
75245872187Ssthen /** verbose print edns subnet option in pretty print */
75345872187Ssthen static void
75445872187Ssthen subnet_log_print(const char* s, struct edns_option* ecs_opt)
75545872187Ssthen {
75645872187Ssthen 	if(verbosity >= VERB_ALGO) {
75745872187Ssthen 		char buf[256];
75845872187Ssthen 		char* str = buf;
75945872187Ssthen 		size_t str_len = sizeof(buf);
76045872187Ssthen 		if(!ecs_opt) {
76145872187Ssthen 			verbose(VERB_ALGO, "%s (null)", s);
76245872187Ssthen 			return;
76345872187Ssthen 		}
76445872187Ssthen 		(void)sldns_wire2str_edns_subnet_print(&str, &str_len,
76545872187Ssthen 			ecs_opt->opt_data, ecs_opt->opt_len);
76645872187Ssthen 		verbose(VERB_ALGO, "%s %s", s, buf);
76745872187Ssthen 	}
76845872187Ssthen }
76945872187Ssthen 
7702be9e038Ssthen int
7712be9e038Ssthen ecs_edns_back_parsed(struct module_qstate* qstate, int id,
7722be9e038Ssthen 	void* ATTR_UNUSED(cbargs))
7732be9e038Ssthen {
7742be9e038Ssthen 	struct subnet_qstate *sq;
7752be9e038Ssthen 	struct edns_option* ecs_opt;
7762be9e038Ssthen 
7772be9e038Ssthen 	if(!(sq=(struct subnet_qstate*)qstate->minfo[id]))
7782be9e038Ssthen 		return 1;
7792be9e038Ssthen 	if((ecs_opt = edns_opt_list_find(
7802be9e038Ssthen 		qstate->edns_opts_back_in,
781a3167c07Ssthen 		qstate->env->cfg->client_subnet_opcode)) &&
782a3167c07Ssthen 		parse_subnet_option(ecs_opt, &sq->ecs_server_in) &&
783a3167c07Ssthen 		sq->subnet_sent && sq->ecs_server_in.subnet_validdata) {
78445872187Ssthen 			subnet_log_print("answer has edns subnet", ecs_opt);
7852be9e038Ssthen 			/* Only skip global cache store if we sent an ECS option
7862be9e038Ssthen 			 * and received one back. Answers from non-whitelisted
787bdfc4d55Sflorian 			 * servers will end up in global cache. Answers for
7882be9e038Ssthen 			 * queries with 0 source will not (unless nameserver
7892be9e038Ssthen 			 * does not support ECS). */
7902be9e038Ssthen 			qstate->no_cache_store = 1;
791a3167c07Ssthen 			if(!sq->track_max_scope || (sq->track_max_scope &&
792a3167c07Ssthen 				sq->ecs_server_in.subnet_scope_mask >
793a3167c07Ssthen 				sq->max_scope))
794a3167c07Ssthen 				sq->max_scope = sq->ecs_server_in.subnet_scope_mask;
795d896b962Ssthen 	} else if(sq->subnet_sent_no_subnet) {
796d896b962Ssthen 		/* The answer can be stored as scope 0, not in global cache. */
797d896b962Ssthen 		qstate->no_cache_store = 1;
7982be9e038Ssthen 	}
7992be9e038Ssthen 
8002be9e038Ssthen 	return 1;
8012be9e038Ssthen }
8022be9e038Ssthen 
8032be9e038Ssthen void
8042be9e038Ssthen subnetmod_operate(struct module_qstate *qstate, enum module_ev event,
8052be9e038Ssthen 	int id, struct outbound_entry* outbound)
8062be9e038Ssthen {
8072be9e038Ssthen 	struct subnet_env *sne = qstate->env->modinfo[id];
8082be9e038Ssthen 	struct subnet_qstate *sq = (struct subnet_qstate*)qstate->minfo[id];
8092be9e038Ssthen 
810191f22c6Ssthen 	verbose(VERB_QUERY, "subnetcache[module %d] operate: extstate:%s "
8112be9e038Ssthen 		"event:%s", id, strextstate(qstate->ext_state[id]),
8122be9e038Ssthen 		strmodulevent(event));
813191f22c6Ssthen 	log_query_info(VERB_QUERY, "subnetcache operate: query", &qstate->qinfo);
8142be9e038Ssthen 
8152be9e038Ssthen 	if((event == module_event_new || event == module_event_pass) &&
8162be9e038Ssthen 		sq == NULL) {
8172be9e038Ssthen 		struct edns_option* ecs_opt;
8182be9e038Ssthen 		if(!subnet_new_qstate(qstate, id)) {
8192be9e038Ssthen 			qstate->return_msg = NULL;
8202be9e038Ssthen 			qstate->ext_state[id] = module_finished;
8212be9e038Ssthen 			return;
8222be9e038Ssthen 		}
8232be9e038Ssthen 
8242be9e038Ssthen 		sq = (struct subnet_qstate*)qstate->minfo[id];
8252be9e038Ssthen 
8262be9e038Ssthen 		if((ecs_opt = edns_opt_list_find(
8272be9e038Ssthen 			qstate->edns_opts_front_in,
8282be9e038Ssthen 			qstate->env->cfg->client_subnet_opcode))) {
8292be9e038Ssthen 			if(!parse_subnet_option(ecs_opt, &sq->ecs_client_in)) {
8302be9e038Ssthen 				/* Wrongly formatted ECS option. RFC mandates to
8312be9e038Ssthen 				 * return FORMERROR. */
8322be9e038Ssthen 				qstate->return_rcode = LDNS_RCODE_FORMERR;
8332be9e038Ssthen 				qstate->ext_state[id] = module_finished;
8342be9e038Ssthen 				return;
8352be9e038Ssthen 			}
83645872187Ssthen 			subnet_log_print("query has edns subnet", ecs_opt);
8372be9e038Ssthen 			sq->subnet_downstream = 1;
8382be9e038Ssthen 		}
8392be9e038Ssthen 		else if(qstate->mesh_info->reply_list) {
8402be9e038Ssthen 			subnet_option_from_ss(
84145872187Ssthen 				&qstate->mesh_info->reply_list->query_reply.client_addr,
8422be9e038Ssthen 				&sq->ecs_client_in, qstate->env->cfg);
8432be9e038Ssthen 		}
8448b7325afSsthen 		else if(qstate->client_addr.ss_family != AF_UNSPEC) {
8458b7325afSsthen 			subnet_option_from_ss(
8468b7325afSsthen 				&qstate->client_addr,
8478b7325afSsthen 				&sq->ecs_client_in, qstate->env->cfg);
8488b7325afSsthen 		}
8492be9e038Ssthen 
8502be9e038Ssthen 		if(sq->ecs_client_in.subnet_validdata == 0) {
8512be9e038Ssthen 			/* No clients are interested in result or we could not
8522be9e038Ssthen 			 * parse it, we don't do client subnet */
8532be9e038Ssthen 			sq->ecs_server_out.subnet_validdata = 0;
854191f22c6Ssthen 			verbose(VERB_ALGO, "subnetcache: pass to next module");
8552be9e038Ssthen 			qstate->ext_state[id] = module_wait_module;
8562be9e038Ssthen 			return;
8572be9e038Ssthen 		}
8582be9e038Ssthen 
8593150e5f6Ssthen 		/* Limit to minimum allowed source mask */
8603150e5f6Ssthen 		if(sq->ecs_client_in.subnet_source_mask != 0 && (
8613150e5f6Ssthen 			(sq->ecs_client_in.subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4 &&
8623150e5f6Ssthen 			 sq->ecs_client_in.subnet_source_mask < qstate->env->cfg->min_client_subnet_ipv4) ||
8633150e5f6Ssthen 			(sq->ecs_client_in.subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP6 &&
8643150e5f6Ssthen 			 sq->ecs_client_in.subnet_source_mask < qstate->env->cfg->min_client_subnet_ipv6))) {
8653150e5f6Ssthen 				qstate->return_rcode = LDNS_RCODE_REFUSED;
8663150e5f6Ssthen 				qstate->ext_state[id] = module_finished;
8673150e5f6Ssthen 				return;
8683150e5f6Ssthen 		}
8693150e5f6Ssthen 
870d1e2768aSsthen 		if(!sq->started_no_cache_lookup && !qstate->blacklist) {
8712be9e038Ssthen 			lock_rw_wrlock(&sne->biglock);
8728b7325afSsthen 			if(qstate->mesh_info->reply_list &&
8738b7325afSsthen 				lookup_and_reply(qstate, id, sq,
8748b7325afSsthen 				qstate->env->cfg->prefetch)) {
8752308e98cSsthen 				sne->num_msg_cache++;
8762be9e038Ssthen 				lock_rw_unlock(&sne->biglock);
877191f22c6Ssthen 				verbose(VERB_QUERY, "subnetcache: answered from cache");
8782be9e038Ssthen 				qstate->ext_state[id] = module_finished;
8792be9e038Ssthen 
8800bdb4f62Ssthen 				subnet_ecs_opt_list_append(&sq->ecs_client_out,
881d1e2768aSsthen 					&qstate->edns_opts_front_out, qstate,
882d1e2768aSsthen 					qstate->region);
88345872187Ssthen 				if(verbosity >= VERB_ALGO) {
88445872187Ssthen 					subnet_log_print("reply has edns subnet",
88545872187Ssthen 						edns_opt_list_find(
88645872187Ssthen 						qstate->edns_opts_front_out,
88745872187Ssthen 						qstate->env->cfg->
88845872187Ssthen 						client_subnet_opcode));
88945872187Ssthen 				}
8902be9e038Ssthen 				return;
8912be9e038Ssthen 			}
8922be9e038Ssthen 			lock_rw_unlock(&sne->biglock);
893d1e2768aSsthen 		}
8942be9e038Ssthen 
8952be9e038Ssthen 		sq->ecs_server_out.subnet_addr_fam =
8962be9e038Ssthen 			sq->ecs_client_in.subnet_addr_fam;
8972be9e038Ssthen 		sq->ecs_server_out.subnet_source_mask =
8982be9e038Ssthen 			sq->ecs_client_in.subnet_source_mask;
8992be9e038Ssthen 		/* Limit source prefix to configured maximum */
9002be9e038Ssthen 		if(sq->ecs_server_out.subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4
9012be9e038Ssthen 			&& sq->ecs_server_out.subnet_source_mask >
9022be9e038Ssthen 			qstate->env->cfg->max_client_subnet_ipv4)
9032be9e038Ssthen 			sq->ecs_server_out.subnet_source_mask =
9042be9e038Ssthen 				qstate->env->cfg->max_client_subnet_ipv4;
9052be9e038Ssthen 		else if(sq->ecs_server_out.subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP6
9062be9e038Ssthen 			&& sq->ecs_server_out.subnet_source_mask >
9072be9e038Ssthen 			qstate->env->cfg->max_client_subnet_ipv6)
9082be9e038Ssthen 			sq->ecs_server_out.subnet_source_mask =
9092be9e038Ssthen 				qstate->env->cfg->max_client_subnet_ipv6;
9102be9e038Ssthen 		/* Safe to copy completely, even if the source is limited by the
9110bdb4f62Ssthen 		 * configuration. subnet_ecs_opt_list_append() will limit the address.
9122be9e038Ssthen 		 * */
9132be9e038Ssthen 		memcpy(&sq->ecs_server_out.subnet_addr,
9142be9e038Ssthen 			sq->ecs_client_in.subnet_addr, INET6_SIZE);
9152be9e038Ssthen 		sq->ecs_server_out.subnet_scope_mask = 0;
9162be9e038Ssthen 		sq->ecs_server_out.subnet_validdata = 1;
9172be9e038Ssthen 		if(sq->ecs_server_out.subnet_source_mask != 0 &&
918bdfc4d55Sflorian 			qstate->env->cfg->client_subnet_always_forward &&
9192be9e038Ssthen 			sq->subnet_downstream)
9202be9e038Ssthen 			/* ECS specific data required, do not look at the global
9212be9e038Ssthen 			 * cache in other modules. */
9222be9e038Ssthen 			qstate->no_cache_lookup = 1;
9232be9e038Ssthen 
9242be9e038Ssthen 		/* pass request to next module */
9252be9e038Ssthen 		verbose(VERB_ALGO,
926191f22c6Ssthen 			"subnetcache: not found in cache. pass to next module");
9272be9e038Ssthen 		qstate->ext_state[id] = module_wait_module;
9282be9e038Ssthen 		return;
9292be9e038Ssthen 	}
9302be9e038Ssthen 	/* Query handed back by next module, we have a 'final' answer */
9312be9e038Ssthen 	if(sq && event == module_event_moddone) {
9322be9e038Ssthen 		qstate->ext_state[id] = eval_response(qstate, id, sq);
9332308e98cSsthen 		if(qstate->ext_state[id] == module_finished &&
9342308e98cSsthen 			qstate->return_msg) {
9350bdb4f62Ssthen 			subnet_ecs_opt_list_append(&sq->ecs_client_out,
936d1e2768aSsthen 				&qstate->edns_opts_front_out, qstate,
937d1e2768aSsthen 				qstate->region);
93845872187Ssthen 			if(verbosity >= VERB_ALGO) {
93945872187Ssthen 				subnet_log_print("reply has edns subnet",
94045872187Ssthen 					edns_opt_list_find(
94145872187Ssthen 					qstate->edns_opts_front_out,
94245872187Ssthen 					qstate->env->cfg->
94345872187Ssthen 					client_subnet_opcode));
94445872187Ssthen 			}
9452be9e038Ssthen 		}
9463150e5f6Ssthen 		qstate->no_cache_store = sq->started_no_cache_store;
947d1e2768aSsthen 		qstate->no_cache_lookup = sq->started_no_cache_lookup;
9482be9e038Ssthen 		return;
9492be9e038Ssthen 	}
9502be9e038Ssthen 	if(sq && outbound) {
9512be9e038Ssthen 		return;
9522be9e038Ssthen 	}
9532be9e038Ssthen 	/* We are being revisited */
9542be9e038Ssthen 	if(event == module_event_pass || event == module_event_new) {
9552be9e038Ssthen 		/* Just pass it on, we already did the work */
956191f22c6Ssthen 		verbose(VERB_ALGO, "subnetcache: pass to next module");
9572be9e038Ssthen 		qstate->ext_state[id] = module_wait_module;
9582be9e038Ssthen 		return;
9592be9e038Ssthen 	}
9602be9e038Ssthen 	if(!sq && (event == module_event_moddone)) {
9612be9e038Ssthen 		/* during priming, module done but we never started */
9622be9e038Ssthen 		qstate->ext_state[id] = module_finished;
9632be9e038Ssthen 		return;
9642be9e038Ssthen 	}
965191f22c6Ssthen 	log_err("subnetcache: bad event %s", strmodulevent(event));
9662be9e038Ssthen 	qstate->ext_state[id] = module_error;
9672be9e038Ssthen 	return;
9682be9e038Ssthen }
9692be9e038Ssthen 
9702be9e038Ssthen void
9712be9e038Ssthen subnetmod_clear(struct module_qstate *ATTR_UNUSED(qstate),
9722be9e038Ssthen 	int ATTR_UNUSED(id))
9732be9e038Ssthen {
9742be9e038Ssthen 	/* qstate has no data outside region */
9752be9e038Ssthen }
9762be9e038Ssthen 
9772be9e038Ssthen void
9782be9e038Ssthen subnetmod_inform_super(struct module_qstate *ATTR_UNUSED(qstate),
9792be9e038Ssthen 	int ATTR_UNUSED(id), struct module_qstate *ATTR_UNUSED(super))
9802be9e038Ssthen {
9812be9e038Ssthen 	/* Not used */
9822be9e038Ssthen }
9832be9e038Ssthen 
9842be9e038Ssthen size_t
9852be9e038Ssthen subnetmod_get_mem(struct module_env *env, int id)
9862be9e038Ssthen {
9872be9e038Ssthen 	struct subnet_env *sn_env = env->modinfo[id];
9882be9e038Ssthen 	if (!sn_env) return 0;
9892be9e038Ssthen 	return sizeof(*sn_env) +
9902be9e038Ssthen 		slabhash_get_mem(sn_env->subnet_msg_cache) +
9912be9e038Ssthen 		ecs_whitelist_get_mem(sn_env->whitelist);
9922be9e038Ssthen }
9932be9e038Ssthen 
9942be9e038Ssthen /**
9952be9e038Ssthen  * The module function block
9962be9e038Ssthen  */
9972be9e038Ssthen static struct module_func_block subnetmod_block = {
998*98bc733bSsthen 	"subnetcache",
999*98bc733bSsthen 	NULL, NULL, &subnetmod_init, &subnetmod_deinit, &subnetmod_operate,
10002be9e038Ssthen 	&subnetmod_inform_super, &subnetmod_clear, &subnetmod_get_mem
10012be9e038Ssthen };
10022be9e038Ssthen 
10032be9e038Ssthen struct module_func_block*
10042be9e038Ssthen subnetmod_get_funcblock(void)
10052be9e038Ssthen {
10062be9e038Ssthen 	return &subnetmod_block;
10072be9e038Ssthen }
10082be9e038Ssthen 
10092be9e038Ssthen /** Wrappers for static functions to unit test */
10102be9e038Ssthen size_t
10112be9e038Ssthen unittest_wrapper_subnetmod_sizefunc(void *elemptr)
10122be9e038Ssthen {
10132be9e038Ssthen 	return sizefunc(elemptr);
10142be9e038Ssthen }
10152be9e038Ssthen 
10162be9e038Ssthen #endif  /* CLIENT_SUBNET */
1017