aboutsummaryrefslogtreecommitdiff
path: root/external/unbound/services/cache/infra.c
diff options
context:
space:
mode:
Diffstat (limited to 'external/unbound/services/cache/infra.c')
-rw-r--r--external/unbound/services/cache/infra.c997
1 files changed, 0 insertions, 997 deletions
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;
-}