xref: /netbsd-src/external/bsd/unbound/dist/cachedb/cachedb.c (revision 91f7d55fb697b5e0475da4718fa34c3a3ebeac85)
13b6c3722Schristos /*
23b6c3722Schristos  * cachedb/cachedb.c - cache from a database external to the program module
33b6c3722Schristos  *
43b6c3722Schristos  * Copyright (c) 2016, NLnet Labs. All rights reserved.
53b6c3722Schristos  *
63b6c3722Schristos  * This software is open source.
73b6c3722Schristos  *
83b6c3722Schristos  * Redistribution and use in source and binary forms, with or without
93b6c3722Schristos  * modification, are permitted provided that the following conditions
103b6c3722Schristos  * are met:
113b6c3722Schristos  *
123b6c3722Schristos  * Redistributions of source code must retain the above copyright notice,
133b6c3722Schristos  * this list of conditions and the following disclaimer.
143b6c3722Schristos  *
153b6c3722Schristos  * Redistributions in binary form must reproduce the above copyright notice,
163b6c3722Schristos  * this list of conditions and the following disclaimer in the documentation
173b6c3722Schristos  * and/or other materials provided with the distribution.
183b6c3722Schristos  *
193b6c3722Schristos  * Neither the name of the NLNET LABS nor the names of its contributors may
203b6c3722Schristos  * be used to endorse or promote products derived from this software without
213b6c3722Schristos  * specific prior written permission.
223b6c3722Schristos  *
233b6c3722Schristos  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
243b6c3722Schristos  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
253b6c3722Schristos  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
263b6c3722Schristos  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
273b6c3722Schristos  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
283b6c3722Schristos  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
293b6c3722Schristos  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
303b6c3722Schristos  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
313b6c3722Schristos  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
323b6c3722Schristos  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
333b6c3722Schristos  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
343b6c3722Schristos  */
353b6c3722Schristos 
363b6c3722Schristos /**
373b6c3722Schristos  * \file
383b6c3722Schristos  *
393b6c3722Schristos  * This file contains a module that uses an external database to cache
403b6c3722Schristos  * dns responses.
413b6c3722Schristos  */
423b6c3722Schristos 
433b6c3722Schristos #include "config.h"
443b6c3722Schristos #ifdef USE_CACHEDB
453b6c3722Schristos #include "cachedb/cachedb.h"
467cd94d69Schristos #include "cachedb/redis.h"
473b6c3722Schristos #include "util/regional.h"
483b6c3722Schristos #include "util/net_help.h"
493b6c3722Schristos #include "util/config_file.h"
503b6c3722Schristos #include "util/data/msgreply.h"
513b6c3722Schristos #include "util/data/msgencode.h"
523b6c3722Schristos #include "services/cache/dns.h"
533b6c3722Schristos #include "validator/val_neg.h"
543b6c3722Schristos #include "validator/val_secalgo.h"
553b6c3722Schristos #include "iterator/iter_utils.h"
563b6c3722Schristos #include "sldns/parseutil.h"
573b6c3722Schristos #include "sldns/wire2str.h"
583b6c3722Schristos #include "sldns/sbuffer.h"
593b6c3722Schristos 
607cd94d69Schristos /* header file for htobe64 */
617cd94d69Schristos #ifdef HAVE_ENDIAN_H
627cd94d69Schristos #  include <endian.h>
637cd94d69Schristos #endif
647cd94d69Schristos #ifdef HAVE_SYS_ENDIAN_H
657cd94d69Schristos #  include <sys/endian.h>
667cd94d69Schristos #endif
67d0eba39bSchristos 
68d0eba39bSchristos #ifndef HAVE_HTOBE64
697cd94d69Schristos #  ifdef HAVE_LIBKERN_OSBYTEORDER_H
707cd94d69Schristos      /* In practice this is specific to MacOS X.  We assume it doesn't have
717cd94d69Schristos       * htobe64/be64toh but has alternatives with a different name. */
727cd94d69Schristos #    include <libkern/OSByteOrder.h>
737cd94d69Schristos #    define htobe64(x) OSSwapHostToBigInt64(x)
747cd94d69Schristos #    define be64toh(x) OSSwapBigToHostInt64(x)
75d0eba39bSchristos #  else
76d0eba39bSchristos      /* not OSX */
7701049ae6Schristos      /* Some compilers do not define __BYTE_ORDER__, like IBM XLC on AIX */
7801049ae6Schristos #    if __BIG_ENDIAN__
7901049ae6Schristos #      define be64toh(n) (n)
8001049ae6Schristos #      define htobe64(n) (n)
8101049ae6Schristos #    else
8201049ae6Schristos #      define be64toh(n) (((uint64_t)htonl((n) & 0xFFFFFFFF) << 32) | htonl((n) >> 32))
8301049ae6Schristos #      define htobe64(n) (((uint64_t)htonl((n) & 0xFFFFFFFF) << 32) | htonl((n) >> 32))
84d0eba39bSchristos #    endif /* _ENDIAN */
85d0eba39bSchristos #  endif /* HAVE_LIBKERN_OSBYTEORDER_H */
86d0eba39bSchristos #endif /* HAVE_BE64TOH */
8701049ae6Schristos 
883b6c3722Schristos /** the unit test testframe for cachedb, its module state contains
893b6c3722Schristos  * a cache for a couple queries (in memory). */
903b6c3722Schristos struct testframe_moddata {
910cd9f4ecSchristos 	/** lock for mutex */
920cd9f4ecSchristos 	lock_basic_type lock;
933b6c3722Schristos 	/** key for single stored data element, NULL if none */
943b6c3722Schristos 	char* stored_key;
953b6c3722Schristos 	/** data for single stored data element, NULL if none */
963b6c3722Schristos 	uint8_t* stored_data;
973b6c3722Schristos 	/** length of stored data */
983b6c3722Schristos 	size_t stored_datalen;
993b6c3722Schristos };
1003b6c3722Schristos 
1013b6c3722Schristos static int
testframe_init(struct module_env * env,struct cachedb_env * cachedb_env)1023b6c3722Schristos testframe_init(struct module_env* env, struct cachedb_env* cachedb_env)
1033b6c3722Schristos {
1040cd9f4ecSchristos 	struct testframe_moddata* d;
1053b6c3722Schristos 	verbose(VERB_ALGO, "testframe_init");
1060cd9f4ecSchristos 	d = (struct testframe_moddata*)calloc(1,
1073b6c3722Schristos 		sizeof(struct testframe_moddata));
1080cd9f4ecSchristos 	cachedb_env->backend_data = (void*)d;
1093b6c3722Schristos 	if(!cachedb_env->backend_data) {
1103b6c3722Schristos 		log_err("out of memory");
1113b6c3722Schristos 		return 0;
1123b6c3722Schristos 	}
113*91f7d55fSchristos 	/* Register an EDNS option (65534) to bypass the worker cache lookup
114*91f7d55fSchristos 	 * for testing */
115*91f7d55fSchristos 	if(!edns_register_option(LDNS_EDNS_UNBOUND_CACHEDB_TESTFRAME_TEST,
116*91f7d55fSchristos 		1 /* bypass cache */,
117*91f7d55fSchristos 		0 /* no aggregation */, env)) {
118*91f7d55fSchristos 		log_err("testframe_init, could not register test opcode");
119*91f7d55fSchristos 		free(d);
120*91f7d55fSchristos 		return 0;
121*91f7d55fSchristos 	}
1220cd9f4ecSchristos 	lock_basic_init(&d->lock);
1230cd9f4ecSchristos 	lock_protect(&d->lock, d, sizeof(*d));
1243b6c3722Schristos 	return 1;
1253b6c3722Schristos }
1263b6c3722Schristos 
1273b6c3722Schristos static void
testframe_deinit(struct module_env * env,struct cachedb_env * cachedb_env)1283b6c3722Schristos testframe_deinit(struct module_env* env, struct cachedb_env* cachedb_env)
1293b6c3722Schristos {
1303b6c3722Schristos 	struct testframe_moddata* d = (struct testframe_moddata*)
1313b6c3722Schristos 		cachedb_env->backend_data;
1323b6c3722Schristos 	(void)env;
1333b6c3722Schristos 	verbose(VERB_ALGO, "testframe_deinit");
1343b6c3722Schristos 	if(!d)
1353b6c3722Schristos 		return;
1360cd9f4ecSchristos 	lock_basic_destroy(&d->lock);
1373b6c3722Schristos 	free(d->stored_key);
1383b6c3722Schristos 	free(d->stored_data);
1393b6c3722Schristos 	free(d);
1403b6c3722Schristos }
1413b6c3722Schristos 
1423b6c3722Schristos static int
testframe_lookup(struct module_env * env,struct cachedb_env * cachedb_env,char * key,struct sldns_buffer * result_buffer)1433b6c3722Schristos testframe_lookup(struct module_env* env, struct cachedb_env* cachedb_env,
1443b6c3722Schristos 	char* key, struct sldns_buffer* result_buffer)
1453b6c3722Schristos {
1463b6c3722Schristos 	struct testframe_moddata* d = (struct testframe_moddata*)
1473b6c3722Schristos 		cachedb_env->backend_data;
1483b6c3722Schristos 	(void)env;
1493b6c3722Schristos 	verbose(VERB_ALGO, "testframe_lookup of %s", key);
1500cd9f4ecSchristos 	lock_basic_lock(&d->lock);
1513b6c3722Schristos 	if(d->stored_key && strcmp(d->stored_key, key) == 0) {
1520cd9f4ecSchristos 		if(d->stored_datalen > sldns_buffer_capacity(result_buffer)) {
1530cd9f4ecSchristos 			lock_basic_unlock(&d->lock);
1543b6c3722Schristos 			return 0; /* too large */
1550cd9f4ecSchristos 		}
1563b6c3722Schristos 		verbose(VERB_ALGO, "testframe_lookup found %d bytes",
1573b6c3722Schristos 			(int)d->stored_datalen);
1583b6c3722Schristos 		sldns_buffer_clear(result_buffer);
1593b6c3722Schristos 		sldns_buffer_write(result_buffer, d->stored_data,
1603b6c3722Schristos 			d->stored_datalen);
1613b6c3722Schristos 		sldns_buffer_flip(result_buffer);
1620cd9f4ecSchristos 		lock_basic_unlock(&d->lock);
1633b6c3722Schristos 		return 1;
1643b6c3722Schristos 	}
1650cd9f4ecSchristos 	lock_basic_unlock(&d->lock);
1663b6c3722Schristos 	return 0;
1673b6c3722Schristos }
1683b6c3722Schristos 
1693b6c3722Schristos static void
testframe_store(struct module_env * env,struct cachedb_env * cachedb_env,char * key,uint8_t * data,size_t data_len,time_t ATTR_UNUSED (ttl))1703b6c3722Schristos testframe_store(struct module_env* env, struct cachedb_env* cachedb_env,
171d0eba39bSchristos 	char* key, uint8_t* data, size_t data_len, time_t ATTR_UNUSED(ttl))
1723b6c3722Schristos {
1733b6c3722Schristos 	struct testframe_moddata* d = (struct testframe_moddata*)
1743b6c3722Schristos 		cachedb_env->backend_data;
1753b6c3722Schristos 	(void)env;
1760cd9f4ecSchristos 	lock_basic_lock(&d->lock);
1773b6c3722Schristos 	verbose(VERB_ALGO, "testframe_store %s (%d bytes)", key, (int)data_len);
1783b6c3722Schristos 
1793b6c3722Schristos 	/* free old data element (if any) */
1803b6c3722Schristos 	free(d->stored_key);
1813b6c3722Schristos 	d->stored_key = NULL;
1823b6c3722Schristos 	free(d->stored_data);
1833b6c3722Schristos 	d->stored_data = NULL;
1843b6c3722Schristos 	d->stored_datalen = 0;
1853b6c3722Schristos 
1863b6c3722Schristos 	d->stored_data = memdup(data, data_len);
1873b6c3722Schristos 	if(!d->stored_data) {
1880cd9f4ecSchristos 		lock_basic_unlock(&d->lock);
1893b6c3722Schristos 		log_err("out of memory");
1903b6c3722Schristos 		return;
1913b6c3722Schristos 	}
1923b6c3722Schristos 	d->stored_datalen = data_len;
1933b6c3722Schristos 	d->stored_key = strdup(key);
1943b6c3722Schristos 	if(!d->stored_key) {
1953b6c3722Schristos 		free(d->stored_data);
1963b6c3722Schristos 		d->stored_data = NULL;
1973b6c3722Schristos 		d->stored_datalen = 0;
1980cd9f4ecSchristos 		lock_basic_unlock(&d->lock);
1993b6c3722Schristos 		return;
2003b6c3722Schristos 	}
2010cd9f4ecSchristos 	lock_basic_unlock(&d->lock);
2023b6c3722Schristos 	/* (key,data) successfully stored */
2033b6c3722Schristos }
2043b6c3722Schristos 
2053b6c3722Schristos /** The testframe backend is for unit tests */
2063b6c3722Schristos static struct cachedb_backend testframe_backend = { "testframe",
2073b6c3722Schristos 	testframe_init, testframe_deinit, testframe_lookup, testframe_store
2083b6c3722Schristos };
2093b6c3722Schristos 
2103b6c3722Schristos /** find a particular backend from possible backends */
2113b6c3722Schristos static struct cachedb_backend*
cachedb_find_backend(const char * str)2123b6c3722Schristos cachedb_find_backend(const char* str)
2133b6c3722Schristos {
2147cd94d69Schristos #ifdef USE_REDIS
2157cd94d69Schristos 	if(strcmp(str, redis_backend.name) == 0)
2167cd94d69Schristos 		return &redis_backend;
2177cd94d69Schristos #endif
2183b6c3722Schristos 	if(strcmp(str, testframe_backend.name) == 0)
2193b6c3722Schristos 		return &testframe_backend;
2203b6c3722Schristos 	/* TODO add more backends here */
2213b6c3722Schristos 	return NULL;
2223b6c3722Schristos }
2233b6c3722Schristos 
2243b6c3722Schristos /** apply configuration to cachedb module 'global' state */
2253b6c3722Schristos static int
cachedb_apply_cfg(struct cachedb_env * cachedb_env,struct config_file * cfg)2263b6c3722Schristos cachedb_apply_cfg(struct cachedb_env* cachedb_env, struct config_file* cfg)
2273b6c3722Schristos {
2280cd9f4ecSchristos 	const char* backend_str = cfg->cachedb_backend;
229*91f7d55fSchristos 	if(!backend_str || *backend_str==0)
230*91f7d55fSchristos 		return 1;
2313b6c3722Schristos 	cachedb_env->backend = cachedb_find_backend(backend_str);
2323b6c3722Schristos 	if(!cachedb_env->backend) {
2330cd9f4ecSchristos 		log_err("cachedb: cannot find backend name '%s'", backend_str);
2340cd9f4ecSchristos 		return 0;
2353b6c3722Schristos 	}
2360cd9f4ecSchristos 
2373b6c3722Schristos 	/* TODO see if more configuration needs to be applied or not */
2383b6c3722Schristos 	return 1;
2393b6c3722Schristos }
2403b6c3722Schristos 
2413b6c3722Schristos int
cachedb_init(struct module_env * env,int id)2423b6c3722Schristos cachedb_init(struct module_env* env, int id)
2433b6c3722Schristos {
2443b6c3722Schristos 	struct cachedb_env* cachedb_env = (struct cachedb_env*)calloc(1,
2453b6c3722Schristos 		sizeof(struct cachedb_env));
2463b6c3722Schristos 	if(!cachedb_env) {
2473b6c3722Schristos 		log_err("malloc failure");
2483b6c3722Schristos 		return 0;
2493b6c3722Schristos 	}
2503b6c3722Schristos 	env->modinfo[id] = (void*)cachedb_env;
2513b6c3722Schristos 	if(!cachedb_apply_cfg(cachedb_env, env->cfg)) {
2523b6c3722Schristos 		log_err("cachedb: could not apply configuration settings.");
25301049ae6Schristos 		free(cachedb_env);
25401049ae6Schristos 		env->modinfo[id] = NULL;
2553b6c3722Schristos 		return 0;
2563b6c3722Schristos 	}
2573b6c3722Schristos 	/* see if a backend is selected */
2583b6c3722Schristos 	if(!cachedb_env->backend || !cachedb_env->backend->name)
2593b6c3722Schristos 		return 1;
2603b6c3722Schristos 	if(!(*cachedb_env->backend->init)(env, cachedb_env)) {
2613b6c3722Schristos 		log_err("cachedb: could not init %s backend",
2623b6c3722Schristos 			cachedb_env->backend->name);
26301049ae6Schristos 		free(cachedb_env);
26401049ae6Schristos 		env->modinfo[id] = NULL;
2653b6c3722Schristos 		return 0;
2663b6c3722Schristos 	}
2673b6c3722Schristos 	cachedb_env->enabled = 1;
268*91f7d55fSchristos 	if(env->cfg->serve_expired && env->cfg->serve_expired_reply_ttl)
269d0eba39bSchristos 		log_warn(
270d0eba39bSchristos 			"cachedb: serve-expired-reply-ttl is set but not working for data "
271*91f7d55fSchristos 			"originating from the external cache; 0 TTL is used for those.");
272*91f7d55fSchristos 	if(env->cfg->serve_expired && env->cfg->serve_expired_client_timeout)
273d0eba39bSchristos 		log_warn(
274d0eba39bSchristos 			"cachedb: serve-expired-client-timeout is set but not working for "
275d0eba39bSchristos 			"data originating from the external cache; expired data are used "
276d0eba39bSchristos 			"in the reply without first trying to refresh the data.");
2773b6c3722Schristos 	return 1;
2783b6c3722Schristos }
2793b6c3722Schristos 
2803b6c3722Schristos void
cachedb_deinit(struct module_env * env,int id)2813b6c3722Schristos cachedb_deinit(struct module_env* env, int id)
2823b6c3722Schristos {
2833b6c3722Schristos 	struct cachedb_env* cachedb_env;
2843b6c3722Schristos 	if(!env || !env->modinfo[id])
2853b6c3722Schristos 		return;
2863b6c3722Schristos 	cachedb_env = (struct cachedb_env*)env->modinfo[id];
2873b6c3722Schristos 	if(cachedb_env->enabled) {
2883b6c3722Schristos 		(*cachedb_env->backend->deinit)(env, cachedb_env);
2893b6c3722Schristos 	}
2903b6c3722Schristos 	free(cachedb_env);
2913b6c3722Schristos 	env->modinfo[id] = NULL;
2923b6c3722Schristos }
2933b6c3722Schristos 
2943b6c3722Schristos /** new query for cachedb */
2953b6c3722Schristos static int
cachedb_new(struct module_qstate * qstate,int id)2963b6c3722Schristos cachedb_new(struct module_qstate* qstate, int id)
2973b6c3722Schristos {
2983b6c3722Schristos 	struct cachedb_qstate* iq = (struct cachedb_qstate*)regional_alloc(
2993b6c3722Schristos 		qstate->region, sizeof(struct cachedb_qstate));
3003b6c3722Schristos 	qstate->minfo[id] = iq;
3013b6c3722Schristos 	if(!iq)
3023b6c3722Schristos 		return 0;
3033b6c3722Schristos 	memset(iq, 0, sizeof(*iq));
3043b6c3722Schristos 	/* initialise it */
3053b6c3722Schristos 	/* TODO */
3063b6c3722Schristos 
3073b6c3722Schristos 	return 1;
3083b6c3722Schristos }
3093b6c3722Schristos 
3103b6c3722Schristos /**
3113b6c3722Schristos  * Return an error
3123b6c3722Schristos  * @param qstate: our query state
3133b6c3722Schristos  * @param id: module id
3143b6c3722Schristos  * @param rcode: error code (DNS errcode).
3153b6c3722Schristos  * @return: 0 for use by caller, to make notation easy, like:
3163b6c3722Schristos  * 	return error_response(..).
3173b6c3722Schristos  */
3183b6c3722Schristos static int
error_response(struct module_qstate * qstate,int id,int rcode)3193b6c3722Schristos error_response(struct module_qstate* qstate, int id, int rcode)
3203b6c3722Schristos {
3213b6c3722Schristos 	verbose(VERB_QUERY, "return error response %s",
3223b6c3722Schristos 		sldns_lookup_by_id(sldns_rcodes, rcode)?
3233b6c3722Schristos 		sldns_lookup_by_id(sldns_rcodes, rcode)->name:"??");
3243b6c3722Schristos 	qstate->return_rcode = rcode;
3253b6c3722Schristos 	qstate->return_msg = NULL;
3263b6c3722Schristos 	qstate->ext_state[id] = module_finished;
3273b6c3722Schristos 	return 0;
3283b6c3722Schristos }
3293b6c3722Schristos 
3303b6c3722Schristos /**
3313b6c3722Schristos  * Hash the query name, type, class and dbacess-secret into lookup buffer.
3323b6c3722Schristos  * @param qstate: query state with query info
3333b6c3722Schristos  * 	and env->cfg with secret.
3343b6c3722Schristos  * @param buf: returned buffer with hash to lookup
3353b6c3722Schristos  * @param len: length of the buffer.
3363b6c3722Schristos  */
3373b6c3722Schristos static void
calc_hash(struct module_qstate * qstate,char * buf,size_t len)3383b6c3722Schristos calc_hash(struct module_qstate* qstate, char* buf, size_t len)
3393b6c3722Schristos {
3403b6c3722Schristos 	uint8_t clear[1024];
3413b6c3722Schristos 	size_t clen = 0;
3423b6c3722Schristos 	uint8_t hash[CACHEDB_HASHSIZE/8];
3433b6c3722Schristos 	const char* hex = "0123456789ABCDEF";
344d0eba39bSchristos 	const char* secret = qstate->env->cfg->cachedb_secret;
3453b6c3722Schristos 	size_t i;
3463b6c3722Schristos 
3473b6c3722Schristos 	/* copy the hash info into the clear buffer */
3483b6c3722Schristos 	if(clen + qstate->qinfo.qname_len < sizeof(clear)) {
3493b6c3722Schristos 		memmove(clear+clen, qstate->qinfo.qname,
3503b6c3722Schristos 			qstate->qinfo.qname_len);
3513b6c3722Schristos 		clen += qstate->qinfo.qname_len;
3523b6c3722Schristos 	}
3533b6c3722Schristos 	if(clen + 4 < sizeof(clear)) {
3543b6c3722Schristos 		uint16_t t = htons(qstate->qinfo.qtype);
3553b6c3722Schristos 		uint16_t c = htons(qstate->qinfo.qclass);
3563b6c3722Schristos 		memmove(clear+clen, &t, 2);
3573b6c3722Schristos 		memmove(clear+clen+2, &c, 2);
3583b6c3722Schristos 		clen += 4;
3593b6c3722Schristos 	}
3603b6c3722Schristos 	if(secret && secret[0] && clen + strlen(secret) < sizeof(clear)) {
3613b6c3722Schristos 		memmove(clear+clen, secret, strlen(secret));
3623b6c3722Schristos 		clen += strlen(secret);
3633b6c3722Schristos 	}
3643b6c3722Schristos 
3653b6c3722Schristos 	/* hash the buffer */
3663b6c3722Schristos 	secalgo_hash_sha256(clear, clen, hash);
36701049ae6Schristos #ifdef HAVE_EXPLICIT_BZERO
36801049ae6Schristos 	explicit_bzero(clear, clen);
36901049ae6Schristos #else
3703b6c3722Schristos 	memset(clear, 0, clen);
37101049ae6Schristos #endif
3723b6c3722Schristos 
3733b6c3722Schristos 	/* hex encode output for portability (some online dbs need
3743b6c3722Schristos 	 * no nulls, no control characters, and so on) */
3753b6c3722Schristos 	log_assert(len >= sizeof(hash)*2 + 1);
3763b6c3722Schristos 	(void)len;
3773b6c3722Schristos 	for(i=0; i<sizeof(hash); i++) {
3783b6c3722Schristos 		buf[i*2] = hex[(hash[i]&0xf0)>>4];
3793b6c3722Schristos 		buf[i*2+1] = hex[hash[i]&0x0f];
3803b6c3722Schristos 	}
3813b6c3722Schristos 	buf[sizeof(hash)*2] = 0;
3823b6c3722Schristos }
3833b6c3722Schristos 
3843b6c3722Schristos /** convert data from return_msg into the data buffer */
3853b6c3722Schristos static int
prep_data(struct module_qstate * qstate,struct sldns_buffer * buf)3863b6c3722Schristos prep_data(struct module_qstate* qstate, struct sldns_buffer* buf)
3873b6c3722Schristos {
3883b6c3722Schristos 	uint64_t timestamp, expiry;
3893b6c3722Schristos 	size_t oldlim;
3903b6c3722Schristos 	struct edns_data edns;
3913b6c3722Schristos 	memset(&edns, 0, sizeof(edns));
3923b6c3722Schristos 	edns.edns_present = 1;
3933b6c3722Schristos 	edns.bits = EDNS_DO;
3943b6c3722Schristos 	edns.ext_rcode = 0;
3953b6c3722Schristos 	edns.edns_version = EDNS_ADVERTISED_VERSION;
3963b6c3722Schristos 	edns.udp_size = EDNS_ADVERTISED_SIZE;
3973b6c3722Schristos 
3983b6c3722Schristos 	if(!qstate->return_msg || !qstate->return_msg->rep)
3993b6c3722Schristos 		return 0;
400*91f7d55fSchristos 	/* do not store failures like SERVFAIL in the cachedb, this avoids
401*91f7d55fSchristos 	 * overwriting expired, valid, content with broken content. */
402*91f7d55fSchristos 	if(FLAGS_GET_RCODE(qstate->return_msg->rep->flags) !=
403*91f7d55fSchristos 		LDNS_RCODE_NOERROR &&
404*91f7d55fSchristos 	   FLAGS_GET_RCODE(qstate->return_msg->rep->flags) !=
405*91f7d55fSchristos 		LDNS_RCODE_NXDOMAIN &&
406*91f7d55fSchristos 	   FLAGS_GET_RCODE(qstate->return_msg->rep->flags) !=
407*91f7d55fSchristos 		LDNS_RCODE_YXDOMAIN)
408*91f7d55fSchristos 		return 0;
4090cd9f4ecSchristos 	/* We don't store the reply if its TTL is 0 unless serve-expired is
4100cd9f4ecSchristos 	 * enabled.  Such a reply won't be reusable and simply be a waste for
4110cd9f4ecSchristos 	 * the backend.  It's also compatible with the default behavior of
4120cd9f4ecSchristos 	 * dns_cache_store_msg(). */
4130cd9f4ecSchristos 	if(qstate->return_msg->rep->ttl == 0 &&
4140cd9f4ecSchristos 		!qstate->env->cfg->serve_expired)
4150cd9f4ecSchristos 		return 0;
416*91f7d55fSchristos 
417*91f7d55fSchristos 	/* The EDE is added to the out-list so it is encoded in the cached message */
418*91f7d55fSchristos 	if (qstate->env->cfg->ede && qstate->return_msg->rep->reason_bogus != LDNS_EDE_NONE) {
419*91f7d55fSchristos 		edns_opt_list_append_ede(&edns.opt_list_out, qstate->env->scratch,
420*91f7d55fSchristos 					qstate->return_msg->rep->reason_bogus,
421*91f7d55fSchristos 					qstate->return_msg->rep->reason_bogus_str);
422*91f7d55fSchristos 	}
423*91f7d55fSchristos 
4243b6c3722Schristos 	if(verbosity >= VERB_ALGO)
4253b6c3722Schristos 		log_dns_msg("cachedb encoding", &qstate->return_msg->qinfo,
4263b6c3722Schristos 	                qstate->return_msg->rep);
4273b6c3722Schristos 	if(!reply_info_answer_encode(&qstate->return_msg->qinfo,
4283b6c3722Schristos 		qstate->return_msg->rep, 0, qstate->query_flags,
4293b6c3722Schristos 		buf, 0, 1, qstate->env->scratch, 65535, &edns, 1, 0))
4303b6c3722Schristos 		return 0;
4313b6c3722Schristos 
4323b6c3722Schristos 	/* TTLs in the return_msg are relative to time(0) so we have to
4333b6c3722Schristos 	 * store that, we also store the smallest ttl in the packet+time(0)
4343b6c3722Schristos 	 * as the packet expiry time */
4353b6c3722Schristos 	/* qstate->return_msg->rep->ttl contains that relative shortest ttl */
4363b6c3722Schristos 	timestamp = (uint64_t)*qstate->env->now;
4373b6c3722Schristos 	expiry = timestamp + (uint64_t)qstate->return_msg->rep->ttl;
4383b6c3722Schristos 	timestamp = htobe64(timestamp);
4393b6c3722Schristos 	expiry = htobe64(expiry);
4403b6c3722Schristos 	oldlim = sldns_buffer_limit(buf);
4413b6c3722Schristos 	if(oldlim + sizeof(timestamp)+sizeof(expiry) >=
4423b6c3722Schristos 		sldns_buffer_capacity(buf))
4433b6c3722Schristos 		return 0; /* doesn't fit. */
4443b6c3722Schristos 	sldns_buffer_set_limit(buf, oldlim + sizeof(timestamp)+sizeof(expiry));
4453b6c3722Schristos 	sldns_buffer_write_at(buf, oldlim, &timestamp, sizeof(timestamp));
4463b6c3722Schristos 	sldns_buffer_write_at(buf, oldlim+sizeof(timestamp), &expiry,
4473b6c3722Schristos 		sizeof(expiry));
4483b6c3722Schristos 
4493b6c3722Schristos 	return 1;
4503b6c3722Schristos }
4513b6c3722Schristos 
4523b6c3722Schristos /** check expiry, return true if matches OK */
4533b6c3722Schristos static int
good_expiry_and_qinfo(struct module_qstate * qstate,struct sldns_buffer * buf)4543b6c3722Schristos good_expiry_and_qinfo(struct module_qstate* qstate, struct sldns_buffer* buf)
4553b6c3722Schristos {
4563b6c3722Schristos 	uint64_t expiry;
4573b6c3722Schristos 	/* the expiry time is the last bytes of the buffer */
4583b6c3722Schristos 	if(sldns_buffer_limit(buf) < sizeof(expiry))
4593b6c3722Schristos 		return 0;
4603b6c3722Schristos 	sldns_buffer_read_at(buf, sldns_buffer_limit(buf)-sizeof(expiry),
4613b6c3722Schristos 		&expiry, sizeof(expiry));
4623b6c3722Schristos 	expiry = be64toh(expiry);
4633b6c3722Schristos 
464d0eba39bSchristos 	/* Check if we are allowed to return expired entries:
465d0eba39bSchristos 	 * - serve_expired needs to be set
466d0eba39bSchristos 	 * - if SERVE_EXPIRED_TTL is set make sure that the record is not older
467d0eba39bSchristos 	 *   than that. */
4680cd9f4ecSchristos 	if((time_t)expiry < *qstate->env->now &&
469d0eba39bSchristos 		(!qstate->env->cfg->serve_expired ||
470d0eba39bSchristos 			(SERVE_EXPIRED_TTL &&
471d0eba39bSchristos 			*qstate->env->now - (time_t)expiry > SERVE_EXPIRED_TTL)))
4723b6c3722Schristos 		return 0;
4733b6c3722Schristos 
4743b6c3722Schristos 	return 1;
4753b6c3722Schristos }
4763b6c3722Schristos 
4770cd9f4ecSchristos /* Adjust the TTL of the given RRset by 'subtract'.  If 'subtract' is
4780cd9f4ecSchristos  * negative, set the TTL to 0. */
4790cd9f4ecSchristos static void
packed_rrset_ttl_subtract(struct packed_rrset_data * data,time_t subtract)4800cd9f4ecSchristos packed_rrset_ttl_subtract(struct packed_rrset_data* data, time_t subtract)
4810cd9f4ecSchristos {
4820cd9f4ecSchristos 	size_t i;
4830cd9f4ecSchristos 	size_t total = data->count + data->rrsig_count;
4840cd9f4ecSchristos 	if(subtract >= 0 && data->ttl > subtract)
4850cd9f4ecSchristos 		data->ttl -= subtract;
4860cd9f4ecSchristos 	else	data->ttl = 0;
4870cd9f4ecSchristos 	for(i=0; i<total; i++) {
4880cd9f4ecSchristos 		if(subtract >= 0 && data->rr_ttl[i] > subtract)
4890cd9f4ecSchristos 			data->rr_ttl[i] -= subtract;
4900cd9f4ecSchristos 		else	data->rr_ttl[i] = 0;
4910cd9f4ecSchristos 	}
492d0eba39bSchristos 	data->ttl_add = (subtract < data->ttl_add) ? (data->ttl_add - subtract) : 0;
4930cd9f4ecSchristos }
4940cd9f4ecSchristos 
4950cd9f4ecSchristos /* Adjust the TTL of a DNS message and its RRs by 'adjust'.  If 'adjust' is
4960cd9f4ecSchristos  * negative, set the TTLs to 0. */
4970cd9f4ecSchristos static void
adjust_msg_ttl(struct dns_msg * msg,time_t adjust)4980cd9f4ecSchristos adjust_msg_ttl(struct dns_msg* msg, time_t adjust)
4990cd9f4ecSchristos {
5000cd9f4ecSchristos 	size_t i;
5010cd9f4ecSchristos 	if(adjust >= 0 && msg->rep->ttl > adjust)
5020cd9f4ecSchristos 		msg->rep->ttl -= adjust;
503d0eba39bSchristos 	else
504d0eba39bSchristos 		msg->rep->ttl = 0;
5050cd9f4ecSchristos 	msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl);
506f42d8de7Schristos 	msg->rep->serve_expired_ttl = msg->rep->ttl + SERVE_EXPIRED_TTL;
5070cd9f4ecSchristos 
5080cd9f4ecSchristos 	for(i=0; i<msg->rep->rrset_count; i++) {
5090cd9f4ecSchristos 		packed_rrset_ttl_subtract((struct packed_rrset_data*)msg->
5100cd9f4ecSchristos 			rep->rrsets[i]->entry.data, adjust);
5110cd9f4ecSchristos 	}
5120cd9f4ecSchristos }
5130cd9f4ecSchristos 
5143b6c3722Schristos /** convert dns message in buffer to return_msg */
5153b6c3722Schristos static int
parse_data(struct module_qstate * qstate,struct sldns_buffer * buf)5163b6c3722Schristos parse_data(struct module_qstate* qstate, struct sldns_buffer* buf)
5173b6c3722Schristos {
5183b6c3722Schristos 	struct msg_parse* prs;
5193b6c3722Schristos 	struct edns_data edns;
520*91f7d55fSchristos 	struct edns_option* ede;
5213b6c3722Schristos 	uint64_t timestamp, expiry;
5223b6c3722Schristos 	time_t adjust;
5233b6c3722Schristos 	size_t lim = sldns_buffer_limit(buf);
5243b6c3722Schristos 	if(lim < LDNS_HEADER_SIZE+sizeof(timestamp)+sizeof(expiry))
5253b6c3722Schristos 		return 0; /* too short */
5263b6c3722Schristos 
5273b6c3722Schristos 	/* remove timestamp and expiry from end */
5283b6c3722Schristos 	sldns_buffer_read_at(buf, lim-sizeof(expiry), &expiry, sizeof(expiry));
5293b6c3722Schristos 	sldns_buffer_read_at(buf, lim-sizeof(expiry)-sizeof(timestamp),
5303b6c3722Schristos 		&timestamp, sizeof(timestamp));
5313b6c3722Schristos 	expiry = be64toh(expiry);
5323b6c3722Schristos 	timestamp = be64toh(timestamp);
5333b6c3722Schristos 
5343b6c3722Schristos 	/* parse DNS packet */
5353b6c3722Schristos 	regional_free_all(qstate->env->scratch);
5363b6c3722Schristos 	prs = (struct msg_parse*)regional_alloc(qstate->env->scratch,
5373b6c3722Schristos 		sizeof(struct msg_parse));
5383b6c3722Schristos 	if(!prs)
5393b6c3722Schristos 		return 0; /* out of memory */
5403b6c3722Schristos 	memset(prs, 0, sizeof(*prs));
5413b6c3722Schristos 	memset(&edns, 0, sizeof(edns));
5423b6c3722Schristos 	sldns_buffer_set_limit(buf, lim - sizeof(expiry)-sizeof(timestamp));
5433b6c3722Schristos 	if(parse_packet(buf, prs, qstate->env->scratch) != LDNS_RCODE_NOERROR) {
5443b6c3722Schristos 		sldns_buffer_set_limit(buf, lim);
5453b6c3722Schristos 		return 0;
5463b6c3722Schristos 	}
5477a540f2bSchristos 	if(parse_extract_edns_from_response_msg(prs, &edns, qstate->env->scratch) !=
5483b6c3722Schristos 		LDNS_RCODE_NOERROR) {
5493b6c3722Schristos 		sldns_buffer_set_limit(buf, lim);
5503b6c3722Schristos 		return 0;
5513b6c3722Schristos 	}
5523b6c3722Schristos 
5533b6c3722Schristos 	qstate->return_msg = dns_alloc_msg(buf, prs, qstate->region);
5543b6c3722Schristos 	sldns_buffer_set_limit(buf, lim);
5553b6c3722Schristos 	if(!qstate->return_msg)
5563b6c3722Schristos 		return 0;
5573b6c3722Schristos 
558*91f7d55fSchristos 	/* We find the EDE in the in-list after parsing */
559*91f7d55fSchristos 	if(qstate->env->cfg->ede &&
560*91f7d55fSchristos 		(ede = edns_opt_list_find(edns.opt_list_in, LDNS_EDNS_EDE))) {
561*91f7d55fSchristos 		if(ede->opt_len >= 2) {
562*91f7d55fSchristos 			qstate->return_msg->rep->reason_bogus =
563*91f7d55fSchristos 				sldns_read_uint16(ede->opt_data);
564*91f7d55fSchristos 		}
565*91f7d55fSchristos 		/* allocate space and store the error string and it's size */
566*91f7d55fSchristos 		if(ede->opt_len > 2) {
567*91f7d55fSchristos 			size_t ede_len = ede->opt_len - 2;
568*91f7d55fSchristos 			qstate->return_msg->rep->reason_bogus_str = regional_alloc(
569*91f7d55fSchristos 				qstate->region, sizeof(char) * (ede_len+1));
570*91f7d55fSchristos 			memcpy(qstate->return_msg->rep->reason_bogus_str,
571*91f7d55fSchristos 				ede->opt_data+2, ede_len);
572*91f7d55fSchristos 			qstate->return_msg->rep->reason_bogus_str[ede_len] = 0;
573*91f7d55fSchristos 		}
574*91f7d55fSchristos 	}
575*91f7d55fSchristos 
5763b6c3722Schristos 	qstate->return_rcode = LDNS_RCODE_NOERROR;
5773b6c3722Schristos 
5783b6c3722Schristos 	/* see how much of the TTL expired, and remove it */
5790cd9f4ecSchristos 	if(*qstate->env->now <= (time_t)timestamp) {
5800cd9f4ecSchristos 		verbose(VERB_ALGO, "cachedb msg adjust by zero");
5810cd9f4ecSchristos 		return 1; /* message from the future (clock skew?) */
5823b6c3722Schristos 	}
5830cd9f4ecSchristos 	adjust = *qstate->env->now - (time_t)timestamp;
5840cd9f4ecSchristos 	if(qstate->return_msg->rep->ttl < adjust) {
5850cd9f4ecSchristos 		verbose(VERB_ALGO, "cachedb msg expired");
5860cd9f4ecSchristos 		/* If serve-expired is enabled, we still use an expired message
5870cd9f4ecSchristos 		 * setting the TTL to 0. */
588*91f7d55fSchristos 		if(!qstate->env->cfg->serve_expired ||
589*91f7d55fSchristos 			(FLAGS_GET_RCODE(qstate->return_msg->rep->flags)
590*91f7d55fSchristos 			!= LDNS_RCODE_NOERROR &&
591*91f7d55fSchristos 			FLAGS_GET_RCODE(qstate->return_msg->rep->flags)
592*91f7d55fSchristos 			!= LDNS_RCODE_NXDOMAIN &&
593*91f7d55fSchristos 			FLAGS_GET_RCODE(qstate->return_msg->rep->flags)
594*91f7d55fSchristos 			!= LDNS_RCODE_YXDOMAIN))
5950cd9f4ecSchristos 			return 0; /* message expired */
596*91f7d55fSchristos 		else
597*91f7d55fSchristos 			adjust = -1;
5980cd9f4ecSchristos 	}
5990cd9f4ecSchristos 	verbose(VERB_ALGO, "cachedb msg adjusted down by %d", (int)adjust);
6000cd9f4ecSchristos 	adjust_msg_ttl(qstate->return_msg, adjust);
6013b6c3722Schristos 
6020cd9f4ecSchristos 	/* Similar to the unbound worker, if serve-expired is enabled and
6030cd9f4ecSchristos 	 * the msg would be considered to be expired, mark the state so a
6040cd9f4ecSchristos 	 * refetch will be scheduled.  The comparison between 'expiry' and
6050cd9f4ecSchristos 	 * 'now' should be redundant given how these values were calculated,
6060cd9f4ecSchristos 	 * but we check it just in case as does good_expiry_and_qinfo(). */
6070cd9f4ecSchristos 	if(qstate->env->cfg->serve_expired &&
6080cd9f4ecSchristos 		(adjust == -1 || (time_t)expiry < *qstate->env->now)) {
6090cd9f4ecSchristos 		qstate->need_refetch = 1;
6100cd9f4ecSchristos 	}
6110cd9f4ecSchristos 
6120cd9f4ecSchristos 	return 1;
6133b6c3722Schristos }
6143b6c3722Schristos 
6153b6c3722Schristos /**
6163b6c3722Schristos  * Lookup the qstate.qinfo in extcache, store in qstate.return_msg.
6173b6c3722Schristos  * return true if lookup was successful.
6183b6c3722Schristos  */
6193b6c3722Schristos static int
cachedb_extcache_lookup(struct module_qstate * qstate,struct cachedb_env * ie)6203b6c3722Schristos cachedb_extcache_lookup(struct module_qstate* qstate, struct cachedb_env* ie)
6213b6c3722Schristos {
6223b6c3722Schristos 	char key[(CACHEDB_HASHSIZE/8)*2+1];
6233b6c3722Schristos 	calc_hash(qstate, key, sizeof(key));
6243b6c3722Schristos 
6253b6c3722Schristos 	/* call backend to fetch data for key into scratch buffer */
6263b6c3722Schristos 	if( !(*ie->backend->lookup)(qstate->env, ie, key,
6273b6c3722Schristos 		qstate->env->scratch_buffer)) {
6283b6c3722Schristos 		return 0;
6293b6c3722Schristos 	}
6303b6c3722Schristos 
6313b6c3722Schristos 	/* check expiry date and check if query-data matches */
6323b6c3722Schristos 	if( !good_expiry_and_qinfo(qstate, qstate->env->scratch_buffer) ) {
6333b6c3722Schristos 		return 0;
6343b6c3722Schristos 	}
6353b6c3722Schristos 
6363b6c3722Schristos 	/* parse dns message into return_msg */
6373b6c3722Schristos 	if( !parse_data(qstate, qstate->env->scratch_buffer) ) {
6383b6c3722Schristos 		return 0;
6393b6c3722Schristos 	}
6403b6c3722Schristos 	return 1;
6413b6c3722Schristos }
6423b6c3722Schristos 
6433b6c3722Schristos /**
6443b6c3722Schristos  * Store the qstate.return_msg in extcache for key qstate.info
6453b6c3722Schristos  */
6463b6c3722Schristos static void
cachedb_extcache_store(struct module_qstate * qstate,struct cachedb_env * ie)6473b6c3722Schristos cachedb_extcache_store(struct module_qstate* qstate, struct cachedb_env* ie)
6483b6c3722Schristos {
6493b6c3722Schristos 	char key[(CACHEDB_HASHSIZE/8)*2+1];
6503b6c3722Schristos 	calc_hash(qstate, key, sizeof(key));
6513b6c3722Schristos 
6523b6c3722Schristos 	/* prepare data in scratch buffer */
6533b6c3722Schristos 	if(!prep_data(qstate, qstate->env->scratch_buffer))
6543b6c3722Schristos 		return;
6553b6c3722Schristos 
6563b6c3722Schristos 	/* call backend */
6573b6c3722Schristos 	(*ie->backend->store)(qstate->env, ie, key,
6583b6c3722Schristos 		sldns_buffer_begin(qstate->env->scratch_buffer),
659d0eba39bSchristos 		sldns_buffer_limit(qstate->env->scratch_buffer),
660d0eba39bSchristos 		qstate->return_msg->rep->ttl);
6613b6c3722Schristos }
6623b6c3722Schristos 
6633b6c3722Schristos /**
6643b6c3722Schristos  * See if unbound's internal cache can answer the query
6653b6c3722Schristos  */
6663b6c3722Schristos static int
cachedb_intcache_lookup(struct module_qstate * qstate,struct cachedb_env * cde)667*91f7d55fSchristos cachedb_intcache_lookup(struct module_qstate* qstate, struct cachedb_env* cde)
6683b6c3722Schristos {
6697a540f2bSchristos 	uint8_t* dpname=NULL;
6707a540f2bSchristos 	size_t dpnamelen=0;
6713b6c3722Schristos 	struct dns_msg* msg;
672*91f7d55fSchristos 	/* for testframe bypass this lookup */
673*91f7d55fSchristos 	if(cde->backend == &testframe_backend) {
674*91f7d55fSchristos 		return 0;
675*91f7d55fSchristos 	}
6767a540f2bSchristos 	if(iter_stub_fwd_no_cache(qstate, &qstate->qinfo,
6777a540f2bSchristos 		&dpname, &dpnamelen))
6787a540f2bSchristos 		return 0; /* no cache for these queries */
6793b6c3722Schristos 	msg = dns_cache_lookup(qstate->env, qstate->qinfo.qname,
6803b6c3722Schristos 		qstate->qinfo.qname_len, qstate->qinfo.qtype,
6813b6c3722Schristos 		qstate->qinfo.qclass, qstate->query_flags,
6827cd94d69Schristos 		qstate->region, qstate->env->scratch,
6837a540f2bSchristos 		1, /* no partial messages with only a CNAME */
6847a540f2bSchristos 		dpname, dpnamelen
6857cd94d69Schristos 		);
6867cd94d69Schristos 	if(!msg && qstate->env->neg_cache &&
6877cd94d69Schristos 		iter_qname_indicates_dnssec(qstate->env, &qstate->qinfo)) {
6883b6c3722Schristos 		/* lookup in negative cache; may result in
6893b6c3722Schristos 		 * NOERROR/NODATA or NXDOMAIN answers that need validation */
6903b6c3722Schristos 		msg = val_neg_getmsg(qstate->env->neg_cache, &qstate->qinfo,
6913b6c3722Schristos 			qstate->region, qstate->env->rrset_cache,
6923b6c3722Schristos 			qstate->env->scratch_buffer,
6937cd94d69Schristos 			*qstate->env->now, 1/*add SOA*/, NULL,
6947cd94d69Schristos 			qstate->env->cfg);
6953b6c3722Schristos 	}
6963b6c3722Schristos 	if(!msg)
6973b6c3722Schristos 		return 0;
6983b6c3722Schristos 	/* this is the returned msg */
6993b6c3722Schristos 	qstate->return_rcode = LDNS_RCODE_NOERROR;
7003b6c3722Schristos 	qstate->return_msg = msg;
7013b6c3722Schristos 	return 1;
7023b6c3722Schristos }
7033b6c3722Schristos 
7043b6c3722Schristos /**
7053b6c3722Schristos  * Store query into the internal cache of unbound.
7063b6c3722Schristos  */
7073b6c3722Schristos static void
cachedb_intcache_store(struct module_qstate * qstate)7083b6c3722Schristos cachedb_intcache_store(struct module_qstate* qstate)
7093b6c3722Schristos {
7100cd9f4ecSchristos 	uint32_t store_flags = qstate->query_flags;
7110cd9f4ecSchristos 
7120cd9f4ecSchristos 	if(qstate->env->cfg->serve_expired)
7130cd9f4ecSchristos 		store_flags |= DNSCACHE_STORE_ZEROTTL;
7143b6c3722Schristos 	if(!qstate->return_msg)
7153b6c3722Schristos 		return;
7163b6c3722Schristos 	(void)dns_cache_store(qstate->env, &qstate->qinfo,
7173b6c3722Schristos 		qstate->return_msg->rep, 0, qstate->prefetch_leeway, 0,
7187a540f2bSchristos 		qstate->region, store_flags, qstate->qstarttime);
7193b6c3722Schristos }
7203b6c3722Schristos 
7213b6c3722Schristos /**
7223b6c3722Schristos  * Handle a cachedb module event with a query
7233b6c3722Schristos  * @param qstate: query state (from the mesh), passed between modules.
7243b6c3722Schristos  * 	contains qstate->env module environment with global caches and so on.
7253b6c3722Schristos  * @param iq: query state specific for this module.  per-query.
7263b6c3722Schristos  * @param ie: environment specific for this module.  global.
7273b6c3722Schristos  * @param id: module id.
7283b6c3722Schristos  */
7293b6c3722Schristos static void
cachedb_handle_query(struct module_qstate * qstate,struct cachedb_qstate * ATTR_UNUSED (iq),struct cachedb_env * ie,int id)7303b6c3722Schristos cachedb_handle_query(struct module_qstate* qstate,
7313b6c3722Schristos 	struct cachedb_qstate* ATTR_UNUSED(iq),
7323b6c3722Schristos 	struct cachedb_env* ie, int id)
7333b6c3722Schristos {
734*91f7d55fSchristos 	qstate->is_cachedb_answer = 0;
7353b6c3722Schristos 	/* check if we are enabled, and skip if so */
7363b6c3722Schristos 	if(!ie->enabled) {
7373b6c3722Schristos 		/* pass request to next module */
7383b6c3722Schristos 		qstate->ext_state[id] = module_wait_module;
7393b6c3722Schristos 		return;
7403b6c3722Schristos 	}
7413b6c3722Schristos 
7420cd9f4ecSchristos 	if(qstate->blacklist || qstate->no_cache_lookup) {
7430cd9f4ecSchristos 		/* cache is blacklisted or we are instructed from edns to not look */
7443b6c3722Schristos 		/* pass request to next module */
7453b6c3722Schristos 		qstate->ext_state[id] = module_wait_module;
7463b6c3722Schristos 		return;
7473b6c3722Schristos 	}
7483b6c3722Schristos 
749d0eba39bSchristos 	/* lookup inside unbound's internal cache.
750d0eba39bSchristos 	 * This does not look for expired entries. */
751*91f7d55fSchristos 	if(cachedb_intcache_lookup(qstate, ie)) {
7520cd9f4ecSchristos 		if(verbosity >= VERB_ALGO) {
7530cd9f4ecSchristos 			if(qstate->return_msg->rep)
7543b6c3722Schristos 				log_dns_msg("cachedb internal cache lookup",
7553b6c3722Schristos 					&qstate->return_msg->qinfo,
7563b6c3722Schristos 					qstate->return_msg->rep);
7570cd9f4ecSchristos 			else log_info("cachedb internal cache lookup: rcode %s",
758d0eba39bSchristos 				sldns_lookup_by_id(sldns_rcodes, qstate->return_rcode)
759d0eba39bSchristos 				?sldns_lookup_by_id(sldns_rcodes, qstate->return_rcode)->name
760d0eba39bSchristos 				:"??");
7610cd9f4ecSchristos 		}
7623b6c3722Schristos 		/* we are done with the query */
7633b6c3722Schristos 		qstate->ext_state[id] = module_finished;
7643b6c3722Schristos 		return;
7653b6c3722Schristos 	}
7663b6c3722Schristos 
7673b6c3722Schristos 	/* ask backend cache to see if we have data */
7683b6c3722Schristos 	if(cachedb_extcache_lookup(qstate, ie)) {
7693b6c3722Schristos 		if(verbosity >= VERB_ALGO)
7703b6c3722Schristos 			log_dns_msg(ie->backend->name,
7713b6c3722Schristos 				&qstate->return_msg->qinfo,
7723b6c3722Schristos 				qstate->return_msg->rep);
7733b6c3722Schristos 		/* store this result in internal cache */
7743b6c3722Schristos 		cachedb_intcache_store(qstate);
775d0eba39bSchristos 		/* In case we have expired data but there is a client timer for expired
776d0eba39bSchristos 		 * answers, pass execution to next module in order to try updating the
777d0eba39bSchristos 		 * data first.
778d0eba39bSchristos 		 * TODO: this needs revisit. The expired data stored from cachedb has
779d0eba39bSchristos 		 * 0 TTL which is picked up by iterator later when looking in the cache.
780d0eba39bSchristos 		 * Document that ext cachedb does not work properly with
781d0eba39bSchristos 		 * serve_stale_reply_ttl yet. */
782d0eba39bSchristos 		if(qstate->need_refetch && qstate->serve_expired_data &&
783d0eba39bSchristos 			qstate->serve_expired_data->timer) {
784d0eba39bSchristos 				qstate->return_msg = NULL;
785d0eba39bSchristos 				qstate->ext_state[id] = module_wait_module;
786d0eba39bSchristos 				return;
787d0eba39bSchristos 		}
788*91f7d55fSchristos 		qstate->is_cachedb_answer = 1;
7893b6c3722Schristos 		/* we are done with the query */
7903b6c3722Schristos 		qstate->ext_state[id] = module_finished;
7913b6c3722Schristos 		return;
7923b6c3722Schristos 	}
7933b6c3722Schristos 
7943b6c3722Schristos 	/* no cache fetches */
7953b6c3722Schristos 	/* pass request to next module */
7963b6c3722Schristos 	qstate->ext_state[id] = module_wait_module;
7973b6c3722Schristos }
7983b6c3722Schristos 
7993b6c3722Schristos /**
8003b6c3722Schristos  * Handle a cachedb module event with a response from the iterator.
8013b6c3722Schristos  * @param qstate: query state (from the mesh), passed between modules.
8023b6c3722Schristos  * 	contains qstate->env module environment with global caches and so on.
8033b6c3722Schristos  * @param iq: query state specific for this module.  per-query.
8043b6c3722Schristos  * @param ie: environment specific for this module.  global.
8053b6c3722Schristos  * @param id: module id.
8063b6c3722Schristos  */
8073b6c3722Schristos static void
cachedb_handle_response(struct module_qstate * qstate,struct cachedb_qstate * ATTR_UNUSED (iq),struct cachedb_env * ie,int id)8083b6c3722Schristos cachedb_handle_response(struct module_qstate* qstate,
8093b6c3722Schristos 	struct cachedb_qstate* ATTR_UNUSED(iq), struct cachedb_env* ie, int id)
8103b6c3722Schristos {
811*91f7d55fSchristos 	qstate->is_cachedb_answer = 0;
8120cd9f4ecSchristos 	/* check if we are not enabled or instructed to not cache, and skip */
8130cd9f4ecSchristos 	if(!ie->enabled || qstate->no_cache_store) {
8143b6c3722Schristos 		/* we are done with the query */
8153b6c3722Schristos 		qstate->ext_state[id] = module_finished;
8163b6c3722Schristos 		return;
8173b6c3722Schristos 	}
818*91f7d55fSchristos 	if(qstate->env->cfg->cachedb_no_store) {
819*91f7d55fSchristos 		/* do not store the item in the external cache */
820*91f7d55fSchristos 		qstate->ext_state[id] = module_finished;
821*91f7d55fSchristos 		return;
822*91f7d55fSchristos 	}
8233b6c3722Schristos 
8243b6c3722Schristos 	/* store the item into the backend cache */
8253b6c3722Schristos 	cachedb_extcache_store(qstate, ie);
8263b6c3722Schristos 
8273b6c3722Schristos 	/* we are done with the query */
8283b6c3722Schristos 	qstate->ext_state[id] = module_finished;
8293b6c3722Schristos }
8303b6c3722Schristos 
8313b6c3722Schristos void
cachedb_operate(struct module_qstate * qstate,enum module_ev event,int id,struct outbound_entry * outbound)8323b6c3722Schristos cachedb_operate(struct module_qstate* qstate, enum module_ev event, int id,
8333b6c3722Schristos 	struct outbound_entry* outbound)
8343b6c3722Schristos {
8353b6c3722Schristos 	struct cachedb_env* ie = (struct cachedb_env*)qstate->env->modinfo[id];
8363b6c3722Schristos 	struct cachedb_qstate* iq = (struct cachedb_qstate*)qstate->minfo[id];
8373b6c3722Schristos 	verbose(VERB_QUERY, "cachedb[module %d] operate: extstate:%s event:%s",
8383b6c3722Schristos 		id, strextstate(qstate->ext_state[id]), strmodulevent(event));
8393b6c3722Schristos 	if(iq) log_query_info(VERB_QUERY, "cachedb operate: query",
8403b6c3722Schristos 		&qstate->qinfo);
8413b6c3722Schristos 
8423b6c3722Schristos 	/* perform cachedb state machine */
8433b6c3722Schristos 	if((event == module_event_new || event == module_event_pass) &&
8443b6c3722Schristos 		iq == NULL) {
8453b6c3722Schristos 		if(!cachedb_new(qstate, id)) {
8463b6c3722Schristos 			(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
8473b6c3722Schristos 			return;
8483b6c3722Schristos 		}
8493b6c3722Schristos 		iq = (struct cachedb_qstate*)qstate->minfo[id];
8503b6c3722Schristos 	}
8513b6c3722Schristos 	if(iq && (event == module_event_pass || event == module_event_new)) {
8523b6c3722Schristos 		cachedb_handle_query(qstate, iq, ie, id);
8533b6c3722Schristos 		return;
8543b6c3722Schristos 	}
8553b6c3722Schristos 	if(iq && (event == module_event_moddone)) {
8563b6c3722Schristos 		cachedb_handle_response(qstate, iq, ie, id);
8573b6c3722Schristos 		return;
8583b6c3722Schristos 	}
8593b6c3722Schristos 	if(iq && outbound) {
8603b6c3722Schristos 		/* cachedb does not need to process responses at this time
8613b6c3722Schristos 		 * ignore it.
8623b6c3722Schristos 		cachedb_process_response(qstate, iq, ie, id, outbound, event);
8633b6c3722Schristos 		*/
8643b6c3722Schristos 		return;
8653b6c3722Schristos 	}
8663b6c3722Schristos 	if(event == module_event_error) {
8673b6c3722Schristos 		verbose(VERB_ALGO, "got called with event error, giving up");
8683b6c3722Schristos 		(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
8693b6c3722Schristos 		return;
8703b6c3722Schristos 	}
8710cd9f4ecSchristos 	if(!iq && (event == module_event_moddone)) {
8720cd9f4ecSchristos 		/* during priming, module done but we never started */
8730cd9f4ecSchristos 		qstate->ext_state[id] = module_finished;
8740cd9f4ecSchristos 		return;
8750cd9f4ecSchristos 	}
8763b6c3722Schristos 
8773b6c3722Schristos 	log_err("bad event for cachedb");
8783b6c3722Schristos 	(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
8793b6c3722Schristos }
8803b6c3722Schristos 
8813b6c3722Schristos void
cachedb_inform_super(struct module_qstate * ATTR_UNUSED (qstate),int ATTR_UNUSED (id),struct module_qstate * ATTR_UNUSED (super))8823b6c3722Schristos cachedb_inform_super(struct module_qstate* ATTR_UNUSED(qstate),
8833b6c3722Schristos 	int ATTR_UNUSED(id), struct module_qstate* ATTR_UNUSED(super))
8843b6c3722Schristos {
8853b6c3722Schristos 	/* cachedb does not use subordinate requests at this time */
8863b6c3722Schristos 	verbose(VERB_ALGO, "cachedb inform_super was called");
8873b6c3722Schristos }
8883b6c3722Schristos 
8893b6c3722Schristos void
cachedb_clear(struct module_qstate * qstate,int id)8903b6c3722Schristos cachedb_clear(struct module_qstate* qstate, int id)
8913b6c3722Schristos {
8923b6c3722Schristos 	struct cachedb_qstate* iq;
8933b6c3722Schristos 	if(!qstate)
8943b6c3722Schristos 		return;
8953b6c3722Schristos 	iq = (struct cachedb_qstate*)qstate->minfo[id];
8963b6c3722Schristos 	if(iq) {
8973b6c3722Schristos 		/* free contents of iq */
8983b6c3722Schristos 		/* TODO */
8993b6c3722Schristos 	}
9003b6c3722Schristos 	qstate->minfo[id] = NULL;
9013b6c3722Schristos }
9023b6c3722Schristos 
9033b6c3722Schristos size_t
cachedb_get_mem(struct module_env * env,int id)9043b6c3722Schristos cachedb_get_mem(struct module_env* env, int id)
9053b6c3722Schristos {
9063b6c3722Schristos 	struct cachedb_env* ie = (struct cachedb_env*)env->modinfo[id];
9073b6c3722Schristos 	if(!ie)
9083b6c3722Schristos 		return 0;
9093b6c3722Schristos 	return sizeof(*ie); /* TODO - more mem */
9103b6c3722Schristos }
9113b6c3722Schristos 
9123b6c3722Schristos /**
9133b6c3722Schristos  * The cachedb function block
9143b6c3722Schristos  */
9153b6c3722Schristos static struct module_func_block cachedb_block = {
9163b6c3722Schristos 	"cachedb",
9173b6c3722Schristos 	&cachedb_init, &cachedb_deinit, &cachedb_operate,
9183b6c3722Schristos 	&cachedb_inform_super, &cachedb_clear, &cachedb_get_mem
9193b6c3722Schristos };
9203b6c3722Schristos 
9213b6c3722Schristos struct module_func_block*
cachedb_get_funcblock(void)9223b6c3722Schristos cachedb_get_funcblock(void)
9233b6c3722Schristos {
9243b6c3722Schristos 	return &cachedb_block;
9253b6c3722Schristos }
9263b6c3722Schristos #endif /* USE_CACHEDB */
927