From 5a957fd750b205f38cb3440522a279421f8b7bdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Berkay=20=C3=96zdemirci?= Date: Fri, 10 Apr 2026 09:05:07 +0300 Subject: [PATCH] Fix EDNS OPT record corruption in DNS cache The TTL computation and assignment loops treat OPT record's Hdr.Ttl as a regular TTL, but per RFC 6891 it encodes EDNS0 metadata (ExtRCode|Version|Flags). This corrupts cached responses causing systemd-resolved to reject them with EDNS version 255. Also fix pointer aliasing: storeCache() stored raw *dns.Msg pointer so subsequent mutations by Exchange() corrupted cached data. - Skip OPT records in all TTL loops (Exchange + loadResponse) - Use message.Copy() in storeCache() to isolate cache from mutations --- dns/client.go | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/dns/client.go b/dns/client.go index 70b53c951..1a2ee8f8c 100644 --- a/dns/client.go +++ b/dns/client.go @@ -283,6 +283,9 @@ func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, m if timeToLive == 0 { for _, recordList := range [][]dns.RR{response.Answer, response.Ns, response.Extra} { for _, record := range recordList { + if record.Header().Rrtype == dns.TypeOPT { + continue + } if timeToLive == 0 || record.Header().Ttl > 0 && record.Header().Ttl < timeToLive { timeToLive = record.Header().Ttl } @@ -294,6 +297,9 @@ func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, m } for _, recordList := range [][]dns.RR{response.Answer, response.Ns, response.Extra} { for _, record := range recordList { + if record.Header().Rrtype == dns.TypeOPT { + continue + } record.Header().Ttl = timeToLive } } @@ -381,21 +387,21 @@ func (c *Client) storeCache(transport adapter.DNSTransport, question dns.Questio } if c.disableExpire { if !c.independentCache { - c.cache.Add(question, message) + c.cache.Add(question, message.Copy()) } else { c.transportCache.Add(transportCacheKey{ Question: question, transportTag: transport.Tag(), - }, message) + }, message.Copy()) } } else { if !c.independentCache { - c.cache.AddWithLifetime(question, message, time.Second*time.Duration(timeToLive)) + c.cache.AddWithLifetime(question, message.Copy(), time.Second*time.Duration(timeToLive)) } else { c.transportCache.AddWithLifetime(transportCacheKey{ Question: question, transportTag: transport.Tag(), - }, message, time.Second*time.Duration(timeToLive)) + }, message.Copy(), time.Second*time.Duration(timeToLive)) } } } @@ -486,6 +492,9 @@ func (c *Client) loadResponse(question dns.Question, transport adapter.DNSTransp var originTTL int for _, recordList := range [][]dns.RR{response.Answer, response.Ns, response.Extra} { for _, record := range recordList { + if record.Header().Rrtype == dns.TypeOPT { + continue + } if originTTL == 0 || record.Header().Ttl > 0 && int(record.Header().Ttl) < originTTL { originTTL = int(record.Header().Ttl) } @@ -500,12 +509,18 @@ func (c *Client) loadResponse(question dns.Question, transport adapter.DNSTransp duration := uint32(originTTL - nowTTL) for _, recordList := range [][]dns.RR{response.Answer, response.Ns, response.Extra} { for _, record := range recordList { + if record.Header().Rrtype == dns.TypeOPT { + continue + } record.Header().Ttl = record.Header().Ttl - duration } } } else { for _, recordList := range [][]dns.RR{response.Answer, response.Ns, response.Extra} { for _, record := range recordList { + if record.Header().Rrtype == dns.TypeOPT { + continue + } record.Header().Ttl = uint32(nowTTL) } }