From: Oliver Chen Date: Fri, 25 Apr 2025 01:04:25 +0000 (+0000) Subject: Add indicator for cache hit rules to know if hit a stale entry X-Git-Tag: dnsdist-2.0.0-alpha2~55^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7311c1e83010a51367f5bd12b38f20c940217dca;p=thirdparty%2Fpdns.git Add indicator for cache hit rules to know if hit a stale entry --- diff --git a/pdns/dnsdistdist/dnsdist-cache.cc b/pdns/dnsdistdist/dnsdist-cache.cc index 6d9e1fc3e1..81b4748a4d 100644 --- a/pdns/dnsdistdist/dnsdist-cache.cc +++ b/pdns/dnsdistdist/dnsdist-cache.cc @@ -307,6 +307,7 @@ bool DNSDistPacketCache::get(DNSQuestion& dnsQuestion, uint16_t queryId, uint32_ } else { age = (value.validity - value.added) - d_settings.d_staleTTL; + dnsQuestion.ids.staleCacheHit = true; } } diff --git a/pdns/dnsdistdist/dnsdist-idstate.hh b/pdns/dnsdistdist/dnsdist-idstate.hh index a804029136..a829d3589b 100644 --- a/pdns/dnsdistdist/dnsdist-idstate.hh +++ b/pdns/dnsdistdist/dnsdist-idstate.hh @@ -176,6 +176,7 @@ struct InternalQueryState bool forwardedOverUDP{false}; bool selfGenerated{false}; bool cacheHit{false}; + bool staleCacheHit{false}; }; struct IDState diff --git a/pdns/dnsdistdist/dnsdist-lua-bindings-dnsquestion.cc b/pdns/dnsdistdist/dnsdist-lua-bindings-dnsquestion.cc index aebe104911..676eaeb775 100644 --- a/pdns/dnsdistdist/dnsdist-lua-bindings-dnsquestion.cc +++ b/pdns/dnsdistdist/dnsdist-lua-bindings-dnsquestion.cc @@ -661,5 +661,9 @@ void setupLuaBindingsDNSQuestion([[maybe_unused]] LuaContext& luaCtx) luaCtx.registerFunction (DNSResponse::*)(void) const>("getSelectedBackend", [](const DNSResponse& dnsResponse) { return dnsResponse.d_downstream; }); + + luaCtx.registerFunction("getStaleCacheHit", [](DNSResponse& dnsResponse) { + return dnsResponse.ids.staleCacheHit; + }); #endif /* DISABLE_NON_FFI_DQ_BINDINGS */ } diff --git a/pdns/dnsdistdist/dnsdist-lua-ffi-interface.h b/pdns/dnsdistdist/dnsdist-lua-ffi-interface.h index 875a3aa73e..42eb0747e0 100644 --- a/pdns/dnsdistdist/dnsdist-lua-ffi-interface.h +++ b/pdns/dnsdistdist/dnsdist-lua-ffi-interface.h @@ -162,6 +162,7 @@ void dnsdist_ffi_dnsresponse_limit_ttl(dnsdist_ffi_dnsresponse_t* dr, uint32_t m void dnsdist_ffi_dnsresponse_set_max_returned_ttl(dnsdist_ffi_dnsresponse_t* dr, uint32_t max) __attribute__ ((visibility ("default"))); void dnsdist_ffi_dnsresponse_clear_records_type(dnsdist_ffi_dnsresponse_t* dr, uint16_t qtype) __attribute__ ((visibility ("default"))); bool dnsdist_ffi_dnsresponse_rebase(dnsdist_ffi_dnsresponse_t* dr, const char* initialName, size_t initialNameSize) __attribute__ ((visibility ("default"))); +bool dnsdist_ffi_dnsresponse_get_stale_cache_hit(const dnsdist_ffi_dnsresponse_t* dr) __attribute__ ((visibility ("default"))); bool dnsdist_ffi_dnsquestion_set_async(dnsdist_ffi_dnsquestion_t* dq, uint16_t asyncID, uint16_t queryID, uint32_t timeoutMs) __attribute__ ((visibility ("default"))); bool dnsdist_ffi_dnsresponse_set_async(dnsdist_ffi_dnsquestion_t* dq, uint16_t asyncID, uint16_t queryID, uint32_t timeoutMs) __attribute__ ((visibility ("default"))); diff --git a/pdns/dnsdistdist/dnsdist-lua-ffi.cc b/pdns/dnsdistdist/dnsdist-lua-ffi.cc index a63c088d12..d5ed9f8ec0 100644 --- a/pdns/dnsdistdist/dnsdist-lua-ffi.cc +++ b/pdns/dnsdistdist/dnsdist-lua-ffi.cc @@ -840,6 +840,11 @@ bool dnsdist_ffi_dnsresponse_rebase(dnsdist_ffi_dnsresponse_t* dr, const char* i return true; } +bool dnsdist_ffi_dnsresponse_get_stale_cache_hit(const dnsdist_ffi_dnsresponse_t* dr) +{ + return dr->dr->ids.staleCacheHit; +} + bool dnsdist_ffi_dnsquestion_set_async(dnsdist_ffi_dnsquestion_t* dq, uint16_t asyncID, uint16_t queryID, uint32_t timeoutMs) { try { diff --git a/pdns/dnsdistdist/docs/reference/dq.rst b/pdns/dnsdistdist/docs/reference/dq.rst index 65337d7012..8364f590bc 100644 --- a/pdns/dnsdistdist/docs/reference/dq.rst +++ b/pdns/dnsdistdist/docs/reference/dq.rst @@ -438,12 +438,18 @@ DNSResponse object If the value is really needed while the response is being processed, it is possible to set a tag while the query is processed, as tags will be passed to the response object. It also has additional methods: - .. method:: DNSResponse.getSelectedBackend() -> Server + .. method:: DNSResponse:getSelectedBackend() -> Server .. versionadded:: 1.9.0 Get the selected backend :class:`Server` or nil + .. method:: DNSResponse:getStaleCacheHit() -> bool + + .. versionadded:: 2.0.0 + + Get the indicator of whether the cache lookup hit a stale entry. + .. method:: DNSResponse:editTTLs(func) The function ``func`` is invoked for every entry in the answer, authority and additional section. diff --git a/pdns/dnsdistdist/test-dnsdist-lua-ffi.cc b/pdns/dnsdistdist/test-dnsdist-lua-ffi.cc index bb544f0dc3..7504f0787c 100644 --- a/pdns/dnsdistdist/test-dnsdist-lua-ffi.cc +++ b/pdns/dnsdistdist/test-dnsdist-lua-ffi.cc @@ -445,6 +445,10 @@ BOOST_AUTO_TEST_CASE(test_Response) dnsdist_ffi_dnsresponse_clear_records_type(nullptr, QType::A); dnsdist_ffi_dnsresponse_clear_records_type(&lightDR, QType::A); } + + { + BOOST_CHECK_EQUAL(dnsdist_ffi_dnsresponse_get_stale_cache_hit(&lightDR), false); + } } BOOST_AUTO_TEST_CASE(test_Server) diff --git a/regression-tests.dnsdist/test_CacheHitResponses.py b/regression-tests.dnsdist/test_CacheHitResponses.py index f74e8b7723..fc7425ed7c 100644 --- a/regression-tests.dnsdist/test_CacheHitResponses.py +++ b/regression-tests.dnsdist/test_CacheHitResponses.py @@ -87,3 +87,55 @@ class TestCacheHitResponses(DNSDistTest): TestCacheHitResponses._responsesCounter[key] = 0 self.assertEqual(total, 2) + +class TestStaleCacheHitResponses(DNSDistTest): + + _consoleKey = DNSDistTest.generateConsoleKey() + _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii') + _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort'] + _config_template = """ + pc = newPacketCache(100, {maxTTL=86400, minTTL=1}) + getPool(""):setCache(pc) + setStaleCacheEntriesTTL(600) + setKey("%s") + controlSocket("127.0.0.1:%d") + newServer{address="127.0.0.1:%s"} + function hitCache(dr) if dr:getStaleCacheHit() then return DNSResponseAction.Drop end return DNSResponseAction.None end + addCacheHitResponseAction(SuffixMatchNodeRule("dropstaleentry.cachehitresponses.tests.powerdns.com."), LuaResponseAction(hitCache)) + """ + + def testDroppedWhenStaleCached(self): + """ + CacheHitResponse: Drop when served from the stale cache entry + """ + ttl = 5 + name = 'dropstaleentry.cachehitresponses.tests.powerdns.com.' + query = dns.message.make_query(name, 'AAAA', 'IN') + response = dns.message.make_response(query) + rrset = dns.rrset.from_text(name, + ttl, + dns.rdataclass.IN, + dns.rdatatype.AAAA, + '::1') + response.answer.append(rrset) + + # first query to fill the cache + (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) + self.assertTrue(receivedQuery) + self.assertTrue(receivedResponse) + receivedQuery.id = query.id + self.assertEqual(query, receivedQuery) + self.assertEqual(receivedResponse, response) + + # mark server down + self.sendConsoleCommand("getServer(0):setDown()") + + # next query should hit the cache within ttl + (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False) + self.assertEqual(receivedResponse, response) + + time.sleep(ttl + 1) + + # further query should hit stale cache thus dropped + (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False) + self.assertEqual(receivedResponse, None)