cachedb.c revision 356345
1/*
2 * cachedb/cachedb.c - cache from a database external to the program module
3 *
4 * Copyright (c) 2016, NLnet Labs. All rights reserved.
5 *
6 * This software is open source.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
14 *
15 * Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
18 *
19 * Neither the name of the NLNET LABS nor the names of its contributors may
20 * be used to endorse or promote products derived from this software without
21 * specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35
36/**
37 * \file
38 *
39 * This file contains a module that uses an external database to cache
40 * dns responses.
41 */
42
43#include "config.h"
44#ifdef USE_CACHEDB
45#include "cachedb/cachedb.h"
46#include "cachedb/redis.h"
47#include "util/regional.h"
48#include "util/net_help.h"
49#include "util/config_file.h"
50#include "util/data/msgreply.h"
51#include "util/data/msgencode.h"
52#include "services/cache/dns.h"
53#include "validator/val_neg.h"
54#include "validator/val_secalgo.h"
55#include "iterator/iter_utils.h"
56#include "sldns/parseutil.h"
57#include "sldns/wire2str.h"
58#include "sldns/sbuffer.h"
59
60/* header file for htobe64 */
61#ifdef HAVE_ENDIAN_H
62#  include <endian.h>
63#endif
64#ifdef HAVE_SYS_ENDIAN_H
65#  include <sys/endian.h>
66#endif
67#ifdef HAVE_LIBKERN_OSBYTEORDER_H
68/* In practice this is specific to MacOS X.  We assume it doesn't have
69* htobe64/be64toh but has alternatives with a different name. */
70#  include <libkern/OSByteOrder.h>
71#  define htobe64(x) OSSwapHostToBigInt64(x)
72#  define be64toh(x) OSSwapBigToHostInt64(x)
73#endif
74
75/* Some compilers do not define __BYTE_ORDER__, like IBM XLC on AIX */
76#ifndef be64toh
77#if defined(__sun) || defined(_AIX)
78#  if __BIG_ENDIAN__
79#    define be64toh(n) (n)
80#    define htobe64(n) (n)
81#  else
82#    define be64toh(n) (((uint64_t)htonl((n) & 0xFFFFFFFF) << 32) | htonl((n) >> 32))
83#    define htobe64(n) (((uint64_t)htonl((n) & 0xFFFFFFFF) << 32) | htonl((n) >> 32))
84#  endif
85#endif
86#endif /* be64toh */
87
88/** the unit test testframe for cachedb, its module state contains
89 * a cache for a couple queries (in memory). */
90struct testframe_moddata {
91	/** lock for mutex */
92	lock_basic_type lock;
93	/** key for single stored data element, NULL if none */
94	char* stored_key;
95	/** data for single stored data element, NULL if none */
96	uint8_t* stored_data;
97	/** length of stored data */
98	size_t stored_datalen;
99};
100
101static int
102testframe_init(struct module_env* env, struct cachedb_env* cachedb_env)
103{
104	struct testframe_moddata* d;
105	(void)env;
106	verbose(VERB_ALGO, "testframe_init");
107	d = (struct testframe_moddata*)calloc(1,
108		sizeof(struct testframe_moddata));
109	cachedb_env->backend_data = (void*)d;
110	if(!cachedb_env->backend_data) {
111		log_err("out of memory");
112		return 0;
113	}
114	lock_basic_init(&d->lock);
115	lock_protect(&d->lock, d, sizeof(*d));
116	return 1;
117}
118
119static void
120testframe_deinit(struct module_env* env, struct cachedb_env* cachedb_env)
121{
122	struct testframe_moddata* d = (struct testframe_moddata*)
123		cachedb_env->backend_data;
124	(void)env;
125	verbose(VERB_ALGO, "testframe_deinit");
126	if(!d)
127		return;
128	lock_basic_destroy(&d->lock);
129	free(d->stored_key);
130	free(d->stored_data);
131	free(d);
132}
133
134static int
135testframe_lookup(struct module_env* env, struct cachedb_env* cachedb_env,
136	char* key, struct sldns_buffer* result_buffer)
137{
138	struct testframe_moddata* d = (struct testframe_moddata*)
139		cachedb_env->backend_data;
140	(void)env;
141	verbose(VERB_ALGO, "testframe_lookup of %s", key);
142	lock_basic_lock(&d->lock);
143	if(d->stored_key && strcmp(d->stored_key, key) == 0) {
144		if(d->stored_datalen > sldns_buffer_capacity(result_buffer)) {
145			lock_basic_unlock(&d->lock);
146			return 0; /* too large */
147		}
148		verbose(VERB_ALGO, "testframe_lookup found %d bytes",
149			(int)d->stored_datalen);
150		sldns_buffer_clear(result_buffer);
151		sldns_buffer_write(result_buffer, d->stored_data,
152			d->stored_datalen);
153		sldns_buffer_flip(result_buffer);
154		lock_basic_unlock(&d->lock);
155		return 1;
156	}
157	lock_basic_unlock(&d->lock);
158	return 0;
159}
160
161static void
162testframe_store(struct module_env* env, struct cachedb_env* cachedb_env,
163	char* key, uint8_t* data, size_t data_len)
164{
165	struct testframe_moddata* d = (struct testframe_moddata*)
166		cachedb_env->backend_data;
167	(void)env;
168	lock_basic_lock(&d->lock);
169	verbose(VERB_ALGO, "testframe_store %s (%d bytes)", key, (int)data_len);
170
171	/* free old data element (if any) */
172	free(d->stored_key);
173	d->stored_key = NULL;
174	free(d->stored_data);
175	d->stored_data = NULL;
176	d->stored_datalen = 0;
177
178	d->stored_data = memdup(data, data_len);
179	if(!d->stored_data) {
180		lock_basic_unlock(&d->lock);
181		log_err("out of memory");
182		return;
183	}
184	d->stored_datalen = data_len;
185	d->stored_key = strdup(key);
186	if(!d->stored_key) {
187		free(d->stored_data);
188		d->stored_data = NULL;
189		d->stored_datalen = 0;
190		lock_basic_unlock(&d->lock);
191		return;
192	}
193	lock_basic_unlock(&d->lock);
194	/* (key,data) successfully stored */
195}
196
197/** The testframe backend is for unit tests */
198static struct cachedb_backend testframe_backend = { "testframe",
199	testframe_init, testframe_deinit, testframe_lookup, testframe_store
200};
201
202/** find a particular backend from possible backends */
203static struct cachedb_backend*
204cachedb_find_backend(const char* str)
205{
206#ifdef USE_REDIS
207	if(strcmp(str, redis_backend.name) == 0)
208		return &redis_backend;
209#endif
210	if(strcmp(str, testframe_backend.name) == 0)
211		return &testframe_backend;
212	/* TODO add more backends here */
213	return NULL;
214}
215
216/** apply configuration to cachedb module 'global' state */
217static int
218cachedb_apply_cfg(struct cachedb_env* cachedb_env, struct config_file* cfg)
219{
220	const char* backend_str = cfg->cachedb_backend;
221
222	/* If unspecified we use the in-memory test DB. */
223	if(!backend_str)
224		backend_str = "testframe";
225	cachedb_env->backend = cachedb_find_backend(backend_str);
226	if(!cachedb_env->backend) {
227		log_err("cachedb: cannot find backend name '%s'", backend_str);
228		return 0;
229	}
230
231	/* TODO see if more configuration needs to be applied or not */
232	return 1;
233}
234
235int
236cachedb_init(struct module_env* env, int id)
237{
238	struct cachedb_env* cachedb_env = (struct cachedb_env*)calloc(1,
239		sizeof(struct cachedb_env));
240	if(!cachedb_env) {
241		log_err("malloc failure");
242		return 0;
243	}
244	env->modinfo[id] = (void*)cachedb_env;
245	if(!cachedb_apply_cfg(cachedb_env, env->cfg)) {
246		log_err("cachedb: could not apply configuration settings.");
247		free(cachedb_env);
248		env->modinfo[id] = NULL;
249		return 0;
250	}
251	/* see if a backend is selected */
252	if(!cachedb_env->backend || !cachedb_env->backend->name)
253		return 1;
254	if(!(*cachedb_env->backend->init)(env, cachedb_env)) {
255		log_err("cachedb: could not init %s backend",
256			cachedb_env->backend->name);
257		free(cachedb_env);
258		env->modinfo[id] = NULL;
259		return 0;
260	}
261	cachedb_env->enabled = 1;
262	return 1;
263}
264
265void
266cachedb_deinit(struct module_env* env, int id)
267{
268	struct cachedb_env* cachedb_env;
269	if(!env || !env->modinfo[id])
270		return;
271	cachedb_env = (struct cachedb_env*)env->modinfo[id];
272	/* free contents */
273	/* TODO */
274	if(cachedb_env->enabled) {
275		(*cachedb_env->backend->deinit)(env, cachedb_env);
276	}
277
278	free(cachedb_env);
279	env->modinfo[id] = NULL;
280}
281
282/** new query for cachedb */
283static int
284cachedb_new(struct module_qstate* qstate, int id)
285{
286	struct cachedb_qstate* iq = (struct cachedb_qstate*)regional_alloc(
287		qstate->region, sizeof(struct cachedb_qstate));
288	qstate->minfo[id] = iq;
289	if(!iq)
290		return 0;
291	memset(iq, 0, sizeof(*iq));
292	/* initialise it */
293	/* TODO */
294
295	return 1;
296}
297
298/**
299 * Return an error
300 * @param qstate: our query state
301 * @param id: module id
302 * @param rcode: error code (DNS errcode).
303 * @return: 0 for use by caller, to make notation easy, like:
304 * 	return error_response(..).
305 */
306static int
307error_response(struct module_qstate* qstate, int id, int rcode)
308{
309	verbose(VERB_QUERY, "return error response %s",
310		sldns_lookup_by_id(sldns_rcodes, rcode)?
311		sldns_lookup_by_id(sldns_rcodes, rcode)->name:"??");
312	qstate->return_rcode = rcode;
313	qstate->return_msg = NULL;
314	qstate->ext_state[id] = module_finished;
315	return 0;
316}
317
318/**
319 * Hash the query name, type, class and dbacess-secret into lookup buffer.
320 * @param qstate: query state with query info
321 * 	and env->cfg with secret.
322 * @param buf: returned buffer with hash to lookup
323 * @param len: length of the buffer.
324 */
325static void
326calc_hash(struct module_qstate* qstate, char* buf, size_t len)
327{
328	uint8_t clear[1024];
329	size_t clen = 0;
330	uint8_t hash[CACHEDB_HASHSIZE/8];
331	const char* hex = "0123456789ABCDEF";
332	const char* secret = qstate->env->cfg->cachedb_secret ?
333		qstate->env->cfg->cachedb_secret : "default";
334	size_t i;
335
336	/* copy the hash info into the clear buffer */
337	if(clen + qstate->qinfo.qname_len < sizeof(clear)) {
338		memmove(clear+clen, qstate->qinfo.qname,
339			qstate->qinfo.qname_len);
340		clen += qstate->qinfo.qname_len;
341	}
342	if(clen + 4 < sizeof(clear)) {
343		uint16_t t = htons(qstate->qinfo.qtype);
344		uint16_t c = htons(qstate->qinfo.qclass);
345		memmove(clear+clen, &t, 2);
346		memmove(clear+clen+2, &c, 2);
347		clen += 4;
348	}
349	if(secret && secret[0] && clen + strlen(secret) < sizeof(clear)) {
350		memmove(clear+clen, secret, strlen(secret));
351		clen += strlen(secret);
352	}
353
354	/* hash the buffer */
355	secalgo_hash_sha256(clear, clen, hash);
356#ifdef HAVE_EXPLICIT_BZERO
357	explicit_bzero(clear, clen);
358#else
359	memset(clear, 0, clen);
360#endif
361
362	/* hex encode output for portability (some online dbs need
363	 * no nulls, no control characters, and so on) */
364	log_assert(len >= sizeof(hash)*2 + 1);
365	(void)len;
366	for(i=0; i<sizeof(hash); i++) {
367		buf[i*2] = hex[(hash[i]&0xf0)>>4];
368		buf[i*2+1] = hex[hash[i]&0x0f];
369	}
370	buf[sizeof(hash)*2] = 0;
371}
372
373/** convert data from return_msg into the data buffer */
374static int
375prep_data(struct module_qstate* qstate, struct sldns_buffer* buf)
376{
377	uint64_t timestamp, expiry;
378	size_t oldlim;
379	struct edns_data edns;
380	memset(&edns, 0, sizeof(edns));
381	edns.edns_present = 1;
382	edns.bits = EDNS_DO;
383	edns.ext_rcode = 0;
384	edns.edns_version = EDNS_ADVERTISED_VERSION;
385	edns.udp_size = EDNS_ADVERTISED_SIZE;
386
387	if(!qstate->return_msg || !qstate->return_msg->rep)
388		return 0;
389	/* We don't store the reply if its TTL is 0 unless serve-expired is
390	 * enabled.  Such a reply won't be reusable and simply be a waste for
391	 * the backend.  It's also compatible with the default behavior of
392	 * dns_cache_store_msg(). */
393	if(qstate->return_msg->rep->ttl == 0 &&
394		!qstate->env->cfg->serve_expired)
395		return 0;
396	if(verbosity >= VERB_ALGO)
397		log_dns_msg("cachedb encoding", &qstate->return_msg->qinfo,
398	                qstate->return_msg->rep);
399	if(!reply_info_answer_encode(&qstate->return_msg->qinfo,
400		qstate->return_msg->rep, 0, qstate->query_flags,
401		buf, 0, 1, qstate->env->scratch, 65535, &edns, 1, 0))
402		return 0;
403
404	/* TTLs in the return_msg are relative to time(0) so we have to
405	 * store that, we also store the smallest ttl in the packet+time(0)
406	 * as the packet expiry time */
407	/* qstate->return_msg->rep->ttl contains that relative shortest ttl */
408	timestamp = (uint64_t)*qstate->env->now;
409	expiry = timestamp + (uint64_t)qstate->return_msg->rep->ttl;
410	timestamp = htobe64(timestamp);
411	expiry = htobe64(expiry);
412	oldlim = sldns_buffer_limit(buf);
413	if(oldlim + sizeof(timestamp)+sizeof(expiry) >=
414		sldns_buffer_capacity(buf))
415		return 0; /* doesn't fit. */
416	sldns_buffer_set_limit(buf, oldlim + sizeof(timestamp)+sizeof(expiry));
417	sldns_buffer_write_at(buf, oldlim, &timestamp, sizeof(timestamp));
418	sldns_buffer_write_at(buf, oldlim+sizeof(timestamp), &expiry,
419		sizeof(expiry));
420
421	return 1;
422}
423
424/** check expiry, return true if matches OK */
425static int
426good_expiry_and_qinfo(struct module_qstate* qstate, struct sldns_buffer* buf)
427{
428	uint64_t expiry;
429	/* the expiry time is the last bytes of the buffer */
430	if(sldns_buffer_limit(buf) < sizeof(expiry))
431		return 0;
432	sldns_buffer_read_at(buf, sldns_buffer_limit(buf)-sizeof(expiry),
433		&expiry, sizeof(expiry));
434	expiry = be64toh(expiry);
435
436	if((time_t)expiry < *qstate->env->now &&
437		!qstate->env->cfg->serve_expired)
438		return 0;
439
440	return 1;
441}
442
443/* Adjust the TTL of the given RRset by 'subtract'.  If 'subtract' is
444 * negative, set the TTL to 0. */
445static void
446packed_rrset_ttl_subtract(struct packed_rrset_data* data, time_t subtract)
447{
448        size_t i;
449        size_t total = data->count + data->rrsig_count;
450	if(subtract >= 0 && data->ttl > subtract)
451		data->ttl -= subtract;
452	else	data->ttl = 0;
453        for(i=0; i<total; i++) {
454		if(subtract >= 0 && data->rr_ttl[i] > subtract)
455                	data->rr_ttl[i] -= subtract;
456                else	data->rr_ttl[i] = 0;
457	}
458}
459
460/* Adjust the TTL of a DNS message and its RRs by 'adjust'.  If 'adjust' is
461 * negative, set the TTLs to 0. */
462static void
463adjust_msg_ttl(struct dns_msg* msg, time_t adjust)
464{
465	size_t i;
466	if(adjust >= 0 && msg->rep->ttl > adjust)
467		msg->rep->ttl -= adjust;
468	else	msg->rep->ttl = 0;
469	msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl);
470	msg->rep->serve_expired_ttl = msg->rep->ttl + SERVE_EXPIRED_TTL;
471
472	for(i=0; i<msg->rep->rrset_count; i++) {
473		packed_rrset_ttl_subtract((struct packed_rrset_data*)msg->
474			rep->rrsets[i]->entry.data, adjust);
475	}
476}
477
478/** convert dns message in buffer to return_msg */
479static int
480parse_data(struct module_qstate* qstate, struct sldns_buffer* buf)
481{
482	struct msg_parse* prs;
483	struct edns_data edns;
484	uint64_t timestamp, expiry;
485	time_t adjust;
486	size_t lim = sldns_buffer_limit(buf);
487	if(lim < LDNS_HEADER_SIZE+sizeof(timestamp)+sizeof(expiry))
488		return 0; /* too short */
489
490	/* remove timestamp and expiry from end */
491	sldns_buffer_read_at(buf, lim-sizeof(expiry), &expiry, sizeof(expiry));
492	sldns_buffer_read_at(buf, lim-sizeof(expiry)-sizeof(timestamp),
493		&timestamp, sizeof(timestamp));
494	expiry = be64toh(expiry);
495	timestamp = be64toh(timestamp);
496
497	/* parse DNS packet */
498	regional_free_all(qstate->env->scratch);
499	prs = (struct msg_parse*)regional_alloc(qstate->env->scratch,
500		sizeof(struct msg_parse));
501	if(!prs)
502		return 0; /* out of memory */
503	memset(prs, 0, sizeof(*prs));
504	memset(&edns, 0, sizeof(edns));
505	sldns_buffer_set_limit(buf, lim - sizeof(expiry)-sizeof(timestamp));
506	if(parse_packet(buf, prs, qstate->env->scratch) != LDNS_RCODE_NOERROR) {
507		sldns_buffer_set_limit(buf, lim);
508		return 0;
509	}
510	if(parse_extract_edns(prs, &edns, qstate->env->scratch) !=
511		LDNS_RCODE_NOERROR) {
512		sldns_buffer_set_limit(buf, lim);
513		return 0;
514	}
515
516	qstate->return_msg = dns_alloc_msg(buf, prs, qstate->region);
517	sldns_buffer_set_limit(buf, lim);
518	if(!qstate->return_msg)
519		return 0;
520
521	qstate->return_rcode = LDNS_RCODE_NOERROR;
522
523	/* see how much of the TTL expired, and remove it */
524	if(*qstate->env->now <= (time_t)timestamp) {
525		verbose(VERB_ALGO, "cachedb msg adjust by zero");
526		return 1; /* message from the future (clock skew?) */
527	}
528	adjust = *qstate->env->now - (time_t)timestamp;
529	if(qstate->return_msg->rep->ttl < adjust) {
530		verbose(VERB_ALGO, "cachedb msg expired");
531		/* If serve-expired is enabled, we still use an expired message
532		 * setting the TTL to 0. */
533		if(qstate->env->cfg->serve_expired)
534			adjust = -1;
535		else
536			return 0; /* message expired */
537	}
538	verbose(VERB_ALGO, "cachedb msg adjusted down by %d", (int)adjust);
539	adjust_msg_ttl(qstate->return_msg, adjust);
540
541	/* Similar to the unbound worker, if serve-expired is enabled and
542	 * the msg would be considered to be expired, mark the state so a
543	 * refetch will be scheduled.  The comparison between 'expiry' and
544	 * 'now' should be redundant given how these values were calculated,
545	 * but we check it just in case as does good_expiry_and_qinfo(). */
546	if(qstate->env->cfg->serve_expired &&
547		(adjust == -1 || (time_t)expiry < *qstate->env->now)) {
548		qstate->need_refetch = 1;
549	}
550
551	return 1;
552}
553
554/**
555 * Lookup the qstate.qinfo in extcache, store in qstate.return_msg.
556 * return true if lookup was successful.
557 */
558static int
559cachedb_extcache_lookup(struct module_qstate* qstate, struct cachedb_env* ie)
560{
561	char key[(CACHEDB_HASHSIZE/8)*2+1];
562	calc_hash(qstate, key, sizeof(key));
563
564	/* call backend to fetch data for key into scratch buffer */
565	if( !(*ie->backend->lookup)(qstate->env, ie, key,
566		qstate->env->scratch_buffer)) {
567		return 0;
568	}
569
570	/* check expiry date and check if query-data matches */
571	if( !good_expiry_and_qinfo(qstate, qstate->env->scratch_buffer) ) {
572		return 0;
573	}
574
575	/* parse dns message into return_msg */
576	if( !parse_data(qstate, qstate->env->scratch_buffer) ) {
577		return 0;
578	}
579	return 1;
580}
581
582/**
583 * Store the qstate.return_msg in extcache for key qstate.info
584 */
585static void
586cachedb_extcache_store(struct module_qstate* qstate, struct cachedb_env* ie)
587{
588	char key[(CACHEDB_HASHSIZE/8)*2+1];
589	calc_hash(qstate, key, sizeof(key));
590
591	/* prepare data in scratch buffer */
592	if(!prep_data(qstate, qstate->env->scratch_buffer))
593		return;
594
595	/* call backend */
596	(*ie->backend->store)(qstate->env, ie, key,
597		sldns_buffer_begin(qstate->env->scratch_buffer),
598		sldns_buffer_limit(qstate->env->scratch_buffer));
599}
600
601/**
602 * See if unbound's internal cache can answer the query
603 */
604static int
605cachedb_intcache_lookup(struct module_qstate* qstate)
606{
607	struct dns_msg* msg;
608	msg = dns_cache_lookup(qstate->env, qstate->qinfo.qname,
609		qstate->qinfo.qname_len, qstate->qinfo.qtype,
610		qstate->qinfo.qclass, qstate->query_flags,
611		qstate->region, qstate->env->scratch,
612		1 /* no partial messages with only a CNAME */
613		);
614	if(!msg && qstate->env->neg_cache &&
615		iter_qname_indicates_dnssec(qstate->env, &qstate->qinfo)) {
616		/* lookup in negative cache; may result in
617		 * NOERROR/NODATA or NXDOMAIN answers that need validation */
618		msg = val_neg_getmsg(qstate->env->neg_cache, &qstate->qinfo,
619			qstate->region, qstate->env->rrset_cache,
620			qstate->env->scratch_buffer,
621			*qstate->env->now, 1/*add SOA*/, NULL,
622			qstate->env->cfg);
623	}
624	if(!msg)
625		return 0;
626	/* this is the returned msg */
627	qstate->return_rcode = LDNS_RCODE_NOERROR;
628	qstate->return_msg = msg;
629	return 1;
630}
631
632/**
633 * Store query into the internal cache of unbound.
634 */
635static void
636cachedb_intcache_store(struct module_qstate* qstate)
637{
638	uint32_t store_flags = qstate->query_flags;
639
640	if(qstate->env->cfg->serve_expired)
641		store_flags |= DNSCACHE_STORE_ZEROTTL;
642	if(!qstate->return_msg)
643		return;
644	(void)dns_cache_store(qstate->env, &qstate->qinfo,
645		qstate->return_msg->rep, 0, qstate->prefetch_leeway, 0,
646		qstate->region, store_flags);
647}
648
649/**
650 * Handle a cachedb module event with a query
651 * @param qstate: query state (from the mesh), passed between modules.
652 * 	contains qstate->env module environment with global caches and so on.
653 * @param iq: query state specific for this module.  per-query.
654 * @param ie: environment specific for this module.  global.
655 * @param id: module id.
656 */
657static void
658cachedb_handle_query(struct module_qstate* qstate,
659	struct cachedb_qstate* ATTR_UNUSED(iq),
660	struct cachedb_env* ie, int id)
661{
662	/* check if we are enabled, and skip if so */
663	if(!ie->enabled) {
664		/* pass request to next module */
665		qstate->ext_state[id] = module_wait_module;
666		return;
667	}
668
669	if(qstate->blacklist || qstate->no_cache_lookup) {
670		/* cache is blacklisted or we are instructed from edns to not look */
671		/* pass request to next module */
672		qstate->ext_state[id] = module_wait_module;
673		return;
674	}
675
676	/* lookup inside unbound's internal cache */
677	if(cachedb_intcache_lookup(qstate)) {
678		if(verbosity >= VERB_ALGO) {
679			if(qstate->return_msg->rep)
680				log_dns_msg("cachedb internal cache lookup",
681					&qstate->return_msg->qinfo,
682					qstate->return_msg->rep);
683			else log_info("cachedb internal cache lookup: rcode %s",
684				sldns_lookup_by_id(sldns_rcodes, qstate->return_rcode)?
685				sldns_lookup_by_id(sldns_rcodes, qstate->return_rcode)->name:"??");
686		}
687		/* we are done with the query */
688		qstate->ext_state[id] = module_finished;
689		return;
690	}
691
692	/* ask backend cache to see if we have data */
693	if(cachedb_extcache_lookup(qstate, ie)) {
694		if(verbosity >= VERB_ALGO)
695			log_dns_msg(ie->backend->name,
696				&qstate->return_msg->qinfo,
697				qstate->return_msg->rep);
698		/* store this result in internal cache */
699		cachedb_intcache_store(qstate);
700		/* we are done with the query */
701		qstate->ext_state[id] = module_finished;
702		return;
703	}
704
705	/* no cache fetches */
706	/* pass request to next module */
707	qstate->ext_state[id] = module_wait_module;
708}
709
710/**
711 * Handle a cachedb module event with a response from the iterator.
712 * @param qstate: query state (from the mesh), passed between modules.
713 * 	contains qstate->env module environment with global caches and so on.
714 * @param iq: query state specific for this module.  per-query.
715 * @param ie: environment specific for this module.  global.
716 * @param id: module id.
717 */
718static void
719cachedb_handle_response(struct module_qstate* qstate,
720	struct cachedb_qstate* ATTR_UNUSED(iq), struct cachedb_env* ie, int id)
721{
722	/* check if we are not enabled or instructed to not cache, and skip */
723	if(!ie->enabled || qstate->no_cache_store) {
724		/* we are done with the query */
725		qstate->ext_state[id] = module_finished;
726		return;
727	}
728
729	/* store the item into the backend cache */
730	cachedb_extcache_store(qstate, ie);
731
732	/* we are done with the query */
733	qstate->ext_state[id] = module_finished;
734}
735
736void
737cachedb_operate(struct module_qstate* qstate, enum module_ev event, int id,
738	struct outbound_entry* outbound)
739{
740	struct cachedb_env* ie = (struct cachedb_env*)qstate->env->modinfo[id];
741	struct cachedb_qstate* iq = (struct cachedb_qstate*)qstate->minfo[id];
742	verbose(VERB_QUERY, "cachedb[module %d] operate: extstate:%s event:%s",
743		id, strextstate(qstate->ext_state[id]), strmodulevent(event));
744	if(iq) log_query_info(VERB_QUERY, "cachedb operate: query",
745		&qstate->qinfo);
746
747	/* perform cachedb state machine */
748	if((event == module_event_new || event == module_event_pass) &&
749		iq == NULL) {
750		if(!cachedb_new(qstate, id)) {
751			(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
752			return;
753		}
754		iq = (struct cachedb_qstate*)qstate->minfo[id];
755	}
756	if(iq && (event == module_event_pass || event == module_event_new)) {
757		cachedb_handle_query(qstate, iq, ie, id);
758		return;
759	}
760	if(iq && (event == module_event_moddone)) {
761		cachedb_handle_response(qstate, iq, ie, id);
762		return;
763	}
764	if(iq && outbound) {
765		/* cachedb does not need to process responses at this time
766		 * ignore it.
767		cachedb_process_response(qstate, iq, ie, id, outbound, event);
768		*/
769		return;
770	}
771	if(event == module_event_error) {
772		verbose(VERB_ALGO, "got called with event error, giving up");
773		(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
774		return;
775	}
776	if(!iq && (event == module_event_moddone)) {
777		/* during priming, module done but we never started */
778		qstate->ext_state[id] = module_finished;
779		return;
780	}
781
782	log_err("bad event for cachedb");
783	(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
784}
785
786void
787cachedb_inform_super(struct module_qstate* ATTR_UNUSED(qstate),
788	int ATTR_UNUSED(id), struct module_qstate* ATTR_UNUSED(super))
789{
790	/* cachedb does not use subordinate requests at this time */
791	verbose(VERB_ALGO, "cachedb inform_super was called");
792}
793
794void
795cachedb_clear(struct module_qstate* qstate, int id)
796{
797	struct cachedb_qstate* iq;
798	if(!qstate)
799		return;
800	iq = (struct cachedb_qstate*)qstate->minfo[id];
801	if(iq) {
802		/* free contents of iq */
803		/* TODO */
804	}
805	qstate->minfo[id] = NULL;
806}
807
808size_t
809cachedb_get_mem(struct module_env* env, int id)
810{
811	struct cachedb_env* ie = (struct cachedb_env*)env->modinfo[id];
812	if(!ie)
813		return 0;
814	return sizeof(*ie); /* TODO - more mem */
815}
816
817/**
818 * The cachedb function block
819 */
820static struct module_func_block cachedb_block = {
821	"cachedb",
822	&cachedb_init, &cachedb_deinit, &cachedb_operate,
823	&cachedb_inform_super, &cachedb_clear, &cachedb_get_mem
824};
825
826struct module_func_block*
827cachedb_get_funcblock(void)
828{
829	return &cachedb_block;
830}
831#endif /* USE_CACHEDB */
832