12ee382b6Ssthen /* 22ee382b6Ssthen * cachedb/cachedb.c - cache from a database external to the program module 32ee382b6Ssthen * 42ee382b6Ssthen * Copyright (c) 2016, NLnet Labs. All rights reserved. 52ee382b6Ssthen * 62ee382b6Ssthen * This software is open source. 72ee382b6Ssthen * 82ee382b6Ssthen * Redistribution and use in source and binary forms, with or without 92ee382b6Ssthen * modification, are permitted provided that the following conditions 102ee382b6Ssthen * are met: 112ee382b6Ssthen * 122ee382b6Ssthen * Redistributions of source code must retain the above copyright notice, 132ee382b6Ssthen * this list of conditions and the following disclaimer. 142ee382b6Ssthen * 152ee382b6Ssthen * Redistributions in binary form must reproduce the above copyright notice, 162ee382b6Ssthen * this list of conditions and the following disclaimer in the documentation 172ee382b6Ssthen * and/or other materials provided with the distribution. 182ee382b6Ssthen * 192ee382b6Ssthen * Neither the name of the NLNET LABS nor the names of its contributors may 202ee382b6Ssthen * be used to endorse or promote products derived from this software without 212ee382b6Ssthen * specific prior written permission. 222ee382b6Ssthen * 232ee382b6Ssthen * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 242ee382b6Ssthen * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 252ee382b6Ssthen * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 262ee382b6Ssthen * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 272ee382b6Ssthen * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 282ee382b6Ssthen * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 292ee382b6Ssthen * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 302ee382b6Ssthen * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 312ee382b6Ssthen * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 322ee382b6Ssthen * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 332ee382b6Ssthen * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 342ee382b6Ssthen */ 352ee382b6Ssthen 362ee382b6Ssthen /** 372ee382b6Ssthen * \file 382ee382b6Ssthen * 392ee382b6Ssthen * This file contains a module that uses an external database to cache 402ee382b6Ssthen * dns responses. 412ee382b6Ssthen */ 422ee382b6Ssthen 432ee382b6Ssthen #include "config.h" 442ee382b6Ssthen #ifdef USE_CACHEDB 452ee382b6Ssthen #include "cachedb/cachedb.h" 4620237c55Ssthen #include "cachedb/redis.h" 472ee382b6Ssthen #include "util/regional.h" 482ee382b6Ssthen #include "util/net_help.h" 492ee382b6Ssthen #include "util/config_file.h" 502ee382b6Ssthen #include "util/data/msgreply.h" 512ee382b6Ssthen #include "util/data/msgencode.h" 522ee382b6Ssthen #include "services/cache/dns.h" 532bdc0ed1Ssthen #include "services/mesh.h" 542bdc0ed1Ssthen #include "services/modstack.h" 552ee382b6Ssthen #include "validator/val_neg.h" 562ee382b6Ssthen #include "validator/val_secalgo.h" 572ee382b6Ssthen #include "iterator/iter_utils.h" 582ee382b6Ssthen #include "sldns/parseutil.h" 592ee382b6Ssthen #include "sldns/wire2str.h" 602ee382b6Ssthen #include "sldns/sbuffer.h" 612ee382b6Ssthen 6220237c55Ssthen /* header file for htobe64 */ 6320237c55Ssthen #ifdef HAVE_ENDIAN_H 6420237c55Ssthen # include <endian.h> 6520237c55Ssthen #endif 6620237c55Ssthen #ifdef HAVE_SYS_ENDIAN_H 6720237c55Ssthen # include <sys/endian.h> 6820237c55Ssthen #endif 69eaf2578eSsthen 70eaf2578eSsthen #ifndef HAVE_HTOBE64 7120237c55Ssthen # ifdef HAVE_LIBKERN_OSBYTEORDER_H 7220237c55Ssthen /* In practice this is specific to MacOS X. We assume it doesn't have 7320237c55Ssthen * htobe64/be64toh but has alternatives with a different name. */ 7420237c55Ssthen # include <libkern/OSByteOrder.h> 7520237c55Ssthen # define htobe64(x) OSSwapHostToBigInt64(x) 7620237c55Ssthen # define be64toh(x) OSSwapBigToHostInt64(x) 77eaf2578eSsthen # else 78eaf2578eSsthen /* not OSX */ 79550cf4a9Ssthen /* Some compilers do not define __BYTE_ORDER__, like IBM XLC on AIX */ 80550cf4a9Ssthen # if __BIG_ENDIAN__ 81550cf4a9Ssthen # define be64toh(n) (n) 82550cf4a9Ssthen # define htobe64(n) (n) 83550cf4a9Ssthen # else 84550cf4a9Ssthen # define be64toh(n) (((uint64_t)htonl((n) & 0xFFFFFFFF) << 32) | htonl((n) >> 32)) 85550cf4a9Ssthen # define htobe64(n) (((uint64_t)htonl((n) & 0xFFFFFFFF) << 32) | htonl((n) >> 32)) 86eaf2578eSsthen # endif /* _ENDIAN */ 87eaf2578eSsthen # endif /* HAVE_LIBKERN_OSBYTEORDER_H */ 88eaf2578eSsthen #endif /* HAVE_BE64TOH */ 89550cf4a9Ssthen 902ee382b6Ssthen /** the unit test testframe for cachedb, its module state contains 912ee382b6Ssthen * a cache for a couple queries (in memory). */ 922ee382b6Ssthen struct testframe_moddata { 937191de28Ssthen /** lock for mutex */ 947191de28Ssthen lock_basic_type lock; 952ee382b6Ssthen /** key for single stored data element, NULL if none */ 962ee382b6Ssthen char* stored_key; 972ee382b6Ssthen /** data for single stored data element, NULL if none */ 982ee382b6Ssthen uint8_t* stored_data; 992ee382b6Ssthen /** length of stored data */ 1002ee382b6Ssthen size_t stored_datalen; 1012ee382b6Ssthen }; 1022ee382b6Ssthen 1032ee382b6Ssthen static int 1042ee382b6Ssthen testframe_init(struct module_env* env, struct cachedb_env* cachedb_env) 1052ee382b6Ssthen { 1067191de28Ssthen struct testframe_moddata* d; 1072ee382b6Ssthen verbose(VERB_ALGO, "testframe_init"); 1087191de28Ssthen d = (struct testframe_moddata*)calloc(1, 1092ee382b6Ssthen sizeof(struct testframe_moddata)); 1107191de28Ssthen cachedb_env->backend_data = (void*)d; 1112ee382b6Ssthen if(!cachedb_env->backend_data) { 1122ee382b6Ssthen log_err("out of memory"); 1132ee382b6Ssthen return 0; 1142ee382b6Ssthen } 1158b7325afSsthen /* Register an EDNS option (65534) to bypass the worker cache lookup 1168b7325afSsthen * for testing */ 1178b7325afSsthen if(!edns_register_option(LDNS_EDNS_UNBOUND_CACHEDB_TESTFRAME_TEST, 1188b7325afSsthen 1 /* bypass cache */, 1198b7325afSsthen 0 /* no aggregation */, env)) { 1208b7325afSsthen log_err("testframe_init, could not register test opcode"); 1218b7325afSsthen free(d); 1228b7325afSsthen return 0; 1238b7325afSsthen } 1247191de28Ssthen lock_basic_init(&d->lock); 1257191de28Ssthen lock_protect(&d->lock, d, sizeof(*d)); 1262ee382b6Ssthen return 1; 1272ee382b6Ssthen } 1282ee382b6Ssthen 1292ee382b6Ssthen static void 1302ee382b6Ssthen testframe_deinit(struct module_env* env, struct cachedb_env* cachedb_env) 1312ee382b6Ssthen { 1322ee382b6Ssthen struct testframe_moddata* d = (struct testframe_moddata*) 1332ee382b6Ssthen cachedb_env->backend_data; 1342ee382b6Ssthen (void)env; 1352ee382b6Ssthen verbose(VERB_ALGO, "testframe_deinit"); 1362ee382b6Ssthen if(!d) 1372ee382b6Ssthen return; 1387191de28Ssthen lock_basic_destroy(&d->lock); 1392ee382b6Ssthen free(d->stored_key); 1402ee382b6Ssthen free(d->stored_data); 1412ee382b6Ssthen free(d); 1422ee382b6Ssthen } 1432ee382b6Ssthen 1442ee382b6Ssthen static int 1452ee382b6Ssthen testframe_lookup(struct module_env* env, struct cachedb_env* cachedb_env, 1462ee382b6Ssthen char* key, struct sldns_buffer* result_buffer) 1472ee382b6Ssthen { 1482ee382b6Ssthen struct testframe_moddata* d = (struct testframe_moddata*) 1492ee382b6Ssthen cachedb_env->backend_data; 1502ee382b6Ssthen (void)env; 1512ee382b6Ssthen verbose(VERB_ALGO, "testframe_lookup of %s", key); 1527191de28Ssthen lock_basic_lock(&d->lock); 1532ee382b6Ssthen if(d->stored_key && strcmp(d->stored_key, key) == 0) { 1547191de28Ssthen if(d->stored_datalen > sldns_buffer_capacity(result_buffer)) { 1557191de28Ssthen lock_basic_unlock(&d->lock); 1562ee382b6Ssthen return 0; /* too large */ 1577191de28Ssthen } 1582ee382b6Ssthen verbose(VERB_ALGO, "testframe_lookup found %d bytes", 1592ee382b6Ssthen (int)d->stored_datalen); 1602ee382b6Ssthen sldns_buffer_clear(result_buffer); 1612ee382b6Ssthen sldns_buffer_write(result_buffer, d->stored_data, 1622ee382b6Ssthen d->stored_datalen); 1632ee382b6Ssthen sldns_buffer_flip(result_buffer); 1647191de28Ssthen lock_basic_unlock(&d->lock); 1652ee382b6Ssthen return 1; 1662ee382b6Ssthen } 1677191de28Ssthen lock_basic_unlock(&d->lock); 1682ee382b6Ssthen return 0; 1692ee382b6Ssthen } 1702ee382b6Ssthen 1712ee382b6Ssthen static void 1722ee382b6Ssthen testframe_store(struct module_env* env, struct cachedb_env* cachedb_env, 173a3167c07Ssthen char* key, uint8_t* data, size_t data_len, time_t ATTR_UNUSED(ttl)) 1742ee382b6Ssthen { 1752ee382b6Ssthen struct testframe_moddata* d = (struct testframe_moddata*) 1762ee382b6Ssthen cachedb_env->backend_data; 1772ee382b6Ssthen (void)env; 1787191de28Ssthen lock_basic_lock(&d->lock); 1792ee382b6Ssthen verbose(VERB_ALGO, "testframe_store %s (%d bytes)", key, (int)data_len); 1802ee382b6Ssthen 1812ee382b6Ssthen /* free old data element (if any) */ 1822ee382b6Ssthen free(d->stored_key); 1832ee382b6Ssthen d->stored_key = NULL; 1842ee382b6Ssthen free(d->stored_data); 1852ee382b6Ssthen d->stored_data = NULL; 1862ee382b6Ssthen d->stored_datalen = 0; 1872ee382b6Ssthen 1882ee382b6Ssthen d->stored_data = memdup(data, data_len); 1892ee382b6Ssthen if(!d->stored_data) { 1907191de28Ssthen lock_basic_unlock(&d->lock); 1912ee382b6Ssthen log_err("out of memory"); 1922ee382b6Ssthen return; 1932ee382b6Ssthen } 1942ee382b6Ssthen d->stored_datalen = data_len; 1952ee382b6Ssthen d->stored_key = strdup(key); 1962ee382b6Ssthen if(!d->stored_key) { 1972ee382b6Ssthen free(d->stored_data); 1982ee382b6Ssthen d->stored_data = NULL; 1992ee382b6Ssthen d->stored_datalen = 0; 2007191de28Ssthen lock_basic_unlock(&d->lock); 2012ee382b6Ssthen return; 2022ee382b6Ssthen } 2037191de28Ssthen lock_basic_unlock(&d->lock); 2042ee382b6Ssthen /* (key,data) successfully stored */ 2052ee382b6Ssthen } 2062ee382b6Ssthen 2072ee382b6Ssthen /** The testframe backend is for unit tests */ 2082ee382b6Ssthen static struct cachedb_backend testframe_backend = { "testframe", 2092ee382b6Ssthen testframe_init, testframe_deinit, testframe_lookup, testframe_store 2102ee382b6Ssthen }; 2112ee382b6Ssthen 2122ee382b6Ssthen /** find a particular backend from possible backends */ 2132ee382b6Ssthen static struct cachedb_backend* 2142ee382b6Ssthen cachedb_find_backend(const char* str) 2152ee382b6Ssthen { 21620237c55Ssthen #ifdef USE_REDIS 21720237c55Ssthen if(strcmp(str, redis_backend.name) == 0) 21820237c55Ssthen return &redis_backend; 21920237c55Ssthen #endif 2202ee382b6Ssthen if(strcmp(str, testframe_backend.name) == 0) 2212ee382b6Ssthen return &testframe_backend; 2222ee382b6Ssthen /* TODO add more backends here */ 2232ee382b6Ssthen return NULL; 2242ee382b6Ssthen } 2252ee382b6Ssthen 2262ee382b6Ssthen /** apply configuration to cachedb module 'global' state */ 2272ee382b6Ssthen static int 2282ee382b6Ssthen cachedb_apply_cfg(struct cachedb_env* cachedb_env, struct config_file* cfg) 2292ee382b6Ssthen { 2307191de28Ssthen const char* backend_str = cfg->cachedb_backend; 2318b7325afSsthen if(!backend_str || *backend_str==0) 2328b7325afSsthen return 1; 2332ee382b6Ssthen cachedb_env->backend = cachedb_find_backend(backend_str); 2342ee382b6Ssthen if(!cachedb_env->backend) { 2357191de28Ssthen log_err("cachedb: cannot find backend name '%s'", backend_str); 2362be9e038Ssthen return 0; 2372ee382b6Ssthen } 2387191de28Ssthen 2392ee382b6Ssthen /* TODO see if more configuration needs to be applied or not */ 2402ee382b6Ssthen return 1; 2412ee382b6Ssthen } 2422ee382b6Ssthen 2432ee382b6Ssthen int 2442ee382b6Ssthen cachedb_init(struct module_env* env, int id) 2452ee382b6Ssthen { 2462ee382b6Ssthen struct cachedb_env* cachedb_env = (struct cachedb_env*)calloc(1, 2472ee382b6Ssthen sizeof(struct cachedb_env)); 2482ee382b6Ssthen if(!cachedb_env) { 2492ee382b6Ssthen log_err("malloc failure"); 2502ee382b6Ssthen return 0; 2512ee382b6Ssthen } 2522ee382b6Ssthen env->modinfo[id] = (void*)cachedb_env; 2532ee382b6Ssthen if(!cachedb_apply_cfg(cachedb_env, env->cfg)) { 2542ee382b6Ssthen log_err("cachedb: could not apply configuration settings."); 255ebf5bb73Ssthen free(cachedb_env); 256ebf5bb73Ssthen env->modinfo[id] = NULL; 2572ee382b6Ssthen return 0; 2582ee382b6Ssthen } 2592ee382b6Ssthen /* see if a backend is selected */ 2602ee382b6Ssthen if(!cachedb_env->backend || !cachedb_env->backend->name) 2612ee382b6Ssthen return 1; 2622ee382b6Ssthen if(!(*cachedb_env->backend->init)(env, cachedb_env)) { 2632ee382b6Ssthen log_err("cachedb: could not init %s backend", 2642ee382b6Ssthen cachedb_env->backend->name); 265ebf5bb73Ssthen free(cachedb_env); 266ebf5bb73Ssthen env->modinfo[id] = NULL; 2672ee382b6Ssthen return 0; 2682ee382b6Ssthen } 2692ee382b6Ssthen cachedb_env->enabled = 1; 2702ee382b6Ssthen return 1; 2712ee382b6Ssthen } 2722ee382b6Ssthen 2732ee382b6Ssthen void 2742ee382b6Ssthen cachedb_deinit(struct module_env* env, int id) 2752ee382b6Ssthen { 2762ee382b6Ssthen struct cachedb_env* cachedb_env; 2772ee382b6Ssthen if(!env || !env->modinfo[id]) 2782ee382b6Ssthen return; 2792ee382b6Ssthen cachedb_env = (struct cachedb_env*)env->modinfo[id]; 2802ee382b6Ssthen if(cachedb_env->enabled) { 2812ee382b6Ssthen (*cachedb_env->backend->deinit)(env, cachedb_env); 2822ee382b6Ssthen } 2832ee382b6Ssthen free(cachedb_env); 2842ee382b6Ssthen env->modinfo[id] = NULL; 2852ee382b6Ssthen } 2862ee382b6Ssthen 2872ee382b6Ssthen /** new query for cachedb */ 2882ee382b6Ssthen static int 2892ee382b6Ssthen cachedb_new(struct module_qstate* qstate, int id) 2902ee382b6Ssthen { 2912ee382b6Ssthen struct cachedb_qstate* iq = (struct cachedb_qstate*)regional_alloc( 2922ee382b6Ssthen qstate->region, sizeof(struct cachedb_qstate)); 2932ee382b6Ssthen qstate->minfo[id] = iq; 2942ee382b6Ssthen if(!iq) 2952ee382b6Ssthen return 0; 2962ee382b6Ssthen memset(iq, 0, sizeof(*iq)); 2972ee382b6Ssthen /* initialise it */ 2982ee382b6Ssthen /* TODO */ 2992ee382b6Ssthen 3002ee382b6Ssthen return 1; 3012ee382b6Ssthen } 3022ee382b6Ssthen 3032ee382b6Ssthen /** 3042ee382b6Ssthen * Return an error 3052ee382b6Ssthen * @param qstate: our query state 3062ee382b6Ssthen * @param id: module id 3072ee382b6Ssthen * @param rcode: error code (DNS errcode). 3082ee382b6Ssthen * @return: 0 for use by caller, to make notation easy, like: 3092ee382b6Ssthen * return error_response(..). 3102ee382b6Ssthen */ 3112ee382b6Ssthen static int 3122ee382b6Ssthen error_response(struct module_qstate* qstate, int id, int rcode) 3132ee382b6Ssthen { 3142ee382b6Ssthen verbose(VERB_QUERY, "return error response %s", 3152ee382b6Ssthen sldns_lookup_by_id(sldns_rcodes, rcode)? 3162ee382b6Ssthen sldns_lookup_by_id(sldns_rcodes, rcode)->name:"??"); 3172ee382b6Ssthen qstate->return_rcode = rcode; 3182ee382b6Ssthen qstate->return_msg = NULL; 3192ee382b6Ssthen qstate->ext_state[id] = module_finished; 3202ee382b6Ssthen return 0; 3212ee382b6Ssthen } 3222ee382b6Ssthen 3232ee382b6Ssthen /** 3242ee382b6Ssthen * Hash the query name, type, class and dbacess-secret into lookup buffer. 325*98bc733bSsthen * @param qinfo: query info 326*98bc733bSsthen * @param env: with env->cfg with secret. 3272ee382b6Ssthen * @param buf: returned buffer with hash to lookup 3282ee382b6Ssthen * @param len: length of the buffer. 3292ee382b6Ssthen */ 3302ee382b6Ssthen static void 331*98bc733bSsthen calc_hash(struct query_info* qinfo, struct module_env* env, char* buf, 332*98bc733bSsthen size_t len) 3332ee382b6Ssthen { 3342ee382b6Ssthen uint8_t clear[1024]; 3352ee382b6Ssthen size_t clen = 0; 3362ee382b6Ssthen uint8_t hash[CACHEDB_HASHSIZE/8]; 3372ee382b6Ssthen const char* hex = "0123456789ABCDEF"; 338*98bc733bSsthen const char* secret = env->cfg->cachedb_secret; 3392ee382b6Ssthen size_t i; 3402ee382b6Ssthen 3412ee382b6Ssthen /* copy the hash info into the clear buffer */ 342*98bc733bSsthen if(clen + qinfo->qname_len < sizeof(clear)) { 343*98bc733bSsthen memmove(clear+clen, qinfo->qname, qinfo->qname_len); 344*98bc733bSsthen clen += qinfo->qname_len; 3452ee382b6Ssthen } 3462ee382b6Ssthen if(clen + 4 < sizeof(clear)) { 347*98bc733bSsthen uint16_t t = htons(qinfo->qtype); 348*98bc733bSsthen uint16_t c = htons(qinfo->qclass); 3492ee382b6Ssthen memmove(clear+clen, &t, 2); 3502ee382b6Ssthen memmove(clear+clen+2, &c, 2); 3512ee382b6Ssthen clen += 4; 3522ee382b6Ssthen } 3532ee382b6Ssthen if(secret && secret[0] && clen + strlen(secret) < sizeof(clear)) { 3542ee382b6Ssthen memmove(clear+clen, secret, strlen(secret)); 3552ee382b6Ssthen clen += strlen(secret); 3562ee382b6Ssthen } 3572ee382b6Ssthen 3582ee382b6Ssthen /* hash the buffer */ 3592ee382b6Ssthen secalgo_hash_sha256(clear, clen, hash); 360ebf5bb73Ssthen #ifdef HAVE_EXPLICIT_BZERO 361ebf5bb73Ssthen explicit_bzero(clear, clen); 362ebf5bb73Ssthen #else 3632ee382b6Ssthen memset(clear, 0, clen); 364ebf5bb73Ssthen #endif 3652ee382b6Ssthen 3662ee382b6Ssthen /* hex encode output for portability (some online dbs need 3672ee382b6Ssthen * no nulls, no control characters, and so on) */ 3682ee382b6Ssthen log_assert(len >= sizeof(hash)*2 + 1); 3692ee382b6Ssthen (void)len; 3702ee382b6Ssthen for(i=0; i<sizeof(hash); i++) { 3712ee382b6Ssthen buf[i*2] = hex[(hash[i]&0xf0)>>4]; 3722ee382b6Ssthen buf[i*2+1] = hex[hash[i]&0x0f]; 3732ee382b6Ssthen } 3742ee382b6Ssthen buf[sizeof(hash)*2] = 0; 3752ee382b6Ssthen } 3762ee382b6Ssthen 3772ee382b6Ssthen /** convert data from return_msg into the data buffer */ 3782ee382b6Ssthen static int 3792ee382b6Ssthen prep_data(struct module_qstate* qstate, struct sldns_buffer* buf) 3802ee382b6Ssthen { 3812ee382b6Ssthen uint64_t timestamp, expiry; 3822ee382b6Ssthen size_t oldlim; 3832ee382b6Ssthen struct edns_data edns; 3842ee382b6Ssthen memset(&edns, 0, sizeof(edns)); 3852ee382b6Ssthen edns.edns_present = 1; 3862ee382b6Ssthen edns.bits = EDNS_DO; 3872ee382b6Ssthen edns.ext_rcode = 0; 3882ee382b6Ssthen edns.edns_version = EDNS_ADVERTISED_VERSION; 3892ee382b6Ssthen edns.udp_size = EDNS_ADVERTISED_SIZE; 3902ee382b6Ssthen 3912ee382b6Ssthen if(!qstate->return_msg || !qstate->return_msg->rep) 3922ee382b6Ssthen return 0; 3938b7325afSsthen /* do not store failures like SERVFAIL in the cachedb, this avoids 3948b7325afSsthen * overwriting expired, valid, content with broken content. */ 3958b7325afSsthen if(FLAGS_GET_RCODE(qstate->return_msg->rep->flags) != 3968b7325afSsthen LDNS_RCODE_NOERROR && 3978b7325afSsthen FLAGS_GET_RCODE(qstate->return_msg->rep->flags) != 3988b7325afSsthen LDNS_RCODE_NXDOMAIN && 3998b7325afSsthen FLAGS_GET_RCODE(qstate->return_msg->rep->flags) != 4008b7325afSsthen LDNS_RCODE_YXDOMAIN) 4018b7325afSsthen return 0; 402bdfc4d55Sflorian /* We don't store the reply if its TTL is 0 unless serve-expired is 403bdfc4d55Sflorian * enabled. Such a reply won't be reusable and simply be a waste for 404bdfc4d55Sflorian * the backend. It's also compatible with the default behavior of 405bdfc4d55Sflorian * dns_cache_store_msg(). */ 406bdfc4d55Sflorian if(qstate->return_msg->rep->ttl == 0 && 407bdfc4d55Sflorian !qstate->env->cfg->serve_expired) 408bdfc4d55Sflorian return 0; 4098b7325afSsthen 4108b7325afSsthen /* The EDE is added to the out-list so it is encoded in the cached message */ 4118b7325afSsthen if (qstate->env->cfg->ede && qstate->return_msg->rep->reason_bogus != LDNS_EDE_NONE) { 4128b7325afSsthen edns_opt_list_append_ede(&edns.opt_list_out, qstate->env->scratch, 4138b7325afSsthen qstate->return_msg->rep->reason_bogus, 4148b7325afSsthen qstate->return_msg->rep->reason_bogus_str); 4158b7325afSsthen } 4168b7325afSsthen 4172ee382b6Ssthen if(verbosity >= VERB_ALGO) 4182ee382b6Ssthen log_dns_msg("cachedb encoding", &qstate->return_msg->qinfo, 4192ee382b6Ssthen qstate->return_msg->rep); 4202ee382b6Ssthen if(!reply_info_answer_encode(&qstate->return_msg->qinfo, 4212ee382b6Ssthen qstate->return_msg->rep, 0, qstate->query_flags, 4222ee382b6Ssthen buf, 0, 1, qstate->env->scratch, 65535, &edns, 1, 0)) 4232ee382b6Ssthen return 0; 4242ee382b6Ssthen 4252ee382b6Ssthen /* TTLs in the return_msg are relative to time(0) so we have to 4262ee382b6Ssthen * store that, we also store the smallest ttl in the packet+time(0) 4272ee382b6Ssthen * as the packet expiry time */ 4282ee382b6Ssthen /* qstate->return_msg->rep->ttl contains that relative shortest ttl */ 4292ee382b6Ssthen timestamp = (uint64_t)*qstate->env->now; 4302ee382b6Ssthen expiry = timestamp + (uint64_t)qstate->return_msg->rep->ttl; 4312ee382b6Ssthen timestamp = htobe64(timestamp); 4322ee382b6Ssthen expiry = htobe64(expiry); 4332ee382b6Ssthen oldlim = sldns_buffer_limit(buf); 4342ee382b6Ssthen if(oldlim + sizeof(timestamp)+sizeof(expiry) >= 4352ee382b6Ssthen sldns_buffer_capacity(buf)) 4362ee382b6Ssthen return 0; /* doesn't fit. */ 4372ee382b6Ssthen sldns_buffer_set_limit(buf, oldlim + sizeof(timestamp)+sizeof(expiry)); 4382ee382b6Ssthen sldns_buffer_write_at(buf, oldlim, ×tamp, sizeof(timestamp)); 4392ee382b6Ssthen sldns_buffer_write_at(buf, oldlim+sizeof(timestamp), &expiry, 4402ee382b6Ssthen sizeof(expiry)); 4412ee382b6Ssthen 4422ee382b6Ssthen return 1; 4432ee382b6Ssthen } 4442ee382b6Ssthen 4452ee382b6Ssthen /** check expiry, return true if matches OK */ 4462ee382b6Ssthen static int 4472ee382b6Ssthen good_expiry_and_qinfo(struct module_qstate* qstate, struct sldns_buffer* buf) 4482ee382b6Ssthen { 4492ee382b6Ssthen uint64_t expiry; 4502ee382b6Ssthen /* the expiry time is the last bytes of the buffer */ 4512ee382b6Ssthen if(sldns_buffer_limit(buf) < sizeof(expiry)) 4522ee382b6Ssthen return 0; 4532ee382b6Ssthen sldns_buffer_read_at(buf, sldns_buffer_limit(buf)-sizeof(expiry), 4542ee382b6Ssthen &expiry, sizeof(expiry)); 4552ee382b6Ssthen expiry = be64toh(expiry); 4562ee382b6Ssthen 457eaf2578eSsthen /* Check if we are allowed to return expired entries: 458eaf2578eSsthen * - serve_expired needs to be set 459eaf2578eSsthen * - if SERVE_EXPIRED_TTL is set make sure that the record is not older 460eaf2578eSsthen * than that. */ 461bdfc4d55Sflorian if((time_t)expiry < *qstate->env->now && 462eaf2578eSsthen (!qstate->env->cfg->serve_expired || 463eaf2578eSsthen (SERVE_EXPIRED_TTL && 464eaf2578eSsthen *qstate->env->now - (time_t)expiry > SERVE_EXPIRED_TTL))) 4652ee382b6Ssthen return 0; 4662ee382b6Ssthen 4672ee382b6Ssthen return 1; 4682ee382b6Ssthen } 4692ee382b6Ssthen 470bdfc4d55Sflorian /* Adjust the TTL of the given RRset by 'subtract'. If 'subtract' is 471bdfc4d55Sflorian * negative, set the TTL to 0. */ 4722be9e038Ssthen static void 4732be9e038Ssthen packed_rrset_ttl_subtract(struct packed_rrset_data* data, time_t subtract) 4742be9e038Ssthen { 4752be9e038Ssthen size_t i; 4762be9e038Ssthen size_t total = data->count + data->rrsig_count; 477bdfc4d55Sflorian if(subtract >= 0 && data->ttl > subtract) 4782be9e038Ssthen data->ttl -= subtract; 4792be9e038Ssthen else data->ttl = 0; 4802be9e038Ssthen for(i=0; i<total; i++) { 481bdfc4d55Sflorian if(subtract >= 0 && data->rr_ttl[i] > subtract) 4822be9e038Ssthen data->rr_ttl[i] -= subtract; 4832be9e038Ssthen else data->rr_ttl[i] = 0; 4842be9e038Ssthen } 4859982a05dSsthen data->ttl_add = (subtract < data->ttl_add) ? (data->ttl_add - subtract) : 0; 4862be9e038Ssthen } 4872be9e038Ssthen 488bdfc4d55Sflorian /* Adjust the TTL of a DNS message and its RRs by 'adjust'. If 'adjust' is 489bdfc4d55Sflorian * negative, set the TTLs to 0. */ 4902be9e038Ssthen static void 4912be9e038Ssthen adjust_msg_ttl(struct dns_msg* msg, time_t adjust) 4922be9e038Ssthen { 4932be9e038Ssthen size_t i; 494bdfc4d55Sflorian if(adjust >= 0 && msg->rep->ttl > adjust) 4952be9e038Ssthen msg->rep->ttl -= adjust; 496eaf2578eSsthen else 497eaf2578eSsthen msg->rep->ttl = 0; 4982be9e038Ssthen msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl); 4992308e98cSsthen msg->rep->serve_expired_ttl = msg->rep->ttl + SERVE_EXPIRED_TTL; 5002be9e038Ssthen 5012be9e038Ssthen for(i=0; i<msg->rep->rrset_count; i++) { 5022be9e038Ssthen packed_rrset_ttl_subtract((struct packed_rrset_data*)msg-> 5032be9e038Ssthen rep->rrsets[i]->entry.data, adjust); 5042be9e038Ssthen } 5052be9e038Ssthen } 5062be9e038Ssthen 5072bdc0ed1Ssthen /* Set the TTL of the given RRset to fixed value. */ 5082bdc0ed1Ssthen static void 5092bdc0ed1Ssthen packed_rrset_ttl_set(struct packed_rrset_data* data, time_t ttl) 5102bdc0ed1Ssthen { 5112bdc0ed1Ssthen size_t i; 5122bdc0ed1Ssthen size_t total = data->count + data->rrsig_count; 5132bdc0ed1Ssthen data->ttl = ttl; 5142bdc0ed1Ssthen for(i=0; i<total; i++) { 5152bdc0ed1Ssthen data->rr_ttl[i] = ttl; 5162bdc0ed1Ssthen } 5172bdc0ed1Ssthen data->ttl_add = 0; 5182bdc0ed1Ssthen } 5192bdc0ed1Ssthen 5202bdc0ed1Ssthen /* Set the TTL of a DNS message and its RRs by to a fixed value. */ 5212bdc0ed1Ssthen static void 5222bdc0ed1Ssthen set_msg_ttl(struct dns_msg* msg, time_t ttl) 5232bdc0ed1Ssthen { 5242bdc0ed1Ssthen size_t i; 5252bdc0ed1Ssthen msg->rep->ttl = ttl; 5262bdc0ed1Ssthen msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl); 5272bdc0ed1Ssthen msg->rep->serve_expired_ttl = msg->rep->ttl + SERVE_EXPIRED_TTL; 5282bdc0ed1Ssthen 5292bdc0ed1Ssthen for(i=0; i<msg->rep->rrset_count; i++) { 5302bdc0ed1Ssthen packed_rrset_ttl_set((struct packed_rrset_data*)msg-> 5312bdc0ed1Ssthen rep->rrsets[i]->entry.data, ttl); 5322bdc0ed1Ssthen } 5332bdc0ed1Ssthen } 5342bdc0ed1Ssthen 5352ee382b6Ssthen /** convert dns message in buffer to return_msg */ 5362ee382b6Ssthen static int 5372bdc0ed1Ssthen parse_data(struct module_qstate* qstate, struct sldns_buffer* buf, 5382bdc0ed1Ssthen int* msg_expired) 5392ee382b6Ssthen { 5402ee382b6Ssthen struct msg_parse* prs; 5412ee382b6Ssthen struct edns_data edns; 5428b7325afSsthen struct edns_option* ede; 5432ee382b6Ssthen uint64_t timestamp, expiry; 5442ee382b6Ssthen time_t adjust; 5452ee382b6Ssthen size_t lim = sldns_buffer_limit(buf); 5462ee382b6Ssthen if(lim < LDNS_HEADER_SIZE+sizeof(timestamp)+sizeof(expiry)) 5472ee382b6Ssthen return 0; /* too short */ 5482ee382b6Ssthen 5492ee382b6Ssthen /* remove timestamp and expiry from end */ 5502ee382b6Ssthen sldns_buffer_read_at(buf, lim-sizeof(expiry), &expiry, sizeof(expiry)); 5512ee382b6Ssthen sldns_buffer_read_at(buf, lim-sizeof(expiry)-sizeof(timestamp), 5522ee382b6Ssthen ×tamp, sizeof(timestamp)); 5532ee382b6Ssthen expiry = be64toh(expiry); 5542ee382b6Ssthen timestamp = be64toh(timestamp); 5552ee382b6Ssthen 5562ee382b6Ssthen /* parse DNS packet */ 5572ee382b6Ssthen regional_free_all(qstate->env->scratch); 5582ee382b6Ssthen prs = (struct msg_parse*)regional_alloc(qstate->env->scratch, 5592ee382b6Ssthen sizeof(struct msg_parse)); 5602ee382b6Ssthen if(!prs) 5612ee382b6Ssthen return 0; /* out of memory */ 5622ee382b6Ssthen memset(prs, 0, sizeof(*prs)); 5632ee382b6Ssthen memset(&edns, 0, sizeof(edns)); 5642ee382b6Ssthen sldns_buffer_set_limit(buf, lim - sizeof(expiry)-sizeof(timestamp)); 5652ee382b6Ssthen if(parse_packet(buf, prs, qstate->env->scratch) != LDNS_RCODE_NOERROR) { 5662ee382b6Ssthen sldns_buffer_set_limit(buf, lim); 5672ee382b6Ssthen return 0; 5682ee382b6Ssthen } 569e21c60efSsthen if(parse_extract_edns_from_response_msg(prs, &edns, qstate->env->scratch) != 5702ee382b6Ssthen LDNS_RCODE_NOERROR) { 5712ee382b6Ssthen sldns_buffer_set_limit(buf, lim); 5722ee382b6Ssthen return 0; 5732ee382b6Ssthen } 5742ee382b6Ssthen 5752ee382b6Ssthen qstate->return_msg = dns_alloc_msg(buf, prs, qstate->region); 5762ee382b6Ssthen sldns_buffer_set_limit(buf, lim); 5772ee382b6Ssthen if(!qstate->return_msg) 5782ee382b6Ssthen return 0; 5792ee382b6Ssthen 5808b7325afSsthen /* We find the EDE in the in-list after parsing */ 5818b7325afSsthen if(qstate->env->cfg->ede && 5828b7325afSsthen (ede = edns_opt_list_find(edns.opt_list_in, LDNS_EDNS_EDE))) { 5838b7325afSsthen if(ede->opt_len >= 2) { 5848b7325afSsthen qstate->return_msg->rep->reason_bogus = 5858b7325afSsthen sldns_read_uint16(ede->opt_data); 5868b7325afSsthen } 5878b7325afSsthen /* allocate space and store the error string and it's size */ 5888b7325afSsthen if(ede->opt_len > 2) { 5898b7325afSsthen size_t ede_len = ede->opt_len - 2; 5908b7325afSsthen qstate->return_msg->rep->reason_bogus_str = regional_alloc( 5918b7325afSsthen qstate->region, sizeof(char) * (ede_len+1)); 5928b7325afSsthen memcpy(qstate->return_msg->rep->reason_bogus_str, 5938b7325afSsthen ede->opt_data+2, ede_len); 5948b7325afSsthen qstate->return_msg->rep->reason_bogus_str[ede_len] = 0; 5958b7325afSsthen } 5968b7325afSsthen } 5978b7325afSsthen 5982ee382b6Ssthen qstate->return_rcode = LDNS_RCODE_NOERROR; 5992ee382b6Ssthen 6002ee382b6Ssthen /* see how much of the TTL expired, and remove it */ 6012be9e038Ssthen if(*qstate->env->now <= (time_t)timestamp) { 6022be9e038Ssthen verbose(VERB_ALGO, "cachedb msg adjust by zero"); 6032be9e038Ssthen return 1; /* message from the future (clock skew?) */ 6042ee382b6Ssthen } 6052be9e038Ssthen adjust = *qstate->env->now - (time_t)timestamp; 6062be9e038Ssthen if(qstate->return_msg->rep->ttl < adjust) { 6072be9e038Ssthen verbose(VERB_ALGO, "cachedb msg expired"); 6082bdc0ed1Ssthen *msg_expired = 1; 609bdfc4d55Sflorian /* If serve-expired is enabled, we still use an expired message 610bdfc4d55Sflorian * setting the TTL to 0. */ 6118b7325afSsthen if(!qstate->env->cfg->serve_expired || 6128b7325afSsthen (FLAGS_GET_RCODE(qstate->return_msg->rep->flags) 6138b7325afSsthen != LDNS_RCODE_NOERROR && 6148b7325afSsthen FLAGS_GET_RCODE(qstate->return_msg->rep->flags) 6158b7325afSsthen != LDNS_RCODE_NXDOMAIN && 6168b7325afSsthen FLAGS_GET_RCODE(qstate->return_msg->rep->flags) 6178b7325afSsthen != LDNS_RCODE_YXDOMAIN)) 6182be9e038Ssthen return 0; /* message expired */ 6198b7325afSsthen else 6208b7325afSsthen adjust = -1; 6212be9e038Ssthen } 6222be9e038Ssthen verbose(VERB_ALGO, "cachedb msg adjusted down by %d", (int)adjust); 6232be9e038Ssthen adjust_msg_ttl(qstate->return_msg, adjust); 624bdfc4d55Sflorian 625bdfc4d55Sflorian /* Similar to the unbound worker, if serve-expired is enabled and 626bdfc4d55Sflorian * the msg would be considered to be expired, mark the state so a 627bdfc4d55Sflorian * refetch will be scheduled. The comparison between 'expiry' and 628bdfc4d55Sflorian * 'now' should be redundant given how these values were calculated, 629bdfc4d55Sflorian * but we check it just in case as does good_expiry_and_qinfo(). */ 630bdfc4d55Sflorian if(qstate->env->cfg->serve_expired && 6312bdc0ed1Ssthen !qstate->env->cfg->serve_expired_client_timeout && 632bdfc4d55Sflorian (adjust == -1 || (time_t)expiry < *qstate->env->now)) { 633bdfc4d55Sflorian qstate->need_refetch = 1; 634bdfc4d55Sflorian } 635bdfc4d55Sflorian 6362be9e038Ssthen return 1; 6372ee382b6Ssthen } 6382ee382b6Ssthen 6392ee382b6Ssthen /** 6402ee382b6Ssthen * Lookup the qstate.qinfo in extcache, store in qstate.return_msg. 6412ee382b6Ssthen * return true if lookup was successful. 6422ee382b6Ssthen */ 6432ee382b6Ssthen static int 6442bdc0ed1Ssthen cachedb_extcache_lookup(struct module_qstate* qstate, struct cachedb_env* ie, 6452bdc0ed1Ssthen int* msg_expired) 6462ee382b6Ssthen { 6472ee382b6Ssthen char key[(CACHEDB_HASHSIZE/8)*2+1]; 648*98bc733bSsthen calc_hash(&qstate->qinfo, qstate->env, key, sizeof(key)); 6492ee382b6Ssthen 6502ee382b6Ssthen /* call backend to fetch data for key into scratch buffer */ 6512ee382b6Ssthen if( !(*ie->backend->lookup)(qstate->env, ie, key, 6522ee382b6Ssthen qstate->env->scratch_buffer)) { 6532ee382b6Ssthen return 0; 6542ee382b6Ssthen } 6552ee382b6Ssthen 6562ee382b6Ssthen /* check expiry date and check if query-data matches */ 6572ee382b6Ssthen if( !good_expiry_and_qinfo(qstate, qstate->env->scratch_buffer) ) { 6582ee382b6Ssthen return 0; 6592ee382b6Ssthen } 6602ee382b6Ssthen 6612ee382b6Ssthen /* parse dns message into return_msg */ 6622bdc0ed1Ssthen if( !parse_data(qstate, qstate->env->scratch_buffer, msg_expired) ) { 6632ee382b6Ssthen return 0; 6642ee382b6Ssthen } 6652ee382b6Ssthen return 1; 6662ee382b6Ssthen } 6672ee382b6Ssthen 6682ee382b6Ssthen /** 6692ee382b6Ssthen * Store the qstate.return_msg in extcache for key qstate.info 6702ee382b6Ssthen */ 6712ee382b6Ssthen static void 6722ee382b6Ssthen cachedb_extcache_store(struct module_qstate* qstate, struct cachedb_env* ie) 6732ee382b6Ssthen { 6742ee382b6Ssthen char key[(CACHEDB_HASHSIZE/8)*2+1]; 675*98bc733bSsthen calc_hash(&qstate->qinfo, qstate->env, key, sizeof(key)); 6762ee382b6Ssthen 6772ee382b6Ssthen /* prepare data in scratch buffer */ 6782ee382b6Ssthen if(!prep_data(qstate, qstate->env->scratch_buffer)) 6792ee382b6Ssthen return; 6802ee382b6Ssthen 6812ee382b6Ssthen /* call backend */ 6822ee382b6Ssthen (*ie->backend->store)(qstate->env, ie, key, 6832ee382b6Ssthen sldns_buffer_begin(qstate->env->scratch_buffer), 684a3167c07Ssthen sldns_buffer_limit(qstate->env->scratch_buffer), 685a3167c07Ssthen qstate->return_msg->rep->ttl); 6862ee382b6Ssthen } 6872ee382b6Ssthen 6882ee382b6Ssthen /** 6892ee382b6Ssthen * See if unbound's internal cache can answer the query 6902ee382b6Ssthen */ 6912ee382b6Ssthen static int 6928b7325afSsthen cachedb_intcache_lookup(struct module_qstate* qstate, struct cachedb_env* cde) 6932ee382b6Ssthen { 6942bdc0ed1Ssthen uint8_t dpname_storage[LDNS_MAX_DOMAINLEN+1]; 695191f22c6Ssthen uint8_t* dpname=NULL; 696191f22c6Ssthen size_t dpnamelen=0; 6972ee382b6Ssthen struct dns_msg* msg; 6988b7325afSsthen /* for testframe bypass this lookup */ 6998b7325afSsthen if(cde->backend == &testframe_backend) { 7008b7325afSsthen return 0; 7018b7325afSsthen } 702191f22c6Ssthen if(iter_stub_fwd_no_cache(qstate, &qstate->qinfo, 7032bdc0ed1Ssthen &dpname, &dpnamelen, dpname_storage, sizeof(dpname_storage))) 704191f22c6Ssthen return 0; /* no cache for these queries */ 7052ee382b6Ssthen msg = dns_cache_lookup(qstate->env, qstate->qinfo.qname, 7062ee382b6Ssthen qstate->qinfo.qname_len, qstate->qinfo.qtype, 7072ee382b6Ssthen qstate->qinfo.qclass, qstate->query_flags, 708938a3a5eSflorian qstate->region, qstate->env->scratch, 709191f22c6Ssthen 1, /* no partial messages with only a CNAME */ 710191f22c6Ssthen dpname, dpnamelen 711938a3a5eSflorian ); 71220237c55Ssthen if(!msg && qstate->env->neg_cache && 71320237c55Ssthen iter_qname_indicates_dnssec(qstate->env, &qstate->qinfo)) { 7142ee382b6Ssthen /* lookup in negative cache; may result in 7152ee382b6Ssthen * NOERROR/NODATA or NXDOMAIN answers that need validation */ 7162ee382b6Ssthen msg = val_neg_getmsg(qstate->env->neg_cache, &qstate->qinfo, 7172ee382b6Ssthen qstate->region, qstate->env->rrset_cache, 7182ee382b6Ssthen qstate->env->scratch_buffer, 719938a3a5eSflorian *qstate->env->now, 1/*add SOA*/, NULL, 720938a3a5eSflorian qstate->env->cfg); 7212ee382b6Ssthen } 7222ee382b6Ssthen if(!msg) 7232ee382b6Ssthen return 0; 7242ee382b6Ssthen /* this is the returned msg */ 7252ee382b6Ssthen qstate->return_rcode = LDNS_RCODE_NOERROR; 7262ee382b6Ssthen qstate->return_msg = msg; 7272ee382b6Ssthen return 1; 7282ee382b6Ssthen } 7292ee382b6Ssthen 7302ee382b6Ssthen /** 7312ee382b6Ssthen * Store query into the internal cache of unbound. 7322ee382b6Ssthen */ 7332ee382b6Ssthen static void 7342bdc0ed1Ssthen cachedb_intcache_store(struct module_qstate* qstate, int msg_expired) 7352ee382b6Ssthen { 736bdfc4d55Sflorian uint32_t store_flags = qstate->query_flags; 7372bdc0ed1Ssthen int serve_expired = qstate->env->cfg->serve_expired; 738bdfc4d55Sflorian 739bdfc4d55Sflorian if(qstate->env->cfg->serve_expired) 740bdfc4d55Sflorian store_flags |= DNSCACHE_STORE_ZEROTTL; 7412ee382b6Ssthen if(!qstate->return_msg) 7422ee382b6Ssthen return; 7432bdc0ed1Ssthen if(serve_expired && msg_expired) { 7442bdc0ed1Ssthen /* Set TTLs to a value such that value + *env->now is 7452bdc0ed1Ssthen * going to be now-3 seconds. Making it expired 7462bdc0ed1Ssthen * in the cache. */ 7472bdc0ed1Ssthen set_msg_ttl(qstate->return_msg, (time_t)-3); 748*98bc733bSsthen /* The expired entry does not get checked by the validator 749*98bc733bSsthen * and we need a validation value for it. */ 750*98bc733bSsthen if(qstate->env->cfg->cachedb_check_when_serve_expired) 751*98bc733bSsthen qstate->return_msg->rep->security = sec_status_insecure; 7522bdc0ed1Ssthen } 7532ee382b6Ssthen (void)dns_cache_store(qstate->env, &qstate->qinfo, 7542ee382b6Ssthen qstate->return_msg->rep, 0, qstate->prefetch_leeway, 0, 755d1e2768aSsthen qstate->region, store_flags, qstate->qstarttime); 7562bdc0ed1Ssthen if(serve_expired && msg_expired) { 7572bdc0ed1Ssthen if(qstate->env->cfg->serve_expired_client_timeout) { 7582bdc0ed1Ssthen /* No expired response from the query state, the 7592bdc0ed1Ssthen * query resolution needs to continue and it can 7602bdc0ed1Ssthen * pick up the expired result after the timer out 7612bdc0ed1Ssthen * of cache. */ 7622bdc0ed1Ssthen return; 7632bdc0ed1Ssthen } 7642bdc0ed1Ssthen /* set TTLs to zero again */ 7652bdc0ed1Ssthen adjust_msg_ttl(qstate->return_msg, -1); 7662bdc0ed1Ssthen /* Send serve expired responses based on the cachedb 7672bdc0ed1Ssthen * returned message, that was just stored in the cache. 7682bdc0ed1Ssthen * It can then continue to work on this query. */ 7692bdc0ed1Ssthen mesh_respond_serve_expired(qstate->mesh_info); 7702bdc0ed1Ssthen } 7712ee382b6Ssthen } 7722ee382b6Ssthen 7732ee382b6Ssthen /** 7742ee382b6Ssthen * Handle a cachedb module event with a query 7752ee382b6Ssthen * @param qstate: query state (from the mesh), passed between modules. 7762ee382b6Ssthen * contains qstate->env module environment with global caches and so on. 7772ee382b6Ssthen * @param iq: query state specific for this module. per-query. 7782ee382b6Ssthen * @param ie: environment specific for this module. global. 7792ee382b6Ssthen * @param id: module id. 7802ee382b6Ssthen */ 7812ee382b6Ssthen static void 7822ee382b6Ssthen cachedb_handle_query(struct module_qstate* qstate, 7832ee382b6Ssthen struct cachedb_qstate* ATTR_UNUSED(iq), 7842ee382b6Ssthen struct cachedb_env* ie, int id) 7852ee382b6Ssthen { 7862bdc0ed1Ssthen int msg_expired = 0; 7878b7325afSsthen qstate->is_cachedb_answer = 0; 7882ee382b6Ssthen /* check if we are enabled, and skip if so */ 7892ee382b6Ssthen if(!ie->enabled) { 7902ee382b6Ssthen /* pass request to next module */ 7912ee382b6Ssthen qstate->ext_state[id] = module_wait_module; 7922ee382b6Ssthen return; 7932ee382b6Ssthen } 7942ee382b6Ssthen 79577079be7Ssthen if(qstate->blacklist || qstate->no_cache_lookup) { 79677079be7Ssthen /* cache is blacklisted or we are instructed from edns to not look */ 7972ee382b6Ssthen /* pass request to next module */ 7982ee382b6Ssthen qstate->ext_state[id] = module_wait_module; 7992ee382b6Ssthen return; 8002ee382b6Ssthen } 8012ee382b6Ssthen 802eaf2578eSsthen /* lookup inside unbound's internal cache. 803eaf2578eSsthen * This does not look for expired entries. */ 8048b7325afSsthen if(cachedb_intcache_lookup(qstate, ie)) { 80577079be7Ssthen if(verbosity >= VERB_ALGO) { 80677079be7Ssthen if(qstate->return_msg->rep) 8072ee382b6Ssthen log_dns_msg("cachedb internal cache lookup", 8082ee382b6Ssthen &qstate->return_msg->qinfo, 8092ee382b6Ssthen qstate->return_msg->rep); 81077079be7Ssthen else log_info("cachedb internal cache lookup: rcode %s", 811eaf2578eSsthen sldns_lookup_by_id(sldns_rcodes, qstate->return_rcode) 812eaf2578eSsthen ?sldns_lookup_by_id(sldns_rcodes, qstate->return_rcode)->name 813eaf2578eSsthen :"??"); 81477079be7Ssthen } 8152ee382b6Ssthen /* we are done with the query */ 8162ee382b6Ssthen qstate->ext_state[id] = module_finished; 8172ee382b6Ssthen return; 8182ee382b6Ssthen } 8192ee382b6Ssthen 8202ee382b6Ssthen /* ask backend cache to see if we have data */ 8212bdc0ed1Ssthen if(cachedb_extcache_lookup(qstate, ie, &msg_expired)) { 8222ee382b6Ssthen if(verbosity >= VERB_ALGO) 8232ee382b6Ssthen log_dns_msg(ie->backend->name, 8242ee382b6Ssthen &qstate->return_msg->qinfo, 8252ee382b6Ssthen qstate->return_msg->rep); 8262ee382b6Ssthen /* store this result in internal cache */ 8272bdc0ed1Ssthen cachedb_intcache_store(qstate, msg_expired); 828eaf2578eSsthen /* In case we have expired data but there is a client timer for expired 829eaf2578eSsthen * answers, pass execution to next module in order to try updating the 830eaf2578eSsthen * data first. 831eaf2578eSsthen * TODO: this needs revisit. The expired data stored from cachedb has 832eaf2578eSsthen * 0 TTL which is picked up by iterator later when looking in the cache. 8332bdc0ed1Ssthen */ 8342bdc0ed1Ssthen if(qstate->env->cfg->serve_expired && msg_expired) { 8352bdc0ed1Ssthen qstate->return_msg = NULL; 8362bdc0ed1Ssthen qstate->ext_state[id] = module_wait_module; 8372bdc0ed1Ssthen /* The expired reply is sent with 8382bdc0ed1Ssthen * mesh_respond_serve_expired, and so 8392bdc0ed1Ssthen * the need_refetch is not used. */ 8402bdc0ed1Ssthen qstate->need_refetch = 0; 8412bdc0ed1Ssthen return; 8422bdc0ed1Ssthen } 843eaf2578eSsthen if(qstate->need_refetch && qstate->serve_expired_data && 844eaf2578eSsthen qstate->serve_expired_data->timer) { 845eaf2578eSsthen qstate->return_msg = NULL; 846eaf2578eSsthen qstate->ext_state[id] = module_wait_module; 847eaf2578eSsthen return; 848eaf2578eSsthen } 8498b7325afSsthen qstate->is_cachedb_answer = 1; 8502ee382b6Ssthen /* we are done with the query */ 8512ee382b6Ssthen qstate->ext_state[id] = module_finished; 8522ee382b6Ssthen return; 8532ee382b6Ssthen } 8542ee382b6Ssthen 8552bdc0ed1Ssthen if(qstate->serve_expired_data && 8562bdc0ed1Ssthen qstate->env->cfg->cachedb_check_when_serve_expired && 8572bdc0ed1Ssthen !qstate->env->cfg->serve_expired_client_timeout) { 8582bdc0ed1Ssthen /* Reply with expired data if any to client, because cachedb 8592bdc0ed1Ssthen * also has no useful, current data */ 8602bdc0ed1Ssthen mesh_respond_serve_expired(qstate->mesh_info); 8612bdc0ed1Ssthen } 8622bdc0ed1Ssthen 8632ee382b6Ssthen /* no cache fetches */ 8642ee382b6Ssthen /* pass request to next module */ 8652ee382b6Ssthen qstate->ext_state[id] = module_wait_module; 8662ee382b6Ssthen } 8672ee382b6Ssthen 8682ee382b6Ssthen /** 8692ee382b6Ssthen * Handle a cachedb module event with a response from the iterator. 8702ee382b6Ssthen * @param qstate: query state (from the mesh), passed between modules. 8712ee382b6Ssthen * contains qstate->env module environment with global caches and so on. 8722ee382b6Ssthen * @param iq: query state specific for this module. per-query. 8732ee382b6Ssthen * @param ie: environment specific for this module. global. 8742ee382b6Ssthen * @param id: module id. 8752ee382b6Ssthen */ 8762ee382b6Ssthen static void 8772ee382b6Ssthen cachedb_handle_response(struct module_qstate* qstate, 8782ee382b6Ssthen struct cachedb_qstate* ATTR_UNUSED(iq), struct cachedb_env* ie, int id) 8792ee382b6Ssthen { 8808b7325afSsthen qstate->is_cachedb_answer = 0; 88177079be7Ssthen /* check if we are not enabled or instructed to not cache, and skip */ 88277079be7Ssthen if(!ie->enabled || qstate->no_cache_store) { 8832ee382b6Ssthen /* we are done with the query */ 8842ee382b6Ssthen qstate->ext_state[id] = module_finished; 8852ee382b6Ssthen return; 8862ee382b6Ssthen } 887d896b962Ssthen if(qstate->env->cfg->cachedb_no_store) { 888d896b962Ssthen /* do not store the item in the external cache */ 889d896b962Ssthen qstate->ext_state[id] = module_finished; 890d896b962Ssthen return; 891d896b962Ssthen } 8922ee382b6Ssthen 8932ee382b6Ssthen /* store the item into the backend cache */ 8942ee382b6Ssthen cachedb_extcache_store(qstate, ie); 8952ee382b6Ssthen 8962ee382b6Ssthen /* we are done with the query */ 8972ee382b6Ssthen qstate->ext_state[id] = module_finished; 8982ee382b6Ssthen } 8992ee382b6Ssthen 9002ee382b6Ssthen void 9012ee382b6Ssthen cachedb_operate(struct module_qstate* qstate, enum module_ev event, int id, 9022ee382b6Ssthen struct outbound_entry* outbound) 9032ee382b6Ssthen { 9042ee382b6Ssthen struct cachedb_env* ie = (struct cachedb_env*)qstate->env->modinfo[id]; 9052ee382b6Ssthen struct cachedb_qstate* iq = (struct cachedb_qstate*)qstate->minfo[id]; 9062ee382b6Ssthen verbose(VERB_QUERY, "cachedb[module %d] operate: extstate:%s event:%s", 9072ee382b6Ssthen id, strextstate(qstate->ext_state[id]), strmodulevent(event)); 9082ee382b6Ssthen if(iq) log_query_info(VERB_QUERY, "cachedb operate: query", 9092ee382b6Ssthen &qstate->qinfo); 9102ee382b6Ssthen 9112ee382b6Ssthen /* perform cachedb state machine */ 9122ee382b6Ssthen if((event == module_event_new || event == module_event_pass) && 9132ee382b6Ssthen iq == NULL) { 9142ee382b6Ssthen if(!cachedb_new(qstate, id)) { 9152ee382b6Ssthen (void)error_response(qstate, id, LDNS_RCODE_SERVFAIL); 9162ee382b6Ssthen return; 9172ee382b6Ssthen } 9182ee382b6Ssthen iq = (struct cachedb_qstate*)qstate->minfo[id]; 9192ee382b6Ssthen } 9202ee382b6Ssthen if(iq && (event == module_event_pass || event == module_event_new)) { 9212ee382b6Ssthen cachedb_handle_query(qstate, iq, ie, id); 9222ee382b6Ssthen return; 9232ee382b6Ssthen } 9242ee382b6Ssthen if(iq && (event == module_event_moddone)) { 9252ee382b6Ssthen cachedb_handle_response(qstate, iq, ie, id); 9262ee382b6Ssthen return; 9272ee382b6Ssthen } 9282ee382b6Ssthen if(iq && outbound) { 9292ee382b6Ssthen /* cachedb does not need to process responses at this time 9302ee382b6Ssthen * ignore it. 9312ee382b6Ssthen cachedb_process_response(qstate, iq, ie, id, outbound, event); 9322ee382b6Ssthen */ 9332ee382b6Ssthen return; 9342ee382b6Ssthen } 9352ee382b6Ssthen if(event == module_event_error) { 9362ee382b6Ssthen verbose(VERB_ALGO, "got called with event error, giving up"); 9372ee382b6Ssthen (void)error_response(qstate, id, LDNS_RCODE_SERVFAIL); 9382ee382b6Ssthen return; 9392ee382b6Ssthen } 94077079be7Ssthen if(!iq && (event == module_event_moddone)) { 94177079be7Ssthen /* during priming, module done but we never started */ 94277079be7Ssthen qstate->ext_state[id] = module_finished; 94377079be7Ssthen return; 94477079be7Ssthen } 9452ee382b6Ssthen 9462ee382b6Ssthen log_err("bad event for cachedb"); 9472ee382b6Ssthen (void)error_response(qstate, id, LDNS_RCODE_SERVFAIL); 9482ee382b6Ssthen } 9492ee382b6Ssthen 9502ee382b6Ssthen void 9512ee382b6Ssthen cachedb_inform_super(struct module_qstate* ATTR_UNUSED(qstate), 9522ee382b6Ssthen int ATTR_UNUSED(id), struct module_qstate* ATTR_UNUSED(super)) 9532ee382b6Ssthen { 9542ee382b6Ssthen /* cachedb does not use subordinate requests at this time */ 9552ee382b6Ssthen verbose(VERB_ALGO, "cachedb inform_super was called"); 9562ee382b6Ssthen } 9572ee382b6Ssthen 9582ee382b6Ssthen void 9592ee382b6Ssthen cachedb_clear(struct module_qstate* qstate, int id) 9602ee382b6Ssthen { 9612ee382b6Ssthen struct cachedb_qstate* iq; 9622ee382b6Ssthen if(!qstate) 9632ee382b6Ssthen return; 9642ee382b6Ssthen iq = (struct cachedb_qstate*)qstate->minfo[id]; 9652ee382b6Ssthen if(iq) { 9662ee382b6Ssthen /* free contents of iq */ 9672ee382b6Ssthen /* TODO */ 9682ee382b6Ssthen } 9692ee382b6Ssthen qstate->minfo[id] = NULL; 9702ee382b6Ssthen } 9712ee382b6Ssthen 9722ee382b6Ssthen size_t 9732ee382b6Ssthen cachedb_get_mem(struct module_env* env, int id) 9742ee382b6Ssthen { 9752ee382b6Ssthen struct cachedb_env* ie = (struct cachedb_env*)env->modinfo[id]; 9762ee382b6Ssthen if(!ie) 9772ee382b6Ssthen return 0; 9782ee382b6Ssthen return sizeof(*ie); /* TODO - more mem */ 9792ee382b6Ssthen } 9802ee382b6Ssthen 9812ee382b6Ssthen /** 9822ee382b6Ssthen * The cachedb function block 9832ee382b6Ssthen */ 9842ee382b6Ssthen static struct module_func_block cachedb_block = { 9852ee382b6Ssthen "cachedb", 986*98bc733bSsthen NULL, NULL, &cachedb_init, &cachedb_deinit, &cachedb_operate, 9872ee382b6Ssthen &cachedb_inform_super, &cachedb_clear, &cachedb_get_mem 9882ee382b6Ssthen }; 9892ee382b6Ssthen 9902ee382b6Ssthen struct module_func_block* 9912ee382b6Ssthen cachedb_get_funcblock(void) 9922ee382b6Ssthen { 9932ee382b6Ssthen return &cachedb_block; 9942ee382b6Ssthen } 9952bdc0ed1Ssthen 9962bdc0ed1Ssthen int 9972bdc0ed1Ssthen cachedb_is_enabled(struct module_stack* mods, struct module_env* env) 9982bdc0ed1Ssthen { 9992bdc0ed1Ssthen struct cachedb_env* ie; 10002bdc0ed1Ssthen int id = modstack_find(mods, "cachedb"); 10012bdc0ed1Ssthen if(id == -1) 10022bdc0ed1Ssthen return 0; 10032bdc0ed1Ssthen ie = (struct cachedb_env*)env->modinfo[id]; 10042bdc0ed1Ssthen if(ie && ie->enabled) 10052bdc0ed1Ssthen return 1; 10062bdc0ed1Ssthen return 0; 10072bdc0ed1Ssthen } 10082bdc0ed1Ssthen 10092bdc0ed1Ssthen void cachedb_msg_remove(struct module_qstate* qstate) 10102bdc0ed1Ssthen { 1011*98bc733bSsthen cachedb_msg_remove_qinfo(qstate->env, &qstate->qinfo); 1012*98bc733bSsthen } 10132bdc0ed1Ssthen 1014*98bc733bSsthen void cachedb_msg_remove_qinfo(struct module_env* env, struct query_info* qinfo) 1015*98bc733bSsthen { 1016*98bc733bSsthen char key[(CACHEDB_HASHSIZE/8)*2+1]; 1017*98bc733bSsthen int id = modstack_find(env->modstack, "cachedb"); 1018*98bc733bSsthen struct cachedb_env* ie = (struct cachedb_env*)env->modinfo[id]; 1019*98bc733bSsthen 1020*98bc733bSsthen log_query_info(VERB_ALGO, "cachedb msg remove", qinfo); 1021*98bc733bSsthen calc_hash(qinfo, env, key, sizeof(key)); 1022*98bc733bSsthen sldns_buffer_clear(env->scratch_buffer); 1023*98bc733bSsthen sldns_buffer_write_u32(env->scratch_buffer, 0); 1024*98bc733bSsthen sldns_buffer_flip(env->scratch_buffer); 10252bdc0ed1Ssthen 10262bdc0ed1Ssthen /* call backend */ 1027*98bc733bSsthen (*ie->backend->store)(env, ie, key, 1028*98bc733bSsthen sldns_buffer_begin(env->scratch_buffer), 1029*98bc733bSsthen sldns_buffer_limit(env->scratch_buffer), 10302bdc0ed1Ssthen 0); 10312bdc0ed1Ssthen } 10322ee382b6Ssthen #endif /* USE_CACHEDB */ 1033