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, ×tamp, 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 ×tamp, 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