mirror of
https://github.com/AdguardTeam/AdGuardHome.git
synced 2024-12-15 19:31:45 +03:00
a2ca8b5b4a
Squashed commit of the following: commit a5607f811ab4642a0e34d3042240e3a9aafc3d84 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Wed Aug 30 15:44:26 2023 +0300 home: upgrade yaml commit 0593e4da17613b8770c4567fdd5fbfde31631e6f Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Wed Aug 30 15:01:14 2023 +0300 home: upgrade yaml commit 59ec4ba0bbe59fb2e95290f4f8101891ce4ff959 Merge: 6555941f5a325c9b6b
Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Wed Aug 30 14:57:41 2023 +0300 Merge remote-tracking branch 'origin/master' into AG-25263-filtering-config # Conflicts: # internal/dnsforward/http.go # internal/dnsforward/http_test.go # internal/dnsforward/process.go # internal/dnsforward/process_internal_test.go commit 6555941f57e99aa8bc2473b8be9e4f096ce665e0 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Wed Aug 30 14:38:01 2023 +0300 docs: changelog commit c66d14cecbb1f08869a90c46e58d08ec03e29de5 Merge: a50ff1622aac36a2d2
Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Wed Aug 30 12:58:41 2023 +0300 Merge remote-tracking branch 'origin/master' into AG-25263-filtering-config commit a50ff162291f7953912c1bb195bd7b5d802d0ab3 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Wed Aug 30 11:22:12 2023 +0300 home: imp code commit ef40d073e00d6acb164c0d680ed9971c4e75fbb7 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue Aug 29 16:19:14 2023 +0300 home: imp code commit 5fa09a95e901a72dc5d2a90ba47c96842152587b Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue Aug 29 14:47:48 2023 +0300 home: imp code commit 52bb295120f468faf94c953d0e795d1d58696ef1 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue Aug 29 13:33:01 2023 +0300 home: imp code commit 24cfccf071385d660aeba4ced744ebbacc819686 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue Aug 29 12:24:20 2023 +0300 filtering: imp code commit 758242b69ee5f351b25407340f989ec6b90d3607 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue Aug 29 10:32:51 2023 +0300 home: imp code commit 906deaa3bf86af5038fb05a54c34cc9511500ac9 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Mon Aug 28 14:46:01 2023 +0300 dnsforward: imp code commit 978bb508349cd0b6a1a0bd3df5b879b6a4dd9e29 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Mon Aug 28 14:40:31 2023 +0300 home: imp code commit d5b8dd5c930b0ccbe612f320a9908f2849370fd4 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Mon Aug 28 12:34:32 2023 +0300 filtering: imp code commit d3e5726df6bc93112329ec81bc83932ccf64b8de Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Mon Aug 28 11:57:33 2023 +0300 home: upgrade yaml commit 75d701553595786d5695540c001e6d46153351a9 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Mon Aug 28 11:46:55 2023 +0300 home: upgrade yaml commit cd8cb623b0e6eb00fd05fb62b7445ca4315b94e0 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Mon Aug 28 11:29:16 2023 +0300 docs: changelog commit 31c098d6f8d68a4afa30246c8d067a1ef4586de9 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Mon Aug 28 11:08:48 2023 +0300 docs: changelog commit 24c88dd5d42b0168a13ea11b3f760eae1e804558 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Sun Aug 27 19:12:55 2023 +0300 home: imp code commit 94f2d386c9c1b6be5df06525078c90a3d068641f Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Sun Aug 27 16:15:05 2023 +0300 home: upgrade yaml ... and 10 more commits
310 lines
7.5 KiB
Go
310 lines
7.5 KiB
Go
package dnsforward
|
|
|
|
import (
|
|
"net"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
|
|
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
|
"github.com/AdguardTeam/dnsproxy/proxy"
|
|
"github.com/AdguardTeam/dnsproxy/upstream"
|
|
"github.com/AdguardTeam/golibs/netutil"
|
|
"github.com/AdguardTeam/golibs/testutil"
|
|
"github.com/miekg/dns"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// maxDNS64SynTTL is the maximum TTL for synthesized DNS64 responses with no SOA
|
|
// records in seconds.
|
|
//
|
|
// If the SOA RR was not delivered with the negative response to the AAAA query,
|
|
// then the DNS64 SHOULD use the TTL of the original A RR or 600 seconds,
|
|
// whichever is shorter.
|
|
//
|
|
// See https://datatracker.ietf.org/doc/html/rfc6147#section-5.1.7.
|
|
const maxDNS64SynTTL uint32 = 600
|
|
|
|
// 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(t *testing.T, name string, qtype uint16, ttl uint32, val any) (rr dns.RR) {
|
|
t.Helper()
|
|
|
|
switch qtype {
|
|
case dns.TypeA:
|
|
rr = &dns.A{A: testutil.RequireTypeAssert[net.IP](t, val)}
|
|
case dns.TypeAAAA:
|
|
rr = &dns.AAAA{AAAA: testutil.RequireTypeAssert[net.IP](t, val)}
|
|
case dns.TypeCNAME:
|
|
rr = &dns.CNAME{Target: testutil.RequireTypeAssert[string](t, 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](t, val)}
|
|
default:
|
|
t.Fatalf("unsupported qtype: %d", qtype)
|
|
}
|
|
|
|
*rr.Header() = dns.RR_Header{
|
|
Name: name,
|
|
Rrtype: qtype,
|
|
Class: dns.ClassINET,
|
|
Ttl: ttl,
|
|
}
|
|
|
|
return rr
|
|
}
|
|
|
|
func TestServer_HandleDNSRequest_dns64(t *testing.T) {
|
|
const (
|
|
ipv4Domain = "ipv4.only."
|
|
ipv6Domain = "ipv6.only."
|
|
soaDomain = "ipv4.soa."
|
|
mappedDomain = "filterable.ipv6."
|
|
anotherDomain = "another.domain."
|
|
|
|
pointedDomain = "local1234.ipv4."
|
|
globDomain = "real1234.ipv4."
|
|
)
|
|
|
|
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")
|
|
|
|
ptr64Domain, err := netutil.IPToReversedAddr(mappedIPv6)
|
|
require.NoError(t, err)
|
|
ptr64Domain = dns.Fqdn(ptr64Domain)
|
|
|
|
ptrGlobDomain, err := netutil.IPToReversedAddr(someIPv4)
|
|
require.NoError(t, err)
|
|
ptrGlobDomain = dns.Fqdn(ptrGlobDomain)
|
|
|
|
const (
|
|
sectionAnswer = iota
|
|
sectionAuthority
|
|
sectionAdditional
|
|
|
|
sectionsNum
|
|
)
|
|
|
|
// answerMap is a convenience alias for describing the upstream response for
|
|
// a given question type.
|
|
type answerMap = map[uint16][sectionsNum][]dns.RR
|
|
|
|
pt := testutil.PanicT{}
|
|
newUps := func(answers answerMap) (u upstream.Upstream) {
|
|
return aghtest.NewUpstreamMock(func(req *dns.Msg) (resp *dns.Msg, err error) {
|
|
q := req.Question[0]
|
|
require.Contains(pt, answers, q.Qtype)
|
|
|
|
answer := answers[q.Qtype]
|
|
|
|
resp = (&dns.Msg{}).SetReply(req)
|
|
resp.Answer = answer[sectionAnswer]
|
|
resp.Ns = answer[sectionAuthority]
|
|
resp.Extra = answer[sectionAdditional]
|
|
|
|
return resp, nil
|
|
})
|
|
}
|
|
|
|
testCases := []struct {
|
|
name string
|
|
qname string
|
|
upsAns answerMap
|
|
wantAns []dns.RR
|
|
qtype uint16
|
|
}{{
|
|
name: "simple_a",
|
|
qname: ipv4Domain,
|
|
upsAns: answerMap{
|
|
dns.TypeA: {
|
|
sectionAnswer: {newRR(t, ipv4Domain, dns.TypeA, 3600, someIPv4)},
|
|
},
|
|
dns.TypeAAAA: {},
|
|
},
|
|
wantAns: []dns.RR{&dns.A{
|
|
Hdr: dns.RR_Header{
|
|
Name: ipv4Domain,
|
|
Rrtype: dns.TypeA,
|
|
Class: dns.ClassINET,
|
|
Ttl: 3600,
|
|
Rdlength: 4,
|
|
},
|
|
A: someIPv4,
|
|
}},
|
|
qtype: dns.TypeA,
|
|
}, {
|
|
name: "simple_aaaa",
|
|
qname: ipv6Domain,
|
|
upsAns: answerMap{
|
|
dns.TypeA: {},
|
|
dns.TypeAAAA: {
|
|
sectionAnswer: {newRR(t, ipv6Domain, dns.TypeAAAA, 3600, someIPv6)},
|
|
},
|
|
},
|
|
wantAns: []dns.RR{&dns.AAAA{
|
|
Hdr: dns.RR_Header{
|
|
Name: ipv6Domain,
|
|
Rrtype: dns.TypeAAAA,
|
|
Class: dns.ClassINET,
|
|
Ttl: 3600,
|
|
Rdlength: 16,
|
|
},
|
|
AAAA: someIPv6,
|
|
}},
|
|
qtype: dns.TypeAAAA,
|
|
}, {
|
|
name: "actual_dns64",
|
|
qname: ipv4Domain,
|
|
upsAns: answerMap{
|
|
dns.TypeA: {
|
|
sectionAnswer: {newRR(t, ipv4Domain, dns.TypeA, 3600, someIPv4)},
|
|
},
|
|
dns.TypeAAAA: {},
|
|
},
|
|
wantAns: []dns.RR{&dns.AAAA{
|
|
Hdr: dns.RR_Header{
|
|
Name: ipv4Domain,
|
|
Rrtype: dns.TypeAAAA,
|
|
Class: dns.ClassINET,
|
|
Ttl: maxDNS64SynTTL,
|
|
Rdlength: 16,
|
|
},
|
|
AAAA: mappedIPv6,
|
|
}},
|
|
qtype: dns.TypeAAAA,
|
|
}, {
|
|
name: "actual_dns64_soattl",
|
|
qname: soaDomain,
|
|
upsAns: answerMap{
|
|
dns.TypeA: {
|
|
sectionAnswer: {newRR(t, soaDomain, dns.TypeA, 3600, someIPv4)},
|
|
},
|
|
dns.TypeAAAA: {
|
|
sectionAuthority: {newRR(t, soaDomain, dns.TypeSOA, maxDNS64SynTTL+50, nil)},
|
|
},
|
|
},
|
|
wantAns: []dns.RR{&dns.AAAA{
|
|
Hdr: dns.RR_Header{
|
|
Name: soaDomain,
|
|
Rrtype: dns.TypeAAAA,
|
|
Class: dns.ClassINET,
|
|
Ttl: maxDNS64SynTTL + 50,
|
|
Rdlength: 16,
|
|
},
|
|
AAAA: mappedIPv6,
|
|
}},
|
|
qtype: dns.TypeAAAA,
|
|
}, {
|
|
name: "filtered",
|
|
qname: mappedDomain,
|
|
upsAns: answerMap{
|
|
dns.TypeA: {},
|
|
dns.TypeAAAA: {
|
|
sectionAnswer: {
|
|
newRR(t, mappedDomain, dns.TypeAAAA, 3600, net.ParseIP("64:ff9b::506:708")),
|
|
newRR(t, mappedDomain, dns.TypeCNAME, 3600, anotherDomain),
|
|
},
|
|
},
|
|
},
|
|
wantAns: []dns.RR{&dns.CNAME{
|
|
Hdr: dns.RR_Header{
|
|
Name: mappedDomain,
|
|
Rrtype: dns.TypeCNAME,
|
|
Class: dns.ClassINET,
|
|
Ttl: 3600,
|
|
Rdlength: 16,
|
|
},
|
|
Target: anotherDomain,
|
|
}},
|
|
qtype: dns.TypeAAAA,
|
|
}, {
|
|
name: "ptr",
|
|
qname: ptr64Domain,
|
|
upsAns: nil,
|
|
wantAns: []dns.RR{&dns.PTR{
|
|
Hdr: dns.RR_Header{
|
|
Name: ptr64Domain,
|
|
Rrtype: dns.TypePTR,
|
|
Class: dns.ClassINET,
|
|
Ttl: 3600,
|
|
Rdlength: 16,
|
|
},
|
|
Ptr: pointedDomain,
|
|
}},
|
|
qtype: dns.TypePTR,
|
|
}, {
|
|
name: "ptr_glob",
|
|
qname: ptrGlobDomain,
|
|
upsAns: answerMap{
|
|
dns.TypePTR: {
|
|
sectionAnswer: {newRR(t, ptrGlobDomain, dns.TypePTR, 3600, globDomain)},
|
|
},
|
|
},
|
|
wantAns: []dns.RR{&dns.PTR{
|
|
Hdr: dns.RR_Header{
|
|
Name: ptrGlobDomain,
|
|
Rrtype: dns.TypePTR,
|
|
Class: dns.ClassINET,
|
|
Ttl: 3600,
|
|
Rdlength: 15,
|
|
},
|
|
Ptr: globDomain,
|
|
}},
|
|
qtype: dns.TypePTR,
|
|
}}
|
|
|
|
localRR := newRR(t, ptr64Domain, dns.TypePTR, 3600, pointedDomain)
|
|
localUps := aghtest.NewUpstreamMock(func(req *dns.Msg) (resp *dns.Msg, err error) {
|
|
require.Equal(pt, req.Question[0].Name, ptr64Domain)
|
|
resp = (&dns.Msg{}).SetReply(req)
|
|
resp.Answer = []dns.RR{localRR}
|
|
|
|
return resp, nil
|
|
})
|
|
|
|
client := &dns.Client{
|
|
Net: "tcp",
|
|
Timeout: 1 * time.Second,
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
// TODO(e.burkov): It seems [proxy.Proxy] isn't intended to be reused
|
|
// right after stop, due to a data race in [proxy.Proxy.Init] method
|
|
// when setting an OOB size. As a temporary workaround, recreate the
|
|
// whole server for each test case.
|
|
s := createTestServer(t, &filtering.Config{
|
|
BlockingMode: filtering.BlockingModeDefault,
|
|
}, ServerConfig{
|
|
UDPListenAddrs: []*net.UDPAddr{{}},
|
|
TCPListenAddrs: []*net.TCPAddr{{}},
|
|
UseDNS64: true,
|
|
Config: Config{
|
|
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
|
},
|
|
}, localUps)
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{newUps(tc.upsAns)}
|
|
startDeferStop(t, s)
|
|
|
|
req := (&dns.Msg{}).SetQuestion(tc.qname, tc.qtype)
|
|
|
|
resp, _, excErr := client.Exchange(req, s.dnsProxy.Addr(proxy.ProtoTCP).String())
|
|
require.NoError(t, excErr)
|
|
|
|
require.Equal(t, tc.wantAns, resp.Answer)
|
|
})
|
|
}
|
|
}
|