diff options
Diffstat (limited to 'external/unbound/services/cache')
-rw-r--r-- | external/unbound/services/cache/dns.c | 910 | ||||
-rw-r--r-- | external/unbound/services/cache/dns.h | 211 | ||||
-rw-r--r-- | external/unbound/services/cache/infra.c | 997 | ||||
-rw-r--r-- | external/unbound/services/cache/infra.h | 462 | ||||
-rw-r--r-- | external/unbound/services/cache/rrset.c | 419 | ||||
-rw-r--r-- | external/unbound/services/cache/rrset.h | 231 |
6 files changed, 0 insertions, 3230 deletions
diff --git a/external/unbound/services/cache/dns.c b/external/unbound/services/cache/dns.c deleted file mode 100644 index a8fde9f28..000000000 --- a/external/unbound/services/cache/dns.c +++ /dev/null @@ -1,910 +0,0 @@ -/* - * services/cache/dns.c - Cache services for DNS using msg and rrset caches. - * - * Copyright (c) 2007, NLnet Labs. All rights reserved. - * - * This software is open source. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of the NLNET LABS nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * \file - * - * This file contains the DNS cache. - */ -#include "config.h" -#include "iterator/iter_delegpt.h" -#include "validator/val_nsec.h" -#include "services/cache/dns.h" -#include "services/cache/rrset.h" -#include "util/data/msgreply.h" -#include "util/data/packed_rrset.h" -#include "util/data/dname.h" -#include "util/module.h" -#include "util/net_help.h" -#include "util/regional.h" -#include "util/config_file.h" -#include "sldns/sbuffer.h" - -/** store rrsets in the rrset cache. - * @param env: module environment with caches. - * @param rep: contains list of rrsets to store. - * @param now: current time. - * @param leeway: during prefetch how much leeway to update TTLs. - * This makes rrsets (other than type NS) timeout sooner so they get - * updated with a new full TTL. - * Type NS does not get this, because it must not be refreshed from the - * child domain, but keep counting down properly. - * @param pside: if from parentside discovered NS, so that its NS is okay - * in a prefetch situation to be updated (without becoming sticky). - * @param qrep: update rrsets here if cache is better - * @param region: for qrep allocs. - */ -static void -store_rrsets(struct module_env* env, struct reply_info* rep, time_t now, - time_t leeway, int pside, struct reply_info* qrep, - struct regional* region) -{ - size_t i; - /* see if rrset already exists in cache, if not insert it. */ - for(i=0; i<rep->rrset_count; i++) { - rep->ref[i].key = rep->rrsets[i]; - rep->ref[i].id = rep->rrsets[i]->id; - /* update ref if it was in the cache */ - switch(rrset_cache_update(env->rrset_cache, &rep->ref[i], - env->alloc, now + ((ntohs(rep->ref[i].key->rk.type)== - LDNS_RR_TYPE_NS && !pside)?0:leeway))) { - case 0: /* ref unchanged, item inserted */ - break; - case 2: /* ref updated, cache is superior */ - if(region) { - struct ub_packed_rrset_key* ck; - lock_rw_rdlock(&rep->ref[i].key->entry.lock); - /* if deleted rrset, do not copy it */ - if(rep->ref[i].key->id == 0) - ck = NULL; - else ck = packed_rrset_copy_region( - rep->ref[i].key, region, now); - lock_rw_unlock(&rep->ref[i].key->entry.lock); - if(ck) { - /* use cached copy if memory allows */ - qrep->rrsets[i] = ck; - } - } - /* no break: also copy key item */ - case 1: /* ref updated, item inserted */ - rep->rrsets[i] = rep->ref[i].key; - } - } -} - -void -dns_cache_store_msg(struct module_env* env, struct query_info* qinfo, - hashvalue_type hash, struct reply_info* rep, time_t leeway, int pside, - struct reply_info* qrep, struct regional* region) -{ - struct msgreply_entry* e; - time_t ttl = rep->ttl; - size_t i; - - /* store RRsets */ - for(i=0; i<rep->rrset_count; i++) { - rep->ref[i].key = rep->rrsets[i]; - rep->ref[i].id = rep->rrsets[i]->id; - } - - /* there was a reply_info_sortref(rep) here but it seems to be - * unnecessary, because the cache gets locked per rrset. */ - reply_info_set_ttls(rep, *env->now); - store_rrsets(env, rep, *env->now, leeway, pside, qrep, region); - if(ttl == 0) { - /* we do not store the message, but we did store the RRs, - * which could be useful for delegation information */ - verbose(VERB_ALGO, "TTL 0: dropped msg from cache"); - free(rep); - return; - } - - /* store msg in the cache */ - reply_info_sortref(rep); - if(!(e = query_info_entrysetup(qinfo, rep, hash))) { - log_err("store_msg: malloc failed"); - return; - } - slabhash_insert(env->msg_cache, hash, &e->entry, rep, env->alloc); -} - -/** find closest NS or DNAME and returns the rrset (locked) */ -static struct ub_packed_rrset_key* -find_closest_of_type(struct module_env* env, uint8_t* qname, size_t qnamelen, - uint16_t qclass, time_t now, uint16_t searchtype, int stripfront) -{ - struct ub_packed_rrset_key *rrset; - uint8_t lablen; - - if(stripfront) { - /* strip off so that DNAMEs have strict subdomain match */ - lablen = *qname; - qname += lablen + 1; - qnamelen -= lablen + 1; - } - - /* snip off front part of qname until the type is found */ - while(qnamelen > 0) { - if((rrset = rrset_cache_lookup(env->rrset_cache, qname, - qnamelen, searchtype, qclass, 0, now, 0))) - return rrset; - - /* snip off front label */ - lablen = *qname; - qname += lablen + 1; - qnamelen -= lablen + 1; - } - return NULL; -} - -/** add addr to additional section */ -static void -addr_to_additional(struct ub_packed_rrset_key* rrset, struct regional* region, - struct dns_msg* msg, time_t now) -{ - if((msg->rep->rrsets[msg->rep->rrset_count] = - packed_rrset_copy_region(rrset, region, now))) { - msg->rep->ar_numrrsets++; - msg->rep->rrset_count++; - } -} - -/** lookup message in message cache */ -static struct msgreply_entry* -msg_cache_lookup(struct module_env* env, uint8_t* qname, size_t qnamelen, - uint16_t qtype, uint16_t qclass, uint16_t flags, time_t now, int wr) -{ - struct lruhash_entry* e; - struct query_info k; - hashvalue_type h; - - k.qname = qname; - k.qname_len = qnamelen; - k.qtype = qtype; - k.qclass = qclass; - k.local_alias = NULL; - h = query_info_hash(&k, flags); - e = slabhash_lookup(env->msg_cache, h, &k, wr); - - if(!e) return NULL; - if( now > ((struct reply_info*)e->data)->ttl ) { - lock_rw_unlock(&e->lock); - return NULL; - } - return (struct msgreply_entry*)e->key; -} - -/** find and add A and AAAA records for nameservers in delegpt */ -static int -find_add_addrs(struct module_env* env, uint16_t qclass, - struct regional* region, struct delegpt* dp, time_t now, - struct dns_msg** msg) -{ - struct delegpt_ns* ns; - struct msgreply_entry* neg; - struct ub_packed_rrset_key* akey; - for(ns = dp->nslist; ns; ns = ns->next) { - akey = rrset_cache_lookup(env->rrset_cache, ns->name, - ns->namelen, LDNS_RR_TYPE_A, qclass, 0, now, 0); - if(akey) { - if(!delegpt_add_rrset_A(dp, region, akey, 0)) { - lock_rw_unlock(&akey->entry.lock); - return 0; - } - if(msg) - addr_to_additional(akey, region, *msg, now); - lock_rw_unlock(&akey->entry.lock); - } else { - /* BIT_CD on false because delegpt lookup does - * not use dns64 translation */ - neg = msg_cache_lookup(env, ns->name, ns->namelen, - LDNS_RR_TYPE_A, qclass, 0, now, 0); - if(neg) { - delegpt_add_neg_msg(dp, neg); - lock_rw_unlock(&neg->entry.lock); - } - } - akey = rrset_cache_lookup(env->rrset_cache, ns->name, - ns->namelen, LDNS_RR_TYPE_AAAA, qclass, 0, now, 0); - if(akey) { - if(!delegpt_add_rrset_AAAA(dp, region, akey, 0)) { - lock_rw_unlock(&akey->entry.lock); - return 0; - } - if(msg) - addr_to_additional(akey, region, *msg, now); - lock_rw_unlock(&akey->entry.lock); - } else { - /* BIT_CD on false because delegpt lookup does - * not use dns64 translation */ - neg = msg_cache_lookup(env, ns->name, ns->namelen, - LDNS_RR_TYPE_AAAA, qclass, 0, now, 0); - if(neg) { - delegpt_add_neg_msg(dp, neg); - lock_rw_unlock(&neg->entry.lock); - } - } - } - return 1; -} - -/** find and add A and AAAA records for missing nameservers in delegpt */ -int -cache_fill_missing(struct module_env* env, uint16_t qclass, - struct regional* region, struct delegpt* dp) -{ - struct delegpt_ns* ns; - struct msgreply_entry* neg; - struct ub_packed_rrset_key* akey; - time_t now = *env->now; - for(ns = dp->nslist; ns; ns = ns->next) { - akey = rrset_cache_lookup(env->rrset_cache, ns->name, - ns->namelen, LDNS_RR_TYPE_A, qclass, 0, now, 0); - if(akey) { - if(!delegpt_add_rrset_A(dp, region, akey, ns->lame)) { - lock_rw_unlock(&akey->entry.lock); - return 0; - } - log_nametypeclass(VERB_ALGO, "found in cache", - ns->name, LDNS_RR_TYPE_A, qclass); - lock_rw_unlock(&akey->entry.lock); - } else { - /* BIT_CD on false because delegpt lookup does - * not use dns64 translation */ - neg = msg_cache_lookup(env, ns->name, ns->namelen, - LDNS_RR_TYPE_A, qclass, 0, now, 0); - if(neg) { - delegpt_add_neg_msg(dp, neg); - lock_rw_unlock(&neg->entry.lock); - } - } - akey = rrset_cache_lookup(env->rrset_cache, ns->name, - ns->namelen, LDNS_RR_TYPE_AAAA, qclass, 0, now, 0); - if(akey) { - if(!delegpt_add_rrset_AAAA(dp, region, akey, ns->lame)) { - lock_rw_unlock(&akey->entry.lock); - return 0; - } - log_nametypeclass(VERB_ALGO, "found in cache", - ns->name, LDNS_RR_TYPE_AAAA, qclass); - lock_rw_unlock(&akey->entry.lock); - } else { - /* BIT_CD on false because delegpt lookup does - * not use dns64 translation */ - neg = msg_cache_lookup(env, ns->name, ns->namelen, - LDNS_RR_TYPE_AAAA, qclass, 0, now, 0); - if(neg) { - delegpt_add_neg_msg(dp, neg); - lock_rw_unlock(&neg->entry.lock); - } - } - } - return 1; -} - -/** find and add DS or NSEC to delegation msg */ -static void -find_add_ds(struct module_env* env, struct regional* region, - struct dns_msg* msg, struct delegpt* dp, time_t now) -{ - /* Lookup the DS or NSEC at the delegation point. */ - struct ub_packed_rrset_key* rrset = rrset_cache_lookup( - env->rrset_cache, dp->name, dp->namelen, LDNS_RR_TYPE_DS, - msg->qinfo.qclass, 0, now, 0); - if(!rrset) { - /* NOTE: this won't work for alternate NSEC schemes - * (opt-in, NSEC3) */ - rrset = rrset_cache_lookup(env->rrset_cache, dp->name, - dp->namelen, LDNS_RR_TYPE_NSEC, msg->qinfo.qclass, - 0, now, 0); - /* Note: the PACKED_RRSET_NSEC_AT_APEX flag is not used. - * since this is a referral, we need the NSEC at the parent - * side of the zone cut, not the NSEC at apex side. */ - if(rrset && nsec_has_type(rrset, LDNS_RR_TYPE_DS)) { - lock_rw_unlock(&rrset->entry.lock); - rrset = NULL; /* discard wrong NSEC */ - } - } - if(rrset) { - /* add it to auth section. This is the second rrset. */ - if((msg->rep->rrsets[msg->rep->rrset_count] = - packed_rrset_copy_region(rrset, region, now))) { - msg->rep->ns_numrrsets++; - msg->rep->rrset_count++; - } - lock_rw_unlock(&rrset->entry.lock); - } -} - -struct dns_msg* -dns_msg_create(uint8_t* qname, size_t qnamelen, uint16_t qtype, - uint16_t qclass, struct regional* region, size_t capacity) -{ - struct dns_msg* msg = (struct dns_msg*)regional_alloc(region, - sizeof(struct dns_msg)); - if(!msg) - return NULL; - msg->qinfo.qname = regional_alloc_init(region, qname, qnamelen); - if(!msg->qinfo.qname) - return NULL; - msg->qinfo.qname_len = qnamelen; - msg->qinfo.qtype = qtype; - msg->qinfo.qclass = qclass; - msg->qinfo.local_alias = NULL; - /* non-packed reply_info, because it needs to grow the array */ - msg->rep = (struct reply_info*)regional_alloc_zero(region, - sizeof(struct reply_info)-sizeof(struct rrset_ref)); - if(!msg->rep) - return NULL; - if(capacity > RR_COUNT_MAX) - return NULL; /* integer overflow protection */ - msg->rep->flags = BIT_QR; /* with QR, no AA */ - msg->rep->qdcount = 1; - msg->rep->rrsets = (struct ub_packed_rrset_key**) - regional_alloc(region, - capacity*sizeof(struct ub_packed_rrset_key*)); - if(!msg->rep->rrsets) - return NULL; - return msg; -} - -int -dns_msg_authadd(struct dns_msg* msg, struct regional* region, - struct ub_packed_rrset_key* rrset, time_t now) -{ - if(!(msg->rep->rrsets[msg->rep->rrset_count++] = - packed_rrset_copy_region(rrset, region, now))) - return 0; - msg->rep->ns_numrrsets++; - return 1; -} - -/** add rrset to answer section */ -static int -dns_msg_ansadd(struct dns_msg* msg, struct regional* region, - struct ub_packed_rrset_key* rrset, time_t now) -{ - if(!(msg->rep->rrsets[msg->rep->rrset_count++] = - packed_rrset_copy_region(rrset, region, now))) - return 0; - msg->rep->an_numrrsets++; - return 1; -} - -struct delegpt* -dns_cache_find_delegation(struct module_env* env, uint8_t* qname, - size_t qnamelen, uint16_t qtype, uint16_t qclass, - struct regional* region, struct dns_msg** msg, time_t now) -{ - /* try to find closest NS rrset */ - struct ub_packed_rrset_key* nskey; - struct packed_rrset_data* nsdata; - struct delegpt* dp; - - nskey = find_closest_of_type(env, qname, qnamelen, qclass, now, - LDNS_RR_TYPE_NS, 0); - if(!nskey) /* hope the caller has hints to prime or something */ - return NULL; - nsdata = (struct packed_rrset_data*)nskey->entry.data; - /* got the NS key, create delegation point */ - dp = delegpt_create(region); - if(!dp || !delegpt_set_name(dp, region, nskey->rk.dname)) { - lock_rw_unlock(&nskey->entry.lock); - log_err("find_delegation: out of memory"); - return NULL; - } - /* create referral message */ - if(msg) { - /* allocate the array to as much as we could need: - * NS rrset + DS/NSEC rrset + - * A rrset for every NS RR - * AAAA rrset for every NS RR - */ - *msg = dns_msg_create(qname, qnamelen, qtype, qclass, region, - 2 + nsdata->count*2); - if(!*msg || !dns_msg_authadd(*msg, region, nskey, now)) { - lock_rw_unlock(&nskey->entry.lock); - log_err("find_delegation: out of memory"); - return NULL; - } - } - if(!delegpt_rrset_add_ns(dp, region, nskey, 0)) - log_err("find_delegation: addns out of memory"); - lock_rw_unlock(&nskey->entry.lock); /* first unlock before next lookup*/ - /* find and add DS/NSEC (if any) */ - if(msg) - find_add_ds(env, region, *msg, dp, now); - /* find and add A entries */ - if(!find_add_addrs(env, qclass, region, dp, now, msg)) - log_err("find_delegation: addrs out of memory"); - return dp; -} - -/** allocate dns_msg from query_info and reply_info */ -static struct dns_msg* -gen_dns_msg(struct regional* region, struct query_info* q, size_t num) -{ - struct dns_msg* msg = (struct dns_msg*)regional_alloc(region, - sizeof(struct dns_msg)); - if(!msg) - return NULL; - memcpy(&msg->qinfo, q, sizeof(struct query_info)); - msg->qinfo.qname = regional_alloc_init(region, q->qname, q->qname_len); - if(!msg->qinfo.qname) - return NULL; - /* allocate replyinfo struct and rrset key array separately */ - msg->rep = (struct reply_info*)regional_alloc(region, - sizeof(struct reply_info) - sizeof(struct rrset_ref)); - if(!msg->rep) - return NULL; - if(num > RR_COUNT_MAX) - return NULL; /* integer overflow protection */ - msg->rep->rrsets = (struct ub_packed_rrset_key**) - regional_alloc(region, - num * sizeof(struct ub_packed_rrset_key*)); - if(!msg->rep->rrsets) - return NULL; - return msg; -} - -struct dns_msg* -tomsg(struct module_env* env, struct query_info* q, struct reply_info* r, - struct regional* region, time_t now, struct regional* scratch) -{ - struct dns_msg* msg; - size_t i; - if(now > r->ttl) - return NULL; - msg = gen_dns_msg(region, q, r->rrset_count); - if(!msg) - return NULL; - msg->rep->flags = r->flags; - msg->rep->qdcount = r->qdcount; - msg->rep->ttl = r->ttl - now; - if(r->prefetch_ttl > now) - msg->rep->prefetch_ttl = r->prefetch_ttl - now; - else msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl); - msg->rep->security = r->security; - msg->rep->an_numrrsets = r->an_numrrsets; - msg->rep->ns_numrrsets = r->ns_numrrsets; - msg->rep->ar_numrrsets = r->ar_numrrsets; - msg->rep->rrset_count = r->rrset_count; - msg->rep->authoritative = r->authoritative; - if(!rrset_array_lock(r->ref, r->rrset_count, now)) - return NULL; - if(r->an_numrrsets > 0 && (r->rrsets[0]->rk.type == htons( - LDNS_RR_TYPE_CNAME) || r->rrsets[0]->rk.type == htons( - LDNS_RR_TYPE_DNAME)) && !reply_check_cname_chain(q, r)) { - /* cname chain is now invalid, reconstruct msg */ - rrset_array_unlock(r->ref, r->rrset_count); - return NULL; - } - if(r->security == sec_status_secure && !reply_all_rrsets_secure(r)) { - /* message rrsets have changed status, revalidate */ - rrset_array_unlock(r->ref, r->rrset_count); - return NULL; - } - for(i=0; i<msg->rep->rrset_count; i++) { - msg->rep->rrsets[i] = packed_rrset_copy_region(r->rrsets[i], - region, now); - if(!msg->rep->rrsets[i]) { - rrset_array_unlock(r->ref, r->rrset_count); - return NULL; - } - } - if(env) - rrset_array_unlock_touch(env->rrset_cache, scratch, r->ref, - r->rrset_count); - else - rrset_array_unlock(r->ref, r->rrset_count); - return msg; -} - -/** synthesize RRset-only response from cached RRset item */ -static struct dns_msg* -rrset_msg(struct ub_packed_rrset_key* rrset, struct regional* region, - time_t now, struct query_info* q) -{ - struct dns_msg* msg; - struct packed_rrset_data* d = (struct packed_rrset_data*) - rrset->entry.data; - if(now > d->ttl) - return NULL; - msg = gen_dns_msg(region, q, 1); /* only the CNAME (or other) RRset */ - if(!msg) - return NULL; - msg->rep->flags = BIT_QR; /* reply, no AA, no error */ - msg->rep->authoritative = 0; /* reply stored in cache can't be authoritative */ - msg->rep->qdcount = 1; - msg->rep->ttl = d->ttl - now; - msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl); - msg->rep->security = sec_status_unchecked; - msg->rep->an_numrrsets = 1; - msg->rep->ns_numrrsets = 0; - msg->rep->ar_numrrsets = 0; - msg->rep->rrset_count = 1; - msg->rep->rrsets[0] = packed_rrset_copy_region(rrset, region, now); - if(!msg->rep->rrsets[0]) /* copy CNAME */ - return NULL; - return msg; -} - -/** synthesize DNAME+CNAME response from cached DNAME item */ -static struct dns_msg* -synth_dname_msg(struct ub_packed_rrset_key* rrset, struct regional* region, - time_t now, struct query_info* q) -{ - struct dns_msg* msg; - struct ub_packed_rrset_key* ck; - struct packed_rrset_data* newd, *d = (struct packed_rrset_data*) - rrset->entry.data; - uint8_t* newname, *dtarg = NULL; - size_t newlen, dtarglen; - if(now > d->ttl) - return NULL; - /* only allow validated (with DNSSEC) DNAMEs used from cache - * for insecure DNAMEs, query again. */ - if(d->security != sec_status_secure) - return NULL; - msg = gen_dns_msg(region, q, 2); /* DNAME + CNAME RRset */ - if(!msg) - return NULL; - msg->rep->flags = BIT_QR; /* reply, no AA, no error */ - msg->rep->authoritative = 0; /* reply stored in cache can't be authoritative */ - msg->rep->qdcount = 1; - msg->rep->ttl = d->ttl - now; - msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl); - msg->rep->security = sec_status_unchecked; - msg->rep->an_numrrsets = 1; - msg->rep->ns_numrrsets = 0; - msg->rep->ar_numrrsets = 0; - msg->rep->rrset_count = 1; - msg->rep->rrsets[0] = packed_rrset_copy_region(rrset, region, now); - if(!msg->rep->rrsets[0]) /* copy DNAME */ - return NULL; - /* synth CNAME rrset */ - get_cname_target(rrset, &dtarg, &dtarglen); - if(!dtarg) - return NULL; - newlen = q->qname_len + dtarglen - rrset->rk.dname_len; - if(newlen > LDNS_MAX_DOMAINLEN) { - msg->rep->flags |= LDNS_RCODE_YXDOMAIN; - return msg; - } - newname = (uint8_t*)regional_alloc(region, newlen); - if(!newname) - return NULL; - /* new name is concatenation of qname front (without DNAME owner) - * and DNAME target name */ - memcpy(newname, q->qname, q->qname_len-rrset->rk.dname_len); - memmove(newname+(q->qname_len-rrset->rk.dname_len), dtarg, dtarglen); - /* create rest of CNAME rrset */ - ck = (struct ub_packed_rrset_key*)regional_alloc(region, - sizeof(struct ub_packed_rrset_key)); - if(!ck) - return NULL; - memset(&ck->entry, 0, sizeof(ck->entry)); - msg->rep->rrsets[1] = ck; - ck->entry.key = ck; - ck->rk.type = htons(LDNS_RR_TYPE_CNAME); - ck->rk.rrset_class = rrset->rk.rrset_class; - ck->rk.flags = 0; - ck->rk.dname = regional_alloc_init(region, q->qname, q->qname_len); - if(!ck->rk.dname) - return NULL; - ck->rk.dname_len = q->qname_len; - ck->entry.hash = rrset_key_hash(&ck->rk); - newd = (struct packed_rrset_data*)regional_alloc_zero(region, - sizeof(struct packed_rrset_data) + sizeof(size_t) + - sizeof(uint8_t*) + sizeof(time_t) + sizeof(uint16_t) - + newlen); - if(!newd) - return NULL; - ck->entry.data = newd; - newd->ttl = 0; /* 0 for synthesized CNAME TTL */ - newd->count = 1; - newd->rrsig_count = 0; - newd->trust = rrset_trust_ans_noAA; - newd->rr_len = (size_t*)((uint8_t*)newd + - sizeof(struct packed_rrset_data)); - newd->rr_len[0] = newlen + sizeof(uint16_t); - packed_rrset_ptr_fixup(newd); - newd->rr_ttl[0] = newd->ttl; - msg->rep->ttl = newd->ttl; - msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(newd->ttl); - sldns_write_uint16(newd->rr_data[0], newlen); - memmove(newd->rr_data[0] + sizeof(uint16_t), newname, newlen); - msg->rep->an_numrrsets ++; - msg->rep->rrset_count ++; - return msg; -} - -/** Fill TYPE_ANY response with some data from cache */ -static struct dns_msg* -fill_any(struct module_env* env, - uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, - struct regional* region) -{ - time_t now = *env->now; - struct dns_msg* msg = NULL; - uint16_t lookup[] = {LDNS_RR_TYPE_A, LDNS_RR_TYPE_AAAA, - LDNS_RR_TYPE_MX, LDNS_RR_TYPE_SOA, LDNS_RR_TYPE_NS, - LDNS_RR_TYPE_DNAME, 0}; - int i, num=6; /* number of RR types to look up */ - log_assert(lookup[num] == 0); - - for(i=0; i<num; i++) { - /* look up this RR for inclusion in type ANY response */ - struct ub_packed_rrset_key* rrset = rrset_cache_lookup( - env->rrset_cache, qname, qnamelen, lookup[i], - qclass, 0, now, 0); - struct packed_rrset_data *d; - if(!rrset) - continue; - - /* only if rrset from answer section */ - d = (struct packed_rrset_data*)rrset->entry.data; - if(d->trust == rrset_trust_add_noAA || - d->trust == rrset_trust_auth_noAA || - d->trust == rrset_trust_add_AA || - d->trust == rrset_trust_auth_AA) { - lock_rw_unlock(&rrset->entry.lock); - continue; - } - - /* create msg if none */ - if(!msg) { - msg = dns_msg_create(qname, qnamelen, qtype, qclass, - region, (size_t)(num-i)); - if(!msg) { - lock_rw_unlock(&rrset->entry.lock); - return NULL; - } - } - - /* add RRset to response */ - if(!dns_msg_ansadd(msg, region, rrset, now)) { - lock_rw_unlock(&rrset->entry.lock); - return NULL; - } - lock_rw_unlock(&rrset->entry.lock); - } - return msg; -} - -struct dns_msg* -dns_cache_lookup(struct module_env* env, - uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, - uint16_t flags, struct regional* region, struct regional* scratch) -{ - struct lruhash_entry* e; - struct query_info k; - hashvalue_type h; - time_t now = *env->now; - struct ub_packed_rrset_key* rrset; - - /* lookup first, this has both NXdomains and ANSWER responses */ - k.qname = qname; - k.qname_len = qnamelen; - k.qtype = qtype; - k.qclass = qclass; - k.local_alias = NULL; - h = query_info_hash(&k, flags); - e = slabhash_lookup(env->msg_cache, h, &k, 0); - if(e) { - struct msgreply_entry* key = (struct msgreply_entry*)e->key; - struct reply_info* data = (struct reply_info*)e->data; - struct dns_msg* msg = tomsg(env, &key->key, data, region, now, - scratch); - if(msg) { - lock_rw_unlock(&e->lock); - return msg; - } - /* could be msg==NULL; due to TTL or not all rrsets available */ - lock_rw_unlock(&e->lock); - } - - /* see if a DNAME exists. Checked for first, to enforce that DNAMEs - * are more important, the CNAME is resynthesized and thus - * consistent with the DNAME */ - if( (rrset=find_closest_of_type(env, qname, qnamelen, qclass, now, - LDNS_RR_TYPE_DNAME, 1))) { - /* synthesize a DNAME+CNAME message based on this */ - struct dns_msg* msg = synth_dname_msg(rrset, region, now, &k); - if(msg) { - lock_rw_unlock(&rrset->entry.lock); - return msg; - } - lock_rw_unlock(&rrset->entry.lock); - } - - /* see if we have CNAME for this domain, - * but not for DS records (which are part of the parent) */ - if( qtype != LDNS_RR_TYPE_DS && - (rrset=rrset_cache_lookup(env->rrset_cache, qname, qnamelen, - LDNS_RR_TYPE_CNAME, qclass, 0, now, 0))) { - struct dns_msg* msg = rrset_msg(rrset, region, now, &k); - if(msg) { - lock_rw_unlock(&rrset->entry.lock); - return msg; - } - lock_rw_unlock(&rrset->entry.lock); - } - - /* construct DS, DNSKEY, DLV messages from rrset cache. */ - if((qtype == LDNS_RR_TYPE_DS || qtype == LDNS_RR_TYPE_DNSKEY || - qtype == LDNS_RR_TYPE_DLV) && - (rrset=rrset_cache_lookup(env->rrset_cache, qname, qnamelen, - qtype, qclass, 0, now, 0))) { - /* if the rrset is from the additional section, and the - * signatures have fallen off, then do not synthesize a msg - * instead, allow a full query for signed results to happen. - * Forego all rrset data from additional section, because - * some signatures may not be present and cause validation - * failure. - */ - struct packed_rrset_data *d = (struct packed_rrset_data*) - rrset->entry.data; - if(d->trust != rrset_trust_add_noAA && - d->trust != rrset_trust_add_AA && - (qtype == LDNS_RR_TYPE_DS || - (d->trust != rrset_trust_auth_noAA - && d->trust != rrset_trust_auth_AA) )) { - struct dns_msg* msg = rrset_msg(rrset, region, now, &k); - if(msg) { - lock_rw_unlock(&rrset->entry.lock); - return msg; - } - } - lock_rw_unlock(&rrset->entry.lock); - } - - /* stop downwards cache search on NXDOMAIN. - * Empty nonterminals are NOERROR, so an NXDOMAIN for foo - * means bla.foo also does not exist. The DNSSEC proofs are - * the same. We search upwards for NXDOMAINs. */ - if(env->cfg->harden_below_nxdomain) - while(!dname_is_root(k.qname)) { - dname_remove_label(&k.qname, &k.qname_len); - h = query_info_hash(&k, flags); - e = slabhash_lookup(env->msg_cache, h, &k, 0); - if(!e && k.qtype != LDNS_RR_TYPE_A && - env->cfg->qname_minimisation) { - k.qtype = LDNS_RR_TYPE_A; - h = query_info_hash(&k, flags); - e = slabhash_lookup(env->msg_cache, h, &k, 0); - } - if(e) { - struct reply_info* data = (struct reply_info*)e->data; - struct dns_msg* msg; - if(FLAGS_GET_RCODE(data->flags) == LDNS_RCODE_NXDOMAIN - && data->security == sec_status_secure - && (msg=tomsg(env, &k, data, region, now, scratch))){ - lock_rw_unlock(&e->lock); - msg->qinfo.qname=qname; - msg->qinfo.qname_len=qnamelen; - /* check that DNSSEC really works out */ - msg->rep->security = sec_status_unchecked; - return msg; - } - lock_rw_unlock(&e->lock); - } - k.qtype = qtype; - } - - /* fill common RR types for ANY response to avoid requery */ - if(qtype == LDNS_RR_TYPE_ANY) { - return fill_any(env, qname, qnamelen, qtype, qclass, region); - } - - return NULL; -} - -int -dns_cache_store(struct module_env* env, struct query_info* msgqinf, - struct reply_info* msgrep, int is_referral, time_t leeway, int pside, - struct regional* region, uint16_t flags) -{ - struct reply_info* rep = NULL; - /* alloc, malloc properly (not in region, like msg is) */ - rep = reply_info_copy(msgrep, env->alloc, NULL); - if(!rep) - return 0; - /* ttl must be relative ;i.e. 0..86400 not time(0)+86400. - * the env->now is added to message and RRsets in this routine. */ - /* the leeway is used to invalidate other rrsets earlier */ - - if(is_referral) { - /* store rrsets */ - struct rrset_ref ref; - size_t i; - for(i=0; i<rep->rrset_count; i++) { - packed_rrset_ttl_add((struct packed_rrset_data*) - rep->rrsets[i]->entry.data, *env->now); - ref.key = rep->rrsets[i]; - ref.id = rep->rrsets[i]->id; - /*ignore ret: it was in the cache, ref updated */ - /* no leeway for typeNS */ - (void)rrset_cache_update(env->rrset_cache, &ref, - env->alloc, *env->now + - ((ntohs(ref.key->rk.type)==LDNS_RR_TYPE_NS - && !pside) ? 0:leeway)); - } - free(rep); - return 1; - } else { - /* store msg, and rrsets */ - struct query_info qinf; - hashvalue_type h; - - qinf = *msgqinf; - qinf.qname = memdup(msgqinf->qname, msgqinf->qname_len); - if(!qinf.qname) { - reply_info_parsedelete(rep, env->alloc); - return 0; - } - /* fixup flags to be sensible for a reply based on the cache */ - /* this module means that RA is available. It is an answer QR. - * Not AA from cache. Not CD in cache (depends on client bit). */ - rep->flags |= (BIT_RA | BIT_QR); - rep->flags &= ~(BIT_AA | BIT_CD); - h = query_info_hash(&qinf, flags); - dns_cache_store_msg(env, &qinf, h, rep, leeway, pside, msgrep, - region); - /* qname is used inside query_info_entrysetup, and set to - * NULL. If it has not been used, free it. free(0) is safe. */ - free(qinf.qname); - } - return 1; -} - -int -dns_cache_prefetch_adjust(struct module_env* env, struct query_info* qinfo, - time_t adjust, uint16_t flags) -{ - struct msgreply_entry* msg; - msg = msg_cache_lookup(env, qinfo->qname, qinfo->qname_len, - qinfo->qtype, qinfo->qclass, flags, *env->now, 1); - if(msg) { - struct reply_info* rep = (struct reply_info*)msg->entry.data; - if(rep) { - rep->prefetch_ttl += adjust; - lock_rw_unlock(&msg->entry.lock); - return 1; - } - lock_rw_unlock(&msg->entry.lock); - } - return 0; -} diff --git a/external/unbound/services/cache/dns.h b/external/unbound/services/cache/dns.h deleted file mode 100644 index 0dfb68874..000000000 --- a/external/unbound/services/cache/dns.h +++ /dev/null @@ -1,211 +0,0 @@ -/* - * services/cache/dns.h - Cache services for DNS using msg and rrset caches. - * - * Copyright (c) 2007, NLnet Labs. All rights reserved. - * - * This software is open source. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of the NLNET LABS nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * \file - * - * This file contains the DNS cache. - */ - -#ifndef SERVICES_CACHE_DNS_H -#define SERVICES_CACHE_DNS_H -#include "util/storage/lruhash.h" -#include "util/data/msgreply.h" -struct module_env; -struct query_info; -struct reply_info; -struct regional; -struct delegpt; - -/** - * Region allocated message reply - */ -struct dns_msg { - /** query info */ - struct query_info qinfo; - /** reply info - ptr to packed repinfo structure */ - struct reply_info *rep; -}; - -/** - * Allocate a dns_msg with malloc/alloc structure and store in dns cache. - * - * @param env: environment, with alloc structure and dns cache. - * @param qinf: query info, the query for which answer is stored. - * this is allocated in a region, and will be copied to malloc area - * before insertion. - * @param rep: reply in dns_msg from dns_alloc_msg for example. - * this is allocated in a region, and will be copied to malloc area - * before insertion. - * @param is_referral: If true, then the given message to be stored is a - * referral. The cache implementation may use this as a hint. - * It will store only the RRsets, not the message. - * @param leeway: TTL value, if not 0, other rrsets are considered expired - * that many seconds before actual TTL expiry. - * @param pside: if true, information came from a server which was fetched - * from the parentside of the zonecut. This means that the type NS - * can be updated to full TTL even in prefetch situations. - * @param region: region to allocate better entries from cache into. - * (used when is_referral is false). - * @param flags: flags with BIT_CD for AAAA queries in dns64 translation. - * @return 0 on alloc error (out of memory). - */ -int dns_cache_store(struct module_env* env, struct query_info* qinf, - struct reply_info* rep, int is_referral, time_t leeway, int pside, - struct regional* region, uint16_t flags); - -/** - * Store message in the cache. Stores in message cache and rrset cache. - * Both qinfo and rep should be malloced and are put in the cache. - * They should not be used after this call, as they are then in shared cache. - * Does not return errors, they are logged and only lead to less cache. - * - * @param env: module environment with the DNS cache. - * @param qinfo: query info - * @param hash: hash over qinfo. - * @param rep: reply info, together with qinfo makes up the message. - * Adjusts the reply info TTLs to absolute time. - * @param leeway: TTL value, if not 0, other rrsets are considered expired - * that many seconds before actual TTL expiry. - * @param pside: if true, information came from a server which was fetched - * from the parentside of the zonecut. This means that the type NS - * can be updated to full TTL even in prefetch situations. - * @param qrep: message that can be altered with better rrs from cache. - * @param region: to allocate into for qmsg. - */ -void dns_cache_store_msg(struct module_env* env, struct query_info* qinfo, - hashvalue_type hash, struct reply_info* rep, time_t leeway, int pside, - struct reply_info* qrep, struct regional* region); - -/** - * Find a delegation from the cache. - * @param env: module environment with the DNS cache. - * @param qname: query name. - * @param qnamelen: length of qname. - * @param qtype: query type. - * @param qclass: query class. - * @param region: where to allocate result delegation. - * @param msg: if not NULL, delegation message is returned here, synthesized - * from the cache. - * @param timenow: the time now, for checking if TTL on cache entries is OK. - * @return new delegation or NULL on error or if not found in cache. - */ -struct delegpt* dns_cache_find_delegation(struct module_env* env, - uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, - struct regional* region, struct dns_msg** msg, time_t timenow); - -/** - * generate dns_msg from cached message - * @param env: module environment with the DNS cache. NULL if the LRU from cache - * does not need to be touched. - * @param q: query info, contains qname that will make up the dns message. - * @param r: reply info that, together with qname, will make up the dns message. - * @param region: where to allocate dns message. - * @param now: the time now, for check if TTL on cache entry is ok. - * @param scratch: where to allocate temporary data. - * */ -struct dns_msg* tomsg(struct module_env* env, struct query_info* q, - struct reply_info* r, struct regional* region, time_t now, - struct regional* scratch); - -/** - * Find cached message - * @param env: module environment with the DNS cache. - * @param qname: query name. - * @param qnamelen: length of qname. - * @param qtype: query type. - * @param qclass: query class. - * @param flags: flags with BIT_CD for AAAA queries in dns64 translation. - * @param region: where to allocate result. - * @param scratch: where to allocate temporary data. - * @return new response message (alloced in region, rrsets do not have IDs). - * or NULL on error or if not found in cache. - * TTLs are made relative to the current time. - */ -struct dns_msg* dns_cache_lookup(struct module_env* env, - uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, - uint16_t flags, struct regional* region, struct regional* scratch); - -/** - * find and add A and AAAA records for missing nameservers in delegpt - * @param env: module environment with rrset cache - * @param qclass: which class to look in. - * @param region: where to store new dp info. - * @param dp: delegation point to fill missing entries. - * @return false on alloc failure. - */ -int cache_fill_missing(struct module_env* env, uint16_t qclass, - struct regional* region, struct delegpt* dp); - -/** - * Utility, create new, unpacked data structure for cache response. - * QR bit set, no AA. Query set as indicated. Space for number of rrsets. - * @param qname: query section name - * @param qnamelen: len of qname - * @param qtype: query section type - * @param qclass: query section class - * @param region: where to alloc. - * @param capacity: number of rrsets space to create in the array. - * @return new dns_msg struct or NULL on mem fail. - */ -struct dns_msg* dns_msg_create(uint8_t* qname, size_t qnamelen, uint16_t qtype, - uint16_t qclass, struct regional* region, size_t capacity); - -/** - * Add rrset to authority section in unpacked dns_msg message. Must have enough - * space left, does not grow the array. - * @param msg: msg to put it in. - * @param region: region to alloc in - * @param rrset: to add in authority section - * @param now: now. - * @return true if worked, false on fail - */ -int dns_msg_authadd(struct dns_msg* msg, struct regional* region, - struct ub_packed_rrset_key* rrset, time_t now); - -/** - * Adjust the prefetch_ttl for a cached message. This adds a value to the - * prefetch ttl - postponing the time when it will be prefetched for future - * incoming queries. - * @param env: module environment with caches and time. - * @param qinfo: query info for the query that needs adjustment. - * @param adjust: time in seconds to add to the prefetch_leeway. - * @param flags: flags with BIT_CD for AAAA queries in dns64 translation. - * @return false if not in cache. true if added. - */ -int dns_cache_prefetch_adjust(struct module_env* env, struct query_info* qinfo, - time_t adjust, uint16_t flags); - -#endif /* SERVICES_CACHE_DNS_H */ diff --git a/external/unbound/services/cache/infra.c b/external/unbound/services/cache/infra.c deleted file mode 100644 index 314c85ef5..000000000 --- a/external/unbound/services/cache/infra.c +++ /dev/null @@ -1,997 +0,0 @@ -/* - * services/cache/infra.c - infrastructure cache, server rtt and capabilities - * - * Copyright (c) 2007, NLnet Labs. All rights reserved. - * - * This software is open source. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of the NLNET LABS nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * \file - * - * This file contains the infrastructure cache. - */ -#include "config.h" -#include "sldns/rrdef.h" -#include "sldns/str2wire.h" -#include "services/cache/infra.h" -#include "util/storage/slabhash.h" -#include "util/storage/lookup3.h" -#include "util/data/dname.h" -#include "util/log.h" -#include "util/net_help.h" -#include "util/config_file.h" -#include "iterator/iterator.h" - -/** Timeout when only a single probe query per IP is allowed. */ -#define PROBE_MAXRTO 12000 /* in msec */ - -/** number of timeouts for a type when the domain can be blocked ; - * even if another type has completely rtt maxed it, the different type - * can do this number of packets (until those all timeout too) */ -#define TIMEOUT_COUNT_MAX 3 - -/** ratelimit value for delegation point */ -int infra_dp_ratelimit = 0; - -/** ratelimit value for client ip addresses, - * in queries per second. */ -int infra_ip_ratelimit = 0; - -size_t -infra_sizefunc(void* k, void* ATTR_UNUSED(d)) -{ - struct infra_key* key = (struct infra_key*)k; - return sizeof(*key) + sizeof(struct infra_data) + key->namelen - + lock_get_mem(&key->entry.lock); -} - -int -infra_compfunc(void* key1, void* key2) -{ - struct infra_key* k1 = (struct infra_key*)key1; - struct infra_key* k2 = (struct infra_key*)key2; - int r = sockaddr_cmp(&k1->addr, k1->addrlen, &k2->addr, k2->addrlen); - if(r != 0) - return r; - if(k1->namelen != k2->namelen) { - if(k1->namelen < k2->namelen) - return -1; - return 1; - } - return query_dname_compare(k1->zonename, k2->zonename); -} - -void -infra_delkeyfunc(void* k, void* ATTR_UNUSED(arg)) -{ - struct infra_key* key = (struct infra_key*)k; - if(!key) - return; - lock_rw_destroy(&key->entry.lock); - free(key->zonename); - free(key); -} - -void -infra_deldatafunc(void* d, void* ATTR_UNUSED(arg)) -{ - struct infra_data* data = (struct infra_data*)d; - free(data); -} - -size_t -rate_sizefunc(void* k, void* ATTR_UNUSED(d)) -{ - struct rate_key* key = (struct rate_key*)k; - return sizeof(*key) + sizeof(struct rate_data) + key->namelen - + lock_get_mem(&key->entry.lock); -} - -int -rate_compfunc(void* key1, void* key2) -{ - struct rate_key* k1 = (struct rate_key*)key1; - struct rate_key* k2 = (struct rate_key*)key2; - if(k1->namelen != k2->namelen) { - if(k1->namelen < k2->namelen) - return -1; - return 1; - } - return query_dname_compare(k1->name, k2->name); -} - -void -rate_delkeyfunc(void* k, void* ATTR_UNUSED(arg)) -{ - struct rate_key* key = (struct rate_key*)k; - if(!key) - return; - lock_rw_destroy(&key->entry.lock); - free(key->name); - free(key); -} - -void -rate_deldatafunc(void* d, void* ATTR_UNUSED(arg)) -{ - struct rate_data* data = (struct rate_data*)d; - free(data); -} - -/** find or create element in domainlimit tree */ -static struct domain_limit_data* domain_limit_findcreate( - struct infra_cache* infra, char* name) -{ - uint8_t* nm; - int labs; - size_t nmlen; - struct domain_limit_data* d; - - /* parse name */ - nm = sldns_str2wire_dname(name, &nmlen); - if(!nm) { - log_err("could not parse %s", name); - return NULL; - } - labs = dname_count_labels(nm); - - /* can we find it? */ - d = (struct domain_limit_data*)name_tree_find(&infra->domain_limits, - nm, nmlen, labs, LDNS_RR_CLASS_IN); - if(d) { - free(nm); - return d; - } - - /* create it */ - d = (struct domain_limit_data*)calloc(1, sizeof(*d)); - if(!d) { - free(nm); - return NULL; - } - d->node.node.key = &d->node; - d->node.name = nm; - d->node.len = nmlen; - d->node.labs = labs; - d->node.dclass = LDNS_RR_CLASS_IN; - d->lim = -1; - d->below = -1; - if(!name_tree_insert(&infra->domain_limits, &d->node, nm, nmlen, - labs, LDNS_RR_CLASS_IN)) { - log_err("duplicate element in domainlimit tree"); - free(nm); - free(d); - return NULL; - } - return d; -} - -/** insert rate limit configuration into lookup tree */ -static int infra_ratelimit_cfg_insert(struct infra_cache* infra, - struct config_file* cfg) -{ - struct config_str2list* p; - struct domain_limit_data* d; - for(p = cfg->ratelimit_for_domain; p; p = p->next) { - d = domain_limit_findcreate(infra, p->str); - if(!d) - return 0; - d->lim = atoi(p->str2); - } - for(p = cfg->ratelimit_below_domain; p; p = p->next) { - d = domain_limit_findcreate(infra, p->str); - if(!d) - return 0; - d->below = atoi(p->str2); - } - return 1; -} - -struct infra_cache* -infra_create(struct config_file* cfg) -{ - struct infra_cache* infra = (struct infra_cache*)calloc(1, - sizeof(struct infra_cache)); - size_t maxmem = cfg->infra_cache_numhosts * (sizeof(struct infra_key)+ - sizeof(struct infra_data)+INFRA_BYTES_NAME); - infra->hosts = slabhash_create(cfg->infra_cache_slabs, - INFRA_HOST_STARTSIZE, maxmem, &infra_sizefunc, &infra_compfunc, - &infra_delkeyfunc, &infra_deldatafunc, NULL); - if(!infra->hosts) { - free(infra); - return NULL; - } - infra->host_ttl = cfg->host_ttl; - name_tree_init(&infra->domain_limits); - infra_dp_ratelimit = cfg->ratelimit; - if(cfg->ratelimit != 0) { - infra->domain_rates = slabhash_create(cfg->ratelimit_slabs, - INFRA_HOST_STARTSIZE, cfg->ratelimit_size, - &rate_sizefunc, &rate_compfunc, &rate_delkeyfunc, - &rate_deldatafunc, NULL); - if(!infra->domain_rates) { - infra_delete(infra); - return NULL; - } - /* insert config data into ratelimits */ - if(!infra_ratelimit_cfg_insert(infra, cfg)) { - infra_delete(infra); - return NULL; - } - name_tree_init_parents(&infra->domain_limits); - } - infra_ip_ratelimit = cfg->ip_ratelimit; - infra->client_ip_rates = slabhash_create(cfg->ratelimit_slabs, - INFRA_HOST_STARTSIZE, cfg->ip_ratelimit_size, &ip_rate_sizefunc, - &ip_rate_compfunc, &ip_rate_delkeyfunc, &ip_rate_deldatafunc, NULL); - if(!infra->client_ip_rates) { - infra_delete(infra); - return NULL; - } - return infra; -} - -/** delete domain_limit entries */ -static void domain_limit_free(rbnode_type* n, void* ATTR_UNUSED(arg)) -{ - if(n) { - free(((struct domain_limit_data*)n)->node.name); - free(n); - } -} - -void -infra_delete(struct infra_cache* infra) -{ - if(!infra) - return; - slabhash_delete(infra->hosts); - slabhash_delete(infra->domain_rates); - traverse_postorder(&infra->domain_limits, domain_limit_free, NULL); - slabhash_delete(infra->client_ip_rates); - free(infra); -} - -struct infra_cache* -infra_adjust(struct infra_cache* infra, struct config_file* cfg) -{ - size_t maxmem; - if(!infra) - return infra_create(cfg); - infra->host_ttl = cfg->host_ttl; - maxmem = cfg->infra_cache_numhosts * (sizeof(struct infra_key)+ - sizeof(struct infra_data)+INFRA_BYTES_NAME); - if(maxmem != slabhash_get_size(infra->hosts) || - cfg->infra_cache_slabs != infra->hosts->size) { - infra_delete(infra); - infra = infra_create(cfg); - } - return infra; -} - -/** calculate the hash value for a host key - * set use_port to a non-0 number to use the port in - * the hash calculation; 0 to ignore the port.*/ -static hashvalue_type -hash_addr(struct sockaddr_storage* addr, socklen_t addrlen, - int use_port) -{ - hashvalue_type h = 0xab; - /* select the pieces to hash, some OS have changing data inside */ - if(addr_is_ip6(addr, addrlen)) { - struct sockaddr_in6* in6 = (struct sockaddr_in6*)addr; - h = hashlittle(&in6->sin6_family, sizeof(in6->sin6_family), h); - if(use_port){ - h = hashlittle(&in6->sin6_port, sizeof(in6->sin6_port), h); - } - h = hashlittle(&in6->sin6_addr, INET6_SIZE, h); - } else { - struct sockaddr_in* in = (struct sockaddr_in*)addr; - h = hashlittle(&in->sin_family, sizeof(in->sin_family), h); - if(use_port){ - h = hashlittle(&in->sin_port, sizeof(in->sin_port), h); - } - h = hashlittle(&in->sin_addr, INET_SIZE, h); - } - return h; -} - -/** calculate infra hash for a key */ -static hashvalue_type -hash_infra(struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* name) -{ - return dname_query_hash(name, hash_addr(addr, addrlen, 1)); -} - -/** lookup version that does not check host ttl (you check it) */ -struct lruhash_entry* -infra_lookup_nottl(struct infra_cache* infra, struct sockaddr_storage* addr, - socklen_t addrlen, uint8_t* name, size_t namelen, int wr) -{ - struct infra_key k; - k.addrlen = addrlen; - memcpy(&k.addr, addr, addrlen); - k.namelen = namelen; - k.zonename = name; - k.entry.hash = hash_infra(addr, addrlen, name); - k.entry.key = (void*)&k; - k.entry.data = NULL; - return slabhash_lookup(infra->hosts, k.entry.hash, &k, wr); -} - -/** init the data elements */ -static void -data_entry_init(struct infra_cache* infra, struct lruhash_entry* e, - time_t timenow) -{ - struct infra_data* data = (struct infra_data*)e->data; - data->ttl = timenow + infra->host_ttl; - rtt_init(&data->rtt); - data->edns_version = 0; - data->edns_lame_known = 0; - data->probedelay = 0; - data->isdnsseclame = 0; - data->rec_lame = 0; - data->lame_type_A = 0; - data->lame_other = 0; - data->timeout_A = 0; - data->timeout_AAAA = 0; - data->timeout_other = 0; -} - -/** - * Create and init a new entry for a host - * @param infra: infra structure with config parameters. - * @param addr: host address. - * @param addrlen: length of addr. - * @param name: name of zone - * @param namelen: length of name. - * @param tm: time now. - * @return: the new entry or NULL on malloc failure. - */ -static struct lruhash_entry* -new_entry(struct infra_cache* infra, struct sockaddr_storage* addr, - socklen_t addrlen, uint8_t* name, size_t namelen, time_t tm) -{ - struct infra_data* data; - struct infra_key* key = (struct infra_key*)malloc(sizeof(*key)); - if(!key) - return NULL; - data = (struct infra_data*)malloc(sizeof(struct infra_data)); - if(!data) { - free(key); - return NULL; - } - key->zonename = memdup(name, namelen); - if(!key->zonename) { - free(key); - free(data); - return NULL; - } - key->namelen = namelen; - lock_rw_init(&key->entry.lock); - key->entry.hash = hash_infra(addr, addrlen, name); - key->entry.key = (void*)key; - key->entry.data = (void*)data; - key->addrlen = addrlen; - memcpy(&key->addr, addr, addrlen); - data_entry_init(infra, &key->entry, tm); - return &key->entry; -} - -int -infra_host(struct infra_cache* infra, struct sockaddr_storage* addr, - socklen_t addrlen, uint8_t* nm, size_t nmlen, time_t timenow, - int* edns_vs, uint8_t* edns_lame_known, int* to) -{ - struct lruhash_entry* e = infra_lookup_nottl(infra, addr, addrlen, - nm, nmlen, 0); - struct infra_data* data; - int wr = 0; - if(e && ((struct infra_data*)e->data)->ttl < timenow) { - /* it expired, try to reuse existing entry */ - int old = ((struct infra_data*)e->data)->rtt.rto; - uint8_t tA = ((struct infra_data*)e->data)->timeout_A; - uint8_t tAAAA = ((struct infra_data*)e->data)->timeout_AAAA; - uint8_t tother = ((struct infra_data*)e->data)->timeout_other; - lock_rw_unlock(&e->lock); - e = infra_lookup_nottl(infra, addr, addrlen, nm, nmlen, 1); - if(e) { - /* if its still there we have a writelock, init */ - /* re-initialise */ - /* do not touch lameness, it may be valid still */ - data_entry_init(infra, e, timenow); - wr = 1; - /* TOP_TIMEOUT remains on reuse */ - if(old >= USEFUL_SERVER_TOP_TIMEOUT) { - ((struct infra_data*)e->data)->rtt.rto - = USEFUL_SERVER_TOP_TIMEOUT; - ((struct infra_data*)e->data)->timeout_A = tA; - ((struct infra_data*)e->data)->timeout_AAAA = tAAAA; - ((struct infra_data*)e->data)->timeout_other = tother; - } - } - } - if(!e) { - /* insert new entry */ - if(!(e = new_entry(infra, addr, addrlen, nm, nmlen, timenow))) - return 0; - data = (struct infra_data*)e->data; - *edns_vs = data->edns_version; - *edns_lame_known = data->edns_lame_known; - *to = rtt_timeout(&data->rtt); - slabhash_insert(infra->hosts, e->hash, e, data, NULL); - return 1; - } - /* use existing entry */ - data = (struct infra_data*)e->data; - *edns_vs = data->edns_version; - *edns_lame_known = data->edns_lame_known; - *to = rtt_timeout(&data->rtt); - if(*to >= PROBE_MAXRTO && rtt_notimeout(&data->rtt)*4 <= *to) { - /* delay other queries, this is the probe query */ - if(!wr) { - lock_rw_unlock(&e->lock); - e = infra_lookup_nottl(infra, addr,addrlen,nm,nmlen, 1); - if(!e) { /* flushed from cache real fast, no use to - allocate just for the probedelay */ - return 1; - } - data = (struct infra_data*)e->data; - } - /* add 999 to round up the timeout value from msec to sec, - * then add a whole second so it is certain that this probe - * has timed out before the next is allowed */ - data->probedelay = timenow + ((*to)+1999)/1000; - } - lock_rw_unlock(&e->lock); - return 1; -} - -int -infra_set_lame(struct infra_cache* infra, struct sockaddr_storage* addr, - socklen_t addrlen, uint8_t* nm, size_t nmlen, time_t timenow, - int dnsseclame, int reclame, uint16_t qtype) -{ - struct infra_data* data; - struct lruhash_entry* e; - int needtoinsert = 0; - e = infra_lookup_nottl(infra, addr, addrlen, nm, nmlen, 1); - if(!e) { - /* insert it */ - if(!(e = new_entry(infra, addr, addrlen, nm, nmlen, timenow))) { - log_err("set_lame: malloc failure"); - return 0; - } - needtoinsert = 1; - } else if( ((struct infra_data*)e->data)->ttl < timenow) { - /* expired, reuse existing entry */ - data_entry_init(infra, e, timenow); - } - /* got an entry, now set the zone lame */ - data = (struct infra_data*)e->data; - /* merge data (if any) */ - if(dnsseclame) - data->isdnsseclame = 1; - if(reclame) - data->rec_lame = 1; - if(!dnsseclame && !reclame && qtype == LDNS_RR_TYPE_A) - data->lame_type_A = 1; - if(!dnsseclame && !reclame && qtype != LDNS_RR_TYPE_A) - data->lame_other = 1; - /* done */ - if(needtoinsert) - slabhash_insert(infra->hosts, e->hash, e, e->data, NULL); - else { lock_rw_unlock(&e->lock); } - return 1; -} - -void -infra_update_tcp_works(struct infra_cache* infra, - struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* nm, - size_t nmlen) -{ - struct lruhash_entry* e = infra_lookup_nottl(infra, addr, addrlen, - nm, nmlen, 1); - struct infra_data* data; - if(!e) - return; /* doesn't exist */ - data = (struct infra_data*)e->data; - if(data->rtt.rto >= RTT_MAX_TIMEOUT) - /* do not disqualify this server altogether, it is better - * than nothing */ - data->rtt.rto = RTT_MAX_TIMEOUT-1000; - lock_rw_unlock(&e->lock); -} - -int -infra_rtt_update(struct infra_cache* infra, struct sockaddr_storage* addr, - socklen_t addrlen, uint8_t* nm, size_t nmlen, int qtype, - int roundtrip, int orig_rtt, time_t timenow) -{ - struct lruhash_entry* e = infra_lookup_nottl(infra, addr, addrlen, - nm, nmlen, 1); - struct infra_data* data; - int needtoinsert = 0; - int rto = 1; - if(!e) { - if(!(e = new_entry(infra, addr, addrlen, nm, nmlen, timenow))) - return 0; - needtoinsert = 1; - } else if(((struct infra_data*)e->data)->ttl < timenow) { - data_entry_init(infra, e, timenow); - } - /* have an entry, update the rtt */ - data = (struct infra_data*)e->data; - if(roundtrip == -1) { - rtt_lost(&data->rtt, orig_rtt); - if(qtype == LDNS_RR_TYPE_A) { - if(data->timeout_A < TIMEOUT_COUNT_MAX) - data->timeout_A++; - } else if(qtype == LDNS_RR_TYPE_AAAA) { - if(data->timeout_AAAA < TIMEOUT_COUNT_MAX) - data->timeout_AAAA++; - } else { - if(data->timeout_other < TIMEOUT_COUNT_MAX) - data->timeout_other++; - } - } else { - /* if we got a reply, but the old timeout was above server - * selection height, delete the timeout so the server is - * fully available again */ - if(rtt_unclamped(&data->rtt) >= USEFUL_SERVER_TOP_TIMEOUT) - rtt_init(&data->rtt); - rtt_update(&data->rtt, roundtrip); - data->probedelay = 0; - if(qtype == LDNS_RR_TYPE_A) - data->timeout_A = 0; - else if(qtype == LDNS_RR_TYPE_AAAA) - data->timeout_AAAA = 0; - else data->timeout_other = 0; - } - if(data->rtt.rto > 0) - rto = data->rtt.rto; - - if(needtoinsert) - slabhash_insert(infra->hosts, e->hash, e, e->data, NULL); - else { lock_rw_unlock(&e->lock); } - return rto; -} - -long long infra_get_host_rto(struct infra_cache* infra, - struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* nm, - size_t nmlen, struct rtt_info* rtt, int* delay, time_t timenow, - int* tA, int* tAAAA, int* tother) -{ - struct lruhash_entry* e = infra_lookup_nottl(infra, addr, addrlen, - nm, nmlen, 0); - struct infra_data* data; - long long ttl = -2; - if(!e) return -1; - data = (struct infra_data*)e->data; - if(data->ttl >= timenow) { - ttl = (long long)(data->ttl - timenow); - memmove(rtt, &data->rtt, sizeof(*rtt)); - if(timenow < data->probedelay) - *delay = (int)(data->probedelay - timenow); - else *delay = 0; - } - *tA = (int)data->timeout_A; - *tAAAA = (int)data->timeout_AAAA; - *tother = (int)data->timeout_other; - lock_rw_unlock(&e->lock); - return ttl; -} - -int -infra_edns_update(struct infra_cache* infra, struct sockaddr_storage* addr, - socklen_t addrlen, uint8_t* nm, size_t nmlen, int edns_version, - time_t timenow) -{ - struct lruhash_entry* e = infra_lookup_nottl(infra, addr, addrlen, - nm, nmlen, 1); - struct infra_data* data; - int needtoinsert = 0; - if(!e) { - if(!(e = new_entry(infra, addr, addrlen, nm, nmlen, timenow))) - return 0; - needtoinsert = 1; - } else if(((struct infra_data*)e->data)->ttl < timenow) { - data_entry_init(infra, e, timenow); - } - /* have an entry, update the rtt, and the ttl */ - data = (struct infra_data*)e->data; - /* do not update if noEDNS and stored is yesEDNS */ - if(!(edns_version == -1 && (data->edns_version != -1 && - data->edns_lame_known))) { - data->edns_version = edns_version; - data->edns_lame_known = 1; - } - - if(needtoinsert) - slabhash_insert(infra->hosts, e->hash, e, e->data, NULL); - else { lock_rw_unlock(&e->lock); } - return 1; -} - -int -infra_get_lame_rtt(struct infra_cache* infra, - struct sockaddr_storage* addr, socklen_t addrlen, - uint8_t* name, size_t namelen, uint16_t qtype, - int* lame, int* dnsseclame, int* reclame, int* rtt, time_t timenow) -{ - struct infra_data* host; - struct lruhash_entry* e = infra_lookup_nottl(infra, addr, addrlen, - name, namelen, 0); - if(!e) - return 0; - host = (struct infra_data*)e->data; - *rtt = rtt_unclamped(&host->rtt); - if(host->rtt.rto >= PROBE_MAXRTO && timenow < host->probedelay - && rtt_notimeout(&host->rtt)*4 <= host->rtt.rto) { - /* single probe for this domain, and we are not probing */ - /* unless the query type allows a probe to happen */ - if(qtype == LDNS_RR_TYPE_A) { - if(host->timeout_A >= TIMEOUT_COUNT_MAX) - *rtt = USEFUL_SERVER_TOP_TIMEOUT; - else *rtt = USEFUL_SERVER_TOP_TIMEOUT-1000; - } else if(qtype == LDNS_RR_TYPE_AAAA) { - if(host->timeout_AAAA >= TIMEOUT_COUNT_MAX) - *rtt = USEFUL_SERVER_TOP_TIMEOUT; - else *rtt = USEFUL_SERVER_TOP_TIMEOUT-1000; - } else { - if(host->timeout_other >= TIMEOUT_COUNT_MAX) - *rtt = USEFUL_SERVER_TOP_TIMEOUT; - else *rtt = USEFUL_SERVER_TOP_TIMEOUT-1000; - } - } - if(timenow > host->ttl) { - /* expired entry */ - /* see if this can be a re-probe of an unresponsive server */ - /* minus 1000 because that is outside of the RTTBAND, so - * blacklisted servers stay blacklisted if this is chosen */ - if(host->rtt.rto >= USEFUL_SERVER_TOP_TIMEOUT) { - lock_rw_unlock(&e->lock); - *rtt = USEFUL_SERVER_TOP_TIMEOUT-1000; - *lame = 0; - *dnsseclame = 0; - *reclame = 0; - return 1; - } - lock_rw_unlock(&e->lock); - return 0; - } - /* check lameness first */ - if(host->lame_type_A && qtype == LDNS_RR_TYPE_A) { - lock_rw_unlock(&e->lock); - *lame = 1; - *dnsseclame = 0; - *reclame = 0; - return 1; - } else if(host->lame_other && qtype != LDNS_RR_TYPE_A) { - lock_rw_unlock(&e->lock); - *lame = 1; - *dnsseclame = 0; - *reclame = 0; - return 1; - } else if(host->isdnsseclame) { - lock_rw_unlock(&e->lock); - *lame = 0; - *dnsseclame = 1; - *reclame = 0; - return 1; - } else if(host->rec_lame) { - lock_rw_unlock(&e->lock); - *lame = 0; - *dnsseclame = 0; - *reclame = 1; - return 1; - } - /* no lameness for this type of query */ - lock_rw_unlock(&e->lock); - *lame = 0; - *dnsseclame = 0; - *reclame = 0; - return 1; -} - -int infra_find_ratelimit(struct infra_cache* infra, uint8_t* name, - size_t namelen) -{ - int labs = dname_count_labels(name); - struct domain_limit_data* d = (struct domain_limit_data*) - name_tree_lookup(&infra->domain_limits, name, namelen, labs, - LDNS_RR_CLASS_IN); - if(!d) return infra_dp_ratelimit; - - if(d->node.labs == labs && d->lim != -1) - return d->lim; /* exact match */ - - /* find 'below match' */ - if(d->node.labs == labs) - d = (struct domain_limit_data*)d->node.parent; - while(d) { - if(d->below != -1) - return d->below; - d = (struct domain_limit_data*)d->node.parent; - } - return infra_dp_ratelimit; -} - -size_t ip_rate_sizefunc(void* k, void* ATTR_UNUSED(d)) -{ - struct ip_rate_key* key = (struct ip_rate_key*)k; - return sizeof(*key) + sizeof(struct ip_rate_data) - + lock_get_mem(&key->entry.lock); -} - -int ip_rate_compfunc(void* key1, void* key2) -{ - struct ip_rate_key* k1 = (struct ip_rate_key*)key1; - struct ip_rate_key* k2 = (struct ip_rate_key*)key2; - return sockaddr_cmp_addr(&k1->addr, k1->addrlen, - &k2->addr, k2->addrlen); -} - -void ip_rate_delkeyfunc(void* k, void* ATTR_UNUSED(arg)) -{ - struct ip_rate_key* key = (struct ip_rate_key*)k; - if(!key) - return; - lock_rw_destroy(&key->entry.lock); - free(key); -} - -/** find data item in array, for write access, caller unlocks */ -static struct lruhash_entry* infra_find_ratedata(struct infra_cache* infra, - uint8_t* name, size_t namelen, int wr) -{ - struct rate_key key; - hashvalue_type h = dname_query_hash(name, 0xab); - memset(&key, 0, sizeof(key)); - key.name = name; - key.namelen = namelen; - key.entry.hash = h; - return slabhash_lookup(infra->domain_rates, h, &key, wr); -} - -/** find data item in array for ip addresses */ -struct lruhash_entry* infra_find_ip_ratedata(struct infra_cache* infra, - struct comm_reply* repinfo, int wr) -{ - struct ip_rate_key key; - hashvalue_type h = hash_addr(&(repinfo->addr), - repinfo->addrlen, 0); - memset(&key, 0, sizeof(key)); - key.addr = repinfo->addr; - key.addrlen = repinfo->addrlen; - key.entry.hash = h; - return slabhash_lookup(infra->client_ip_rates, h, &key, wr); -} - -/** create rate data item for name, number 1 in now */ -static void infra_create_ratedata(struct infra_cache* infra, - uint8_t* name, size_t namelen, time_t timenow) -{ - hashvalue_type h = dname_query_hash(name, 0xab); - struct rate_key* k = (struct rate_key*)calloc(1, sizeof(*k)); - struct rate_data* d = (struct rate_data*)calloc(1, sizeof(*d)); - if(!k || !d) { - free(k); - free(d); - return; /* alloc failure */ - } - k->namelen = namelen; - k->name = memdup(name, namelen); - if(!k->name) { - free(k); - free(d); - return; /* alloc failure */ - } - lock_rw_init(&k->entry.lock); - k->entry.hash = h; - k->entry.key = k; - k->entry.data = d; - d->qps[0] = 1; - d->timestamp[0] = timenow; - slabhash_insert(infra->domain_rates, h, &k->entry, d, NULL); -} - -/** create rate data item for ip address */ -static void infra_ip_create_ratedata(struct infra_cache* infra, - struct comm_reply* repinfo, time_t timenow) -{ - hashvalue_type h = hash_addr(&(repinfo->addr), - repinfo->addrlen, 0); - struct ip_rate_key* k = (struct ip_rate_key*)calloc(1, sizeof(*k)); - struct ip_rate_data* d = (struct ip_rate_data*)calloc(1, sizeof(*d)); - if(!k || !d) { - free(k); - free(d); - return; /* alloc failure */ - } - k->addr = repinfo->addr; - k->addrlen = repinfo->addrlen; - lock_rw_init(&k->entry.lock); - k->entry.hash = h; - k->entry.key = k; - k->entry.data = d; - d->qps[0] = 1; - d->timestamp[0] = timenow; - slabhash_insert(infra->client_ip_rates, h, &k->entry, d, NULL); -} - -/** find the second and return its rate counter, if none, remove oldest */ -static int* infra_rate_find_second(void* data, time_t t) -{ - struct rate_data* d = (struct rate_data*)data; - int i, oldest; - for(i=0; i<RATE_WINDOW; i++) { - if(d->timestamp[i] == t) - return &(d->qps[i]); - } - /* remove oldest timestamp, and insert it at t with 0 qps */ - oldest = 0; - for(i=0; i<RATE_WINDOW; i++) { - if(d->timestamp[i] < d->timestamp[oldest]) - oldest = i; - } - d->timestamp[oldest] = t; - d->qps[oldest] = 0; - return &(d->qps[oldest]); -} - -int infra_rate_max(void* data, time_t now) -{ - struct rate_data* d = (struct rate_data*)data; - int i, max = 0; - for(i=0; i<RATE_WINDOW; i++) { - if(now-d->timestamp[i] <= RATE_WINDOW) { - if(d->qps[i] > max) - max = d->qps[i]; - } - } - return max; -} - -int infra_ratelimit_inc(struct infra_cache* infra, uint8_t* name, - size_t namelen, time_t timenow) -{ - int lim, max; - struct lruhash_entry* entry; - - if(!infra_dp_ratelimit) - return 1; /* not enabled */ - - /* find ratelimit */ - lim = infra_find_ratelimit(infra, name, namelen); - - /* find or insert ratedata */ - entry = infra_find_ratedata(infra, name, namelen, 1); - if(entry) { - int premax = infra_rate_max(entry->data, timenow); - int* cur = infra_rate_find_second(entry->data, timenow); - (*cur)++; - max = infra_rate_max(entry->data, timenow); - lock_rw_unlock(&entry->lock); - - if(premax < lim && max >= lim) { - char buf[257]; - dname_str(name, buf); - verbose(VERB_OPS, "ratelimit exceeded %s %d", buf, lim); - } - return (max < lim); - } - - /* create */ - infra_create_ratedata(infra, name, namelen, timenow); - return (1 < lim); -} - -void infra_ratelimit_dec(struct infra_cache* infra, uint8_t* name, - size_t namelen, time_t timenow) -{ - struct lruhash_entry* entry; - int* cur; - if(!infra_dp_ratelimit) - return; /* not enabled */ - entry = infra_find_ratedata(infra, name, namelen, 1); - if(!entry) return; /* not cached */ - cur = infra_rate_find_second(entry->data, timenow); - if((*cur) > 0) - (*cur)--; - lock_rw_unlock(&entry->lock); -} - -int infra_ratelimit_exceeded(struct infra_cache* infra, uint8_t* name, - size_t namelen, time_t timenow) -{ - struct lruhash_entry* entry; - int lim, max; - if(!infra_dp_ratelimit) - return 0; /* not enabled */ - - /* find ratelimit */ - lim = infra_find_ratelimit(infra, name, namelen); - - /* find current rate */ - entry = infra_find_ratedata(infra, name, namelen, 0); - if(!entry) - return 0; /* not cached */ - max = infra_rate_max(entry->data, timenow); - lock_rw_unlock(&entry->lock); - - return (max >= lim); -} - -size_t -infra_get_mem(struct infra_cache* infra) -{ - size_t s = sizeof(*infra) + slabhash_get_mem(infra->hosts); - if(infra->domain_rates) s += slabhash_get_mem(infra->domain_rates); - if(infra->client_ip_rates) s += slabhash_get_mem(infra->client_ip_rates); - /* ignore domain_limits because walk through tree is big */ - return s; -} - -int infra_ip_ratelimit_inc(struct infra_cache* infra, - struct comm_reply* repinfo, time_t timenow) -{ - int max; - struct lruhash_entry* entry; - - /* not enabled */ - if(!infra_ip_ratelimit) { - return 1; - } - /* find or insert ratedata */ - entry = infra_find_ip_ratedata(infra, repinfo, 1); - if(entry) { - int premax = infra_rate_max(entry->data, timenow); - int* cur = infra_rate_find_second(entry->data, timenow); - (*cur)++; - max = infra_rate_max(entry->data, timenow); - lock_rw_unlock(&entry->lock); - - if(premax < infra_ip_ratelimit && max >= infra_ip_ratelimit) { - char client_ip[128]; - addr_to_str((struct sockaddr_storage *)&repinfo->addr, - repinfo->addrlen, client_ip, sizeof(client_ip)); - verbose(VERB_OPS, "ratelimit exceeded %s %d", client_ip, - infra_ip_ratelimit); - } - return (max <= infra_ip_ratelimit); - } - - /* create */ - infra_ip_create_ratedata(infra, repinfo, timenow); - return 1; -} diff --git a/external/unbound/services/cache/infra.h b/external/unbound/services/cache/infra.h deleted file mode 100644 index 6f9471a39..000000000 --- a/external/unbound/services/cache/infra.h +++ /dev/null @@ -1,462 +0,0 @@ -/* - * services/cache/infra.h - infrastructure cache, server rtt and capabilities - * - * Copyright (c) 2007, NLnet Labs. All rights reserved. - * - * This software is open source. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of the NLNET LABS nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * \file - * - * This file contains the infrastructure cache, as well as rate limiting. - * Note that there are two sorts of rate-limiting here: - * - Pre-cache, per-query rate limiting (query ratelimits) - * - Post-cache, per-domain name rate limiting (infra-ratelimits) - */ - -#ifndef SERVICES_CACHE_INFRA_H -#define SERVICES_CACHE_INFRA_H -#include "util/storage/lruhash.h" -#include "util/storage/dnstree.h" -#include "util/rtt.h" -#include "util/netevent.h" -#include "util/data/msgreply.h" -struct slabhash; -struct config_file; - -/** - * Host information kept for every server, per zone. - */ -struct infra_key { - /** the host address. */ - struct sockaddr_storage addr; - /** length of addr. */ - socklen_t addrlen; - /** zone name in wireformat */ - uint8_t* zonename; - /** length of zonename */ - size_t namelen; - /** hash table entry, data of type infra_data. */ - struct lruhash_entry entry; -}; - -/** - * Host information encompasses host capabilities and retransmission timeouts. - * And lameness information (notAuthoritative, noEDNS, Recursive) - */ -struct infra_data { - /** TTL value for this entry. absolute time. */ - time_t ttl; - - /** time in seconds (absolute) when probing re-commences, 0 disabled */ - time_t probedelay; - /** round trip times for timeout calculation */ - struct rtt_info rtt; - - /** edns version that the host supports, -1 means no EDNS */ - int edns_version; - /** if the EDNS lameness is already known or not. - * EDNS lame is when EDNS queries or replies are dropped, - * and cause a timeout */ - uint8_t edns_lame_known; - - /** is the host lame (does not serve the zone authoritatively), - * or is the host dnssec lame (does not serve DNSSEC data) */ - uint8_t isdnsseclame; - /** is the host recursion lame (not AA, but RA) */ - uint8_t rec_lame; - /** the host is lame (not authoritative) for A records */ - uint8_t lame_type_A; - /** the host is lame (not authoritative) for other query types */ - uint8_t lame_other; - - /** timeouts counter for type A */ - uint8_t timeout_A; - /** timeouts counter for type AAAA */ - uint8_t timeout_AAAA; - /** timeouts counter for others */ - uint8_t timeout_other; -}; - -/** - * Infra cache - */ -struct infra_cache { - /** The hash table with hosts */ - struct slabhash* hosts; - /** TTL value for host information, in seconds */ - int host_ttl; - /** hash table with query rates per name: rate_key, rate_data */ - struct slabhash* domain_rates; - /** ratelimit settings for domains, struct domain_limit_data */ - rbtree_type domain_limits; - /** hash table with query rates per client ip: ip_rate_key, ip_rate_data */ - struct slabhash* client_ip_rates; -}; - -/** ratelimit, unless overridden by domain_limits, 0 is off */ -extern int infra_dp_ratelimit; - -/** - * ratelimit settings for domains - */ -struct domain_limit_data { - /** key for rbtree, must be first in struct, name of domain */ - struct name_tree_node node; - /** ratelimit for exact match with this name, -1 if not set */ - int lim; - /** ratelimit for names below this name, -1 if not set */ - int below; -}; - -/** - * key for ratelimit lookups, a domain name - */ -struct rate_key { - /** lruhash key entry */ - struct lruhash_entry entry; - /** domain name in uncompressed wireformat */ - uint8_t* name; - /** length of name */ - size_t namelen; -}; - -/** ip ratelimit, 0 is off */ -extern int infra_ip_ratelimit; - -/** - * key for ip_ratelimit lookups, a source IP. - */ -struct ip_rate_key { - /** lruhash key entry */ - struct lruhash_entry entry; - /** client ip information */ - struct sockaddr_storage addr; - /** length of address */ - socklen_t addrlen; -}; - -/** number of seconds to track qps rate */ -#define RATE_WINDOW 2 - -/** - * Data for ratelimits per domain name - * It is incremented when a non-cache-lookup happens for that domain name. - * The name is the delegation point we have for the name. - * If a new delegation point is found (a referral reply), the previous - * delegation point is decremented, and the new one is charged with the query. - */ -struct rate_data { - /** queries counted, for that second. 0 if not in use. */ - int qps[RATE_WINDOW]; - /** what the timestamp is of the qps array members, counter is - * valid for that timestamp. Usually now and now-1. */ - time_t timestamp[RATE_WINDOW]; -}; - -#define ip_rate_data rate_data - -/** infra host cache default hash lookup size */ -#define INFRA_HOST_STARTSIZE 32 -/** bytes per zonename reserved in the hostcache, dnamelen(zonename.com.) */ -#define INFRA_BYTES_NAME 14 - -/** - * Create infra cache. - * @param cfg: config parameters or NULL for defaults. - * @return: new infra cache, or NULL. - */ -struct infra_cache* infra_create(struct config_file* cfg); - -/** - * Delete infra cache. - * @param infra: infrastructure cache to delete. - */ -void infra_delete(struct infra_cache* infra); - -/** - * Adjust infra cache to use updated configuration settings. - * This may clean the cache. Operates a bit like realloc. - * There may be no threading or use by other threads. - * @param infra: existing cache. If NULL a new infra cache is returned. - * @param cfg: config options. - * @return the new infra cache pointer or NULL on error. - */ -struct infra_cache* infra_adjust(struct infra_cache* infra, - struct config_file* cfg); - -/** - * Plain find infra data function (used by the the other functions) - * @param infra: infrastructure cache. - * @param addr: host address. - * @param addrlen: length of addr. - * @param name: domain name of zone. - * @param namelen: length of domain name. - * @param wr: if true, writelock, else readlock. - * @return the entry, could be expired (this is not checked) or NULL. - */ -struct lruhash_entry* infra_lookup_nottl(struct infra_cache* infra, - struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* name, - size_t namelen, int wr); - -/** - * Find host information to send a packet. Creates new entry if not found. - * Lameness is empty. EDNS is 0 (try with first), and rtt is returned for - * the first message to it. - * Use this to send a packet only, because it also locks out others when - * probing is restricted. - * @param infra: infrastructure cache. - * @param addr: host address. - * @param addrlen: length of addr. - * @param name: domain name of zone. - * @param namelen: length of domain name. - * @param timenow: what time it is now. - * @param edns_vs: edns version it supports, is returned. - * @param edns_lame_known: if EDNS lame (EDNS is dropped in transit) has - * already been probed, is returned. - * @param to: timeout to use, is returned. - * @return: 0 on error. - */ -int infra_host(struct infra_cache* infra, struct sockaddr_storage* addr, - socklen_t addrlen, uint8_t* name, size_t namelen, - time_t timenow, int* edns_vs, uint8_t* edns_lame_known, int* to); - -/** - * Set a host to be lame for the given zone. - * @param infra: infrastructure cache. - * @param addr: host address. - * @param addrlen: length of addr. - * @param name: domain name of zone apex. - * @param namelen: length of domain name. - * @param timenow: what time it is now. - * @param dnsseclame: if true the host is set dnssec lame. - * if false, the host is marked lame (not serving the zone). - * @param reclame: if true host is a recursor not AA server. - * if false, dnsseclame or marked lame. - * @param qtype: the query type for which it is lame. - * @return: 0 on error. - */ -int infra_set_lame(struct infra_cache* infra, - struct sockaddr_storage* addr, socklen_t addrlen, - uint8_t* name, size_t namelen, time_t timenow, int dnsseclame, - int reclame, uint16_t qtype); - -/** - * Update rtt information for the host. - * @param infra: infrastructure cache. - * @param addr: host address. - * @param addrlen: length of addr. - * @param name: zone name - * @param namelen: zone name length - * @param qtype: query type. - * @param roundtrip: estimate of roundtrip time in milliseconds or -1 for - * timeout. - * @param orig_rtt: original rtt for the query that timed out (roundtrip==-1). - * ignored if roundtrip != -1. - * @param timenow: what time it is now. - * @return: 0 on error. new rto otherwise. - */ -int infra_rtt_update(struct infra_cache* infra, struct sockaddr_storage* addr, - socklen_t addrlen, uint8_t* name, size_t namelen, int qtype, - int roundtrip, int orig_rtt, time_t timenow); - -/** - * Update information for the host, store that a TCP transaction works. - * @param infra: infrastructure cache. - * @param addr: host address. - * @param addrlen: length of addr. - * @param name: name of zone - * @param namelen: length of name - */ -void infra_update_tcp_works(struct infra_cache* infra, - struct sockaddr_storage* addr, socklen_t addrlen, - uint8_t* name, size_t namelen); - -/** - * Update edns information for the host. - * @param infra: infrastructure cache. - * @param addr: host address. - * @param addrlen: length of addr. - * @param name: name of zone - * @param namelen: length of name - * @param edns_version: the version that it publishes. - * If it is known to support EDNS then no-EDNS is not stored over it. - * @param timenow: what time it is now. - * @return: 0 on error. - */ -int infra_edns_update(struct infra_cache* infra, - struct sockaddr_storage* addr, socklen_t addrlen, - uint8_t* name, size_t namelen, int edns_version, time_t timenow); - -/** - * Get Lameness information and average RTT if host is in the cache. - * This information is to be used for server selection. - * @param infra: infrastructure cache. - * @param addr: host address. - * @param addrlen: length of addr. - * @param name: zone name. - * @param namelen: zone name length. - * @param qtype: the query to be made. - * @param lame: if function returns true, this returns lameness of the zone. - * @param dnsseclame: if function returns true, this returns if the zone - * is dnssec-lame. - * @param reclame: if function returns true, this is if it is recursion lame. - * @param rtt: if function returns true, this returns avg rtt of the server. - * The rtt value is unclamped and reflects recent timeouts. - * @param timenow: what time it is now. - * @return if found in cache, or false if not (or TTL bad). - */ -int infra_get_lame_rtt(struct infra_cache* infra, - struct sockaddr_storage* addr, socklen_t addrlen, - uint8_t* name, size_t namelen, uint16_t qtype, - int* lame, int* dnsseclame, int* reclame, int* rtt, time_t timenow); - -/** - * Get additional (debug) info on timing. - * @param infra: infra cache. - * @param addr: host address. - * @param addrlen: length of addr. - * @param name: zone name - * @param namelen: zone name length - * @param rtt: the rtt_info is copied into here (caller alloced return struct). - * @param delay: probe delay (if any). - * @param timenow: what time it is now. - * @param tA: timeout counter on type A. - * @param tAAAA: timeout counter on type AAAA. - * @param tother: timeout counter on type other. - * @return TTL the infra host element is valid for. If -1: not found in cache. - * TTL -2: found but expired. - */ -long long infra_get_host_rto(struct infra_cache* infra, - struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* name, - size_t namelen, struct rtt_info* rtt, int* delay, time_t timenow, - int* tA, int* tAAAA, int* tother); - -/** - * Increment the query rate counter for a delegation point. - * @param infra: infra cache. - * @param name: zone name - * @param namelen: zone name length - * @param timenow: what time it is now. - * @return 1 if it could be incremented. 0 if the increment overshot the - * ratelimit or if in the previous second the ratelimit was exceeded. - * Failures like alloc failures are not returned (probably as 1). - */ -int infra_ratelimit_inc(struct infra_cache* infra, uint8_t* name, - size_t namelen, time_t timenow); - -/** - * Decrement the query rate counter for a delegation point. - * Because the reply received for the delegation point was pleasant, - * we do not charge this delegation point with it (i.e. it was a referral). - * Should call it with same second as when inc() was called. - * @param infra: infra cache. - * @param name: zone name - * @param namelen: zone name length - * @param timenow: what time it is now. - */ -void infra_ratelimit_dec(struct infra_cache* infra, uint8_t* name, - size_t namelen, time_t timenow); - -/** - * See if the query rate counter for a delegation point is exceeded. - * So, no queries are going to be allowed. - * @param infra: infra cache. - * @param name: zone name - * @param namelen: zone name length - * @param timenow: what time it is now. - * @return true if exceeded. - */ -int infra_ratelimit_exceeded(struct infra_cache* infra, uint8_t* name, - size_t namelen, time_t timenow); - -/** find the maximum rate stored, not too old. 0 if no information. */ -int infra_rate_max(void* data, time_t now); - -/** find the ratelimit in qps for a domain */ -int infra_find_ratelimit(struct infra_cache* infra, uint8_t* name, - size_t namelen); - -/** Update query ratelimit hash and decide - * whether or not a query should be dropped. - * @param infra: infra cache - * @param repinfo: information about client - * @param timenow: what time it is now. - * @return 1 if it could be incremented. 0 if the increment overshot the - * ratelimit and the query should be dropped. */ -int infra_ip_ratelimit_inc(struct infra_cache* infra, - struct comm_reply* repinfo, time_t timenow); - -/** - * Get memory used by the infra cache. - * @param infra: infrastructure cache. - * @return memory in use in bytes. - */ -size_t infra_get_mem(struct infra_cache* infra); - -/** calculate size for the hashtable, does not count size of lameness, - * so the hashtable is a fixed number of items */ -size_t infra_sizefunc(void* k, void* d); - -/** compare two addresses, returns -1, 0, or +1 */ -int infra_compfunc(void* key1, void* key2); - -/** delete key, and destroy the lock */ -void infra_delkeyfunc(void* k, void* arg); - -/** delete data and destroy the lameness hashtable */ -void infra_deldatafunc(void* d, void* arg); - -/** calculate size for the hashtable */ -size_t rate_sizefunc(void* k, void* d); - -/** compare two names, returns -1, 0, or +1 */ -int rate_compfunc(void* key1, void* key2); - -/** delete key, and destroy the lock */ -void rate_delkeyfunc(void* k, void* arg); - -/** delete data */ -void rate_deldatafunc(void* d, void* arg); - -/* calculate size for the client ip hashtable */ -size_t ip_rate_sizefunc(void* k, void* d); - -/* compare two addresses */ -int ip_rate_compfunc(void* key1, void* key2); - -/* delete key, and destroy the lock */ -void ip_rate_delkeyfunc(void* d, void* arg); - -/* delete data */ -#define ip_rate_deldatafunc rate_deldatafunc - -#endif /* SERVICES_CACHE_INFRA_H */ diff --git a/external/unbound/services/cache/rrset.c b/external/unbound/services/cache/rrset.c deleted file mode 100644 index 7e5732b76..000000000 --- a/external/unbound/services/cache/rrset.c +++ /dev/null @@ -1,419 +0,0 @@ -/* - * services/cache/rrset.c - Resource record set cache. - * - * Copyright (c) 2007, NLnet Labs. All rights reserved. - * - * This software is open source. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of the NLNET LABS nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * \file - * - * This file contains the rrset cache. - */ -#include "config.h" -#include "services/cache/rrset.h" -#include "sldns/rrdef.h" -#include "util/storage/slabhash.h" -#include "util/config_file.h" -#include "util/data/packed_rrset.h" -#include "util/data/msgreply.h" -#include "util/regional.h" -#include "util/alloc.h" - -void -rrset_markdel(void* key) -{ - struct ub_packed_rrset_key* r = (struct ub_packed_rrset_key*)key; - r->id = 0; -} - -struct rrset_cache* rrset_cache_create(struct config_file* cfg, - struct alloc_cache* alloc) -{ - size_t slabs = (cfg?cfg->rrset_cache_slabs:HASH_DEFAULT_SLABS); - size_t startarray = HASH_DEFAULT_STARTARRAY; - size_t maxmem = (cfg?cfg->rrset_cache_size:HASH_DEFAULT_MAXMEM); - - struct rrset_cache *r = (struct rrset_cache*)slabhash_create(slabs, - startarray, maxmem, ub_rrset_sizefunc, ub_rrset_compare, - ub_rrset_key_delete, rrset_data_delete, alloc); - slabhash_setmarkdel(&r->table, &rrset_markdel); - return r; -} - -void rrset_cache_delete(struct rrset_cache* r) -{ - if(!r) - return; - slabhash_delete(&r->table); - /* slabhash delete also does free(r), since table is first in struct*/ -} - -struct rrset_cache* rrset_cache_adjust(struct rrset_cache *r, - struct config_file* cfg, struct alloc_cache* alloc) -{ - if(!r || !cfg || cfg->rrset_cache_slabs != r->table.size || - cfg->rrset_cache_size != slabhash_get_size(&r->table)) - { - rrset_cache_delete(r); - r = rrset_cache_create(cfg, alloc); - } - return r; -} - -void -rrset_cache_touch(struct rrset_cache* r, struct ub_packed_rrset_key* key, - hashvalue_type hash, rrset_id_type id) -{ - struct lruhash* table = slabhash_gettable(&r->table, hash); - /* - * This leads to locking problems, deadlocks, if the caller is - * holding any other rrset lock. - * Because a lookup through the hashtable does: - * tablelock -> entrylock (for that entry caller holds) - * And this would do - * entrylock(already held) -> tablelock - * And if two threads do this, it results in deadlock. - * So, the caller must not hold entrylock. - */ - lock_quick_lock(&table->lock); - /* we have locked the hash table, the item can still be deleted. - * because it could already have been reclaimed, but not yet set id=0. - * This is because some lruhash routines have lazy deletion. - * so, we must acquire a lock on the item to verify the id != 0. - * also, with hash not changed, we are using the right slab. - */ - lock_rw_rdlock(&key->entry.lock); - if(key->id == id && key->entry.hash == hash) { - lru_touch(table, &key->entry); - } - lock_rw_unlock(&key->entry.lock); - lock_quick_unlock(&table->lock); -} - -/** see if rrset needs to be updated in the cache */ -static int -need_to_update_rrset(void* nd, void* cd, time_t timenow, int equal, int ns) -{ - struct packed_rrset_data* newd = (struct packed_rrset_data*)nd; - struct packed_rrset_data* cached = (struct packed_rrset_data*)cd; - /* o store if rrset has been validated - * everything better than bogus data - * secure is preferred */ - if( newd->security == sec_status_secure && - cached->security != sec_status_secure) - return 1; - if( cached->security == sec_status_bogus && - newd->security != sec_status_bogus && !equal) - return 1; - /* o if current RRset is more trustworthy - insert it */ - if( newd->trust > cached->trust ) { - /* if the cached rrset is bogus, and this one equal, - * do not update the TTL - let it expire. */ - if(equal && cached->ttl >= timenow && - cached->security == sec_status_bogus) - return 0; - return 1; - } - /* o item in cache has expired */ - if( cached->ttl < timenow ) - return 1; - /* o same trust, but different in data - insert it */ - if( newd->trust == cached->trust && !equal ) { - /* if this is type NS, do not 'stick' to owner that changes - * the NS RRset, but use the old TTL for the new data, and - * update to fetch the latest data. ttl is not expired, because - * that check was before this one. */ - if(ns) { - size_t i; - newd->ttl = cached->ttl; - for(i=0; i<(newd->count+newd->rrsig_count); i++) - if(newd->rr_ttl[i] > newd->ttl) - newd->rr_ttl[i] = newd->ttl; - } - return 1; - } - return 0; -} - -/** Update RRSet special key ID */ -static void -rrset_update_id(struct rrset_ref* ref, struct alloc_cache* alloc) -{ - /* this may clear the cache and invalidate lock below */ - uint64_t newid = alloc_get_id(alloc); - /* obtain writelock */ - lock_rw_wrlock(&ref->key->entry.lock); - /* check if it was deleted in the meantime, if so, skip update */ - if(ref->key->id == ref->id) { - ref->key->id = newid; - ref->id = newid; - } - lock_rw_unlock(&ref->key->entry.lock); -} - -int -rrset_cache_update(struct rrset_cache* r, struct rrset_ref* ref, - struct alloc_cache* alloc, time_t timenow) -{ - struct lruhash_entry* e; - struct ub_packed_rrset_key* k = ref->key; - hashvalue_type h = k->entry.hash; - uint16_t rrset_type = ntohs(k->rk.type); - int equal = 0; - log_assert(ref->id != 0 && k->id != 0); - log_assert(k->rk.dname != NULL); - /* looks up item with a readlock - no editing! */ - if((e=slabhash_lookup(&r->table, h, k, 0)) != 0) { - /* return id and key as they will be used in the cache - * since the lruhash_insert, if item already exists, deallocs - * the passed key in favor of the already stored key. - * because of the small gap (see below) this key ptr and id - * may prove later to be already deleted, which is no problem - * as it only makes a cache miss. - */ - ref->key = (struct ub_packed_rrset_key*)e->key; - ref->id = ref->key->id; - equal = rrsetdata_equal((struct packed_rrset_data*)k->entry. - data, (struct packed_rrset_data*)e->data); - if(!need_to_update_rrset(k->entry.data, e->data, timenow, - equal, (rrset_type==LDNS_RR_TYPE_NS))) { - /* cache is superior, return that value */ - lock_rw_unlock(&e->lock); - ub_packed_rrset_parsedelete(k, alloc); - if(equal) return 2; - return 1; - } - lock_rw_unlock(&e->lock); - /* Go on and insert the passed item. - * small gap here, where entry is not locked. - * possibly entry is updated with something else. - * we then overwrite that with our data. - * this is just too bad, its cache anyway. */ - /* use insert to update entry to manage lruhash - * cache size values nicely. */ - } - log_assert(ref->key->id != 0); - slabhash_insert(&r->table, h, &k->entry, k->entry.data, alloc); - if(e) { - /* For NSEC, NSEC3, DNAME, when rdata is updated, update - * the ID number so that proofs in message cache are - * invalidated */ - if((rrset_type == LDNS_RR_TYPE_NSEC - || rrset_type == LDNS_RR_TYPE_NSEC3 - || rrset_type == LDNS_RR_TYPE_DNAME) && !equal) { - rrset_update_id(ref, alloc); - } - return 1; - } - return 0; -} - -struct ub_packed_rrset_key* -rrset_cache_lookup(struct rrset_cache* r, uint8_t* qname, size_t qnamelen, - uint16_t qtype, uint16_t qclass, uint32_t flags, time_t timenow, - int wr) -{ - struct lruhash_entry* e; - struct ub_packed_rrset_key key; - - key.entry.key = &key; - key.entry.data = NULL; - key.rk.dname = qname; - key.rk.dname_len = qnamelen; - key.rk.type = htons(qtype); - key.rk.rrset_class = htons(qclass); - key.rk.flags = flags; - - key.entry.hash = rrset_key_hash(&key.rk); - - if((e = slabhash_lookup(&r->table, key.entry.hash, &key, wr))) { - /* check TTL */ - struct packed_rrset_data* data = - (struct packed_rrset_data*)e->data; - if(timenow > data->ttl) { - lock_rw_unlock(&e->lock); - return NULL; - } - /* we're done */ - return (struct ub_packed_rrset_key*)e->key; - } - return NULL; -} - -int -rrset_array_lock(struct rrset_ref* ref, size_t count, time_t timenow) -{ - size_t i; - for(i=0; i<count; i++) { - if(i>0 && ref[i].key == ref[i-1].key) - continue; /* only lock items once */ - lock_rw_rdlock(&ref[i].key->entry.lock); - if(ref[i].id != ref[i].key->id || timenow > - ((struct packed_rrset_data*)(ref[i].key->entry.data)) - ->ttl) { - /* failure! rollback our readlocks */ - rrset_array_unlock(ref, i+1); - return 0; - } - } - return 1; -} - -void -rrset_array_unlock(struct rrset_ref* ref, size_t count) -{ - size_t i; - for(i=0; i<count; i++) { - if(i>0 && ref[i].key == ref[i-1].key) - continue; /* only unlock items once */ - lock_rw_unlock(&ref[i].key->entry.lock); - } -} - -void -rrset_array_unlock_touch(struct rrset_cache* r, struct regional* scratch, - struct rrset_ref* ref, size_t count) -{ - hashvalue_type* h; - size_t i; - if(count > RR_COUNT_MAX || !(h = (hashvalue_type*)regional_alloc( - scratch, sizeof(hashvalue_type)*count))) { - log_warn("rrset LRU: memory allocation failed"); - h = NULL; - } else /* store hash values */ - for(i=0; i<count; i++) - h[i] = ref[i].key->entry.hash; - /* unlock */ - for(i=0; i<count; i++) { - if(i>0 && ref[i].key == ref[i-1].key) - continue; /* only unlock items once */ - lock_rw_unlock(&ref[i].key->entry.lock); - } - if(h) { - /* LRU touch, with no rrset locks held */ - for(i=0; i<count; i++) { - if(i>0 && ref[i].key == ref[i-1].key) - continue; /* only touch items once */ - rrset_cache_touch(r, ref[i].key, h[i], ref[i].id); - } - } -} - -void -rrset_update_sec_status(struct rrset_cache* r, - struct ub_packed_rrset_key* rrset, time_t now) -{ - struct packed_rrset_data* updata = - (struct packed_rrset_data*)rrset->entry.data; - struct lruhash_entry* e; - struct packed_rrset_data* cachedata; - - /* hash it again to make sure it has a hash */ - rrset->entry.hash = rrset_key_hash(&rrset->rk); - - e = slabhash_lookup(&r->table, rrset->entry.hash, rrset, 1); - if(!e) - return; /* not in the cache anymore */ - cachedata = (struct packed_rrset_data*)e->data; - if(!rrsetdata_equal(updata, cachedata)) { - lock_rw_unlock(&e->lock); - return; /* rrset has changed in the meantime */ - } - /* update the cached rrset */ - if(updata->security > cachedata->security) { - size_t i; - if(updata->trust > cachedata->trust) - cachedata->trust = updata->trust; - cachedata->security = updata->security; - /* for NS records only shorter TTLs, other types: update it */ - if(ntohs(rrset->rk.type) != LDNS_RR_TYPE_NS || - updata->ttl+now < cachedata->ttl || - cachedata->ttl < now || - updata->security == sec_status_bogus) { - cachedata->ttl = updata->ttl + now; - for(i=0; i<cachedata->count+cachedata->rrsig_count; i++) - cachedata->rr_ttl[i] = updata->rr_ttl[i]+now; - } - } - lock_rw_unlock(&e->lock); -} - -void -rrset_check_sec_status(struct rrset_cache* r, - struct ub_packed_rrset_key* rrset, time_t now) -{ - struct packed_rrset_data* updata = - (struct packed_rrset_data*)rrset->entry.data; - struct lruhash_entry* e; - struct packed_rrset_data* cachedata; - - /* hash it again to make sure it has a hash */ - rrset->entry.hash = rrset_key_hash(&rrset->rk); - - e = slabhash_lookup(&r->table, rrset->entry.hash, rrset, 0); - if(!e) - return; /* not in the cache anymore */ - cachedata = (struct packed_rrset_data*)e->data; - if(now > cachedata->ttl || !rrsetdata_equal(updata, cachedata)) { - lock_rw_unlock(&e->lock); - return; /* expired, or rrset has changed in the meantime */ - } - if(cachedata->security > updata->security) { - updata->security = cachedata->security; - if(cachedata->security == sec_status_bogus) { - size_t i; - updata->ttl = cachedata->ttl - now; - for(i=0; i<cachedata->count+cachedata->rrsig_count; i++) - if(cachedata->rr_ttl[i] < now) - updata->rr_ttl[i] = 0; - else updata->rr_ttl[i] = - cachedata->rr_ttl[i]-now; - } - if(cachedata->trust > updata->trust) - updata->trust = cachedata->trust; - } - lock_rw_unlock(&e->lock); -} - -void rrset_cache_remove(struct rrset_cache* r, uint8_t* nm, size_t nmlen, - uint16_t type, uint16_t dclass, uint32_t flags) -{ - struct ub_packed_rrset_key key; - key.entry.key = &key; - key.rk.dname = nm; - key.rk.dname_len = nmlen; - key.rk.rrset_class = htons(dclass); - key.rk.type = htons(type); - key.rk.flags = flags; - key.entry.hash = rrset_key_hash(&key.rk); - slabhash_remove(&r->table, key.entry.hash, &key); -} diff --git a/external/unbound/services/cache/rrset.h b/external/unbound/services/cache/rrset.h deleted file mode 100644 index d5439ef08..000000000 --- a/external/unbound/services/cache/rrset.h +++ /dev/null @@ -1,231 +0,0 @@ -/* - * services/cache/rrset.h - Resource record set cache. - * - * Copyright (c) 2007, NLnet Labs. All rights reserved. - * - * This software is open source. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of the NLNET LABS nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * \file - * - * This file contains the rrset cache. - */ - -#ifndef SERVICES_CACHE_RRSET_H -#define SERVICES_CACHE_RRSET_H -#include "util/storage/lruhash.h" -#include "util/storage/slabhash.h" -#include "util/data/packed_rrset.h" -struct config_file; -struct alloc_cache; -struct rrset_ref; -struct regional; - -/** - * The rrset cache - * Thin wrapper around hashtable, like a typedef. - */ -struct rrset_cache { - /** uses partitioned hash table */ - struct slabhash table; -}; - -/** - * Create rrset cache - * @param cfg: config settings or NULL for defaults. - * @param alloc: initial default rrset key allocation. - * @return: NULL on error. - */ -struct rrset_cache* rrset_cache_create(struct config_file* cfg, - struct alloc_cache* alloc); - -/** - * Delete rrset cache - * @param r: rrset cache to delete. - */ -void rrset_cache_delete(struct rrset_cache* r); - -/** - * Adjust settings of the cache to settings from the config file. - * May purge the cache. May recreate the cache. - * There may be no threading or use by other threads. - * @param r: rrset cache to adjust (like realloc). - * @param cfg: config settings or NULL for defaults. - * @param alloc: initial default rrset key allocation. - * @return 0 on error, or new rrset cache pointer on success. - */ -struct rrset_cache* rrset_cache_adjust(struct rrset_cache* r, - struct config_file* cfg, struct alloc_cache* alloc); - -/** - * Touch rrset, with given pointer and id. - * Caller may not hold a lock on ANY rrset, this could give deadlock. - * - * This routine is faster than a hashtable lookup: - * o no bin_lock is acquired. - * o no walk through the bin-overflow-list. - * o no comparison of the entry key to find it. - * - * @param r: rrset cache. - * @param key: rrset key. Marked recently used (if it was not deleted - * before the lock is acquired, in that case nothing happens). - * @param hash: hash value of the item. Please read it from the key when - * you have it locked. Used to find slab from slabhash. - * @param id: used to check that the item is unchanged and not deleted. - */ -void rrset_cache_touch(struct rrset_cache* r, struct ub_packed_rrset_key* key, - hashvalue_type hash, rrset_id_type id); - -/** - * Update an rrset in the rrset cache. Stores the information for later use. - * Will lookup if the rrset is in the cache and perform an update if necessary. - * If the item was present, and superior, references are returned to that. - * The passed item is then deallocated with rrset_parsedelete. - * - * A superior rrset is: - * o rrset with better trust value. - * o same trust value, different rdata, newly passed rrset is inserted. - * If rdata is the same, TTL in the cache is updated. - * - * @param r: the rrset cache. - * @param ref: reference (ptr and id) to the rrset. Pass reference setup for - * the new rrset. The reference may be changed if the cached rrset is - * superior. - * Before calling the rrset is presumed newly allocated and changeable. - * Afer calling you do not hold a lock, and the rrset is inserted in - * the hashtable so you need a lock to change it. - * @param alloc: how to allocate (and deallocate) the special rrset key. - * @param timenow: current time (to see if ttl in cache is expired). - * @return: true if the passed reference is updated, false if it is unchanged. - * 0: reference unchanged, inserted in cache. - * 1: reference updated, item is inserted in cache. - * 2: reference updated, item in cache is considered superior. - * also the rdata is equal (but other parameters in cache are superior). - */ -int rrset_cache_update(struct rrset_cache* r, struct rrset_ref* ref, - struct alloc_cache* alloc, time_t timenow); - -/** - * Lookup rrset. You obtain read/write lock. You must unlock before lookup - * anything of else. - * @param r: the rrset cache. - * @param qname: name of rrset to lookup. - * @param qnamelen: length of name of rrset to lookup. - * @param qtype: type of rrset to lookup (host order). - * @param qclass: class of rrset to lookup (host order). - * @param flags: rrset flags, or 0. - * @param timenow: used to compare with TTL. - * @param wr: set true to get writelock. - * @return packed rrset key pointer. Remember to unlock the key.entry.lock. - * or NULL if could not be found or it was timed out. - */ -struct ub_packed_rrset_key* rrset_cache_lookup(struct rrset_cache* r, - uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, - uint32_t flags, time_t timenow, int wr); - -/** - * Obtain readlock on a (sorted) list of rrset references. - * Checks TTLs and IDs of the rrsets and rollbacks locking if not Ok. - * @param ref: array of rrset references (key pointer and ID value). - * duplicate references are allowed and handled. - * @param count: size of array. - * @param timenow: used to compare with TTL. - * @return true on success, false on a failure, which can be that some - * RRsets have timed out, or that they do not exist any more, the - * RRsets have been purged from the cache. - * If true, you hold readlocks on all the ref items. - */ -int rrset_array_lock(struct rrset_ref* ref, size_t count, time_t timenow); - -/** - * Unlock array (sorted) of rrset references. - * @param ref: array of rrset references (key pointer and ID value). - * duplicate references are allowed and handled. - * @param count: size of array. - */ -void rrset_array_unlock(struct rrset_ref* ref, size_t count); - -/** - * Unlock array (sorted) of rrset references and at the same time - * touch LRU on the rrsets. It needs the scratch region for temporary - * storage as it uses the initial locks to obtain hash values. - * @param r: the rrset cache. In this cache LRU is updated. - * @param scratch: region for temporary storage of hash values. - * if memory allocation fails, the lru touch fails silently, - * but locks are released. memory errors are logged. - * @param ref: array of rrset references (key pointer and ID value). - * duplicate references are allowed and handled. - * @param count: size of array. - */ -void rrset_array_unlock_touch(struct rrset_cache* r, struct regional* scratch, - struct rrset_ref* ref, size_t count); - -/** - * Update security status of an rrset. Looks up the rrset. - * If found, checks if rdata is equal. - * If so, it will update the security, trust and rrset-ttl values. - * The values are only updated if security is increased (towards secure). - * @param r: the rrset cache. - * @param rrset: which rrset to attempt to update. This rrset is left - * untouched. The rrset in the cache is updated in-place. - * @param now: current time. - */ -void rrset_update_sec_status(struct rrset_cache* r, - struct ub_packed_rrset_key* rrset, time_t now); - -/** - * Looks up security status of an rrset. Looks up the rrset. - * If found, checks if rdata is equal, and entry did not expire. - * If so, it will update the security, trust and rrset-ttl values. - * @param r: the rrset cache. - * @param rrset: This rrset may change security status due to the cache. - * But its status will only improve, towards secure. - * @param now: current time. - */ -void rrset_check_sec_status(struct rrset_cache* r, - struct ub_packed_rrset_key* rrset, time_t now); - -/** - * Remove an rrset from the cache, by name and type and flags - * @param r: rrset cache - * @param nm: name of rrset - * @param nmlen: length of name - * @param type: type of rrset - * @param dclass: class of rrset, host order - * @param flags: flags of rrset, host order - */ -void rrset_cache_remove(struct rrset_cache* r, uint8_t* nm, size_t nmlen, - uint16_t type, uint16_t dclass, uint32_t flags); - -/** mark rrset to be deleted, set id=0 */ -void rrset_markdel(void* key); - -#endif /* SERVICES_CACHE_RRSET_H */ |