1238106Sdes/*
2238106Sdes * services/cache/infra.c - infrastructure cache, server rtt and capabilities
3238106Sdes *
4238106Sdes * Copyright (c) 2007, NLnet Labs. All rights reserved.
5238106Sdes *
6238106Sdes * This software is open source.
7238106Sdes *
8238106Sdes * Redistribution and use in source and binary forms, with or without
9238106Sdes * modification, are permitted provided that the following conditions
10238106Sdes * are met:
11238106Sdes *
12238106Sdes * Redistributions of source code must retain the above copyright notice,
13238106Sdes * this list of conditions and the following disclaimer.
14238106Sdes *
15238106Sdes * Redistributions in binary form must reproduce the above copyright notice,
16238106Sdes * this list of conditions and the following disclaimer in the documentation
17238106Sdes * and/or other materials provided with the distribution.
18238106Sdes *
19238106Sdes * Neither the name of the NLNET LABS nor the names of its contributors may
20238106Sdes * be used to endorse or promote products derived from this software without
21238106Sdes * specific prior written permission.
22238106Sdes *
23238106Sdes * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24238106Sdes * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25238106Sdes * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26238106Sdes * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
27238106Sdes * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28238106Sdes * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29238106Sdes * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30238106Sdes * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31238106Sdes * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32238106Sdes * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33238106Sdes * POSSIBILITY OF SUCH DAMAGE.
34238106Sdes */
35238106Sdes
36238106Sdes/**
37238106Sdes * \file
38238106Sdes *
39238106Sdes * This file contains the infrastructure cache.
40238106Sdes */
41238106Sdes#include "config.h"
42238106Sdes#include <ldns/rr.h>
43238106Sdes#include "services/cache/infra.h"
44238106Sdes#include "util/storage/slabhash.h"
45238106Sdes#include "util/storage/lookup3.h"
46238106Sdes#include "util/data/dname.h"
47238106Sdes#include "util/log.h"
48238106Sdes#include "util/net_help.h"
49238106Sdes#include "util/config_file.h"
50238106Sdes#include "iterator/iterator.h"
51238106Sdes
52238106Sdes/** Timeout when only a single probe query per IP is allowed. */
53238106Sdes#define PROBE_MAXRTO 12000 /* in msec */
54238106Sdes
55238106Sdes/** number of timeouts for a type when the domain can be blocked ;
56238106Sdes * even if another type has completely rtt maxed it, the different type
57238106Sdes * can do this number of packets (until those all timeout too) */
58238106Sdes#define TIMEOUT_COUNT_MAX 3
59238106Sdes
60238106Sdessize_t
61238106Sdesinfra_sizefunc(void* k, void* ATTR_UNUSED(d))
62238106Sdes{
63238106Sdes	struct infra_key* key = (struct infra_key*)k;
64238106Sdes	return sizeof(*key) + sizeof(struct infra_data) + key->namelen
65238106Sdes		+ lock_get_mem(&key->entry.lock);
66238106Sdes}
67238106Sdes
68238106Sdesint
69238106Sdesinfra_compfunc(void* key1, void* key2)
70238106Sdes{
71238106Sdes	struct infra_key* k1 = (struct infra_key*)key1;
72238106Sdes	struct infra_key* k2 = (struct infra_key*)key2;
73238106Sdes	int r = sockaddr_cmp(&k1->addr, k1->addrlen, &k2->addr, k2->addrlen);
74238106Sdes	if(r != 0)
75238106Sdes		return r;
76238106Sdes	if(k1->namelen != k2->namelen) {
77238106Sdes		if(k1->namelen < k2->namelen)
78238106Sdes			return -1;
79238106Sdes		return 1;
80238106Sdes	}
81238106Sdes	return query_dname_compare(k1->zonename, k2->zonename);
82238106Sdes}
83238106Sdes
84238106Sdesvoid
85238106Sdesinfra_delkeyfunc(void* k, void* ATTR_UNUSED(arg))
86238106Sdes{
87238106Sdes	struct infra_key* key = (struct infra_key*)k;
88238106Sdes	if(!key)
89238106Sdes		return;
90238106Sdes	lock_rw_destroy(&key->entry.lock);
91238106Sdes	free(key->zonename);
92238106Sdes	free(key);
93238106Sdes}
94238106Sdes
95238106Sdesvoid
96238106Sdesinfra_deldatafunc(void* d, void* ATTR_UNUSED(arg))
97238106Sdes{
98238106Sdes	struct infra_data* data = (struct infra_data*)d;
99238106Sdes	free(data);
100238106Sdes}
101238106Sdes
102238106Sdesstruct infra_cache*
103238106Sdesinfra_create(struct config_file* cfg)
104238106Sdes{
105238106Sdes	struct infra_cache* infra = (struct infra_cache*)calloc(1,
106238106Sdes		sizeof(struct infra_cache));
107238106Sdes	size_t maxmem = cfg->infra_cache_numhosts * (sizeof(struct infra_key)+
108238106Sdes		sizeof(struct infra_data)+INFRA_BYTES_NAME);
109238106Sdes	infra->hosts = slabhash_create(cfg->infra_cache_slabs,
110238106Sdes		INFRA_HOST_STARTSIZE, maxmem, &infra_sizefunc, &infra_compfunc,
111238106Sdes		&infra_delkeyfunc, &infra_deldatafunc, NULL);
112238106Sdes	if(!infra->hosts) {
113238106Sdes		free(infra);
114238106Sdes		return NULL;
115238106Sdes	}
116238106Sdes	infra->host_ttl = cfg->host_ttl;
117238106Sdes	return infra;
118238106Sdes}
119238106Sdes
120238106Sdesvoid
121238106Sdesinfra_delete(struct infra_cache* infra)
122238106Sdes{
123238106Sdes	if(!infra)
124238106Sdes		return;
125238106Sdes	slabhash_delete(infra->hosts);
126238106Sdes	free(infra);
127238106Sdes}
128238106Sdes
129238106Sdesstruct infra_cache*
130238106Sdesinfra_adjust(struct infra_cache* infra, struct config_file* cfg)
131238106Sdes{
132238106Sdes	size_t maxmem;
133238106Sdes	if(!infra)
134238106Sdes		return infra_create(cfg);
135238106Sdes	infra->host_ttl = cfg->host_ttl;
136238106Sdes	maxmem = cfg->infra_cache_numhosts * (sizeof(struct infra_key)+
137238106Sdes		sizeof(struct infra_data)+INFRA_BYTES_NAME);
138238106Sdes	if(maxmem != slabhash_get_size(infra->hosts) ||
139238106Sdes		cfg->infra_cache_slabs != infra->hosts->size) {
140238106Sdes		infra_delete(infra);
141238106Sdes		infra = infra_create(cfg);
142238106Sdes	}
143238106Sdes	return infra;
144238106Sdes}
145238106Sdes
146238106Sdes/** calculate the hash value for a host key */
147238106Sdesstatic hashvalue_t
148238106Sdeshash_addr(struct sockaddr_storage* addr, socklen_t addrlen)
149238106Sdes{
150238106Sdes	hashvalue_t h = 0xab;
151238106Sdes	/* select the pieces to hash, some OS have changing data inside */
152238106Sdes	if(addr_is_ip6(addr, addrlen)) {
153238106Sdes		struct sockaddr_in6* in6 = (struct sockaddr_in6*)addr;
154238106Sdes		h = hashlittle(&in6->sin6_family, sizeof(in6->sin6_family), h);
155238106Sdes		h = hashlittle(&in6->sin6_port, sizeof(in6->sin6_port), h);
156238106Sdes		h = hashlittle(&in6->sin6_addr, INET6_SIZE, h);
157238106Sdes	} else {
158238106Sdes		struct sockaddr_in* in = (struct sockaddr_in*)addr;
159238106Sdes		h = hashlittle(&in->sin_family, sizeof(in->sin_family), h);
160238106Sdes		h = hashlittle(&in->sin_port, sizeof(in->sin_port), h);
161238106Sdes		h = hashlittle(&in->sin_addr, INET_SIZE, h);
162238106Sdes	}
163238106Sdes	return h;
164238106Sdes}
165238106Sdes
166238106Sdes/** calculate infra hash for a key */
167238106Sdesstatic hashvalue_t
168238106Sdeshash_infra(struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* name)
169238106Sdes{
170238106Sdes	return dname_query_hash(name, hash_addr(addr, addrlen));
171238106Sdes}
172238106Sdes
173238106Sdes/** lookup version that does not check host ttl (you check it) */
174238106Sdesstruct lruhash_entry*
175238106Sdesinfra_lookup_nottl(struct infra_cache* infra, struct sockaddr_storage* addr,
176238106Sdes	socklen_t addrlen, uint8_t* name, size_t namelen, int wr)
177238106Sdes{
178238106Sdes	struct infra_key k;
179238106Sdes	k.addrlen = addrlen;
180238106Sdes	memcpy(&k.addr, addr, addrlen);
181238106Sdes	k.namelen = namelen;
182238106Sdes	k.zonename = name;
183238106Sdes	k.entry.hash = hash_infra(addr, addrlen, name);
184238106Sdes	k.entry.key = (void*)&k;
185238106Sdes	k.entry.data = NULL;
186238106Sdes	return slabhash_lookup(infra->hosts, k.entry.hash, &k, wr);
187238106Sdes}
188238106Sdes
189238106Sdes/** init the data elements */
190238106Sdesstatic void
191238106Sdesdata_entry_init(struct infra_cache* infra, struct lruhash_entry* e,
192238106Sdes	uint32_t timenow)
193238106Sdes{
194238106Sdes	struct infra_data* data = (struct infra_data*)e->data;
195238106Sdes	data->ttl = timenow + infra->host_ttl;
196238106Sdes	rtt_init(&data->rtt);
197238106Sdes	data->edns_version = 0;
198238106Sdes	data->edns_lame_known = 0;
199238106Sdes	data->probedelay = 0;
200238106Sdes	data->isdnsseclame = 0;
201238106Sdes	data->rec_lame = 0;
202238106Sdes	data->lame_type_A = 0;
203238106Sdes	data->lame_other = 0;
204238106Sdes	data->timeout_A = 0;
205238106Sdes	data->timeout_AAAA = 0;
206238106Sdes	data->timeout_other = 0;
207238106Sdes}
208238106Sdes
209238106Sdes/**
210238106Sdes * Create and init a new entry for a host
211238106Sdes * @param infra: infra structure with config parameters.
212238106Sdes * @param addr: host address.
213238106Sdes * @param addrlen: length of addr.
214238106Sdes * @param name: name of zone
215238106Sdes * @param namelen: length of name.
216238106Sdes * @param tm: time now.
217238106Sdes * @return: the new entry or NULL on malloc failure.
218238106Sdes */
219238106Sdesstatic struct lruhash_entry*
220238106Sdesnew_entry(struct infra_cache* infra, struct sockaddr_storage* addr,
221238106Sdes	socklen_t addrlen, uint8_t* name, size_t namelen, uint32_t tm)
222238106Sdes{
223238106Sdes	struct infra_data* data;
224238106Sdes	struct infra_key* key = (struct infra_key*)malloc(sizeof(*key));
225238106Sdes	if(!key)
226238106Sdes		return NULL;
227238106Sdes	data = (struct infra_data*)malloc(sizeof(struct infra_data));
228238106Sdes	if(!data) {
229238106Sdes		free(key);
230238106Sdes		return NULL;
231238106Sdes	}
232238106Sdes	key->zonename = memdup(name, namelen);
233238106Sdes	if(!key->zonename) {
234238106Sdes		free(key);
235238106Sdes		free(data);
236238106Sdes		return NULL;
237238106Sdes	}
238238106Sdes	key->namelen = namelen;
239238106Sdes	lock_rw_init(&key->entry.lock);
240238106Sdes	key->entry.hash = hash_infra(addr, addrlen, name);
241238106Sdes	key->entry.key = (void*)key;
242238106Sdes	key->entry.data = (void*)data;
243238106Sdes	key->addrlen = addrlen;
244238106Sdes	memcpy(&key->addr, addr, addrlen);
245238106Sdes	data_entry_init(infra, &key->entry, tm);
246238106Sdes	return &key->entry;
247238106Sdes}
248238106Sdes
249238106Sdesint
250238106Sdesinfra_host(struct infra_cache* infra, struct sockaddr_storage* addr,
251238106Sdes        socklen_t addrlen, uint8_t* nm, size_t nmlen, uint32_t timenow,
252238106Sdes	int* edns_vs, uint8_t* edns_lame_known, int* to)
253238106Sdes{
254238106Sdes	struct lruhash_entry* e = infra_lookup_nottl(infra, addr, addrlen,
255238106Sdes		nm, nmlen, 0);
256238106Sdes	struct infra_data* data;
257238106Sdes	int wr = 0;
258238106Sdes	if(e && ((struct infra_data*)e->data)->ttl < timenow) {
259238106Sdes		/* it expired, try to reuse existing entry */
260238106Sdes		int old = ((struct infra_data*)e->data)->rtt.rto;
261238106Sdes		uint8_t tA = ((struct infra_data*)e->data)->timeout_A;
262238106Sdes		uint8_t tAAAA = ((struct infra_data*)e->data)->timeout_AAAA;
263238106Sdes		uint8_t tother = ((struct infra_data*)e->data)->timeout_other;
264238106Sdes		lock_rw_unlock(&e->lock);
265238106Sdes		e = infra_lookup_nottl(infra, addr, addrlen, nm, nmlen, 1);
266238106Sdes		if(e) {
267238106Sdes			/* if its still there we have a writelock, init */
268238106Sdes			/* re-initialise */
269238106Sdes			/* do not touch lameness, it may be valid still */
270238106Sdes			data_entry_init(infra, e, timenow);
271238106Sdes			wr = 1;
272238106Sdes			/* TOP_TIMEOUT remains on reuse */
273238106Sdes			if(old >= USEFUL_SERVER_TOP_TIMEOUT) {
274238106Sdes				((struct infra_data*)e->data)->rtt.rto
275238106Sdes					= USEFUL_SERVER_TOP_TIMEOUT;
276238106Sdes				((struct infra_data*)e->data)->timeout_A = tA;
277238106Sdes				((struct infra_data*)e->data)->timeout_AAAA = tAAAA;
278238106Sdes				((struct infra_data*)e->data)->timeout_other = tother;
279238106Sdes			}
280238106Sdes		}
281238106Sdes	}
282238106Sdes	if(!e) {
283238106Sdes		/* insert new entry */
284238106Sdes		if(!(e = new_entry(infra, addr, addrlen, nm, nmlen, timenow)))
285238106Sdes			return 0;
286238106Sdes		data = (struct infra_data*)e->data;
287238106Sdes		*edns_vs = data->edns_version;
288238106Sdes		*edns_lame_known = data->edns_lame_known;
289238106Sdes		*to = rtt_timeout(&data->rtt);
290238106Sdes		slabhash_insert(infra->hosts, e->hash, e, data, NULL);
291238106Sdes		return 1;
292238106Sdes	}
293238106Sdes	/* use existing entry */
294238106Sdes	data = (struct infra_data*)e->data;
295238106Sdes	*edns_vs = data->edns_version;
296238106Sdes	*edns_lame_known = data->edns_lame_known;
297238106Sdes	*to = rtt_timeout(&data->rtt);
298238106Sdes	if(*to >= PROBE_MAXRTO && rtt_notimeout(&data->rtt)*4 <= *to) {
299238106Sdes		/* delay other queries, this is the probe query */
300238106Sdes		if(!wr) {
301238106Sdes			lock_rw_unlock(&e->lock);
302238106Sdes			e = infra_lookup_nottl(infra, addr,addrlen,nm,nmlen, 1);
303238106Sdes			if(!e) { /* flushed from cache real fast, no use to
304238106Sdes				allocate just for the probedelay */
305238106Sdes				return 1;
306238106Sdes			}
307238106Sdes			data = (struct infra_data*)e->data;
308238106Sdes		}
309238106Sdes		/* add 999 to round up the timeout value from msec to sec,
310238106Sdes		 * then add a whole second so it is certain that this probe
311238106Sdes		 * has timed out before the next is allowed */
312238106Sdes		data->probedelay = timenow + ((*to)+1999)/1000;
313238106Sdes	}
314238106Sdes	lock_rw_unlock(&e->lock);
315238106Sdes	return 1;
316238106Sdes}
317238106Sdes
318238106Sdesint
319238106Sdesinfra_set_lame(struct infra_cache* infra, struct sockaddr_storage* addr,
320238106Sdes	socklen_t addrlen, uint8_t* nm, size_t nmlen, uint32_t timenow,
321238106Sdes	int dnsseclame, int reclame, uint16_t qtype)
322238106Sdes{
323238106Sdes	struct infra_data* data;
324238106Sdes	struct lruhash_entry* e;
325238106Sdes	int needtoinsert = 0;
326238106Sdes	e = infra_lookup_nottl(infra, addr, addrlen, nm, nmlen, 1);
327238106Sdes	if(!e) {
328238106Sdes		/* insert it */
329238106Sdes		if(!(e = new_entry(infra, addr, addrlen, nm, nmlen, timenow))) {
330238106Sdes			log_err("set_lame: malloc failure");
331238106Sdes			return 0;
332238106Sdes		}
333238106Sdes		needtoinsert = 1;
334238106Sdes	} else if( ((struct infra_data*)e->data)->ttl < timenow) {
335238106Sdes		/* expired, reuse existing entry */
336238106Sdes		data_entry_init(infra, e, timenow);
337238106Sdes	}
338238106Sdes	/* got an entry, now set the zone lame */
339238106Sdes	data = (struct infra_data*)e->data;
340238106Sdes	/* merge data (if any) */
341238106Sdes	if(dnsseclame)
342238106Sdes		data->isdnsseclame = 1;
343238106Sdes	if(reclame)
344238106Sdes		data->rec_lame = 1;
345238106Sdes	if(!dnsseclame && !reclame && qtype == LDNS_RR_TYPE_A)
346238106Sdes		data->lame_type_A = 1;
347238106Sdes	if(!dnsseclame  && !reclame && qtype != LDNS_RR_TYPE_A)
348238106Sdes		data->lame_other = 1;
349238106Sdes	/* done */
350238106Sdes	if(needtoinsert)
351238106Sdes		slabhash_insert(infra->hosts, e->hash, e, e->data, NULL);
352238106Sdes	else 	{ lock_rw_unlock(&e->lock); }
353238106Sdes	return 1;
354238106Sdes}
355238106Sdes
356238106Sdesvoid
357238106Sdesinfra_update_tcp_works(struct infra_cache* infra,
358238106Sdes        struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* nm,
359238106Sdes	size_t nmlen)
360238106Sdes{
361238106Sdes	struct lruhash_entry* e = infra_lookup_nottl(infra, addr, addrlen,
362238106Sdes		nm, nmlen, 1);
363238106Sdes	struct infra_data* data;
364238106Sdes	if(!e)
365238106Sdes		return; /* doesn't exist */
366238106Sdes	data = (struct infra_data*)e->data;
367238106Sdes	if(data->rtt.rto >= RTT_MAX_TIMEOUT)
368238106Sdes		/* do not disqualify this server altogether, it is better
369238106Sdes		 * than nothing */
370238106Sdes		data->rtt.rto = RTT_MAX_TIMEOUT-1000;
371238106Sdes	lock_rw_unlock(&e->lock);
372238106Sdes}
373238106Sdes
374238106Sdesint
375238106Sdesinfra_rtt_update(struct infra_cache* infra, struct sockaddr_storage* addr,
376238106Sdes	socklen_t addrlen, uint8_t* nm, size_t nmlen, int qtype,
377238106Sdes	int roundtrip, int orig_rtt, uint32_t timenow)
378238106Sdes{
379238106Sdes	struct lruhash_entry* e = infra_lookup_nottl(infra, addr, addrlen,
380238106Sdes		nm, nmlen, 1);
381238106Sdes	struct infra_data* data;
382238106Sdes	int needtoinsert = 0;
383238106Sdes	int rto = 1;
384238106Sdes	if(!e) {
385238106Sdes		if(!(e = new_entry(infra, addr, addrlen, nm, nmlen, timenow)))
386238106Sdes			return 0;
387238106Sdes		needtoinsert = 1;
388238106Sdes	} else if(((struct infra_data*)e->data)->ttl < timenow) {
389238106Sdes		data_entry_init(infra, e, timenow);
390238106Sdes	}
391238106Sdes	/* have an entry, update the rtt */
392238106Sdes	data = (struct infra_data*)e->data;
393238106Sdes	if(roundtrip == -1) {
394238106Sdes		rtt_lost(&data->rtt, orig_rtt);
395238106Sdes		if(qtype == LDNS_RR_TYPE_A) {
396238106Sdes			if(data->timeout_A < TIMEOUT_COUNT_MAX)
397238106Sdes				data->timeout_A++;
398238106Sdes		} else if(qtype == LDNS_RR_TYPE_AAAA) {
399238106Sdes			if(data->timeout_AAAA < TIMEOUT_COUNT_MAX)
400238106Sdes				data->timeout_AAAA++;
401238106Sdes		} else {
402238106Sdes			if(data->timeout_other < TIMEOUT_COUNT_MAX)
403238106Sdes				data->timeout_other++;
404238106Sdes		}
405238106Sdes	} else {
406249141Sdes		/* if we got a reply, but the old timeout was above server
407249141Sdes		 * selection height, delete the timeout so the server is
408249141Sdes		 * fully available again */
409249141Sdes		if(rtt_unclamped(&data->rtt) >= USEFUL_SERVER_TOP_TIMEOUT)
410249141Sdes			rtt_init(&data->rtt);
411238106Sdes		rtt_update(&data->rtt, roundtrip);
412238106Sdes		data->probedelay = 0;
413238106Sdes		if(qtype == LDNS_RR_TYPE_A)
414238106Sdes			data->timeout_A = 0;
415238106Sdes		else if(qtype == LDNS_RR_TYPE_AAAA)
416238106Sdes			data->timeout_AAAA = 0;
417238106Sdes		else	data->timeout_other = 0;
418238106Sdes	}
419238106Sdes	if(data->rtt.rto > 0)
420238106Sdes		rto = data->rtt.rto;
421238106Sdes
422238106Sdes	if(needtoinsert)
423238106Sdes		slabhash_insert(infra->hosts, e->hash, e, e->data, NULL);
424238106Sdes	else 	{ lock_rw_unlock(&e->lock); }
425238106Sdes	return rto;
426238106Sdes}
427238106Sdes
428238106Sdesint infra_get_host_rto(struct infra_cache* infra,
429238106Sdes        struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* nm,
430238106Sdes	size_t nmlen, struct rtt_info* rtt, int* delay, uint32_t timenow,
431238106Sdes	int* tA, int* tAAAA, int* tother)
432238106Sdes{
433238106Sdes	struct lruhash_entry* e = infra_lookup_nottl(infra, addr, addrlen,
434238106Sdes		nm, nmlen, 0);
435238106Sdes	struct infra_data* data;
436238106Sdes	int ttl = -2;
437238106Sdes	if(!e) return -1;
438238106Sdes	data = (struct infra_data*)e->data;
439238106Sdes	if(data->ttl >= timenow) {
440238106Sdes		ttl = (int)(data->ttl - timenow);
441238106Sdes		memmove(rtt, &data->rtt, sizeof(*rtt));
442238106Sdes		if(timenow < data->probedelay)
443238106Sdes			*delay = (int)(data->probedelay - timenow);
444238106Sdes		else	*delay = 0;
445238106Sdes	}
446238106Sdes	*tA = (int)data->timeout_A;
447238106Sdes	*tAAAA = (int)data->timeout_AAAA;
448238106Sdes	*tother = (int)data->timeout_other;
449238106Sdes	lock_rw_unlock(&e->lock);
450238106Sdes	return ttl;
451238106Sdes}
452238106Sdes
453238106Sdesint
454238106Sdesinfra_edns_update(struct infra_cache* infra, struct sockaddr_storage* addr,
455238106Sdes	socklen_t addrlen, uint8_t* nm, size_t nmlen, int edns_version,
456238106Sdes	uint32_t timenow)
457238106Sdes{
458238106Sdes	struct lruhash_entry* e = infra_lookup_nottl(infra, addr, addrlen,
459238106Sdes		nm, nmlen, 1);
460238106Sdes	struct infra_data* data;
461238106Sdes	int needtoinsert = 0;
462238106Sdes	if(!e) {
463238106Sdes		if(!(e = new_entry(infra, addr, addrlen, nm, nmlen, timenow)))
464238106Sdes			return 0;
465238106Sdes		needtoinsert = 1;
466238106Sdes	} else if(((struct infra_data*)e->data)->ttl < timenow) {
467238106Sdes		data_entry_init(infra, e, timenow);
468238106Sdes	}
469238106Sdes	/* have an entry, update the rtt, and the ttl */
470238106Sdes	data = (struct infra_data*)e->data;
471238106Sdes	/* do not update if noEDNS and stored is yesEDNS */
472238106Sdes	if(!(edns_version == -1 && (data->edns_version != -1 &&
473238106Sdes		data->edns_lame_known))) {
474238106Sdes		data->edns_version = edns_version;
475238106Sdes		data->edns_lame_known = 1;
476238106Sdes	}
477238106Sdes
478238106Sdes	if(needtoinsert)
479238106Sdes		slabhash_insert(infra->hosts, e->hash, e, e->data, NULL);
480238106Sdes	else 	{ lock_rw_unlock(&e->lock); }
481238106Sdes	return 1;
482238106Sdes}
483238106Sdes
484238106Sdesint
485238106Sdesinfra_get_lame_rtt(struct infra_cache* infra,
486238106Sdes        struct sockaddr_storage* addr, socklen_t addrlen,
487238106Sdes        uint8_t* name, size_t namelen, uint16_t qtype,
488238106Sdes	int* lame, int* dnsseclame, int* reclame, int* rtt, uint32_t timenow)
489238106Sdes{
490238106Sdes	struct infra_data* host;
491238106Sdes	struct lruhash_entry* e = infra_lookup_nottl(infra, addr, addrlen,
492238106Sdes		name, namelen, 0);
493238106Sdes	if(!e)
494238106Sdes		return 0;
495238106Sdes	host = (struct infra_data*)e->data;
496238106Sdes	*rtt = rtt_unclamped(&host->rtt);
497238106Sdes	if(host->rtt.rto >= PROBE_MAXRTO && timenow < host->probedelay
498238106Sdes		&& rtt_notimeout(&host->rtt)*4 <= host->rtt.rto) {
499238106Sdes		/* single probe for this domain, and we are not probing */
500238106Sdes		/* unless the query type allows a probe to happen */
501238106Sdes		if(qtype == LDNS_RR_TYPE_A) {
502238106Sdes			if(host->timeout_A >= TIMEOUT_COUNT_MAX)
503238106Sdes				*rtt = USEFUL_SERVER_TOP_TIMEOUT;
504238106Sdes			else	*rtt = USEFUL_SERVER_TOP_TIMEOUT-1000;
505238106Sdes		} else if(qtype == LDNS_RR_TYPE_AAAA) {
506238106Sdes			if(host->timeout_AAAA >= TIMEOUT_COUNT_MAX)
507238106Sdes				*rtt = USEFUL_SERVER_TOP_TIMEOUT;
508238106Sdes			else	*rtt = USEFUL_SERVER_TOP_TIMEOUT-1000;
509238106Sdes		} else {
510238106Sdes			if(host->timeout_other >= TIMEOUT_COUNT_MAX)
511238106Sdes				*rtt = USEFUL_SERVER_TOP_TIMEOUT;
512238106Sdes			else	*rtt = USEFUL_SERVER_TOP_TIMEOUT-1000;
513238106Sdes		}
514238106Sdes	}
515238106Sdes	if(timenow > host->ttl) {
516238106Sdes		/* expired entry */
517238106Sdes		/* see if this can be a re-probe of an unresponsive server */
518238106Sdes		/* minus 1000 because that is outside of the RTTBAND, so
519238106Sdes		 * blacklisted servers stay blacklisted if this is chosen */
520238106Sdes		if(host->rtt.rto >= USEFUL_SERVER_TOP_TIMEOUT) {
521238106Sdes			lock_rw_unlock(&e->lock);
522238106Sdes			*rtt = USEFUL_SERVER_TOP_TIMEOUT-1000;
523238106Sdes			*lame = 0;
524238106Sdes			*dnsseclame = 0;
525238106Sdes			*reclame = 0;
526238106Sdes			return 1;
527238106Sdes		}
528238106Sdes		lock_rw_unlock(&e->lock);
529238106Sdes		return 0;
530238106Sdes	}
531238106Sdes	/* check lameness first */
532238106Sdes	if(host->lame_type_A && qtype == LDNS_RR_TYPE_A) {
533238106Sdes		lock_rw_unlock(&e->lock);
534238106Sdes		*lame = 1;
535238106Sdes		*dnsseclame = 0;
536238106Sdes		*reclame = 0;
537238106Sdes		return 1;
538238106Sdes	} else if(host->lame_other && qtype != LDNS_RR_TYPE_A) {
539238106Sdes		lock_rw_unlock(&e->lock);
540238106Sdes		*lame = 1;
541238106Sdes		*dnsseclame = 0;
542238106Sdes		*reclame = 0;
543238106Sdes		return 1;
544238106Sdes	} else if(host->isdnsseclame) {
545238106Sdes		lock_rw_unlock(&e->lock);
546238106Sdes		*lame = 0;
547238106Sdes		*dnsseclame = 1;
548238106Sdes		*reclame = 0;
549238106Sdes		return 1;
550238106Sdes	} else if(host->rec_lame) {
551238106Sdes		lock_rw_unlock(&e->lock);
552238106Sdes		*lame = 0;
553238106Sdes		*dnsseclame = 0;
554238106Sdes		*reclame = 1;
555238106Sdes		return 1;
556238106Sdes	}
557238106Sdes	/* no lameness for this type of query */
558238106Sdes	lock_rw_unlock(&e->lock);
559238106Sdes	*lame = 0;
560238106Sdes	*dnsseclame = 0;
561238106Sdes	*reclame = 0;
562238106Sdes	return 1;
563238106Sdes}
564238106Sdes
565238106Sdessize_t
566238106Sdesinfra_get_mem(struct infra_cache* infra)
567238106Sdes{
568238106Sdes	return sizeof(*infra) + slabhash_get_mem(infra->hosts);
569238106Sdes}
570