AGH-148 Imp-proxy-tests-vol.4 (30)

This commit is contained in:
Fedor Setrakov
2026-10-07 16:36:21 +03:00
committed by GitHub
parent f2a42ab856
commit 7fcb1cca32
14 changed files with 555 additions and 477 deletions
+56
View File
@@ -30,6 +30,9 @@ const (
// Host is a common host for tests.
Host = "test.example"
// FQDN is the fully-qualified domain name for [Host].
FQDN = Host + "."
// TTL is a common time-to-live value in seconds for tests.
TTL = 300
)
@@ -38,6 +41,12 @@ var (
// LocalhostAnyPort is a [netip.AddrPort] having a value of 127.0.0.1:0.
LocalhostAnyPort = netip.AddrPortFrom(netutil.IPv4Localhost(), 0)
// LocalhostAnyPortUDP is a [net.UDPAddr] for [LocalhostAnyPort].
LocalhostAnyPortUDP = net.UDPAddrFromAddrPort(LocalhostAnyPort)
// LocalhostAnyPortTCP is a [net.TCPAddr] for [LocalhostAnyPort].
LocalhostAnyPortTCP = net.TCPAddrFromAddrPort(LocalhostAnyPort)
// IPv4 is a common IPv4 address for tests.
IPv4 = net.IPv4(192, 0, 2, 1)
)
@@ -104,3 +113,50 @@ func RequireResponse(tb testing.TB, req, reply *dns.Msg) {
require.Equal(tb, IPv4, a.A.To16())
}
// NewRR is a helper that creates a new dns.RR with the given name, qtype, ttl
// and value. It fails the test if the qtype is not supported or the type of
// value doesn't match the qtype. val is expected to be of type [net.IP] if
// qtype is [dns.TypeA] or [dns.TypeAAAA] and a string if qtype is
// [dns.TypeCNAME] or [dns.TypePTR]. qtype is expected to be one of the
// following:
// - [dns.TypeA]
// - [dns.TypeAAAA]
// - [dns.TypeCNAME]
// - [dns.TypeSOA]
// - [dns.TypePTR]
func NewRR(tb testing.TB, name string, qtype uint16, ttl uint32, val any) (rr dns.RR) {
tb.Helper()
switch qtype {
case dns.TypeA:
rr = &dns.A{A: testutil.RequireTypeAssert[net.IP](tb, val)}
case dns.TypeAAAA:
rr = &dns.AAAA{AAAA: testutil.RequireTypeAssert[net.IP](tb, val)}
case dns.TypeCNAME:
rr = &dns.CNAME{Target: testutil.RequireTypeAssert[string](tb, val)}
case dns.TypeSOA:
rr = &dns.SOA{
Ns: "ns." + name,
Mbox: "hostmaster." + name,
Serial: 1,
Refresh: 1,
Retry: 1,
Expire: 1,
Minttl: 1,
}
case dns.TypePTR:
rr = &dns.PTR{Ptr: testutil.RequireTypeAssert[string](tb, val)}
default:
tb.Fatalf("unsupported qtype: %d", qtype)
}
*rr.Header() = dns.RR_Header{
Name: name,
Rrtype: qtype,
Class: dns.ClassINET,
Ttl: ttl,
}
return rr
}
+3 -2
View File
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/require"
)
// TODO(f.setrakov): Externalize when ECS helpers are moved to golibs.
func TestProxy_IsBogusNXDomain(t *testing.T) {
var ans []dns.RR
@@ -24,8 +25,8 @@ func TestProxy_IsBogusNXDomain(t *testing.T) {
prx := mustNew(t, &Config{
Logger: testLogger,
UDPListenAddr: []*net.UDPAddr{net.UDPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)},
TCPListenAddr: []*net.TCPAddr{net.TCPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)},
UDPListenAddr: []*net.UDPAddr{dnsproxytest.LocalhostAnyPortUDP},
TCPListenAddr: []*net.TCPAddr{dnsproxytest.LocalhostAnyPortTCP},
UpstreamConfig: upsConf,
TrustedProxies: dnsproxytest.DefaultTrustedProxies,
CacheEnabled: true,
+38 -29
View File
@@ -52,10 +52,12 @@ func newTestCache(tb testing.TB, conf *cacheConfig) (c *cache) {
func TestServeCached(t *testing.T) {
dnsProxy := mustNew(t, &Config{
Logger: testLogger,
UDPListenAddr: []*net.UDPAddr{net.UDPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)},
TCPListenAddr: []*net.TCPAddr{net.TCPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)},
UpstreamConfig: newTestUpstreamConfig(t, newTestUpstream(t)),
Logger: testLogger,
UDPListenAddr: []*net.UDPAddr{dnsproxytest.LocalhostAnyPortUDP},
TCPListenAddr: []*net.TCPAddr{dnsproxytest.LocalhostAnyPortTCP},
UpstreamConfig: &UpstreamConfig{
Upstreams: []upstream.Upstream{newTestUpstream(t)},
},
TrustedProxies: dnsproxytest.DefaultTrustedProxies,
DNSSECEnabled: false,
CacheEnabled: true,
@@ -70,7 +72,7 @@ func TestServeCached(t *testing.T) {
// Fill the cache.
reply := (&dns.Msg{
Answer: []dns.RR{newRR(t, "google.com.", dns.TypeA, 3600, net.IP{8, 8, 8, 8})},
Answer: []dns.RR{dnsproxytest.NewRR(t, "google.com.", dns.TypeA, 3600, net.IP{8, 8, 8, 8})},
}).SetReply(request)
reply.SetEdns0(defaultUDPBufSize, false)
@@ -191,7 +193,7 @@ func TestCacheDO(t *testing.T) {
MsgHdr: dns.MsgHdr{
Response: true,
},
Answer: []dns.RR{newRR(t, "google.com.", dns.TypeA, 3600, net.IP{8, 8, 8, 8})},
Answer: []dns.RR{dnsproxytest.NewRR(t, "google.com.", dns.TypeA, 3600, net.IP{8, 8, 8, 8})},
}).SetQuestion("google.com.", dns.TypeA)
reply.SetEdns0(4096, false)
@@ -229,7 +231,9 @@ func TestCacheCNAME(t *testing.T) {
MsgHdr: dns.MsgHdr{
Response: true,
},
Answer: []dns.RR{newRR(t, "google.com.", dns.TypeCNAME, 3600, "test.google.com.")},
Answer: []dns.RR{
dnsproxytest.NewRR(t, "google.com.", dns.TypeCNAME, 3600, "test.google.com."),
},
}).SetQuestion("google.com.", dns.TypeA)
testCache.set(request, reply, upstreamWithAddr, testLogger)
@@ -240,7 +244,10 @@ func TestCacheCNAME(t *testing.T) {
})
// Now fill the cache with a cacheable CNAME response.
reply.Answer = append(reply.Answer, newRR(t, "google.com.", dns.TypeA, 3600, net.IP{8, 8, 8, 8}))
reply.Answer = append(
reply.Answer,
dnsproxytest.NewRR(t, "google.com.", dns.TypeA, 3600, net.IP{8, 8, 8, 8}),
)
testCache.set(request, reply, upstreamWithAddr, testLogger)
// We are testing that a proper CNAME response gets cached
@@ -305,10 +312,12 @@ func TestCacheExpiration(t *testing.T) {
t.Parallel()
dnsProxy := mustNew(t, &Config{
Logger: testLogger,
UDPListenAddr: []*net.UDPAddr{net.UDPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)},
TCPListenAddr: []*net.TCPAddr{net.TCPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)},
UpstreamConfig: newTestUpstreamConfig(t, newTestUpstream(t)),
Logger: testLogger,
UDPListenAddr: []*net.UDPAddr{dnsproxytest.LocalhostAnyPortUDP},
TCPListenAddr: []*net.TCPAddr{dnsproxytest.LocalhostAnyPortTCP},
UpstreamConfig: &UpstreamConfig{
Upstreams: []upstream.Upstream{newTestUpstream(t)},
},
TrustedProxies: dnsproxytest.DefaultTrustedProxies,
CacheEnabled: true,
})
@@ -317,9 +326,9 @@ func TestCacheExpiration(t *testing.T) {
// Create dns messages with TTL of 1 second.
rrs := []dns.RR{
newRR(t, "youtube.com.", dns.TypeA, 1, net.IP{173, 194, 221, 198}),
newRR(t, "google.com.", dns.TypeA, 1, net.IP{8, 8, 8, 8}),
newRR(t, "yandex.com.", dns.TypeA, 1, net.IP{213, 180, 204, 62}),
dnsproxytest.NewRR(t, "youtube.com.", dns.TypeA, 1, net.IP{173, 194, 221, 198}),
dnsproxytest.NewRR(t, "google.com.", dns.TypeA, 1, net.IP{8, 8, 8, 8}),
dnsproxytest.NewRR(t, "yandex.com.", dns.TypeA, 1, net.IP{213, 180, 204, 62}),
}
requests := make([]*dns.Msg, 0, len(rrs))
responses := make([]*dns.Msg, 0, len(rrs))
@@ -367,8 +376,8 @@ func TestCacheExpirationWithTTLOverride(t *testing.T) {
dnsProxy := mustNew(t, &Config{
Logger: testLogger,
UDPListenAddr: []*net.UDPAddr{net.UDPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)},
TCPListenAddr: []*net.TCPAddr{net.TCPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)},
UDPListenAddr: []*net.UDPAddr{dnsproxytest.LocalhostAnyPortUDP},
TCPListenAddr: []*net.TCPAddr{dnsproxytest.LocalhostAnyPortTCP},
UpstreamConfig: &UpstreamConfig{
Upstreams: []upstream.Upstream{u},
},
@@ -455,13 +464,13 @@ func TestCache(t *testing.T) {
testCases{
cache: []testEntry{{
q: "google.com.",
a: []dns.RR{newRR(t, "google.com.", dns.TypeA, 3600, net.IP{8, 8, 8, 8})},
a: []dns.RR{dnsproxytest.NewRR(t, "google.com.", dns.TypeA, 3600, net.IP{8, 8, 8, 8})},
t: dns.TypeA,
}},
cases: []testCase{{
ok: require.True,
q: "google.com.",
a: []dns.RR{newRR(t, "google.com.", dns.TypeA, 3600, net.IP{8, 8, 8, 8})},
a: []dns.RR{dnsproxytest.NewRR(t, "google.com.", dns.TypeA, 3600, net.IP{8, 8, 8, 8})},
t: dns.TypeA,
}, {
ok: require.False,
@@ -475,23 +484,23 @@ func TestCache(t *testing.T) {
testCases{
cache: []testEntry{{
q: "gOOgle.com.",
a: []dns.RR{newRR(t, "google.com.", dns.TypeA, 3600, net.IP{8, 8, 8, 8})},
a: []dns.RR{dnsproxytest.NewRR(t, "google.com.", dns.TypeA, 3600, net.IP{8, 8, 8, 8})},
t: dns.TypeA,
}},
cases: []testCase{{
ok: require.True,
q: "gOOgle.com.",
a: []dns.RR{newRR(t, "google.com.", dns.TypeA, 3600, net.IP{8, 8, 8, 8})},
a: []dns.RR{dnsproxytest.NewRR(t, "google.com.", dns.TypeA, 3600, net.IP{8, 8, 8, 8})},
t: dns.TypeA,
}, {
ok: require.True,
q: "google.com.",
a: []dns.RR{newRR(t, "google.com.", dns.TypeA, 3600, net.IP{8, 8, 8, 8})},
a: []dns.RR{dnsproxytest.NewRR(t, "google.com.", dns.TypeA, 3600, net.IP{8, 8, 8, 8})},
t: dns.TypeA,
}, {
ok: require.True,
q: "GOOGLE.COM.",
a: []dns.RR{newRR(t, "google.com.", dns.TypeA, 3600, net.IP{8, 8, 8, 8})},
a: []dns.RR{dnsproxytest.NewRR(t, "google.com.", dns.TypeA, 3600, net.IP{8, 8, 8, 8})},
t: dns.TypeA,
}, {
q: "gOOgle.com.",
@@ -513,7 +522,7 @@ func TestCache(t *testing.T) {
testCases{
cache: []testEntry{{
q: "gOOgle.com.",
a: []dns.RR{newRR(t, "google.com.", dns.TypeA, 0, net.IP{8, 8, 8, 8})},
a: []dns.RR{dnsproxytest.NewRR(t, "google.com.", dns.TypeA, 0, net.IP{8, 8, 8, 8})},
t: dns.TypeA,
}},
cases: []testCase{{
@@ -630,7 +639,7 @@ func setAndGetCache(t *testing.T, c *cache, g *sync.WaitGroup, host, ip string)
MsgHdr: dns.MsgHdr{
Response: true,
},
Answer: []dns.RR{newRR(t, host, dns.TypeA, 1, ipAddr)},
Answer: []dns.RR{dnsproxytest.NewRR(t, host, dns.TypeA, 1, ipAddr)},
}).SetQuestion(host, dns.TypeA)
c.set(req, dnsMsg, upstreamWithAddr, testLogger)
@@ -670,7 +679,7 @@ func TestCache_getWithSubnet(t *testing.T) {
// Add a response with subnet.
resp := (&dns.Msg{
Answer: []dns.RR{newRR(t, testFQDN, dns.TypeA, 1, net.IP{1, 1, 1, 1})},
Answer: []dns.RR{dnsproxytest.NewRR(t, testFQDN, dns.TypeA, 1, net.IP{1, 1, 1, 1})},
}).SetReply(req)
c.setWithSubnet(req, resp, upstreamWithAddr, &net.IPNet{IP: ip1234, Mask: mask16}, testLogger)
@@ -683,13 +692,13 @@ func TestCache_getWithSubnet(t *testing.T) {
// Add a response entry with subnet #2.
resp = (&dns.Msg{
Answer: []dns.RR{newRR(t, testFQDN, dns.TypeA, 1, net.IP{2, 2, 2, 2})},
Answer: []dns.RR{dnsproxytest.NewRR(t, testFQDN, dns.TypeA, 1, net.IP{2, 2, 2, 2})},
}).SetReply(req)
c.setWithSubnet(req, resp, upstreamWithAddr, &net.IPNet{IP: ip2234, Mask: mask16}, testLogger)
// Add a response entry without subnet.
resp = (&dns.Msg{
Answer: []dns.RR{newRR(t, testFQDN, dns.TypeA, 1, net.IP{3, 3, 3, 3})},
Answer: []dns.RR{dnsproxytest.NewRR(t, testFQDN, dns.TypeA, 1, net.IP{3, 3, 3, 3})},
}).SetReply(req)
c.setWithSubnet(req, resp, upstreamWithAddr, &net.IPNet{IP: nil, Mask: nil}, testLogger)
@@ -753,7 +762,7 @@ func TestCache_getWithSubnet_mask(t *testing.T) {
req := (&dns.Msg{}).SetQuestion(testFQDN, dns.TypeA)
resp := (&dns.Msg{
Answer: []dns.RR{newRR(t, testFQDN, dns.TypeA, 300, ansIP)},
Answer: []dns.RR{dnsproxytest.NewRR(t, testFQDN, dns.TypeA, 300, ansIP)},
}).SetReply(req)
// Cache IP network that contains the testIP.
@@ -1,4 +1,4 @@
package proxy
package proxy_test
import (
"context"
@@ -7,7 +7,9 @@ import (
"sync"
"testing"
"github.com/AdguardTeam/dnsproxy/internal/dnsproxytest"
"github.com/AdguardTeam/dnsproxy/dnsproxytest"
proxytest "github.com/AdguardTeam/dnsproxy/internal/dnsproxytest"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/testutil"
@@ -17,11 +19,14 @@ import (
"github.com/stretchr/testify/require"
)
const ipv4OnlyFqdn = "ipv4.only."
// testSynTTL is a common TTL for DNS64 synthesized records.
const testSynTTL uint32 = 600
func TestProxy_HandleDNSRequest_dns64Race(t *testing.T) {
ans := newRR(t, ipv4OnlyFqdn, dns.TypeA, 3600, dnsproxytest.IPv4)
ups := &testUpstream{
fakeHost := "fake.address"
ans := proxytest.NewRR(t, proxytest.FQDN, dns.TypeA, 3600, proxytest.IPv4)
ups := &dnsproxytest.Upstream{
OnExchange: func(_ context.Context, req *dns.Msg) (resp *dns.Msg, err error) {
resp = (&dns.Msg{}).SetReply(req)
if req.Question[0].Qtype == dns.TypeA {
@@ -30,53 +35,55 @@ func TestProxy_HandleDNSRequest_dns64Race(t *testing.T) {
return resp, nil
},
OnAddress: func() (addr string) { return "fake.address" },
OnAddress: func() (addr string) { return fakeHost },
OnClose: func() (err error) { return nil },
}
localUps := &testUpstream{
localUps := &dnsproxytest.Upstream{
OnExchange: func(ctx context.Context, m *dns.Msg) (_ *dns.Msg, _ error) {
panic(testutil.UnexpectedCall(ctx, m))
},
OnAddress: func() (addr string) { return "fake.address" },
OnAddress: func() (addr string) { return fakeHost },
OnClose: func() (err error) { return nil },
}
dnsProxy := mustNew(t, &Config{
dnsProxy, err := proxy.New(&proxy.Config{
Logger: testLogger,
UDPListenAddr: []*net.UDPAddr{net.UDPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)},
TCPListenAddr: []*net.TCPAddr{net.TCPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)},
UDPListenAddr: []*net.UDPAddr{net.UDPAddrFromAddrPort(proxytest.LocalhostAnyPort)},
TCPListenAddr: []*net.TCPAddr{net.TCPAddrFromAddrPort(proxytest.LocalhostAnyPort)},
PrivateSubnets: netutil.SubnetSetFunc(netutil.IsLocallyServed),
UpstreamConfig: &UpstreamConfig{
UpstreamConfig: &proxy.UpstreamConfig{
Upstreams: []upstream.Upstream{ups},
},
PrivateRDNSUpstreamConfig: &UpstreamConfig{
PrivateRDNSUpstreamConfig: &proxy.UpstreamConfig{
Upstreams: []upstream.Upstream{localUps},
},
TrustedProxies: dnsproxytest.DefaultTrustedProxies,
TrustedProxies: proxytest.DefaultTrustedProxies,
UseDNS64: true,
UsePrivateRDNS: true,
// Valid NAT-64 prefix for 2001:67c:27e4:15::64 server.
DNS64Prefs: []netip.Prefix{netip.MustParsePrefix("2001:67c:27e4:1064::/96")},
})
require.NoError(t, err)
servicetest.RequireRun(t, dnsProxy, dnsproxytest.Timeout)
servicetest.RequireRun(t, dnsProxy, proxytest.Timeout)
syncCh := make(chan struct{})
// Send requests.
g := &sync.WaitGroup{}
g.Add(dnsproxytest.MessageCount)
g.Add(proxytest.MessageCount)
addr := dnsProxy.Addr(ProtoTCP).String()
for range dnsproxytest.MessageCount {
addr := dnsProxy.Addr(proxy.ProtoTCP).String()
for range proxytest.MessageCount {
// The [dns.Conn] isn't safe for concurrent use despite the requirements
// from the [net.Conn] documentation.
conn, err := dns.Dial("tcp", addr)
var conn *dns.Conn
conn, err = dns.Dial("tcp", addr)
require.NoError(t, err)
testutil.CleanupAndRequireSuccess(t, conn.Close)
go exchangeTestAAAARequestAsync(t, conn, g, ipv4OnlyFqdn, syncCh)
go exchangeTestAAAARequestAsync(t, conn, g, proxytest.FQDN, syncCh)
}
close(syncCh)
@@ -113,50 +120,12 @@ func exchangeTestAAAARequestAsync(
require.IsType(pt, &dns.AAAA{}, res.Answer[0])
}
// newRR is a helper that creates a new dns.RR with the given name, qtype,
// ttl and value. It fails the test if the qtype is not supported or the type
// of value doesn't match the qtype.
func newRR(tb testing.TB, name string, qtype uint16, ttl uint32, val any) (rr dns.RR) {
tb.Helper()
switch qtype {
case dns.TypeA:
rr = &dns.A{A: testutil.RequireTypeAssert[net.IP](tb, val)}
case dns.TypeAAAA:
rr = &dns.AAAA{AAAA: testutil.RequireTypeAssert[net.IP](tb, val)}
case dns.TypeCNAME:
rr = &dns.CNAME{Target: testutil.RequireTypeAssert[string](tb, val)}
case dns.TypeSOA:
rr = &dns.SOA{
Ns: "ns." + name,
Mbox: "hostmaster." + name,
Serial: 1,
Refresh: 1,
Retry: 1,
Expire: 1,
Minttl: 1,
}
case dns.TypePTR:
rr = &dns.PTR{Ptr: testutil.RequireTypeAssert[string](tb, val)}
default:
tb.Fatalf("unsupported qtype: %d", qtype)
}
*rr.Header() = dns.RR_Header{
Name: name,
Rrtype: qtype,
Class: dns.ClassINET,
Ttl: ttl,
}
return rr
}
// TODO(e.burkov): Refactor the test.
// TODO(f.setrakov): Refactor the test.
func TestProxy_Resolve_dns64(t *testing.T) {
someIPv4 := net.IP{1, 2, 3, 4}
someIPv6 := net.IP{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
mappedIPv6 := net.ParseIP("64:ff9b::102:304")
filteredIPv6 := net.ParseIP("64:ff9b::506:708")
ptr64Domain, err := netutil.IPToReversedAddr(mappedIPv6)
require.NoError(t, err)
@@ -166,8 +135,6 @@ func TestProxy_Resolve_dns64(t *testing.T) {
require.NoError(t, err)
ptrGlobDomain = dns.Fqdn(ptrGlobDomain)
localCliAddr := netip.MustParseAddrPort("192.168.1.1:1234")
const (
domainIPv6 = "ipv6.only."
domainSOA = "ipv4.soa."
@@ -195,7 +162,7 @@ func TestProxy_Resolve_dns64(t *testing.T) {
pt := testutil.NewPanicT(t)
newUps := func(answers answerMap) (u upstream.Upstream) {
return &testUpstream{
return &dnsproxytest.Upstream{
OnExchange: func(_ context.Context, req *dns.Msg) (resp *dns.Msg, err error) {
q := req.Question[0]
require.Contains(pt, answers, q.Qtype)
@@ -214,8 +181,8 @@ func TestProxy_Resolve_dns64(t *testing.T) {
}
}
localRR := newRR(t, ptr64Domain, dns.TypePTR, 3600, domainPointed)
localUps := &testUpstream{
localRR := proxytest.NewRR(t, ptr64Domain, dns.TypePTR, 3600, domainPointed)
localUps := &dnsproxytest.Upstream{
OnExchange: func(_ context.Context, req *dns.Msg) (resp *dns.Msg, err error) {
require.Equal(pt, req.Question[0].Name, ptr64Domain)
resp = (&dns.Msg{}).SetReply(req)
@@ -235,19 +202,20 @@ func TestProxy_Resolve_dns64(t *testing.T) {
qtype uint16
}{{
name: "simple_a",
qname: ipv4OnlyFqdn,
qname: proxytest.FQDN,
upsAns: answerMap{
dns.TypeA: {
sectionAnswer: {newRR(t, ipv4OnlyFqdn, dns.TypeA, 3600, someIPv4)},
sectionAnswer: {proxytest.NewRR(t, proxytest.FQDN, dns.TypeA, 3600, someIPv4)},
},
dns.TypeAAAA: {},
},
wantAns: []dns.RR{&dns.A{
Hdr: dns.RR_Header{
Name: ipv4OnlyFqdn,
Rrtype: dns.TypeA,
Class: dns.ClassINET,
Ttl: 3600,
Name: proxytest.FQDN,
Rrtype: dns.TypeA,
Class: dns.ClassINET,
Ttl: 3600,
Rdlength: 4,
},
A: someIPv4,
}},
@@ -258,34 +226,36 @@ func TestProxy_Resolve_dns64(t *testing.T) {
upsAns: answerMap{
dns.TypeA: {},
dns.TypeAAAA: {
sectionAnswer: {newRR(t, domainIPv6, dns.TypeAAAA, 3600, someIPv6)},
sectionAnswer: {proxytest.NewRR(t, domainIPv6, dns.TypeAAAA, 3600, someIPv6)},
},
},
wantAns: []dns.RR{&dns.AAAA{
Hdr: dns.RR_Header{
Name: domainIPv6,
Rrtype: dns.TypeAAAA,
Class: dns.ClassINET,
Ttl: 3600,
Name: domainIPv6,
Rrtype: dns.TypeAAAA,
Class: dns.ClassINET,
Ttl: 3600,
Rdlength: 16,
},
AAAA: someIPv6,
}},
qtype: dns.TypeAAAA,
}, {
name: "actual_dns64",
qname: ipv4OnlyFqdn,
qname: proxytest.FQDN,
upsAns: answerMap{
dns.TypeA: {
sectionAnswer: {newRR(t, ipv4OnlyFqdn, dns.TypeA, 3600, someIPv4)},
sectionAnswer: {proxytest.NewRR(t, proxytest.FQDN, dns.TypeA, 3600, someIPv4)},
},
dns.TypeAAAA: {},
},
wantAns: []dns.RR{&dns.AAAA{
Hdr: dns.RR_Header{
Name: ipv4OnlyFqdn,
Rrtype: dns.TypeAAAA,
Class: dns.ClassINET,
Ttl: maxDNS64SynTTL,
Name: proxytest.FQDN,
Rrtype: dns.TypeAAAA,
Class: dns.ClassINET,
Ttl: testSynTTL,
Rdlength: 16,
},
AAAA: mappedIPv6,
}},
@@ -295,18 +265,19 @@ func TestProxy_Resolve_dns64(t *testing.T) {
qname: domainSOA,
upsAns: answerMap{
dns.TypeA: {
sectionAnswer: {newRR(t, domainSOA, dns.TypeA, 3600, someIPv4)},
sectionAnswer: {proxytest.NewRR(t, domainSOA, dns.TypeA, 3600, someIPv4)},
},
dns.TypeAAAA: {
sectionAuthority: {newRR(t, domainSOA, dns.TypeSOA, maxDNS64SynTTL+50, nil)},
sectionAuthority: {proxytest.NewRR(t, domainSOA, dns.TypeSOA, testSynTTL+50, nil)},
},
},
wantAns: []dns.RR{&dns.AAAA{
Hdr: dns.RR_Header{
Name: domainSOA,
Rrtype: dns.TypeAAAA,
Class: dns.ClassINET,
Ttl: maxDNS64SynTTL + 50,
Name: domainSOA,
Rrtype: dns.TypeAAAA,
Class: dns.ClassINET,
Ttl: testSynTTL + 50,
Rdlength: 16,
},
AAAA: mappedIPv6,
}},
@@ -318,17 +289,18 @@ func TestProxy_Resolve_dns64(t *testing.T) {
dns.TypeA: {},
dns.TypeAAAA: {
sectionAnswer: {
newRR(t, domainMapped, dns.TypeAAAA, 3600, net.ParseIP("64:ff9b::506:708")),
newRR(t, domainMapped, dns.TypeCNAME, 3600, domainAnother),
proxytest.NewRR(t, domainMapped, dns.TypeAAAA, 3600, filteredIPv6),
proxytest.NewRR(t, domainMapped, dns.TypeCNAME, 3600, domainAnother),
},
},
},
wantAns: []dns.RR{&dns.CNAME{
Hdr: dns.RR_Header{
Name: domainMapped,
Rrtype: dns.TypeCNAME,
Class: dns.ClassINET,
Ttl: 3600,
Name: domainMapped,
Rrtype: dns.TypeCNAME,
Class: dns.ClassINET,
Ttl: 3600,
Rdlength: 16,
},
Target: domainAnother,
}},
@@ -339,10 +311,11 @@ func TestProxy_Resolve_dns64(t *testing.T) {
upsAns: nil,
wantAns: []dns.RR{&dns.PTR{
Hdr: dns.RR_Header{
Name: ptr64Domain,
Rrtype: dns.TypePTR,
Class: dns.ClassINET,
Ttl: 3600,
Name: ptr64Domain,
Rrtype: dns.TypePTR,
Class: dns.ClassINET,
Ttl: 3600,
Rdlength: 16,
},
Ptr: domainPointed,
}},
@@ -352,15 +325,16 @@ func TestProxy_Resolve_dns64(t *testing.T) {
qname: ptrGlobDomain,
upsAns: answerMap{
dns.TypePTR: {
sectionAnswer: {newRR(t, ptrGlobDomain, dns.TypePTR, 3600, domainGlob)},
sectionAnswer: {proxytest.NewRR(t, ptrGlobDomain, dns.TypePTR, 3600, domainGlob)},
},
},
wantAns: []dns.RR{&dns.PTR{
Hdr: dns.RR_Header{
Name: ptrGlobDomain,
Rrtype: dns.TypePTR,
Class: dns.ClassINET,
Ttl: 3600,
Name: ptrGlobDomain,
Rrtype: dns.TypePTR,
Class: dns.ClassINET,
Ttl: 3600,
Rdlength: 15,
},
Ptr: domainGlob,
}},
@@ -371,35 +345,37 @@ func TestProxy_Resolve_dns64(t *testing.T) {
upsAns: answerMap{
dns.TypeA: {
sectionAnswer: {
newRR(t, fqdnCNAMEOnly, dns.TypeCNAME, 3600, fqdnTerminal),
newRR(t, fqdnTerminal, dns.TypeA, 3600, someIPv4),
proxytest.NewRR(t, fqdnCNAMEOnly, dns.TypeCNAME, 3600, fqdnTerminal),
proxytest.NewRR(t, fqdnTerminal, dns.TypeA, 3600, someIPv4),
},
},
dns.TypeAAAA: {
sectionAnswer: {
newRR(t, fqdnCNAMEOnly, dns.TypeCNAME, 3600, fqdnTerminal),
proxytest.NewRR(t, fqdnCNAMEOnly, dns.TypeCNAME, 3600, fqdnTerminal),
},
sectionAuthority: {
newRR(t, fqdnTerminal, dns.TypeSOA, 300, nil),
proxytest.NewRR(t, fqdnTerminal, dns.TypeSOA, 300, nil),
},
},
},
wantAns: []dns.RR{
&dns.CNAME{
Hdr: dns.RR_Header{
Name: fqdnCNAMEOnly,
Rrtype: dns.TypeCNAME,
Class: dns.ClassINET,
Ttl: 3600,
Name: fqdnCNAMEOnly,
Rrtype: dns.TypeCNAME,
Class: dns.ClassINET,
Ttl: 3600,
Rdlength: 15,
},
Target: fqdnTerminal,
},
&dns.AAAA{
Hdr: dns.RR_Header{
Name: fqdnTerminal,
Rrtype: dns.TypeAAAA,
Class: dns.ClassINET,
Ttl: maxDNS64SynTTL,
Name: fqdnTerminal,
Rrtype: dns.TypeAAAA,
Class: dns.ClassINET,
Ttl: testSynTTL,
Rdlength: 16,
},
AAAA: mappedIPv6,
},
@@ -409,39 +385,42 @@ func TestProxy_Resolve_dns64(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
udpAddr := net.UDPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)
tcpAddr := net.TCPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)
udpAddr := net.UDPAddrFromAddrPort(proxytest.LocalhostAnyPort)
tcpAddr := net.TCPAddrFromAddrPort(proxytest.LocalhostAnyPort)
p := mustNew(t, &Config{
var p *proxy.Proxy
p, err = proxy.New(&proxy.Config{
Logger: testLogger,
UDPListenAddr: []*net.UDPAddr{udpAddr},
TCPListenAddr: []*net.TCPAddr{tcpAddr},
UpstreamConfig: &UpstreamConfig{
UpstreamConfig: &proxy.UpstreamConfig{
Upstreams: []upstream.Upstream{newUps(tc.upsAns)},
},
PrivateRDNSUpstreamConfig: &UpstreamConfig{
PrivateRDNSUpstreamConfig: &proxy.UpstreamConfig{
Upstreams: []upstream.Upstream{localUps},
},
TrustedProxies: dnsproxytest.DefaultTrustedProxies,
TrustedProxies: proxytest.DefaultTrustedProxies,
CacheEnabled: true,
UseDNS64: true,
UsePrivateRDNS: true,
PrivateSubnets: netutil.SubnetSetFunc(netutil.IsLocallyServed),
})
require.NoError(t, err)
servicetest.RequireRun(t, p, proxytest.Timeout)
servicetest.RequireRun(t, p, dnsproxytest.Timeout)
dctx := &DNSContext{
Req: (&dns.Msg{}).SetQuestion(tc.qname, tc.qtype),
Addr: localCliAddr,
}
err = p.handleDNSRequest(testutil.ContextWithTimeout(t, defaultTimeout), dctx)
var conn *dns.Conn
conn, err = dns.Dial("tcp", p.Addr(proxy.ProtoTCP).String())
require.NoError(t, err)
res := dctx.Res
err = conn.WriteMsg((&dns.Msg{}).SetQuestion(tc.qname, tc.qtype))
require.NoError(t, err)
var res *dns.Msg
res, err = conn.ReadMsg()
require.NoError(t, err)
require.NotNil(t, res)
assert.Equal(t, tc.wantAns, res.Answer)
})
}
+2 -2
View File
@@ -214,8 +214,8 @@ func TestProxy_Exchange_loadBalance(t *testing.T) {
p := mustNew(t, &Config{
Logger: testLogger,
UDPListenAddr: []*net.UDPAddr{net.UDPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)},
TCPListenAddr: []*net.TCPAddr{net.TCPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)},
UDPListenAddr: []*net.UDPAddr{dnsproxytest.LocalhostAnyPortUDP},
TCPListenAddr: []*net.TCPAddr{dnsproxytest.LocalhostAnyPortTCP},
UpstreamConfig: &UpstreamConfig{
Upstreams: ups,
},
+6 -3
View File
@@ -7,6 +7,7 @@ import (
"testing"
"github.com/AdguardTeam/dnsproxy/internal/dnsproxytest"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/testutil/servicetest"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
@@ -43,10 +44,12 @@ func TestFilteringHandler(t *testing.T) {
dnsProxy := mustNew(t, &Config{
Logger: testLogger,
TrustedProxies: dnsproxytest.DefaultTrustedProxies,
UpstreamConfig: newTestUpstreamConfig(t, newTestUpstream(t)),
UpstreamConfig: &UpstreamConfig{
Upstreams: []upstream.Upstream{newTestUpstream(t)},
},
RequestHandler: reqHandler,
UDPListenAddr: []*net.UDPAddr{net.UDPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)},
TCPListenAddr: []*net.TCPAddr{net.TCPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)},
UDPListenAddr: []*net.UDPAddr{dnsproxytest.LocalhostAnyPortUDP},
TCPListenAddr: []*net.TCPAddr{dnsproxytest.LocalhostAnyPortTCP},
})
servicetest.RequireRun(t, dnsProxy, dnsproxytest.Timeout)
+5 -2
View File
@@ -6,6 +6,7 @@ import (
"time"
"github.com/AdguardTeam/dnsproxy/internal/dnsproxytest"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -18,8 +19,10 @@ func TestLookupNetIP(t *testing.T) {
t.Parallel()
conf := &Config{
Logger: testLogger,
UpstreamConfig: newTestUpstreamConfig(t, newTestUpstream(t)),
Logger: testLogger,
UpstreamConfig: &UpstreamConfig{
Upstreams: []upstream.Upstream{newTestUpstream(t)},
},
}
p, err := New(conf)
+19 -270
View File
@@ -12,7 +12,6 @@ import (
"github.com/AdguardTeam/dnsproxy/internal/dnsproxytest"
"github.com/AdguardTeam/dnsproxy/upstream"
glcache "github.com/AdguardTeam/golibs/cache"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/contextutil"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil"
@@ -67,15 +66,6 @@ func firstIP(resp *dns.Msg) (ip net.IP) {
return nil
}
// newTestUpstreamConfig creates a new UpstreamConfig with given upstreams set.
func newTestUpstreamConfig(tb testing.TB, ups ...upstream.Upstream) (u *UpstreamConfig) {
tb.Helper()
return &UpstreamConfig{
Upstreams: ups,
}
}
// newTestUpstream returns default upstream mock, which responds with single A
// record with [dnsproxytest.IPv4] value to all requests. Other upstream
// methods will be replaced with stub implementations.
@@ -254,8 +244,8 @@ func TestProxy_Resolve_dnssecCache(t *testing.T) {
p := mustNew(t, &Config{
Logger: testLogger,
UDPListenAddr: []*net.UDPAddr{net.UDPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)},
TCPListenAddr: []*net.TCPAddr{net.TCPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)},
UDPListenAddr: []*net.UDPAddr{dnsproxytest.LocalhostAnyPortUDP},
TCPListenAddr: []*net.TCPAddr{dnsproxytest.LocalhostAnyPortTCP},
UpstreamConfig: &UpstreamConfig{Upstreams: []upstream.Upstream{u}},
TrustedProxies: dnsproxytest.DefaultTrustedProxies,
CacheEnabled: true,
@@ -345,257 +335,14 @@ func TestProxy_Resolve_dnssecCache(t *testing.T) {
}
}
func TestProxy_HandleDNSRequest_exchangeWithReservedDomains(t *testing.T) {
t.Parallel()
emptyUpstream := newTestUpstreamWithExchange(
t,
func(_ context.Context, req *dns.Msg) (resp *dns.Msg, err error) {
return (&dns.Msg{}).SetReply(req), nil
},
)
const (
host1 = "example-1.test"
host2 = "test.example-1.test"
fqdn1 = host1 + "."
fqdn2 = host2 + "."
)
upsConf := &UpstreamConfig{
DomainReservedUpstreams: map[string][]upstream.Upstream{
fqdn1: {emptyUpstream},
fqdn2: {},
},
SpecifiedDomainUpstreams: map[string][]upstream.Upstream{
fqdn1: {emptyUpstream},
fqdn2: {},
},
SubdomainExclusions: container.NewMapSet[string](),
Upstreams: []upstream.Upstream{newTestUpstream(t)},
}
dnsProxy := mustNew(t, &Config{
Logger: testLogger,
UDPListenAddr: []*net.UDPAddr{net.UDPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)},
TCPListenAddr: []*net.TCPAddr{net.TCPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)},
UpstreamConfig: upsConf,
TrustedProxies: dnsproxytest.DefaultTrustedProxies,
})
servicetest.RequireRun(t, dnsProxy, dnsproxytest.Timeout)
addr := dnsProxy.Addr(ProtoTCP)
conn, err := dns.Dial("tcp", addr.String())
require.NoError(t, err)
var res *dns.Msg
require.True(t, t.Run("common", func(t *testing.T) {
req := dnsproxytest.NewTestRequest()
err = conn.WriteMsg(req)
require.NoError(t, err)
res, err = conn.ReadMsg()
require.NoError(t, err)
dnsproxytest.RequireResponse(t, req, res)
}))
require.True(t, t.Run("empty_upstream_response", func(t *testing.T) {
req := dnsproxytest.NewTestRequestWithHost(host1)
err = conn.WriteMsg(req)
require.NoError(t, err)
res, err = conn.ReadMsg()
require.NoError(t, err)
require.Nil(t, res.Answer)
}))
require.True(t, t.Run("non_empty_response_for_subdomain", func(t *testing.T) {
req := dnsproxytest.NewTestRequestWithHost(host2)
err = conn.WriteMsg(req)
require.NoError(t, err)
res, err = conn.ReadMsg()
require.NoError(t, err)
dnsproxytest.RequireResponse(t, req, res)
}))
}
func TestProxy_HandleDNSRequest_oneByOneUpstreamsExchange(t *testing.T) {
t.Parallel()
errUpstream := newTestUpstreamWithExchange(
t,
func(_ context.Context, req *dns.Msg) (resp *dns.Msg, err error) {
return nil, assert.AnError
},
)
dnsProxy := mustNew(t, &Config{
Logger: testLogger,
UDPListenAddr: []*net.UDPAddr{net.UDPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)},
TCPListenAddr: []*net.TCPAddr{net.TCPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)},
UpstreamConfig: newTestUpstreamConfig(
t,
errUpstream,
errUpstream,
newTestUpstream(t),
),
TrustedProxies: dnsproxytest.DefaultTrustedProxies,
Fallbacks: newTestUpstreamConfig(t, errUpstream),
})
servicetest.RequireRun(t, dnsProxy, dnsproxytest.Timeout)
addr := dnsProxy.Addr(ProtoTCP)
conn, err := dns.Dial("tcp", addr.String())
require.NoError(t, err)
req := dnsproxytest.NewTestRequest()
err = conn.WriteMsg(req)
require.NoError(t, err)
start := time.Now()
res, err := conn.ReadMsg()
require.NoError(t, err)
dnsproxytest.RequireResponse(t, req, res)
elapsed := time.Since(start)
assert.Greater(t, 3*dnsproxytest.Timeout, elapsed)
}
func TestProxy_HandleDNSRequest_fallback(t *testing.T) {
t.Parallel()
const (
hostSuccess = "specific.example"
hostFail = "failing.example"
hostFallbackSuccess = "fallback.success.example"
fqdnSuccess = hostSuccess + "."
fqdnFail = hostFail + "."
fqdnFallbackSuccess = hostFallbackSuccess + "."
)
responseCh := make(chan uint16)
failCh := make(chan uint16)
pt := testutil.NewPanicT(t)
successExchange := func(_ context.Context, req *dns.Msg) (resp *dns.Msg, err error) {
testutil.RequireSend(pt, responseCh, req.Id, dnsproxytest.Timeout)
return dnsproxytest.NewTestResponse(req), nil
}
successUpstream := newTestUpstreamWithExchange(t, successExchange)
failUpstream := newTestUpstreamWithExchange(
t,
func(_ context.Context, req *dns.Msg) (resp *dns.Msg, err error) {
testutil.RequireSend(pt, failCh, req.Id, dnsproxytest.Timeout)
return nil, assert.AnError
},
)
upsConf := &UpstreamConfig{
DomainReservedUpstreams: map[string][]upstream.Upstream{
fqdnSuccess: {successUpstream},
fqdnFail: {failUpstream},
},
SpecifiedDomainUpstreams: map[string][]upstream.Upstream{
fqdnSuccess: {successUpstream},
fqdnFail: {failUpstream},
},
SubdomainExclusions: container.NewMapSet[string](),
Upstreams: []upstream.Upstream{failUpstream},
}
fallbackConf := &UpstreamConfig{
DomainReservedUpstreams: map[string][]upstream.Upstream{
fqdnFail: {failUpstream},
fqdnFallbackSuccess: {successUpstream},
},
SpecifiedDomainUpstreams: map[string][]upstream.Upstream{
fqdnFail: {failUpstream},
fqdnFallbackSuccess: {successUpstream},
},
SubdomainExclusions: container.NewMapSet[string](),
Upstreams: []upstream.Upstream{failUpstream, successUpstream},
}
dnsProxy := mustNew(t, &Config{
Logger: testLogger,
UDPListenAddr: []*net.UDPAddr{net.UDPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)},
TCPListenAddr: []*net.TCPAddr{net.TCPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)},
UpstreamConfig: upsConf,
TrustedProxies: dnsproxytest.DefaultTrustedProxies,
Fallbacks: fallbackConf,
})
servicetest.RequireRun(t, dnsProxy, dnsproxytest.Timeout)
conn, err := dns.Dial("tcp", dnsProxy.Addr(ProtoTCP).String())
require.NoError(t, err)
testCases := []struct {
name string
wantSignals []chan uint16
}{{
name: dnsproxytest.Host,
wantSignals: []chan uint16{
failCh,
// Both non-specific fallbacks tried.
failCh,
responseCh,
},
}, {
name: hostSuccess,
wantSignals: []chan uint16{
responseCh,
},
}, {
name: hostFail,
wantSignals: []chan uint16{
failCh,
failCh,
},
}, {
name: hostFallbackSuccess,
wantSignals: []chan uint16{
failCh,
responseCh,
},
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
req := dnsproxytest.NewTestRequestWithHost(tc.name)
err = conn.WriteMsg(req)
require.NoError(t, err)
for _, ch := range tc.wantSignals {
reqID, ok := testutil.RequireReceive(testutil.NewPanicT(t), ch, dnsproxytest.Timeout)
require.True(t, ok)
assert.Equal(t, req.Id, reqID)
}
_, err = conn.ReadMsg()
require.NoError(t, err)
})
}
}
func TestProxy_Resolve_exchangeCustomUpstreamConfig(t *testing.T) {
p := mustNew(t, &Config{
Logger: testLogger,
UDPListenAddr: []*net.UDPAddr{net.UDPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)},
TCPListenAddr: []*net.TCPAddr{net.TCPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)},
UpstreamConfig: newTestUpstreamConfig(t, newTestUpstream(t)),
Logger: testLogger,
UDPListenAddr: []*net.UDPAddr{dnsproxytest.LocalhostAnyPortUDP},
TCPListenAddr: []*net.TCPAddr{dnsproxytest.LocalhostAnyPortTCP},
UpstreamConfig: &UpstreamConfig{
Upstreams: []upstream.Upstream{newTestUpstream(t)},
},
TrustedProxies: dnsproxytest.DefaultTrustedProxies,
})
@@ -632,10 +379,12 @@ func TestProxy_Resolve_exchangeCustomUpstreamConfig(t *testing.T) {
func TestProxy_Resolve_customUpstreamConfigCache(t *testing.T) {
prx := mustNew(t, &Config{
Logger: testLogger,
UDPListenAddr: []*net.UDPAddr{net.UDPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)},
TCPListenAddr: []*net.TCPAddr{net.TCPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)},
UpstreamConfig: newTestUpstreamConfig(t, newTestUpstream(t)),
Logger: testLogger,
UDPListenAddr: []*net.UDPAddr{dnsproxytest.LocalhostAnyPortUDP},
TCPListenAddr: []*net.TCPAddr{dnsproxytest.LocalhostAnyPortTCP},
UpstreamConfig: &UpstreamConfig{
Upstreams: []upstream.Upstream{newTestUpstream(t)},
},
TrustedProxies: dnsproxytest.DefaultTrustedProxies,
CacheEnabled: true,
DNSSECEnabled: true,
@@ -767,8 +516,8 @@ func TestProxy_Resolve_ecs(t *testing.T) {
prx := mustNew(t, &Config{
Logger: testLogger,
UDPListenAddr: []*net.UDPAddr{net.UDPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)},
TCPListenAddr: []*net.TCPAddr{net.TCPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)},
UDPListenAddr: []*net.UDPAddr{dnsproxytest.LocalhostAnyPortUDP},
TCPListenAddr: []*net.TCPAddr{dnsproxytest.LocalhostAnyPortTCP},
UpstreamConfig: &UpstreamConfig{
Upstreams: []upstream.Upstream{u},
},
@@ -887,8 +636,8 @@ func TestProxy_Resolve_ecsProxyCacheMinMaxTTL(t *testing.T) {
}}
ecsIP = clientIP
udpAddr := net.UDPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)
tcpAddr := net.TCPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)
udpAddr := dnsproxytest.LocalhostAnyPortUDP
tcpAddr := dnsproxytest.LocalhostAnyPortTCP
prx := mustNew(t, &Config{
Logger: testLogger,
@@ -1098,7 +847,7 @@ func TestProxy_ValidateRequest(t *testing.T) {
p, err := New(&Config{
Logger: testLogger,
UDPListenAddr: []*net.UDPAddr{net.UDPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)},
UDPListenAddr: []*net.UDPAddr{dnsproxytest.LocalhostAnyPortUDP},
UpstreamConfig: &UpstreamConfig{Upstreams: []upstream.Upstream{ups}},
RefuseAny: true,
PrivateSubnets: privateNets,
+279 -21
View File
@@ -15,6 +15,7 @@ import (
proxytest "github.com/AdguardTeam/dnsproxy/internal/dnsproxytest"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/AdguardTeam/golibs/testutil/servicetest"
@@ -39,30 +40,35 @@ var (
testIPv4 = net.IP{192, 0, 2, 0}
)
// newTestUpstreamConfig creates a new upstream config with the default mock
// upstream, which expects the request will contain a single question with the
// [proxytest.Host] name.
//
// TODO(f.setrakov): DRY with internal version.
func newTestUpstreamConfig(tb testing.TB) (uc *proxy.UpstreamConfig) {
// TODO(f.setrakov): DRY upstream helpers with their internal versions.
// newTestUpstream returns default upstream mock, which responds with single A
// record with [proxytest.IPv4] value to all requests. Other upstream
// methods will be replaced with stub implementations.
func newTestUpstream(tb testing.TB) (uc upstream.Upstream) {
tb.Helper()
onExchange := func(_ context.Context, req *dns.Msg) (resp *dns.Msg, err error) {
pt := testutil.NewPanicT(tb)
require.Len(pt, req.Question, 1)
assert.Equal(pt, dns.Fqdn(proxytest.Host), req.Question[0].Name)
return proxytest.NewTestResponse(req), nil
}
u := &dnsproxytest.Upstream{
return newTestUpstreamWithExchange(tb, onExchange)
}
// newTestUpstreamWithExchange returns upstream mock which uses given callback
// for [upstream.Upstream.Exchange] implementation. Other upstream methods will
// be replaced with stub implementations.
func newTestUpstreamWithExchange(
tb testing.TB,
onExchange func(ctx context.Context, req *dns.Msg) (resp *dns.Msg, err error),
) (uc upstream.Upstream) {
tb.Helper()
return &dnsproxytest.Upstream{
OnAddress: func() (addr string) { return "" },
OnExchange: onExchange,
OnClose: func() (err error) { return nil },
}
return &proxy.UpstreamConfig{
Upstreams: []upstream.Upstream{u},
}
}
// mustStartDefaultProxy starts a new proxy with default settings and returns
@@ -71,10 +77,12 @@ func mustStartDefaultProxy(tb testing.TB) (p *proxy.Proxy) {
tb.Helper()
p, err := proxy.New(&proxy.Config{
Logger: testLogger,
UDPListenAddr: []*net.UDPAddr{net.UDPAddrFromAddrPort(proxytest.LocalhostAnyPort)},
TCPListenAddr: []*net.TCPAddr{net.TCPAddrFromAddrPort(proxytest.LocalhostAnyPort)},
UpstreamConfig: newTestUpstreamConfig(tb),
Logger: testLogger,
UDPListenAddr: []*net.UDPAddr{net.UDPAddrFromAddrPort(proxytest.LocalhostAnyPort)},
TCPListenAddr: []*net.TCPAddr{net.TCPAddrFromAddrPort(proxytest.LocalhostAnyPort)},
UpstreamConfig: &proxy.UpstreamConfig{
Upstreams: []upstream.Upstream{newTestUpstream(tb)},
},
TrustedProxies: proxytest.DefaultTrustedProxies,
})
require.NoError(tb, err)
@@ -186,7 +194,9 @@ func isCachedWithCustomConfig(
}
func TestProxy_HandleDNSRequest_race(t *testing.T) {
upsConf := newTestUpstreamConfig(t)
upsConf := &proxy.UpstreamConfig{
Upstreams: []upstream.Upstream{newTestUpstream(t)},
}
dnsProxy, err := proxy.New(&proxy.Config{
Logger: testLogger,
@@ -487,3 +497,251 @@ func requireReadPacket(tb testing.TB, conn net.Conn) (data []byte) {
return buf[:n]
}
func TestProxy_HandleDNSRequest_exchangeWithReservedDomains(t *testing.T) {
t.Parallel()
emptyUpstream := newTestUpstreamWithExchange(
t,
func(_ context.Context, req *dns.Msg) (resp *dns.Msg, err error) {
return (&dns.Msg{}).SetReply(req), nil
},
)
const (
host1 = "test-1.example"
host2 = "subdomain." + host1
fqdn1 = host1 + "."
fqdn2 = host2 + "."
)
upsConf := &proxy.UpstreamConfig{
DomainReservedUpstreams: map[string][]upstream.Upstream{
fqdn1: {emptyUpstream},
fqdn2: {},
},
SpecifiedDomainUpstreams: map[string][]upstream.Upstream{
fqdn1: {emptyUpstream},
fqdn2: {},
},
SubdomainExclusions: container.NewMapSet[string](),
Upstreams: []upstream.Upstream{newTestUpstream(t)},
}
dnsProxy, err := proxy.New(&proxy.Config{
Logger: testLogger,
UDPListenAddr: []*net.UDPAddr{net.UDPAddrFromAddrPort(proxytest.LocalhostAnyPort)},
TCPListenAddr: []*net.TCPAddr{net.TCPAddrFromAddrPort(proxytest.LocalhostAnyPort)},
UpstreamConfig: upsConf,
TrustedProxies: proxytest.DefaultTrustedProxies,
})
require.NoError(t, err)
servicetest.RequireRun(t, dnsProxy, proxytest.Timeout)
addr := dnsProxy.Addr(proxy.ProtoTCP)
conn, err := dns.Dial("tcp", addr.String())
require.NoError(t, err)
testutil.CleanupAndRequireSuccess(t, conn.Close)
testCases := []struct {
name string
host string
wantResponse bool
}{{
name: "common",
host: proxytest.Host,
wantResponse: true,
}, {
name: "empty_upstream_response",
host: host1,
wantResponse: false,
}, {
name: "non_empty_response_for_subdomain",
host: host2,
wantResponse: true,
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
req := proxytest.NewTestRequestWithHost(tc.host)
err = conn.WriteMsg(req)
require.NoError(t, err)
var res *dns.Msg
res, err = conn.ReadMsg()
require.NoError(t, err)
if tc.wantResponse {
proxytest.RequireResponse(t, req, res)
} else {
require.NotNil(t, res)
require.Empty(t, res.Answer)
}
})
}
}
func TestProxy_HandleDNSRequest_oneByOneUpstreamsExchange(t *testing.T) {
t.Parallel()
errUpstream := newTestUpstreamWithExchange(
t,
func(_ context.Context, req *dns.Msg) (resp *dns.Msg, err error) {
return nil, assert.AnError
},
)
dnsProxy, err := proxy.New(&proxy.Config{
Logger: testLogger,
UDPListenAddr: []*net.UDPAddr{net.UDPAddrFromAddrPort(proxytest.LocalhostAnyPort)},
TCPListenAddr: []*net.TCPAddr{net.TCPAddrFromAddrPort(proxytest.LocalhostAnyPort)},
UpstreamConfig: &proxy.UpstreamConfig{
Upstreams: []upstream.Upstream{errUpstream, errUpstream, newTestUpstream(t)},
},
TrustedProxies: proxytest.DefaultTrustedProxies,
Fallbacks: &proxy.UpstreamConfig{
Upstreams: []upstream.Upstream{errUpstream},
},
})
require.NoError(t, err)
servicetest.RequireRun(t, dnsProxy, proxytest.Timeout)
addr := dnsProxy.Addr(proxy.ProtoTCP)
conn, err := dns.Dial("tcp", addr.String())
require.NoError(t, err)
testutil.CleanupAndRequireSuccess(t, conn.Close)
req := proxytest.NewTestRequest()
err = conn.WriteMsg(req)
require.NoError(t, err)
res, err := conn.ReadMsg()
require.NoError(t, err)
proxytest.RequireResponse(t, req, res)
}
func TestProxy_HandleDNSRequest_fallback(t *testing.T) {
t.Parallel()
const (
hostSuccess = "specific.example"
hostFail = "failing.example"
hostFallbackSuccess = "fallback.success.example"
fqdnSuccess = hostSuccess + "."
fqdnFail = hostFail + "."
fqdnFallbackSuccess = hostFallbackSuccess + "."
)
responseCh := make(chan uint16)
failCh := make(chan uint16)
pt := testutil.NewPanicT(t)
successExchange := func(_ context.Context, req *dns.Msg) (resp *dns.Msg, err error) {
testutil.RequireSend(pt, responseCh, req.Id, proxytest.Timeout)
return proxytest.NewTestResponse(req), nil
}
successUpstream := newTestUpstreamWithExchange(t, successExchange)
failUpstream := newTestUpstreamWithExchange(
t,
func(_ context.Context, req *dns.Msg) (resp *dns.Msg, err error) {
testutil.RequireSend(pt, failCh, req.Id, proxytest.Timeout)
return nil, assert.AnError
},
)
upsConf := &proxy.UpstreamConfig{
DomainReservedUpstreams: map[string][]upstream.Upstream{
fqdnSuccess: {successUpstream},
fqdnFail: {failUpstream},
},
SpecifiedDomainUpstreams: map[string][]upstream.Upstream{
fqdnSuccess: {successUpstream},
fqdnFail: {failUpstream},
},
SubdomainExclusions: container.NewMapSet[string](),
Upstreams: []upstream.Upstream{failUpstream},
}
fallbackConf := &proxy.UpstreamConfig{
DomainReservedUpstreams: map[string][]upstream.Upstream{
fqdnFail: {failUpstream},
fqdnFallbackSuccess: {successUpstream},
},
SpecifiedDomainUpstreams: map[string][]upstream.Upstream{
fqdnFail: {failUpstream},
fqdnFallbackSuccess: {successUpstream},
},
SubdomainExclusions: container.NewMapSet[string](),
Upstreams: []upstream.Upstream{failUpstream, successUpstream},
}
dnsProxy, err := proxy.New(&proxy.Config{
Logger: testLogger,
UDPListenAddr: []*net.UDPAddr{net.UDPAddrFromAddrPort(proxytest.LocalhostAnyPort)},
TCPListenAddr: []*net.TCPAddr{net.TCPAddrFromAddrPort(proxytest.LocalhostAnyPort)},
UpstreamConfig: upsConf,
TrustedProxies: proxytest.DefaultTrustedProxies,
Fallbacks: fallbackConf,
})
require.NoError(t, err)
servicetest.RequireRun(t, dnsProxy, proxytest.Timeout)
conn, err := dns.Dial("tcp", dnsProxy.Addr(proxy.ProtoTCP).String())
require.NoError(t, err)
testutil.CleanupAndRequireSuccess(t, conn.Close)
testCases := []struct {
name string
wantSignals []chan uint16
}{{
name: proxytest.Host,
wantSignals: []chan uint16{
failCh,
failCh,
responseCh,
},
}, {
name: hostSuccess,
wantSignals: []chan uint16{
responseCh,
},
}, {
name: hostFail,
wantSignals: []chan uint16{
failCh,
failCh,
},
}, {
name: hostFallbackSuccess,
wantSignals: []chan uint16{
failCh,
responseCh,
},
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
req := proxytest.NewTestRequestWithHost(tc.name)
err = conn.WriteMsg(req)
require.NoError(t, err)
for _, ch := range tc.wantSignals {
reqID, ok := testutil.RequireReceive(testutil.NewPanicT(t), ch, proxytest.Timeout)
require.True(t, ok)
assert.Equal(t, req.Id, reqID)
}
_, err = conn.ReadMsg()
require.NoError(t, err)
})
}
}
+4 -1
View File
@@ -7,6 +7,7 @@ import (
"github.com/AdguardTeam/dnscrypt"
"github.com/AdguardTeam/dnsproxy/internal/dnsproxytest"
"github.com/AdguardTeam/dnsproxy/internal/nettest"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/testutil"
@@ -54,7 +55,9 @@ func newTestDNSCryptProxy(tb testing.TB) (p *Proxy, rc dnscrypt.ResolverConfig)
DNSCryptTCPListenAddr: []*net.TCPAddr{{
Port: int(port), IP: net.ParseIP(listenIP),
}},
UpstreamConfig: newTestUpstreamConfig(tb, newTestUpstream(tb)),
UpstreamConfig: &UpstreamConfig{
Upstreams: []upstream.Upstream{newTestUpstream(tb)},
},
TrustedProxies: dnsproxytest.DefaultTrustedProxies,
EnableEDNSClientSubnet: true,
CacheEnabled: true,
+12 -7
View File
@@ -17,6 +17,7 @@ import (
proxytest "github.com/AdguardTeam/dnsproxy/dnsproxytest"
"github.com/AdguardTeam/dnsproxy/internal/dnsproxytest"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/httphdr"
"github.com/AdguardTeam/golibs/netutil/urlutil"
"github.com/AdguardTeam/golibs/testutil"
@@ -68,14 +69,16 @@ func TestProxy_HandleDNSRequest_https(t *testing.T) {
HTTP3Enabled: tc.http3,
}
tlsListenAddr := net.TCPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)
quicListenAddr := net.UDPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)
tlsListenAddr := dnsproxytest.LocalhostAnyPortTCP
quicListenAddr := dnsproxytest.LocalhostAnyPortUDP
dnsProxy, err := proxy.New(&proxy.Config{
Logger: testLogger,
TLSListenAddr: []*net.TCPAddr{tlsListenAddr},
QUICListenAddr: []*net.UDPAddr{quicListenAddr},
TLSConfig: tlsConf,
UpstreamConfig: newTestUpstreamConfig(t),
UpstreamConfig: &proxy.UpstreamConfig{
Upstreams: []upstream.Upstream{newTestUpstream(t)},
},
TrustedProxies: dnsproxytest.DefaultTrustedProxies,
HTTPConfig: httpConf,
})
@@ -150,13 +153,15 @@ func testProxyRealIPDetection(
}
trustedProxies := netip.PrefixFrom(trustedProxy, trustedProxy.BitLen())
dnsProxy, err := proxy.New(&proxy.Config{
Logger: testLogger,
UpstreamConfig: newTestUpstreamConfig(tb),
Logger: testLogger,
UpstreamConfig: &proxy.UpstreamConfig{
Upstreams: []upstream.Upstream{newTestUpstream(tb)},
},
TrustedProxies: trustedProxies,
RequestHandler: reqHandler,
TLSConfig: tlsConf,
TLSListenAddr: []*net.TCPAddr{net.TCPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)},
QUICListenAddr: []*net.UDPAddr{net.UDPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)},
TLSListenAddr: []*net.TCPAddr{dnsproxytest.LocalhostAnyPortTCP},
QUICListenAddr: []*net.UDPAddr{dnsproxytest.LocalhostAnyPortUDP},
HTTPConfig: httpConf,
})
require.NoError(tb, err)
+5 -2
View File
@@ -9,6 +9,7 @@ import (
"github.com/AdguardTeam/dnsproxy/internal/dnsproxytest"
"github.com/AdguardTeam/dnsproxy/proxyutil"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/syncutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/AdguardTeam/golibs/testutil/servicetest"
@@ -26,9 +27,11 @@ func TestProxy_HandleDNSRequest_quicTruncatedRequest(t *testing.T) {
conf := &Config{
Logger: testLogger,
QUICListenAddr: []*net.UDPAddr{net.UDPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)},
QUICListenAddr: []*net.UDPAddr{dnsproxytest.LocalhostAnyPortUDP},
TLSConfig: serverConfig,
UpstreamConfig: newTestUpstreamConfig(t, newTestUpstream(t)),
UpstreamConfig: &UpstreamConfig{
Upstreams: []upstream.Upstream{newTestUpstream(t)},
},
TrustedProxies: dnsproxytest.DefaultTrustedProxies,
RequestHandler: &testHandler{
OnHandle: func(ctx context.Context, p *Proxy, d *DNSContext) (_ error) {
+14 -7
View File
@@ -14,6 +14,7 @@ import (
"github.com/AdguardTeam/dnsproxy/internal/dnsproxytest"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/proxyutil"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/testutil"
"github.com/AdguardTeam/golibs/testutil/servicetest"
"github.com/miekg/dns"
@@ -51,9 +52,11 @@ func TestProxy_HandleDNSRequest_quic(t *testing.T) {
conf := &proxy.Config{
Logger: testLogger,
QUICListenAddr: []*net.UDPAddr{net.UDPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)},
QUICListenAddr: []*net.UDPAddr{dnsproxytest.LocalhostAnyPortUDP},
TLSConfig: serverConfig,
UpstreamConfig: newTestUpstreamConfig(t),
UpstreamConfig: &proxy.UpstreamConfig{
Upstreams: []upstream.Upstream{newTestUpstream(t)},
},
TrustedProxies: dnsproxytest.DefaultTrustedProxies,
}
@@ -64,7 +67,9 @@ func TestProxy_HandleDNSRequest_quic(t *testing.T) {
require.False(t, t.Failed())
conf.QUICListenAddr = []*net.UDPAddr{addr}
conf.UpstreamConfig = newTestUpstreamConfig(t)
conf.UpstreamConfig = &proxy.UpstreamConfig{
Upstreams: []upstream.Upstream{newTestUpstream(t)},
}
require.True(t, t.Run("rerun", func(t *testing.T) {
testHandleDNSRequestQUIC(t, conf, tlsConfig)
@@ -115,13 +120,15 @@ func TestProxy_HandleDNSRequest_quicLargePackets(t *testing.T) {
serverConfig, caPem := dnsproxytest.NewTLSConfig(t)
dnsProxy, err := proxy.New(&proxy.Config{
Logger: testLogger,
UpstreamConfig: newTestUpstreamConfig(t),
Logger: testLogger,
UpstreamConfig: &proxy.UpstreamConfig{
Upstreams: []upstream.Upstream{newTestUpstream(t)},
},
TrustedProxies: dnsproxytest.DefaultTrustedProxies,
RequestHandler: reqHandler,
TLSConfig: serverConfig,
TLSListenAddr: []*net.TCPAddr{net.TCPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)},
QUICListenAddr: []*net.UDPAddr{net.UDPAddrFromAddrPort(dnsproxytest.LocalhostAnyPort)},
TLSListenAddr: []*net.TCPAddr{dnsproxytest.LocalhostAnyPortTCP},
QUICListenAddr: []*net.UDPAddr{dnsproxytest.LocalhostAnyPortUDP},
})
require.NoError(t, err)
+3 -1
View File
@@ -39,7 +39,9 @@ func TestProxy_HandleDNSRequest_tls(t *testing.T) {
TLSListenAddr: []*net.TCPAddr{net.TCPAddrFromAddrPort(proxytest.LocalhostAnyPort)},
QUICListenAddr: []*net.UDPAddr{net.UDPAddrFromAddrPort(proxytest.LocalhostAnyPort)},
TLSConfig: serverConfig,
UpstreamConfig: newTestUpstreamConfig(t),
UpstreamConfig: &proxy.UpstreamConfig{
Upstreams: []upstream.Upstream{newTestUpstream(t)},
},
TrustedProxies: proxytest.DefaultTrustedProxies,
})
require.NoError(t, err)