update libunbound

This commit is contained in:
Riccardo Spagni
2015-05-31 16:36:48 +02:00
parent ce974949e2
commit 6a1190792b
59 changed files with 4449 additions and 2465 deletions

View File

@@ -389,6 +389,18 @@ dns_msg_authadd(struct dns_msg* msg, struct regional* region,
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,
@@ -635,6 +647,58 @@ synth_dname_msg(struct ub_packed_rrset_key* rrset, struct regional* region,
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, 0};
int i, num=5; /* 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,
@@ -747,6 +811,11 @@ dns_cache_lookup(struct module_env* env,
}
}
/* 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;
}

View File

@@ -40,6 +40,7 @@
*/
#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"
@@ -57,6 +58,9 @@
* 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;
size_t
infra_sizefunc(void* k, void* ATTR_UNUSED(d))
{
@@ -99,6 +103,114 @@ infra_deldatafunc(void* d, void* ATTR_UNUSED(arg))
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)
{
@@ -114,15 +226,44 @@ infra_create(struct config_file* cfg)
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);
}
return infra;
}
/** delete domain_limit entries */
static void domain_limit_free(rbnode_t* 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);
free(infra);
}
@@ -562,8 +703,178 @@ infra_get_lame_rtt(struct infra_cache* infra,
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;
}
/** 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_t 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);
}
/** 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_t 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);
}
/** 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)
{
return sizeof(*infra) + slabhash_get_mem(infra->hosts);
size_t s = sizeof(*infra) + slabhash_get_mem(infra->hosts);
if(infra->domain_rates) s += slabhash_get_mem(infra->domain_rates);
/* ignore domain_limits because walk through tree is big */
return s;
}

View File

@@ -42,6 +42,7 @@
#ifndef SERVICES_CACHE_INFRA_H
#define SERVICES_CACHE_INFRA_H
#include "util/storage/lruhash.h"
#include "util/storage/dnstree.h"
#include "util/rtt.h"
struct slabhash;
struct config_file;
@@ -108,6 +109,55 @@ struct infra_cache {
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_t domain_limits;
};
/** 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;
};
/** 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];
};
/** infra host cache default hash lookup size */
@@ -286,6 +336,51 @@ long long infra_get_host_rto(struct infra_cache* infra,
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);
/**
* Get memory used by the infra cache.
* @param infra: infrastructure cache.
@@ -306,4 +401,16 @@ 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);
#endif /* SERVICES_CACHE_INFRA_H */