mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-16 15:26:40 +00:00
implements DNS query caching in the DNSForwarder to improve performance and provide fallback responses when upstream DNS servers fail. The cache stores successful DNS query results and serves them when upstream resolution fails. - Added a new cache component to store DNS query results by domain and query type - Integrated cache storage after successful DNS resolutions - Enhanced error handling to serve cached responses as fallback when upstream DNS fails
87 lines
2.3 KiB
Go
87 lines
2.3 KiB
Go
package dnsfwd
|
|
|
|
import (
|
|
"net/netip"
|
|
"testing"
|
|
)
|
|
|
|
func mustAddr(t *testing.T, s string) netip.Addr {
|
|
t.Helper()
|
|
a, err := netip.ParseAddr(s)
|
|
if err != nil {
|
|
t.Fatalf("parse addr %s: %v", s, err)
|
|
}
|
|
return a
|
|
}
|
|
|
|
func TestCacheNormalization(t *testing.T) {
|
|
c := newCache()
|
|
|
|
// Mixed case, without trailing dot
|
|
domainInput := "ExAmPlE.CoM"
|
|
ipv4 := []netip.Addr{mustAddr(t, "1.2.3.4")}
|
|
c.set(domainInput, 1 /* dns.TypeA */, ipv4)
|
|
|
|
// Lookup with lower, with trailing dot
|
|
if got, ok := c.get("example.com.", 1); !ok || len(got) != 1 || got[0].String() != "1.2.3.4" {
|
|
t.Fatalf("expected cached IPv4 result via normalized key, got=%v ok=%v", got, ok)
|
|
}
|
|
|
|
// Lookup with different casing again
|
|
if got, ok := c.get("EXAMPLE.COM", 1); !ok || len(got) != 1 || got[0].String() != "1.2.3.4" {
|
|
t.Fatalf("expected cached IPv4 result via different casing, got=%v ok=%v", got, ok)
|
|
}
|
|
}
|
|
|
|
func TestCacheSeparateTypes(t *testing.T) {
|
|
c := newCache()
|
|
|
|
domain := "test.local"
|
|
ipv4 := []netip.Addr{mustAddr(t, "10.0.0.1")}
|
|
ipv6 := []netip.Addr{mustAddr(t, "2001:db8::1")}
|
|
|
|
c.set(domain, 1 /* A */, ipv4)
|
|
c.set(domain, 28 /* AAAA */, ipv6)
|
|
|
|
got4, ok4 := c.get(domain, 1)
|
|
if !ok4 || len(got4) != 1 || got4[0] != ipv4[0] {
|
|
t.Fatalf("expected A record from cache, got=%v ok=%v", got4, ok4)
|
|
}
|
|
|
|
got6, ok6 := c.get(domain, 28)
|
|
if !ok6 || len(got6) != 1 || got6[0] != ipv6[0] {
|
|
t.Fatalf("expected AAAA record from cache, got=%v ok=%v", got6, ok6)
|
|
}
|
|
}
|
|
|
|
func TestCacheCloneOnGetAndSet(t *testing.T) {
|
|
c := newCache()
|
|
domain := "clone.test"
|
|
|
|
src := []netip.Addr{mustAddr(t, "8.8.8.8")}
|
|
c.set(domain, 1, src)
|
|
|
|
// Mutate source slice; cache should be unaffected
|
|
src[0] = mustAddr(t, "9.9.9.9")
|
|
|
|
got, ok := c.get(domain, 1)
|
|
if !ok || len(got) != 1 || got[0].String() != "8.8.8.8" {
|
|
t.Fatalf("expected cached value to be independent of source slice, got=%v ok=%v", got, ok)
|
|
}
|
|
|
|
// Mutate returned slice; internal cache should remain unchanged
|
|
got[0] = mustAddr(t, "4.4.4.4")
|
|
got2, ok2 := c.get(domain, 1)
|
|
if !ok2 || len(got2) != 1 || got2[0].String() != "8.8.8.8" {
|
|
t.Fatalf("expected returned slice to be a clone, got=%v ok=%v", got2, ok2)
|
|
}
|
|
}
|
|
|
|
func TestCacheMiss(t *testing.T) {
|
|
c := newCache()
|
|
if got, ok := c.get("missing.example", 1); ok || got != nil {
|
|
t.Fatalf("expected cache miss, got=%v ok=%v", got, ok)
|
|
}
|
|
}
|
|
|