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
This commit is contained in:
@@ -283,6 +283,9 @@ func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, m
|
|||||||
if timeToLive == 0 {
|
if timeToLive == 0 {
|
||||||
for _, recordList := range [][]dns.RR{response.Answer, response.Ns, response.Extra} {
|
for _, recordList := range [][]dns.RR{response.Answer, response.Ns, response.Extra} {
|
||||||
for _, record := range recordList {
|
for _, record := range recordList {
|
||||||
|
if record.Header().Rrtype == dns.TypeOPT {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if timeToLive == 0 || record.Header().Ttl > 0 && record.Header().Ttl < timeToLive {
|
if timeToLive == 0 || record.Header().Ttl > 0 && record.Header().Ttl < timeToLive {
|
||||||
timeToLive = record.Header().Ttl
|
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 _, recordList := range [][]dns.RR{response.Answer, response.Ns, response.Extra} {
|
||||||
for _, record := range recordList {
|
for _, record := range recordList {
|
||||||
|
if record.Header().Rrtype == dns.TypeOPT {
|
||||||
|
continue
|
||||||
|
}
|
||||||
record.Header().Ttl = timeToLive
|
record.Header().Ttl = timeToLive
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -381,21 +387,21 @@ func (c *Client) storeCache(transport adapter.DNSTransport, question dns.Questio
|
|||||||
}
|
}
|
||||||
if c.disableExpire {
|
if c.disableExpire {
|
||||||
if !c.independentCache {
|
if !c.independentCache {
|
||||||
c.cache.Add(question, message)
|
c.cache.Add(question, message.Copy())
|
||||||
} else {
|
} else {
|
||||||
c.transportCache.Add(transportCacheKey{
|
c.transportCache.Add(transportCacheKey{
|
||||||
Question: question,
|
Question: question,
|
||||||
transportTag: transport.Tag(),
|
transportTag: transport.Tag(),
|
||||||
}, message)
|
}, message.Copy())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if !c.independentCache {
|
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 {
|
} else {
|
||||||
c.transportCache.AddWithLifetime(transportCacheKey{
|
c.transportCache.AddWithLifetime(transportCacheKey{
|
||||||
Question: question,
|
Question: question,
|
||||||
transportTag: transport.Tag(),
|
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
|
var originTTL int
|
||||||
for _, recordList := range [][]dns.RR{response.Answer, response.Ns, response.Extra} {
|
for _, recordList := range [][]dns.RR{response.Answer, response.Ns, response.Extra} {
|
||||||
for _, record := range recordList {
|
for _, record := range recordList {
|
||||||
|
if record.Header().Rrtype == dns.TypeOPT {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if originTTL == 0 || record.Header().Ttl > 0 && int(record.Header().Ttl) < originTTL {
|
if originTTL == 0 || record.Header().Ttl > 0 && int(record.Header().Ttl) < originTTL {
|
||||||
originTTL = int(record.Header().Ttl)
|
originTTL = int(record.Header().Ttl)
|
||||||
}
|
}
|
||||||
@@ -500,12 +509,18 @@ func (c *Client) loadResponse(question dns.Question, transport adapter.DNSTransp
|
|||||||
duration := uint32(originTTL - nowTTL)
|
duration := uint32(originTTL - nowTTL)
|
||||||
for _, recordList := range [][]dns.RR{response.Answer, response.Ns, response.Extra} {
|
for _, recordList := range [][]dns.RR{response.Answer, response.Ns, response.Extra} {
|
||||||
for _, record := range recordList {
|
for _, record := range recordList {
|
||||||
|
if record.Header().Rrtype == dns.TypeOPT {
|
||||||
|
continue
|
||||||
|
}
|
||||||
record.Header().Ttl = record.Header().Ttl - duration
|
record.Header().Ttl = record.Header().Ttl - duration
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for _, recordList := range [][]dns.RR{response.Answer, response.Ns, response.Extra} {
|
for _, recordList := range [][]dns.RR{response.Answer, response.Ns, response.Extra} {
|
||||||
for _, record := range recordList {
|
for _, record := range recordList {
|
||||||
|
if record.Header().Rrtype == dns.TypeOPT {
|
||||||
|
continue
|
||||||
|
}
|
||||||
record.Header().Ttl = uint32(nowTTL)
|
record.Header().Ttl = uint32(nowTTL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user