| author | Alberto Bertogli
<albertito@blitiri.com.ar> 2026-10-23 18:33:46 UTC |
| committer | Alberto Bertogli
<albertito@blitiri.com.ar> 2026-10-23 18:51:09 UTC |
| parent | a8b9d894d5e1df258466ded86e78d87cafe3ec47 |
| internal/dnsserver/caching_test.go | +7 | -0 |
| internal/dnsserver/resolver.go | +18 | -0 |
diff --git a/internal/dnsserver/caching_test.go b/internal/dnsserver/caching_test.go index 522efe6..00e2b69 100644 --- a/internal/dnsserver/caching_test.go +++ b/internal/dnsserver/caching_test.go @@ -276,6 +276,13 @@ func TestWantToCache(t *testing.T) { r.Question = []dns.Question{ {Name: "other.", Qtype: dns.TypeMX, Qclass: dns.ClassINET}} checkWantToCache(t, q, r, "reply question does not match") + + r = reply.Copy() + r.Answer = nil + for i := 0; i < 300; i++ { + r.Answer = append(r.Answer, mustNewRR(t, "test. A 1.2.3.4")) + } + checkWantToCache(t, q, r, "answer too large") } // Test that we handle the cache filling up. diff --git a/internal/dnsserver/resolver.go b/internal/dnsserver/resolver.go index bc60008..708b009 100644 --- a/internal/dnsserver/resolver.go +++ b/internal/dnsserver/resolver.go @@ -98,6 +98,12 @@ var ( // with Maintain(). maxCacheSize = 2000 + // Maximum size of an answer we will cache, in bytes (uncompressed). + // Larger answers are rare, and in memory they can be much bigger than + // on the wire (each RR gets its own copy of the name), so caching them + // could use a lot of memory. + maxAnswerSize = 4 * 1024 + // Minimum TTL for entries we consider for the cache. minTTL = 2 * time.Minute @@ -248,11 +254,23 @@ func wantToCache(question dns.Question, reply *dns.Msg) error { return fmt.Errorf( "reply question does not match: asked %v, got %v", question, reply.Question[0]) + } else if size := answerSize(reply.Answer); size > maxAnswerSize { + return fmt.Errorf("answer too large (%d bytes)", size) } return nil } +// answerSize returns the uncompressed size of the answer, which is a +// reasonable approximation of how much memory it takes. +func answerSize(answer []dns.RR) int { + size := 0 + for _, rr := range answer { + size += dns.Len(rr) + } + return size +} + func limitTTL(answer []dns.RR) time.Duration { // Use the minimum TTL of all the RRs, as the answer is only valid as // long as all of them are. For example, a long-lived CNAME pointing to a