AdGuardDNS/internal/dnsmsg/svcbmsg_test.go
Andrey Meshkov da0cb6fd0e Sync v2.9.0
2024-10-14 17:44:24 +03:00

255 lines
5.9 KiB
Go

package dnsmsg_test
import (
"net"
"net/netip"
"testing"
"github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
"github.com/AdguardTeam/urlfilter/rules"
"github.com/miekg/dns"
"github.com/quic-go/quic-go/http3"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestConstructor_NewAnswerHTTPS_andSVCB(t *testing.T) {
// Preconditions.
msgs := agdtest.NewConstructor(t)
req := &dns.Msg{
Question: []dns.Question{{
Name: "abcd",
}},
}
// Constants and helper values.
const prio = 32
// Helper functions.
dnssvcb := func(key, value string) (svcb *rules.DNSSVCB) {
svcb = &rules.DNSSVCB{
Target: testFQDN,
Priority: prio,
}
if key == "" {
return svcb
}
svcb.Params = map[string]string{
key: value,
}
return svcb
}
wantsvcb := func(kv dns.SVCBKeyValue) (want *dns.SVCB) {
want = &dns.SVCB{
Hdr: dns.RR_Header{
Name: req.Question[0].Name,
Rrtype: dns.TypeSVCB,
Ttl: agdtest.FilteredResponseTTLSec,
Class: dns.ClassINET,
},
Priority: prio,
Target: testFQDN,
}
if kv != nil {
want.Value = []dns.SVCBKeyValue{kv}
}
return want
}
// Tests.
testCases := []struct {
svcb *rules.DNSSVCB
want *dns.SVCB
name string
}{{
svcb: dnssvcb("", ""),
want: wantsvcb(nil),
name: "no_params",
}, {
svcb: dnssvcb("foo", "bar"),
want: wantsvcb(nil),
name: "invalid",
}, {
svcb: dnssvcb("alpn", http3.NextProtoH3),
want: wantsvcb(&dns.SVCBAlpn{Alpn: []string{http3.NextProtoH3}}),
name: "alpn",
}, {
svcb: dnssvcb("dohpath", "/some/url/path"),
want: wantsvcb(&dns.SVCBDoHPath{Template: "/some/url/path"}),
name: "dohpath",
}, {
svcb: dnssvcb("ech", "AAAA"),
want: wantsvcb(&dns.SVCBECHConfig{ECH: []byte{0, 0, 0}}),
name: "ech",
}, {
svcb: dnssvcb("ech", "%BAD%"),
want: wantsvcb(nil),
name: "ech_invalid",
}, {
svcb: dnssvcb("ipv4hint", testIPv4.String()),
want: wantsvcb(&dns.SVCBIPv4Hint{Hint: []net.IP{net.IP(testIPv4.AsSlice()).To16()}}),
name: "ipv4hint",
}, {
svcb: dnssvcb("ipv4hint", "1.2.3.04"),
want: wantsvcb(nil),
name: "ipv4hint_invalid",
}, {
svcb: dnssvcb("ipv6hint", testIPv6.String()),
want: wantsvcb(&dns.SVCBIPv6Hint{Hint: []net.IP{testIPv6.AsSlice()}}),
name: "ipv6hint",
}, {
svcb: dnssvcb("ipv6hint", ":::1"),
want: wantsvcb(nil),
name: "ipv6hint_invalid",
}, {
svcb: dnssvcb("mandatory", "alpn"),
want: wantsvcb(&dns.SVCBMandatory{Code: []dns.SVCBKey{dns.SVCB_ALPN}}),
name: "mandatory",
}, {
svcb: dnssvcb("mandatory", "invalid"),
want: wantsvcb(nil),
name: "mandatory_invalid",
}, {
svcb: dnssvcb("no-default-alpn", ""),
want: wantsvcb(&dns.SVCBNoDefaultAlpn{}),
name: "no_default_alpn",
}, {
svcb: dnssvcb("port", "8080"),
want: wantsvcb(&dns.SVCBPort{Port: 8080}),
name: "port",
}, {
svcb: dnssvcb("port", "1005008080"),
want: wantsvcb(nil),
name: "bad_port",
}}
for _, tc := range testCases {
t.Run(tc.name+"_https", func(t *testing.T) {
want := &dns.HTTPS{SVCB: *tc.want}
want.Hdr.Rrtype = dns.TypeHTTPS
got := msgs.NewAnswerHTTPS(req, tc.svcb)
assert.Equal(t, want, got)
})
t.Run(tc.name+"_svcb", func(t *testing.T) {
got := msgs.NewAnswerSVCB(req, tc.svcb)
assert.Equal(t, tc.want, got)
})
}
}
func TestConstructor_NewDDR(t *testing.T) {
const (
port uint16 = 12345
prio uint16 = 123
target = "test.target"
targetFQDN = target + "."
dohPath = "/dns-query"
)
msgs := agdtest.NewConstructor(t)
testCases := []struct {
name string
ipv4s []netip.Addr
ipv6s []netip.Addr
wantVals []dns.SVCBKeyValue
proto dnsserver.Protocol
}{{
name: "dot",
ipv4s: nil,
ipv6s: nil,
wantVals: []dns.SVCBKeyValue{
&dns.SVCBAlpn{Alpn: dnsserver.ProtoDoT.ALPN()},
&dns.SVCBPort{Port: port},
},
proto: dnsserver.ProtoDoT,
}, {
name: "doq",
ipv4s: nil,
ipv6s: nil,
wantVals: []dns.SVCBKeyValue{
&dns.SVCBAlpn{Alpn: dnsserver.ProtoDoQ.ALPN()},
&dns.SVCBPort{Port: port},
},
proto: dnsserver.ProtoDoQ,
}, {
name: "doh",
ipv4s: nil,
ipv6s: nil,
wantVals: []dns.SVCBKeyValue{
&dns.SVCBAlpn{Alpn: dnsserver.ProtoDoH.ALPN()},
&dns.SVCBPort{Port: port},
&dns.SVCBDoHPath{Template: dohPath},
},
proto: dnsserver.ProtoDoH,
}, {
name: "dot_ipv4_only",
ipv4s: []netip.Addr{testIPv4},
ipv6s: nil,
wantVals: []dns.SVCBKeyValue{
&dns.SVCBAlpn{Alpn: dnsserver.ProtoDoT.ALPN()},
&dns.SVCBPort{Port: port},
&dns.SVCBIPv4Hint{Hint: []net.IP{testIPv4.AsSlice()}},
},
proto: dnsserver.ProtoDoT,
}, {
name: "dot_ipv6_only",
ipv4s: nil,
ipv6s: []netip.Addr{testIPv6},
wantVals: []dns.SVCBKeyValue{
&dns.SVCBAlpn{Alpn: dnsserver.ProtoDoT.ALPN()},
&dns.SVCBPort{Port: port},
&dns.SVCBIPv6Hint{Hint: []net.IP{testIPv6.AsSlice()}},
},
proto: dnsserver.ProtoDoT,
}, {
name: "dot_ipv4_ipv6",
ipv4s: []netip.Addr{testIPv4},
ipv6s: []netip.Addr{testIPv6},
wantVals: []dns.SVCBKeyValue{
&dns.SVCBAlpn{Alpn: dnsserver.ProtoDoT.ALPN()},
&dns.SVCBPort{Port: port},
&dns.SVCBIPv4Hint{Hint: []net.IP{testIPv4.AsSlice()}},
&dns.SVCBIPv6Hint{Hint: []net.IP{testIPv6.AsSlice()}},
},
proto: dnsserver.ProtoDoT,
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
svcb := msgs.NewDDRTemplate(tc.proto, target, dohPath, tc.ipv4s, tc.ipv6s, port, prio)
require.NotNil(t, svcb)
assert.Equal(t, targetFQDN, svcb.Target)
assert.Equal(t, prio, svcb.Priority)
assert.Equal(t, uint32(agdtest.FilteredResponseTTLSec), svcb.Hdr.Ttl)
assert.ElementsMatch(t, tc.wantVals, svcb.Value)
})
}
for _, unsupProto := range []dnsserver.Protocol{
dnsserver.ProtoDNS,
dnsserver.ProtoDNSCrypt,
} {
t.Run(unsupProto.String(), func(t *testing.T) {
assert.Panics(t, func() {
_ = msgs.NewDDRTemplate(unsupProto, target, "", nil, nil, port, prio)
})
})
}
}