Home | History | Annotate | Line # | Download | only in cachedb
cachedb.c revision 1.1
      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