mirror of
https://github.com/AdguardTeam/AdGuardHome.git
synced 2024-11-28 18:08:51 +03:00
Pull request: dnsforward: ddr support
Merge in DNS/adguard-home from 4463-ddr-support-1 to master Squashed commit of the following: commit 74d8337a9d78e00a0b01301bbf92054fc58aff0d Merge: 7882c56eed449c61
Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Wed Apr 27 10:32:48 2022 +0200 Merge remote-tracking branch 'origin/master' into 4463-ddr-support-1 commit 7882c56eced204b99a0189c839f5b5ef56fcbfd8 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue Apr 26 13:29:16 2022 +0200 all: docs commit 59593cf47f8db2131fb8a4a44ec3721de8f73567 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue Apr 26 13:06:49 2022 +0200 all: docs commit 13bfe00d91b190a2538eeee642ce40abe031ecf2 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue Apr 26 12:58:48 2022 +0200 all: docs commit a663b53d211483a717a480e24e120a201dc3d9da Merge: 53122f6a235316e0
Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue Apr 26 12:33:07 2022 +0200 Merge remote-tracking branch 'origin/master' into 4463-ddr-support-1 commit 53122f6aac8e9ede69de833e367e006f4c5c75c0 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue Apr 26 12:30:56 2022 +0200 dnsforward: ddr support commit 87083ded02c120e1fb3e54b885a1992efd8f780d Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue Apr 26 11:51:06 2022 +0200 dnsforward: ddr support commit 3dc711e0a9ba1a024e7d24527b2a690aa36413ce Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue Apr 26 11:39:59 2022 +0200 dnsforward: imp code commit f63f6a9d65a96960ae2c06aeca2b32aef70d8f63 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue Apr 26 11:34:23 2022 +0200 dnsforward: ddr support commit e64ffcdac8f9428e4c93a6dc99cc3f1bb090af35 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue Apr 26 11:22:20 2022 +0200 dnsforward: ddr support commit 297460946bb1765137c7c3fe3e298cd574635287 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue Apr 26 11:08:59 2022 +0200 dnsforward: imp code commit 61b4e2e0e06e212c31b7a9d1b09fab392ae6dbc4 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Mon Apr 25 14:39:34 2022 +0200 dnsforward: ddr support commit 7c2787e12eb67a02b41cbb4fe36a12671259f9c9 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Mon Apr 25 11:41:42 2022 +0200 all: docs commit 29c2c872843f6d006e6a98144a52e23a4cbe7be9 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Mon Apr 25 11:26:07 2022 +0200 dnsforward: ddr support commit 2d4ba0c4ce4fbbf3d99da8dd92349da2ec9cff13 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Mon Apr 25 11:03:34 2022 +0200 dnsforward: ddr support commit 0efb5b5cd55bcba3dfae35e80209277f0643a87e Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Sun Apr 24 13:07:25 2022 +0200 dnsforward: imp code commit 884381ef04029d5d743834555cb6601d891c2d25 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Sun Apr 24 12:56:41 2022 +0200 dnsforward: imp code commit 41231f24e83a9690d36546e83fd61ddd709050ed Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Fri Apr 22 16:05:47 2022 +0200 dnsforward: ddr support commit 9d9da3f479efa5d5609f9b1e6b0d1a93fc253b9f Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Fri Apr 22 13:46:29 2022 +0200 all: ddr support commit b225363df143d599e9acbf1a6b0bf6d00044dd47 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Fri Apr 22 13:38:27 2022 +0200 dnsforward: imp code ... and 10 more commits
This commit is contained in:
parent
ed449c6186
commit
c4ff80fd3a
5 changed files with 229 additions and 2 deletions
|
@ -23,6 +23,8 @@ and this project adheres to
|
|||
|
||||
### Added
|
||||
|
||||
- Support for Discovery of Designated Resolvers (DDR) according to the
|
||||
[RFC draft][ddr-draft-06] ([#4463]).
|
||||
- The ability to control each source of runtime clients separately via
|
||||
`clients.runtime_sources` configuration object ([#3020]).
|
||||
- The ability to customize the set of networks that are considered private
|
||||
|
@ -143,8 +145,9 @@ In this release, the schema version has changed from 12 to 14.
|
|||
[#4276]: https://github.com/AdguardTeam/AdGuardHome/issues/4276
|
||||
[#4499]: https://github.com/AdguardTeam/AdGuardHome/issues/4499
|
||||
|
||||
[repr]: https://reproducible-builds.org/docs/source-date-epoch/
|
||||
[ddr-draft-06]: https://www.ietf.org/archive/id/draft-ietf-add-ddr-06.html
|
||||
[doq-draft-10]: https://datatracker.ietf.org/doc/html/draft-ietf-dprive-dnsoquic-10#section-10.2
|
||||
[repr]: https://reproducible-builds.org/docs/source-date-epoch/
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -122,6 +122,7 @@ type FilteringConfig struct {
|
|||
EnableDNSSEC bool `yaml:"enable_dnssec"` // Set AD flag in outcoming DNS request
|
||||
EnableEDNSClientSubnet bool `yaml:"edns_client_subnet"` // Enable EDNS Client Subnet option
|
||||
MaxGoroutines uint32 `yaml:"max_goroutines"` // Max. number of parallel goroutines for processing incoming requests
|
||||
HandleDDR bool `yaml:"handle_ddr"` // Handle DDR requests
|
||||
|
||||
// IpsetList is the ipset configuration that allows AdGuard Home to add
|
||||
// IP addresses of the specified domain names to an ipset list. Syntax:
|
||||
|
@ -151,7 +152,7 @@ type TLSConfig struct {
|
|||
PrivateKeyData []byte `yaml:"-" json:"-"`
|
||||
|
||||
// ServerName is the hostname of the server. Currently, it is only being
|
||||
// used for ClientID checking.
|
||||
// used for ClientID checking and Discovery of Designated Resolvers (DDR).
|
||||
ServerName string `yaml:"-" json:"-"`
|
||||
|
||||
cert tls.Certificate
|
||||
|
|
|
@ -76,6 +76,10 @@ const (
|
|||
resultCodeError
|
||||
)
|
||||
|
||||
// ddrHostFQDN is the FQDN used in Discovery of Designated Resolvers (DDR) requests.
|
||||
// See https://www.ietf.org/archive/id/draft-ietf-add-ddr-06.html.
|
||||
const ddrHostFQDN = "_dns.resolver.arpa."
|
||||
|
||||
// handleDNSRequest filters the incoming DNS requests and writes them to the query log
|
||||
func (s *Server) handleDNSRequest(_ *proxy.Proxy, d *proxy.DNSContext) error {
|
||||
ctx := &dnsContext{
|
||||
|
@ -94,6 +98,7 @@ func (s *Server) handleDNSRequest(_ *proxy.Proxy, d *proxy.DNSContext) error {
|
|||
mods := []modProcessFunc{
|
||||
s.processRecursion,
|
||||
s.processInitial,
|
||||
s.processDDRQuery,
|
||||
s.processDetermineLocal,
|
||||
s.processInternalHosts,
|
||||
s.processRestrictLocal,
|
||||
|
@ -241,6 +246,77 @@ func (s *Server) onDHCPLeaseChanged(flags int) {
|
|||
s.setTableIPToHost(ipToHost)
|
||||
}
|
||||
|
||||
// processDDRQuery responds to SVCB query for a special use domain name
|
||||
// ‘_dns.resolver.arpa’. The result contains different types of encryption
|
||||
// supported by current user configuration.
|
||||
//
|
||||
// See https://www.ietf.org/archive/id/draft-ietf-add-ddr-06.html.
|
||||
func (s *Server) processDDRQuery(ctx *dnsContext) (rc resultCode) {
|
||||
d := ctx.proxyCtx
|
||||
question := d.Req.Question[0]
|
||||
|
||||
if !s.conf.HandleDDR {
|
||||
return resultCodeSuccess
|
||||
}
|
||||
|
||||
if question.Name == ddrHostFQDN {
|
||||
// TODO(a.garipov): Check DoQ support in next RFC drafts.
|
||||
if s.dnsProxy.TLSListenAddr == nil && s.dnsProxy.HTTPSListenAddr == nil ||
|
||||
question.Qtype != dns.TypeSVCB {
|
||||
d.Res = s.makeResponse(d.Req)
|
||||
|
||||
return resultCodeFinish
|
||||
}
|
||||
|
||||
d.Res = s.makeDDRResponse(d.Req)
|
||||
|
||||
return resultCodeFinish
|
||||
}
|
||||
|
||||
return resultCodeSuccess
|
||||
}
|
||||
|
||||
// makeDDRResponse creates DDR answer according to server configuration.
|
||||
func (s *Server) makeDDRResponse(req *dns.Msg) (resp *dns.Msg) {
|
||||
resp = s.makeResponse(req)
|
||||
domainName := s.conf.ServerName
|
||||
|
||||
for _, addr := range s.dnsProxy.HTTPSListenAddr {
|
||||
values := []dns.SVCBKeyValue{
|
||||
&dns.SVCBAlpn{Alpn: []string{"h2"}},
|
||||
&dns.SVCBPort{Port: uint16(addr.Port)},
|
||||
&dns.SVCBDoHPath{Template: "/dns-query?dns"},
|
||||
}
|
||||
|
||||
ans := &dns.SVCB{
|
||||
Hdr: s.hdr(req, dns.TypeSVCB),
|
||||
Priority: 1,
|
||||
Target: domainName,
|
||||
Value: values,
|
||||
}
|
||||
|
||||
resp.Answer = append(resp.Answer, ans)
|
||||
}
|
||||
|
||||
for _, addr := range s.dnsProxy.TLSListenAddr {
|
||||
values := []dns.SVCBKeyValue{
|
||||
&dns.SVCBAlpn{Alpn: []string{"dot"}},
|
||||
&dns.SVCBPort{Port: uint16(addr.Port)},
|
||||
}
|
||||
|
||||
ans := &dns.SVCB{
|
||||
Hdr: s.hdr(req, dns.TypeSVCB),
|
||||
Priority: 2,
|
||||
Target: domainName,
|
||||
Value: values,
|
||||
}
|
||||
|
||||
resp.Answer = append(resp.Answer, ans)
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
// processDetermineLocal determines if the client's IP address is from
|
||||
// locally-served network and saves the result into the context.
|
||||
func (s *Server) processDetermineLocal(dctx *dnsContext) (rc resultCode) {
|
||||
|
|
|
@ -14,6 +14,152 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const ddrTestDomainName = "dns.example.net"
|
||||
|
||||
func TestServer_ProcessDDRQuery(t *testing.T) {
|
||||
dohSVCB := &dns.SVCB{
|
||||
Priority: 1,
|
||||
Target: ddrTestDomainName,
|
||||
Value: []dns.SVCBKeyValue{
|
||||
&dns.SVCBAlpn{Alpn: []string{"h2"}},
|
||||
&dns.SVCBPort{Port: 8044},
|
||||
&dns.SVCBDoHPath{Template: "/dns-query?dns"},
|
||||
},
|
||||
}
|
||||
|
||||
dotSVCB := &dns.SVCB{
|
||||
Priority: 2,
|
||||
Target: ddrTestDomainName,
|
||||
Value: []dns.SVCBKeyValue{
|
||||
&dns.SVCBAlpn{Alpn: []string{"dot"}},
|
||||
&dns.SVCBPort{Port: 8043},
|
||||
},
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
host string
|
||||
want []*dns.SVCB
|
||||
wantRes resultCode
|
||||
portDoH int
|
||||
portDoT int
|
||||
qtype uint16
|
||||
ddrEnabled bool
|
||||
}{{
|
||||
name: "pass_host",
|
||||
wantRes: resultCodeSuccess,
|
||||
host: "example.net.",
|
||||
qtype: dns.TypeSVCB,
|
||||
ddrEnabled: true,
|
||||
portDoH: 8043,
|
||||
}, {
|
||||
name: "pass_qtype",
|
||||
wantRes: resultCodeFinish,
|
||||
host: ddrHostFQDN,
|
||||
qtype: dns.TypeA,
|
||||
ddrEnabled: true,
|
||||
portDoH: 8043,
|
||||
}, {
|
||||
name: "pass_disabled_tls",
|
||||
wantRes: resultCodeFinish,
|
||||
host: ddrHostFQDN,
|
||||
qtype: dns.TypeSVCB,
|
||||
ddrEnabled: true,
|
||||
}, {
|
||||
name: "pass_disabled_ddr",
|
||||
wantRes: resultCodeSuccess,
|
||||
host: ddrHostFQDN,
|
||||
qtype: dns.TypeSVCB,
|
||||
ddrEnabled: false,
|
||||
portDoH: 8043,
|
||||
}, {
|
||||
name: "dot",
|
||||
wantRes: resultCodeFinish,
|
||||
want: []*dns.SVCB{dotSVCB},
|
||||
host: ddrHostFQDN,
|
||||
qtype: dns.TypeSVCB,
|
||||
ddrEnabled: true,
|
||||
portDoT: 8043,
|
||||
}, {
|
||||
name: "doh",
|
||||
wantRes: resultCodeFinish,
|
||||
want: []*dns.SVCB{dohSVCB},
|
||||
host: ddrHostFQDN,
|
||||
qtype: dns.TypeSVCB,
|
||||
ddrEnabled: true,
|
||||
portDoH: 8044,
|
||||
}, {
|
||||
name: "dot_doh",
|
||||
wantRes: resultCodeFinish,
|
||||
want: []*dns.SVCB{dotSVCB, dohSVCB},
|
||||
host: ddrHostFQDN,
|
||||
qtype: dns.TypeSVCB,
|
||||
ddrEnabled: true,
|
||||
portDoT: 8043,
|
||||
portDoH: 8044,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
s := prepareTestServer(t, tc.portDoH, tc.portDoT, tc.ddrEnabled)
|
||||
|
||||
req := createTestMessageWithType(tc.host, tc.qtype)
|
||||
|
||||
dctx := &dnsContext{
|
||||
proxyCtx: &proxy.DNSContext{
|
||||
Req: req,
|
||||
},
|
||||
}
|
||||
|
||||
res := s.processDDRQuery(dctx)
|
||||
require.Equal(t, tc.wantRes, res)
|
||||
|
||||
if tc.wantRes != resultCodeFinish {
|
||||
return
|
||||
}
|
||||
|
||||
msg := dctx.proxyCtx.Res
|
||||
require.NotNil(t, msg)
|
||||
|
||||
for _, v := range tc.want {
|
||||
v.Hdr = s.hdr(req, dns.TypeSVCB)
|
||||
}
|
||||
|
||||
assert.ElementsMatch(t, tc.want, msg.Answer)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func prepareTestServer(t *testing.T, portDoH, portDoT int, ddrEnabled bool) (s *Server) {
|
||||
t.Helper()
|
||||
|
||||
proxyConf := proxy.Config{}
|
||||
|
||||
if portDoH > 0 {
|
||||
proxyConf.HTTPSListenAddr = []*net.TCPAddr{{Port: portDoH}}
|
||||
}
|
||||
|
||||
if portDoT > 0 {
|
||||
proxyConf.TLSListenAddr = []*net.TCPAddr{{Port: portDoT}}
|
||||
}
|
||||
|
||||
s = &Server{
|
||||
dnsProxy: &proxy.Proxy{
|
||||
Config: proxyConf,
|
||||
},
|
||||
conf: ServerConfig{
|
||||
FilteringConfig: FilteringConfig{
|
||||
HandleDDR: ddrEnabled,
|
||||
},
|
||||
TLSConfig: TLSConfig{
|
||||
ServerName: ddrTestDomainName,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func TestServer_ProcessDetermineLocal(t *testing.T) {
|
||||
s := &Server{
|
||||
privateNets: netutil.SubnetSetFunc(netutil.IsLocallyServed),
|
||||
|
|
|
@ -187,6 +187,7 @@ var config = &configuration{
|
|||
Ratelimit: 20,
|
||||
RefuseAny: true,
|
||||
AllServers: false,
|
||||
HandleDDR: true,
|
||||
FastestTimeout: timeutil.Duration{
|
||||
Duration: fastip.DefaultPingWaitTimeout,
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue