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)
- 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)
#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;
}
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,
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
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
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
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
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