From: Vladimír Čunát Date: Mon, 16 Mar 2026 10:00:22 +0000 (+0100) Subject: lib/cache: trim TTL of failing stashed packets X-Git-Tag: v5.7.7~6^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=41f4eb5069c7dd5da6218b805b4e5bd48a1b7dce;p=thirdparty%2Fknot-resolver.git lib/cache: trim TTL of failing stashed packets In particular, in STUB mode (i.e. forwarding with dnssec:false + authoritative:false) if a SERVFAIL packet from upstream contained also records, this packet could be cached with long TTL. This issue was reported by Qifan Zhang from Palo Alto Networks. Additionally, let's apply our TTL limits for caching also to the `cache-control: max-age=` HTTP header sent in DoH replies. (cherry picked from commit e4812f51e385334e051851c839b4a262d6224411) --- diff --git a/NEWS b/NEWS index 3195ec947..07a2a9093 100644 --- a/NEWS +++ b/NEWS @@ -8,12 +8,14 @@ Improvements - packaging: rpm: added systemd's sysusers support (!1830) This should also resolve the issue with user and group configuration during installation (GH#130). +- make DoH cache-control header respect our cache's TTL limits (!1832) Bugfixes -------- - respect disablement of QNAME case randomization even after TCP issues - cache: fix wrong TTL in some cases, typically 32768 +- reduce excessive caching of some uncommon failed answers (!1832) Knot Resolver 5.7.6 (2025-07-17) diff --git a/lib/cache/entry_pkt.c b/lib/cache/entry_pkt.c index aaac3fcc4..71af06a52 100644 --- a/lib/cache/entry_pkt.c +++ b/lib/cache/entry_pkt.c @@ -12,10 +12,8 @@ #include "lib/layer/iterate.h" /* kr_response_classify */ #include "lib/cache/impl.h" - /** Compute TTL for a packet. It's minimum TTL or zero. (You can apply limits.) */ -KR_EXPORT -uint32_t packet_ttl(const knot_pkt_t *pkt) +static uint32_t packet_ttl_simple(const knot_pkt_t *pkt) { bool has_ttl = false; uint32_t ttl = TTL_MAX_MAX; @@ -34,6 +32,23 @@ uint32_t packet_ttl(const knot_pkt_t *pkt) } return has_ttl ? ttl : 0; } +/** Compute TTL for a packet. Cache limits do apply here. + * + * Also, failure packets (SERVFAIL in particular) have the minimal TTL. + * TODO: overall design around caching SERVFAIL-inducing situations. */ +KR_EXPORT +uint32_t packet_ttl(const knot_pkt_t *pkt) +{ + struct kr_cache *cache = &the_resolver->cache; + switch (knot_wire_get_rcode(pkt->wire)) { + case KNOT_RCODE_NOERROR: + case KNOT_RCODE_NXDOMAIN: + case KNOT_RCODE_YXDOMAIN: + return MAX(MIN(packet_ttl_simple(pkt), cache->ttl_max), cache->ttl_min); + default: + return cache->ttl_min; + } +} void stash_pkt(const knot_pkt_t *pkt, const struct kr_query *qry, diff --git a/modules/http/http_doh.test.lua b/modules/http/http_doh.test.lua index f0685cb15..b81ff2dfc 100644 --- a/modules/http/http_doh.test.lua +++ b/modules/http/http_doh.test.lua @@ -122,8 +122,8 @@ else if not (headers and pkt) then return end - -- uncacheable - same(headers:get('cache-control'), 'max-age=0', desc .. ': TTL 0') + -- failure but we limit the TTL + same(headers:get('cache-control'), 'max-age=5', desc .. ': TTL 5') same(pkt:rcode(), kres.rcode.SERVFAIL, desc .. ': rcode matches') end @@ -215,8 +215,8 @@ else if not (headers and pkt) then return end - -- uncacheable - same(headers:get('cache-control'), 'max-age=0', desc .. ': TTL 0') + -- failure but we limit the TTL + same(headers:get('cache-control'), 'max-age=5', desc .. ': TTL 5') same(pkt:rcode(), kres.rcode.SERVFAIL, desc .. ': rcode matches') end @@ -373,8 +373,8 @@ else if not (headers and pkt) then return end - -- uncacheable - same(headers:get('cache-control'), 'max-age=0', desc .. ': TTL 0') + -- failure but we limit the TTL + same(headers:get('cache-control'), 'max-age=5', desc .. ': TTL 5') same(pkt:rcode(), kres.rcode.SERVFAIL, desc .. ': rcode matches') end diff --git a/tests/config/doh2.test.lua b/tests/config/doh2.test.lua index d178ecaaa..503943e3f 100644 --- a/tests/config/doh2.test.lua +++ b/tests/config/doh2.test.lua @@ -115,8 +115,8 @@ else if not (headers and pkt) then return end - -- uncacheable - same(headers:get('cache-control'), 'max-age=0', desc .. ': TTL 0') + -- failure but we limit the TTL + same(headers:get('cache-control'), 'max-age=5', desc .. ': TTL 5') same(headers:get('access-control-allow-origin'), '*', desc .. ': CORS headers') same(pkt:rcode(), kres.rcode.SERVFAIL, desc .. ': rcode matches') end @@ -252,8 +252,8 @@ else if not (headers and pkt) then return end - -- uncacheable - same(headers:get('cache-control'), 'max-age=0', desc .. ': TTL 0') + -- failure but we limit the TTL + same(headers:get('cache-control'), 'max-age=5', desc .. ': TTL 5') same(pkt:rcode(), kres.rcode.SERVFAIL, desc .. ': rcode matches') end