only one required. All the others parameters are optional and in seconds.
The second one is the maximum lifetime of an entry in the cache, the third one is
the minimum TTL an entry should have to be considered for insertion in the cache,
-the fourth one is the TTL used for a Server Failure response. The last one is the
-TTL that will be used when a stale cache entry is returned.
+the fourth one is the TTL used for a Server Failure or a Refused response. The last
+one is the TTL that will be used when a stale cache entry is returned.
The `setStaleCacheEntriesTTL(n)` directive can be used to allow `dnsdist` to use
expired entries from the cache when no backend is available. Only entries that have
* `expunge(n)`: remove entries from the cache, leaving at most `n` entries
* `expungeByName(DNSName [, qtype=ANY])`: remove entries matching the supplied DNSName and type from the cache
* `isFull()`: return true if the cache has reached the maximum number of entries
- * `newPacketCache(maxEntries[, maxTTL=86400, minTTL=0, servFailTTL=60, staleTTL=60])`: return a new PacketCache
+ * `newPacketCache(maxEntries[, maxTTL=86400, minTTL=0, temporaryFailureTTL=60, staleTTL=60])`: return a new PacketCache
* `printStats()`: print the cache stats (hits, misses, deferred lookups and deferred inserts)
* `purgeExpired(n)`: remove expired entries from the cache until there is at most `n` entries remaining in the cache
* `toString()`: return the number of entries in the Packet Cache, and the maximum number of entries
#include "dnsparser.hh"
#include "dnsdist-cache.hh"
-DNSDistPacketCache::DNSDistPacketCache(size_t maxEntries, uint32_t maxTTL, uint32_t minTTL, uint32_t servFailTTL, uint32_t staleTTL): d_maxEntries(maxEntries), d_maxTTL(maxTTL), d_servFailTTL(servFailTTL), d_minTTL(minTTL), d_staleTTL(staleTTL)
+DNSDistPacketCache::DNSDistPacketCache(size_t maxEntries, uint32_t maxTTL, uint32_t minTTL, uint32_t tempFailureTTL, uint32_t staleTTL): d_maxEntries(maxEntries), d_maxTTL(maxTTL), d_tempFailureTTL(tempFailureTTL), d_minTTL(minTTL), d_staleTTL(staleTTL)
{
pthread_rwlock_init(&d_lock, 0);
/* we reserve maxEntries + 1 to avoid rehashing from occuring
return true;
}
-void DNSDistPacketCache::insert(uint32_t key, const DNSName& qname, uint16_t qtype, uint16_t qclass, const char* response, uint16_t responseLen, bool tcp, bool servFail)
+void DNSDistPacketCache::insert(uint32_t key, const DNSName& qname, uint16_t qtype, uint16_t qclass, const char* response, uint16_t responseLen, bool tcp, uint8_t rcode)
{
if (responseLen < sizeof(dnsheader))
return;
uint32_t minTTL;
- if (servFail) {
- minTTL = d_servFailTTL;
+ if (rcode == RCode::ServFail || rcode == RCode::Refused) {
+ minTTL = d_tempFailureTTL;
}
else {
minTTL = getMinTTL(response, responseLen);
class DNSDistPacketCache : boost::noncopyable
{
public:
- DNSDistPacketCache(size_t maxEntries, uint32_t maxTTL=86400, uint32_t minTTL=0, uint32_t servFailTTL=60, uint32_t staleTTL=60);
+ DNSDistPacketCache(size_t maxEntries, uint32_t maxTTL=86400, uint32_t minTTL=0, uint32_t tempFailureTTL=60, uint32_t staleTTL=60);
~DNSDistPacketCache();
- void insert(uint32_t key, const DNSName& qname, uint16_t qtype, uint16_t qclass, const char* response, uint16_t responseLen, bool tcp, bool servFail=false);
+ void insert(uint32_t key, const DNSName& qname, uint16_t qtype, uint16_t qclass, const char* response, uint16_t responseLen, bool tcp, uint8_t rcode);
bool get(const DNSQuestion& dq, uint16_t consumed, uint16_t queryId, char* response, uint16_t* responseLen, uint32_t* keyOut, uint32_t allowExpired=0, bool skipAging=false);
void purgeExpired(size_t upTo=0);
void expunge(size_t upTo=0);
std::atomic<uint64_t> d_ttlTooShorts{0};
size_t d_maxEntries;
uint32_t d_maxTTL;
- uint32_t d_servFailTTL;
+ uint32_t d_tempFailureTTL;
uint32_t d_minTTL;
uint32_t d_staleTTL;
};
}
});
- g_lua.writeFunction("newPacketCache", [client](size_t maxEntries, boost::optional<uint32_t> maxTTL, boost::optional<uint32_t> minTTL, boost::optional<uint32_t> servFailTTL, boost::optional<uint32_t> staleTTL) {
- return std::make_shared<DNSDistPacketCache>(maxEntries, maxTTL ? *maxTTL : 86400, minTTL ? *minTTL : 0, servFailTTL ? *servFailTTL : 60, staleTTL ? *staleTTL : 60);
+ g_lua.writeFunction("newPacketCache", [client](size_t maxEntries, boost::optional<uint32_t> maxTTL, boost::optional<uint32_t> minTTL, boost::optional<uint32_t> tempFailTTL, boost::optional<uint32_t> staleTTL) {
+ return std::make_shared<DNSDistPacketCache>(maxEntries, maxTTL ? *maxTTL : 86400, minTTL ? *minTTL : 0, tempFailTTL ? *tempFailTTL : 60, staleTTL ? *staleTTL : 60);
});
g_lua.registerFunction("toString", &DNSDistPacketCache::toString);
g_lua.registerFunction("isFull", &DNSDistPacketCache::isFull);
}
if (packetCache && !dq.skipCache) {
- packetCache->insert(cacheKey, qname, qtype, qclass, response, responseLen, true, dh->rcode == RCode::ServFail);
+ packetCache->insert(cacheKey, qname, qtype, qclass, response, responseLen, true, dh->rcode);
}
#ifdef HAVE_DNSCRYPT
}
if (ids->packetCache && !ids->skipCache) {
- ids->packetCache->insert(ids->cacheKey, ids->qname, ids->qtype, ids->qclass, response, responseLen, false, dh->rcode == RCode::ServFail);
+ ids->packetCache->insert(ids->cacheKey, ids->qname, ids->qtype, ids->qclass, response, responseLen, false, dh->rcode);
}
#ifdef HAVE_DNSCRYPT
bool found = PC.get(dq, a.wirelength(), 0, responseBuf, &responseBufSize, &key);
BOOST_CHECK_EQUAL(found, false);
- PC.insert(key, a, QType::A, QClass::IN, (const char*) response.data(), responseLen, false);
+ PC.insert(key, a, QType::A, QClass::IN, (const char*) response.data(), responseLen, false, 0);
found = PC.get(dq, a.wirelength(), pwR.getHeader()->id, responseBuf, &responseBufSize, &key, 0, true);
if (found == true) {
DNSQuestion dq(&a, QType::A, QClass::IN, &remote, &remote, (struct dnsheader*) query.data(), query.size(), query.size(), false);
PC.get(dq, a.wirelength(), 0, responseBuf, &responseBufSize, &key);
- PC.insert(key, a, QType::A, QClass::IN, (const char*) response.data(), responseLen, false);
+ PC.insert(key, a, QType::A, QClass::IN, (const char*) response.data(), responseLen, false, 0);
}
}
catch(PDNSException& e) {
total += self._responsesCounter[key]
self.assertEquals(total, misses)
+
+class TestCachingFailureTTL(DNSDistTest):
+
+ _failureCacheTTL = 2
+ _config_params = ['_failureCacheTTL', '_testServerPort']
+ _config_template = """
+ pc = newPacketCache(1000, 86400, 0, %d, 60)
+ getPool(""):setCache(pc)
+ newServer{address="127.0.0.1:%s"}
+ """
+ def testCacheServFailTTL(self):
+ """
+ Cache: ServFail TTL
+
+ """
+ misses = 0
+ name = 'servfail.failure.cache.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN')
+ response = dns.message.make_response(query)
+ response.set_rcode(dns.rcode.SERVFAIL)
+
+ # Miss
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(response, receivedResponse)
+ misses += 1
+
+ # next queries should hit the cache
+ (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
+ self.assertEquals(receivedResponse, response)
+
+ time.sleep(self._failureCacheTTL + 1)
+
+ # we should not have cached for longer than failure cache
+ # so it should be a miss
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(response, receivedResponse)
+ misses += 1
+
+ total = 0
+ for key in self._responsesCounter:
+ total += self._responsesCounter[key]
+
+ self.assertEquals(total, misses)
+
+ def testCacheRefusedTTL(self):
+ """
+ Cache: Refused TTL
+
+ """
+ misses = 0
+ name = 'refused.failure.cache.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN')
+ response = dns.message.make_response(query)
+ response.set_rcode(dns.rcode.REFUSED)
+
+ # Miss
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(response, receivedResponse)
+ misses += 1
+
+ # next queries should hit the cache
+ (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
+ self.assertEquals(receivedResponse, response)
+
+ time.sleep(self._failureCacheTTL + 1)
+
+ # we should not have cached for longer than failure cache
+ # so it should be a miss
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(response, receivedResponse)
+ misses += 1
+
+ total = 0
+ for key in self._responsesCounter:
+ total += self._responsesCounter[key]
+
+ self.assertEquals(total, misses)
+
+ def testCacheHeaderOnlyRefusedTTL(self):
+ """
+ Cache: Header-Only Refused TTL
+
+ """
+ misses = 0
+ name = 'header-only-refused.failure.cache.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN')
+ response = dns.message.make_response(query)
+ response.set_rcode(dns.rcode.REFUSED)
+ response.question = []
+
+ # Miss
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(response, receivedResponse)
+ misses += 1
+
+ # next queries should hit the cache
+ (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
+ self.assertEquals(receivedResponse, response)
+
+ time.sleep(self._failureCacheTTL + 1)
+
+ # we should not have cached for longer than failure cache
+ # so it should be a miss
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(response, receivedResponse)
+ misses += 1
+
+ total = 0
+ for key in self._responsesCounter:
+ total += self._responsesCounter[key]
+
+ self.assertEquals(total, misses)