xref: /freebsd/contrib/unbound/cachedb/cachedb.c (revision f0cfa1b168014f56c02b83e5f28412cc5f78d117)
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 "util/regional.h"
47 #include "util/net_help.h"
48 #include "util/config_file.h"
49 #include "util/data/msgreply.h"
50 #include "util/data/msgencode.h"
51 #include "services/cache/dns.h"
52 #include "validator/val_neg.h"
53 #include "validator/val_secalgo.h"
54 #include "iterator/iter_utils.h"
55 #include "sldns/parseutil.h"
56 #include "sldns/wire2str.h"
57 #include "sldns/sbuffer.h"
58 
59 #define CACHEDB_HASHSIZE 256 /* bit hash */
60 
61 /** the unit test testframe for cachedb, its module state contains
62  * a cache for a couple queries (in memory). */
63 struct testframe_moddata {
64 	/** key for single stored data element, NULL if none */
65 	char* stored_key;
66 	/** data for single stored data element, NULL if none */
67 	uint8_t* stored_data;
68 	/** length of stored data */
69 	size_t stored_datalen;
70 };
71 
72 static int
73 testframe_init(struct module_env* env, struct cachedb_env* cachedb_env)
74 {
75 	(void)env;
76 	verbose(VERB_ALGO, "testframe_init");
77 	cachedb_env->backend_data = (void*)calloc(1,
78 		sizeof(struct testframe_moddata));
79 	if(!cachedb_env->backend_data) {
80 		log_err("out of memory");
81 		return 0;
82 	}
83 	return 1;
84 }
85 
86 static void
87 testframe_deinit(struct module_env* env, struct cachedb_env* cachedb_env)
88 {
89 	struct testframe_moddata* d = (struct testframe_moddata*)
90 		cachedb_env->backend_data;
91 	(void)env;
92 	verbose(VERB_ALGO, "testframe_deinit");
93 	if(!d)
94 		return;
95 	free(d->stored_key);
96 	free(d->stored_data);
97 	free(d);
98 }
99 
100 static int
101 testframe_lookup(struct module_env* env, struct cachedb_env* cachedb_env,
102 	char* key, struct sldns_buffer* result_buffer)
103 {
104 	struct testframe_moddata* d = (struct testframe_moddata*)
105 		cachedb_env->backend_data;
106 	(void)env;
107 	verbose(VERB_ALGO, "testframe_lookup of %s", key);
108 	if(d->stored_key && strcmp(d->stored_key, key) == 0) {
109 		if(d->stored_datalen > sldns_buffer_capacity(result_buffer))
110 			return 0; /* too large */
111 		verbose(VERB_ALGO, "testframe_lookup found %d bytes",
112 			(int)d->stored_datalen);
113 		sldns_buffer_clear(result_buffer);
114 		sldns_buffer_write(result_buffer, d->stored_data,
115 			d->stored_datalen);
116 		sldns_buffer_flip(result_buffer);
117 		return 1;
118 	}
119 	return 0;
120 }
121 
122 static void
123 testframe_store(struct module_env* env, struct cachedb_env* cachedb_env,
124 	char* key, uint8_t* data, size_t data_len)
125 {
126 	struct testframe_moddata* d = (struct testframe_moddata*)
127 		cachedb_env->backend_data;
128 	(void)env;
129 	verbose(VERB_ALGO, "testframe_store %s (%d bytes)", key, (int)data_len);
130 
131 	/* free old data element (if any) */
132 	free(d->stored_key);
133 	d->stored_key = NULL;
134 	free(d->stored_data);
135 	d->stored_data = NULL;
136 	d->stored_datalen = 0;
137 
138 	d->stored_data = memdup(data, data_len);
139 	if(!d->stored_data) {
140 		log_err("out of memory");
141 		return;
142 	}
143 	d->stored_datalen = data_len;
144 	d->stored_key = strdup(key);
145 	if(!d->stored_key) {
146 		free(d->stored_data);
147 		d->stored_data = NULL;
148 		d->stored_datalen = 0;
149 		return;
150 	}
151 	/* (key,data) successfully stored */
152 }
153 
154 /** The testframe backend is for unit tests */
155 static struct cachedb_backend testframe_backend = { "testframe",
156 	testframe_init, testframe_deinit, testframe_lookup, testframe_store
157 };
158 
159 /** find a particular backend from possible backends */
160 static struct cachedb_backend*
161 cachedb_find_backend(const char* str)
162 {
163 	if(strcmp(str, testframe_backend.name) == 0)
164 		return &testframe_backend;
165 	/* TODO add more backends here */
166 	return NULL;
167 }
168 
169 /** apply configuration to cachedb module 'global' state */
170 static int
171 cachedb_apply_cfg(struct cachedb_env* cachedb_env, struct config_file* cfg)
172 {
173 	const char* backend_str = "testframe"; /* TODO get from cfg */
174 	if(backend_str && backend_str[0]) {
175 		cachedb_env->backend = cachedb_find_backend(backend_str);
176 		if(!cachedb_env->backend) {
177 			log_err("cachedb: cannot find backend name '%s",
178 				backend_str);
179 			return NULL;
180 		}
181 	}
182 	/* TODO see if more configuration needs to be applied or not */
183 	return 1;
184 }
185 
186 int
187 cachedb_init(struct module_env* env, int id)
188 {
189 	struct cachedb_env* cachedb_env = (struct cachedb_env*)calloc(1,
190 		sizeof(struct cachedb_env));
191 	if(!cachedb_env) {
192 		log_err("malloc failure");
193 		return 0;
194 	}
195 	env->modinfo[id] = (void*)cachedb_env;
196 	if(!cachedb_apply_cfg(cachedb_env, env->cfg)) {
197 		log_err("cachedb: could not apply configuration settings.");
198 		return 0;
199 	}
200 	/* see if a backend is selected */
201 	if(!cachedb_env->backend || !cachedb_env->backend->name)
202 		return 1;
203 	if(!(*cachedb_env->backend->init)(env, cachedb_env)) {
204 		log_err("cachedb: could not init %s backend",
205 			cachedb_env->backend->name);
206 		return 0;
207 	}
208 	cachedb_env->enabled = 1;
209 	return 1;
210 }
211 
212 void
213 cachedb_deinit(struct module_env* env, int id)
214 {
215 	struct cachedb_env* cachedb_env;
216 	if(!env || !env->modinfo[id])
217 		return;
218 	cachedb_env = (struct cachedb_env*)env->modinfo[id];
219 	/* free contents */
220 	/* TODO */
221 	if(cachedb_env->enabled) {
222 		(*cachedb_env->backend->deinit)(env, cachedb_env);
223 	}
224 
225 	free(cachedb_env);
226 	env->modinfo[id] = NULL;
227 }
228 
229 /** new query for cachedb */
230 static int
231 cachedb_new(struct module_qstate* qstate, int id)
232 {
233 	struct cachedb_qstate* iq = (struct cachedb_qstate*)regional_alloc(
234 		qstate->region, sizeof(struct cachedb_qstate));
235 	qstate->minfo[id] = iq;
236 	if(!iq)
237 		return 0;
238 	memset(iq, 0, sizeof(*iq));
239 	/* initialise it */
240 	/* TODO */
241 
242 	return 1;
243 }
244 
245 /**
246  * Return an error
247  * @param qstate: our query state
248  * @param id: module id
249  * @param rcode: error code (DNS errcode).
250  * @return: 0 for use by caller, to make notation easy, like:
251  * 	return error_response(..).
252  */
253 static int
254 error_response(struct module_qstate* qstate, int id, int rcode)
255 {
256 	verbose(VERB_QUERY, "return error response %s",
257 		sldns_lookup_by_id(sldns_rcodes, rcode)?
258 		sldns_lookup_by_id(sldns_rcodes, rcode)->name:"??");
259 	qstate->return_rcode = rcode;
260 	qstate->return_msg = NULL;
261 	qstate->ext_state[id] = module_finished;
262 	return 0;
263 }
264 
265 /**
266  * Hash the query name, type, class and dbacess-secret into lookup buffer.
267  * @param qstate: query state with query info
268  * 	and env->cfg with secret.
269  * @param buf: returned buffer with hash to lookup
270  * @param len: length of the buffer.
271  */
272 static void
273 calc_hash(struct module_qstate* qstate, char* buf, size_t len)
274 {
275 	uint8_t clear[1024];
276 	size_t clen = 0;
277 	uint8_t hash[CACHEDB_HASHSIZE/8];
278 	const char* hex = "0123456789ABCDEF";
279 	const char* secret = "default"; /* TODO: from qstate->env->cfg */
280 	size_t i;
281 
282 	/* copy the hash info into the clear buffer */
283 	if(clen + qstate->qinfo.qname_len < sizeof(clear)) {
284 		memmove(clear+clen, qstate->qinfo.qname,
285 			qstate->qinfo.qname_len);
286 		clen += qstate->qinfo.qname_len;
287 	}
288 	if(clen + 4 < sizeof(clear)) {
289 		uint16_t t = htons(qstate->qinfo.qtype);
290 		uint16_t c = htons(qstate->qinfo.qclass);
291 		memmove(clear+clen, &t, 2);
292 		memmove(clear+clen+2, &c, 2);
293 		clen += 4;
294 	}
295 	if(secret && secret[0] && clen + strlen(secret) < sizeof(clear)) {
296 		memmove(clear+clen, secret, strlen(secret));
297 		clen += strlen(secret);
298 	}
299 
300 	/* hash the buffer */
301 	secalgo_hash_sha256(clear, clen, hash);
302 	memset(clear, 0, clen);
303 
304 	/* hex encode output for portability (some online dbs need
305 	 * no nulls, no control characters, and so on) */
306 	log_assert(len >= sizeof(hash)*2 + 1);
307 	(void)len;
308 	for(i=0; i<sizeof(hash); i++) {
309 		buf[i*2] = hex[(hash[i]&0xf0)>>4];
310 		buf[i*2+1] = hex[hash[i]&0x0f];
311 	}
312 	buf[sizeof(hash)*2] = 0;
313 }
314 
315 /** convert data from return_msg into the data buffer */
316 static int
317 prep_data(struct module_qstate* qstate, struct sldns_buffer* buf)
318 {
319 	uint64_t timestamp, expiry;
320 	size_t oldlim;
321 	struct edns_data edns;
322 	memset(&edns, 0, sizeof(edns));
323 	edns.edns_present = 1;
324 	edns.bits = EDNS_DO;
325 	edns.ext_rcode = 0;
326 	edns.edns_version = EDNS_ADVERTISED_VERSION;
327 	edns.udp_size = EDNS_ADVERTISED_SIZE;
328 
329 	if(!qstate->return_msg || !qstate->return_msg->rep)
330 		return 0;
331 	if(verbosity >= VERB_ALGO)
332 		log_dns_msg("cachedb encoding", &qstate->return_msg->qinfo,
333 	                qstate->return_msg->rep);
334 	if(!reply_info_answer_encode(&qstate->return_msg->qinfo,
335 		qstate->return_msg->rep, 0, qstate->query_flags,
336 		buf, 0, 1, qstate->env->scratch, 65535, &edns, 1, 0))
337 		return 0;
338 
339 	/* TTLs in the return_msg are relative to time(0) so we have to
340 	 * store that, we also store the smallest ttl in the packet+time(0)
341 	 * as the packet expiry time */
342 	/* qstate->return_msg->rep->ttl contains that relative shortest ttl */
343 	timestamp = (uint64_t)*qstate->env->now;
344 	expiry = timestamp + (uint64_t)qstate->return_msg->rep->ttl;
345 	timestamp = htobe64(timestamp);
346 	expiry = htobe64(expiry);
347 	oldlim = sldns_buffer_limit(buf);
348 	if(oldlim + sizeof(timestamp)+sizeof(expiry) >=
349 		sldns_buffer_capacity(buf))
350 		return 0; /* doesn't fit. */
351 	sldns_buffer_set_limit(buf, oldlim + sizeof(timestamp)+sizeof(expiry));
352 	sldns_buffer_write_at(buf, oldlim, &timestamp, sizeof(timestamp));
353 	sldns_buffer_write_at(buf, oldlim+sizeof(timestamp), &expiry,
354 		sizeof(expiry));
355 
356 	return 1;
357 }
358 
359 /** check expiry, return true if matches OK */
360 static int
361 good_expiry_and_qinfo(struct module_qstate* qstate, struct sldns_buffer* buf)
362 {
363 	uint64_t expiry;
364 	/* the expiry time is the last bytes of the buffer */
365 	if(sldns_buffer_limit(buf) < sizeof(expiry))
366 		return 0;
367 	sldns_buffer_read_at(buf, sldns_buffer_limit(buf)-sizeof(expiry),
368 		&expiry, sizeof(expiry));
369 	expiry = be64toh(expiry);
370 
371 	if((time_t)expiry < *qstate->env->now)
372 		return 0;
373 
374 	return 1;
375 }
376 
377 /** convert dns message in buffer to return_msg */
378 static int
379 parse_data(struct module_qstate* qstate, struct sldns_buffer* buf)
380 {
381 	struct msg_parse* prs;
382 	struct edns_data edns;
383 	uint64_t timestamp, expiry;
384 	time_t adjust;
385 	size_t lim = sldns_buffer_limit(buf);
386 	if(lim < LDNS_HEADER_SIZE+sizeof(timestamp)+sizeof(expiry))
387 		return 0; /* too short */
388 
389 	/* remove timestamp and expiry from end */
390 	sldns_buffer_read_at(buf, lim-sizeof(expiry), &expiry, sizeof(expiry));
391 	sldns_buffer_read_at(buf, lim-sizeof(expiry)-sizeof(timestamp),
392 		&timestamp, sizeof(timestamp));
393 	expiry = be64toh(expiry);
394 	timestamp = be64toh(timestamp);
395 
396 	/* parse DNS packet */
397 	regional_free_all(qstate->env->scratch);
398 	prs = (struct msg_parse*)regional_alloc(qstate->env->scratch,
399 		sizeof(struct msg_parse));
400 	if(!prs)
401 		return 0; /* out of memory */
402 	memset(prs, 0, sizeof(*prs));
403 	memset(&edns, 0, sizeof(edns));
404 	sldns_buffer_set_limit(buf, lim - sizeof(expiry)-sizeof(timestamp));
405 	if(parse_packet(buf, prs, qstate->env->scratch) != LDNS_RCODE_NOERROR) {
406 		sldns_buffer_set_limit(buf, lim);
407 		return 0;
408 	}
409 	if(parse_extract_edns(prs, &edns, qstate->env->scratch) !=
410 		LDNS_RCODE_NOERROR) {
411 		sldns_buffer_set_limit(buf, lim);
412 		return 0;
413 	}
414 
415 	qstate->return_msg = dns_alloc_msg(buf, prs, qstate->region);
416 	sldns_buffer_set_limit(buf, lim);
417 	if(!qstate->return_msg)
418 		return 0;
419 
420 	qstate->return_rcode = LDNS_RCODE_NOERROR;
421 
422 	/* see how much of the TTL expired, and remove it */
423 	adjust = *qstate->env->now - (time_t)timestamp;
424 	verbose(VERB_ALGO, "cachedb msg adjusted down by %d", (int)adjust);
425 	/*adjust_msg(qstate->return_msg, adjust);*/
426 	/* TODO:
427 		msg->rep->ttl = r->ttl - adjust;
428 		msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl);
429 		for(i=0; i<d->count + d->rrsig_count; i++) {
430 			if(d->rr_ttl[i] < adjust)
431 				d->rr_ttl[i] = 0;
432 			else    d->rr_ttl[i] -= adjust;
433 		}
434 		if(d->ttl < adjust)
435 			d->ttl = 0;
436 		else    d->ttl -= adjust;
437 		*/
438 	/* TODO */
439 
440 	return 0;
441 }
442 
443 /**
444  * Lookup the qstate.qinfo in extcache, store in qstate.return_msg.
445  * return true if lookup was successful.
446  */
447 static int
448 cachedb_extcache_lookup(struct module_qstate* qstate, struct cachedb_env* ie)
449 {
450 	char key[(CACHEDB_HASHSIZE/8)*2+1];
451 	calc_hash(qstate, key, sizeof(key));
452 
453 	/* call backend to fetch data for key into scratch buffer */
454 	if( !(*ie->backend->lookup)(qstate->env, ie, key,
455 		qstate->env->scratch_buffer)) {
456 		return 0;
457 	}
458 
459 	/* check expiry date and check if query-data matches */
460 	if( !good_expiry_and_qinfo(qstate, qstate->env->scratch_buffer) ) {
461 		return 0;
462 	}
463 
464 	/* parse dns message into return_msg */
465 	if( !parse_data(qstate, qstate->env->scratch_buffer) ) {
466 		return 0;
467 	}
468 	return 1;
469 }
470 
471 /**
472  * Store the qstate.return_msg in extcache for key qstate.info
473  */
474 static void
475 cachedb_extcache_store(struct module_qstate* qstate, struct cachedb_env* ie)
476 {
477 	char key[(CACHEDB_HASHSIZE/8)*2+1];
478 	calc_hash(qstate, key, sizeof(key));
479 
480 	/* prepare data in scratch buffer */
481 	if(!prep_data(qstate, qstate->env->scratch_buffer))
482 		return;
483 
484 	/* call backend */
485 	(*ie->backend->store)(qstate->env, ie, key,
486 		sldns_buffer_begin(qstate->env->scratch_buffer),
487 		sldns_buffer_limit(qstate->env->scratch_buffer));
488 }
489 
490 /**
491  * See if unbound's internal cache can answer the query
492  */
493 static int
494 cachedb_intcache_lookup(struct module_qstate* qstate)
495 {
496 	struct dns_msg* msg;
497 	msg = dns_cache_lookup(qstate->env, qstate->qinfo.qname,
498 		qstate->qinfo.qname_len, qstate->qinfo.qtype,
499 		qstate->qinfo.qclass, qstate->query_flags,
500 		qstate->region, qstate->env->scratch);
501 	if(!msg && qstate->env->neg_cache) {
502 		/* lookup in negative cache; may result in
503 		 * NOERROR/NODATA or NXDOMAIN answers that need validation */
504 		msg = val_neg_getmsg(qstate->env->neg_cache, &qstate->qinfo,
505 			qstate->region, qstate->env->rrset_cache,
506 			qstate->env->scratch_buffer,
507 			*qstate->env->now, 1/*add SOA*/, NULL);
508 	}
509 	if(!msg)
510 		return 0;
511 	/* this is the returned msg */
512 	qstate->return_rcode = LDNS_RCODE_NOERROR;
513 	qstate->return_msg = msg;
514 	return 1;
515 }
516 
517 /**
518  * Store query into the internal cache of unbound.
519  */
520 static void
521 cachedb_intcache_store(struct module_qstate* qstate)
522 {
523 	if(!qstate->return_msg)
524 		return;
525 	(void)dns_cache_store(qstate->env, &qstate->qinfo,
526 		qstate->return_msg->rep, 0, qstate->prefetch_leeway, 0,
527 		qstate->region, qstate->query_flags);
528 }
529 
530 /**
531  * Handle a cachedb module event with a query
532  * @param qstate: query state (from the mesh), passed between modules.
533  * 	contains qstate->env module environment with global caches and so on.
534  * @param iq: query state specific for this module.  per-query.
535  * @param ie: environment specific for this module.  global.
536  * @param id: module id.
537  */
538 static void
539 cachedb_handle_query(struct module_qstate* qstate,
540 	struct cachedb_qstate* ATTR_UNUSED(iq),
541 	struct cachedb_env* ie, int id)
542 {
543 	/* check if we are enabled, and skip if so */
544 	if(!ie->enabled) {
545 		/* pass request to next module */
546 		qstate->ext_state[id] = module_wait_module;
547 		return;
548 	}
549 
550 	if(qstate->blacklist) {
551 		/* cache is blacklisted */
552 		/* pass request to next module */
553 		qstate->ext_state[id] = module_wait_module;
554 		return;
555 	}
556 
557 	/* lookup inside unbound's internal cache */
558 	if(cachedb_intcache_lookup(qstate)) {
559 		if(verbosity >= VERB_ALGO)
560 			log_dns_msg("cachedb internal cache lookup",
561 				&qstate->return_msg->qinfo,
562 				qstate->return_msg->rep);
563 		/* we are done with the query */
564 		qstate->ext_state[id] = module_finished;
565 		return;
566 	}
567 
568 	/* ask backend cache to see if we have data */
569 	if(cachedb_extcache_lookup(qstate, ie)) {
570 		if(verbosity >= VERB_ALGO)
571 			log_dns_msg(ie->backend->name,
572 				&qstate->return_msg->qinfo,
573 				qstate->return_msg->rep);
574 		/* store this result in internal cache */
575 		cachedb_intcache_store(qstate);
576 		/* we are done with the query */
577 		qstate->ext_state[id] = module_finished;
578 		return;
579 	}
580 
581 	/* no cache fetches */
582 	/* pass request to next module */
583 	qstate->ext_state[id] = module_wait_module;
584 }
585 
586 /**
587  * Handle a cachedb module event with a response from the iterator.
588  * @param qstate: query state (from the mesh), passed between modules.
589  * 	contains qstate->env module environment with global caches and so on.
590  * @param iq: query state specific for this module.  per-query.
591  * @param ie: environment specific for this module.  global.
592  * @param id: module id.
593  */
594 static void
595 cachedb_handle_response(struct module_qstate* qstate,
596 	struct cachedb_qstate* ATTR_UNUSED(iq), struct cachedb_env* ie, int id)
597 {
598 	/* check if we are enabled, and skip if not */
599 	if(!ie->enabled) {
600 		/* we are done with the query */
601 		qstate->ext_state[id] = module_finished;
602 		return;
603 	}
604 
605 	/* store the item into the backend cache */
606 	cachedb_extcache_store(qstate, ie);
607 
608 	/* we are done with the query */
609 	qstate->ext_state[id] = module_finished;
610 }
611 
612 void
613 cachedb_operate(struct module_qstate* qstate, enum module_ev event, int id,
614 	struct outbound_entry* outbound)
615 {
616 	struct cachedb_env* ie = (struct cachedb_env*)qstate->env->modinfo[id];
617 	struct cachedb_qstate* iq = (struct cachedb_qstate*)qstate->minfo[id];
618 	verbose(VERB_QUERY, "cachedb[module %d] operate: extstate:%s event:%s",
619 		id, strextstate(qstate->ext_state[id]), strmodulevent(event));
620 	if(iq) log_query_info(VERB_QUERY, "cachedb operate: query",
621 		&qstate->qinfo);
622 
623 	/* perform cachedb state machine */
624 	if((event == module_event_new || event == module_event_pass) &&
625 		iq == NULL) {
626 		if(!cachedb_new(qstate, id)) {
627 			(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
628 			return;
629 		}
630 		iq = (struct cachedb_qstate*)qstate->minfo[id];
631 	}
632 	if(iq && (event == module_event_pass || event == module_event_new)) {
633 		cachedb_handle_query(qstate, iq, ie, id);
634 		return;
635 	}
636 	if(iq && (event == module_event_moddone)) {
637 		cachedb_handle_response(qstate, iq, ie, id);
638 		return;
639 	}
640 	if(iq && outbound) {
641 		/* cachedb does not need to process responses at this time
642 		 * ignore it.
643 		cachedb_process_response(qstate, iq, ie, id, outbound, event);
644 		*/
645 		return;
646 	}
647 	if(event == module_event_error) {
648 		verbose(VERB_ALGO, "got called with event error, giving up");
649 		(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
650 		return;
651 	}
652 
653 	log_err("bad event for cachedb");
654 	(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
655 }
656 
657 void
658 cachedb_inform_super(struct module_qstate* ATTR_UNUSED(qstate),
659 	int ATTR_UNUSED(id), struct module_qstate* ATTR_UNUSED(super))
660 {
661 	/* cachedb does not use subordinate requests at this time */
662 	verbose(VERB_ALGO, "cachedb inform_super was called");
663 }
664 
665 void
666 cachedb_clear(struct module_qstate* qstate, int id)
667 {
668 	struct cachedb_qstate* iq;
669 	if(!qstate)
670 		return;
671 	iq = (struct cachedb_qstate*)qstate->minfo[id];
672 	if(iq) {
673 		/* free contents of iq */
674 		/* TODO */
675 	}
676 	qstate->minfo[id] = NULL;
677 }
678 
679 size_t
680 cachedb_get_mem(struct module_env* env, int id)
681 {
682 	struct cachedb_env* ie = (struct cachedb_env*)env->modinfo[id];
683 	if(!ie)
684 		return 0;
685 	return sizeof(*ie); /* TODO - more mem */
686 }
687 
688 /**
689  * The cachedb function block
690  */
691 static struct module_func_block cachedb_block = {
692 	"cachedb",
693 	&cachedb_init, &cachedb_deinit, &cachedb_operate,
694 	&cachedb_inform_super, &cachedb_clear, &cachedb_get_mem
695 };
696 
697 struct module_func_block*
698 cachedb_get_funcblock(void)
699 {
700 	return &cachedb_block;
701 }
702 #endif /* USE_CACHEDB */
703