mirror of
https://github.com/AdguardTeam/dnsproxy.git
synced 2026-10-12 10:09:55 +00:00
Pull request 284: 6156 domain-specific ds
Merge in GO/dnsproxy from 6156-domain-specific-ds to master
Squashed commit of the following:
commit 7714a5eb34da8cbf0874731da2fa27db382c1312
Merge: e244189 c30fcc5
Author: Eugene Burkov <[email protected]>
Date: Thu Oct 5 15:27:17 2023 +0300
Merge branch 'master' into 6156-domain-specific-ds
commit e244189f9a70eed01f5180eaf2e84cd666f19826
Author: Eugene Burkov <[email protected]>
Date: Thu Sep 28 19:04:04 2023 +0300
proxy: imp doc, code
commit 0d402a905db0947b1e69aa00f7908564aa219238
Author: Eugene Burkov <[email protected]>
Date: Tue Sep 26 16:23:01 2023 +0300
proxy: add special ds handling
This commit is contained in:
+13
-3
@@ -458,18 +458,28 @@ func (p *Proxy) needsLocalUpstream(req *dns.Msg) (ok bool) {
|
||||
// firstly considers custom upstreams if those aren't empty and then the
|
||||
// configured ones. The returned slice may be empty or nil.
|
||||
func (p *Proxy) selectUpstreams(d *DNSContext) (upstreams []upstream.Upstream) {
|
||||
host := d.Req.Question[0].Name
|
||||
q := d.Req.Question[0]
|
||||
host := q.Name
|
||||
qtype := q.Qtype
|
||||
|
||||
if !p.needsLocalUpstream(d.Req) {
|
||||
// TODO(e.burkov): Use the same logic for private upstreams as well,
|
||||
// when those start supporting non-PTR requests.
|
||||
getUpstreams := (*UpstreamConfig).getUpstreamsForDomain
|
||||
if qtype == dns.TypeDS {
|
||||
getUpstreams = (*UpstreamConfig).getUpstreamsForDS
|
||||
}
|
||||
|
||||
if custom := d.CustomUpstreamConfig; custom != nil {
|
||||
// Try to use custom.
|
||||
upstreams = custom.getUpstreamsForDomain(host)
|
||||
upstreams = getUpstreams(custom, host)
|
||||
if len(upstreams) > 0 {
|
||||
return upstreams
|
||||
}
|
||||
}
|
||||
|
||||
// Use configured.
|
||||
return p.UpstreamConfig.getUpstreamsForDomain(host)
|
||||
return getUpstreams(p.UpstreamConfig, host)
|
||||
}
|
||||
|
||||
// Use private upstreams.
|
||||
|
||||
+24
-11
@@ -211,8 +211,6 @@ func (uc *UpstreamConfig) getUpstreamsForDomain(host string) (ups []upstream.Ups
|
||||
return uc.Upstreams
|
||||
}
|
||||
|
||||
var ok bool
|
||||
|
||||
dotsCount := strings.Count(host, ".")
|
||||
if dotsCount < 2 {
|
||||
host = UnqualifiedNames
|
||||
@@ -223,21 +221,36 @@ func (uc *UpstreamConfig) getUpstreamsForDomain(host string) (ups []upstream.Ups
|
||||
}
|
||||
}
|
||||
|
||||
for i := 1; i <= dotsCount; i++ {
|
||||
h := strings.SplitAfterN(host, ".", i)
|
||||
name := h[i-1]
|
||||
|
||||
ups, ok = uc.lookupUpstreams(name)
|
||||
if !ok {
|
||||
continue
|
||||
for host != "" {
|
||||
var ok bool
|
||||
if ups, ok = uc.lookupUpstreams(host); ok {
|
||||
return ups
|
||||
}
|
||||
|
||||
return ups
|
||||
_, host, _ = strings.Cut(host, ".")
|
||||
}
|
||||
|
||||
return uc.Upstreams
|
||||
}
|
||||
|
||||
// getUpstreamsForDS is like [getUpstreamsForDomain], but intended for DS
|
||||
// queries only, so that it matches the host without the first label.
|
||||
//
|
||||
// A DS RRset SHOULD be present at a delegation point when the child zone is
|
||||
// signed. The DS RRset MAY contain multiple records, each referencing a public
|
||||
// key in the child zone used to verify the RRSIGs in that zone. All DS RRsets
|
||||
// in a zone MUST be signed, and DS RRsets MUST NOT appear at a zone's apex.
|
||||
//
|
||||
// See https://datatracker.ietf.org/doc/html/rfc4035#section-2.4
|
||||
func (uc *UpstreamConfig) getUpstreamsForDS(host string) (ups []upstream.Upstream) {
|
||||
_, host, found := strings.Cut(host, ".")
|
||||
if !found {
|
||||
return uc.Upstreams
|
||||
}
|
||||
|
||||
return uc.getUpstreamsForDomain(host)
|
||||
}
|
||||
|
||||
// lookupSubdomainExclusion returns upstreams for the host from subdomain
|
||||
// exclusions list.
|
||||
func (uc *UpstreamConfig) lookupSubdomainExclusion(host string) (u []upstream.Upstream) {
|
||||
@@ -266,7 +279,7 @@ func (uc *UpstreamConfig) lookupUpstreams(name string) (ups []upstream.Upstream,
|
||||
|
||||
if len(ups) == 0 {
|
||||
// The domain has been excluded from reserved upstreams querying.
|
||||
return uc.Upstreams, true
|
||||
ups = uc.Upstreams
|
||||
}
|
||||
|
||||
return ups, true
|
||||
|
||||
+114
-23
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGetUpstreamsForDomain(t *testing.T) {
|
||||
func TestUpstreamConfig_GetUpstreamsForDomain(t *testing.T) {
|
||||
upstreams := []string{
|
||||
"[/google.com/local/]4.3.2.1",
|
||||
"[/www.google.com//]1.2.3.4",
|
||||
@@ -28,12 +28,95 @@ func TestGetUpstreamsForDomain(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
assertUpstreamsForDomain(t, config, "www.google.com.", []string{"1.2.3.4:53", "tls://1.1.1.1:853"})
|
||||
assertUpstreamsForDomain(t, config, "www2.google.com.", []string{"4.3.2.1:53"})
|
||||
assertUpstreamsForDomain(t, config, "internal.local.", []string{"4.3.2.1:53"})
|
||||
assertUpstreamsForDomain(t, config, "google.", []string{"1.2.3.4:53"})
|
||||
assertUpstreamsForDomain(t, config, "_acme-challenge.example.org.", []string{})
|
||||
assertUpstreamsForDomain(t, config, "maps.google.com.", []string{})
|
||||
testCases := []struct {
|
||||
name string
|
||||
in string
|
||||
want []string
|
||||
}{{
|
||||
name: "direct_match",
|
||||
in: "www.google.com.",
|
||||
want: []string{"1.2.3.4:53", "tls://1.1.1.1:853"},
|
||||
}, {
|
||||
name: "subdomain_match",
|
||||
in: "www2.google.com.",
|
||||
want: []string{"4.3.2.1:53"},
|
||||
}, {
|
||||
name: "second_subdomain_match",
|
||||
in: "internal.local.",
|
||||
want: []string{"4.3.2.1:53"},
|
||||
}, {
|
||||
name: "unqualified_match",
|
||||
in: "google.",
|
||||
want: []string{"1.2.3.4:53"},
|
||||
}, {
|
||||
name: "default",
|
||||
in: "_acme-challenge.example.org.",
|
||||
want: []string{},
|
||||
}, {
|
||||
name: "another_default",
|
||||
in: "maps.google.com.",
|
||||
want: []string{},
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ups := config.getUpstreamsForDomain(tc.in)
|
||||
assertUpstreamsForDomain(t, ups, tc.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpstreamConfig_GetUpstreamsForDS(t *testing.T) {
|
||||
upstreams := []string{
|
||||
"[/google.com/local/]4.3.2.1",
|
||||
"[/www.google.com//]1.2.3.4",
|
||||
"[/maps.google.com/]#",
|
||||
"[/www.google.com/]tls://1.1.1.1",
|
||||
"[/_acme-challenge.example.org/]#",
|
||||
}
|
||||
|
||||
config, err := ParseUpstreamsConfig(
|
||||
upstreams,
|
||||
&upstream.Options{
|
||||
InsecureSkipVerify: false,
|
||||
Bootstrap: []string{},
|
||||
Timeout: 1 * time.Second,
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
in string
|
||||
want []string
|
||||
}{{
|
||||
name: "direct_match",
|
||||
in: "www.google.com.",
|
||||
want: []string{"4.3.2.1:53"},
|
||||
}, {
|
||||
name: "subdomain_match",
|
||||
in: "abc.www.google.com.",
|
||||
want: []string{"1.2.3.4:53", "tls://1.1.1.1:853"},
|
||||
}, {
|
||||
name: "unqualified_match",
|
||||
in: "internal.local.",
|
||||
want: []string{"1.2.3.4:53"},
|
||||
}, {
|
||||
name: "more_unqualified_match",
|
||||
in: "google.",
|
||||
want: []string{"1.2.3.4:53"},
|
||||
}, {
|
||||
name: "default",
|
||||
in: "_acme-challenge.example.org.",
|
||||
want: []string{},
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ups := config.getUpstreamsForDS(tc.in)
|
||||
assertUpstreamsForDomain(t, ups, tc.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpstreamConfig_Validate(t *testing.T) {
|
||||
@@ -157,7 +240,8 @@ func TestGetUpstreamsForDomain_wildcards(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assertUpstreamsForDomain(t, uconf, tc.in, tc.want)
|
||||
ups := uconf.getUpstreamsForDomain(tc.in)
|
||||
assertUpstreamsForDomain(t, ups, tc.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -197,7 +281,8 @@ func TestGetUpstreamsForDomain_sub_wildcards(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assertUpstreamsForDomain(t, uconf, tc.in, tc.want)
|
||||
ups := uconf.getUpstreamsForDomain(tc.in)
|
||||
assertUpstreamsForDomain(t, ups, tc.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -238,11 +323,15 @@ func TestGetUpstreamsForDomain_default_wildcards(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assertUpstreamsForDomain(t, uconf, tc.in, tc.want)
|
||||
ups := uconf.getUpstreamsForDomain(tc.in)
|
||||
assertUpstreamsForDomain(t, ups, tc.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// upsSink is the typed sink variable for the result of benchmarked function.
|
||||
var upsSink []upstream.Upstream
|
||||
|
||||
func BenchmarkGetUpstreamsForDomain(b *testing.B) {
|
||||
upstreams := []string{
|
||||
"[/google.com/local/]4.3.2.1",
|
||||
@@ -260,24 +349,26 @@ func BenchmarkGetUpstreamsForDomain(b *testing.B) {
|
||||
},
|
||||
)
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
assertUpstreamsForDomain(b, config, "www.google.com.", []string{"1.2.3.4:53", "tls://1.1.1.1:853"})
|
||||
assertUpstreamsForDomain(b, config, "www2.google.com.", []string{"4.3.2.1:53"})
|
||||
assertUpstreamsForDomain(b, config, "internal.local.", []string{"4.3.2.1:53"})
|
||||
assertUpstreamsForDomain(b, config, "google.", []string{"1.2.3.4:53"})
|
||||
assertUpstreamsForDomain(b, config, "maps.google.com.", []string{})
|
||||
domains := []string{
|
||||
"www.google.com.",
|
||||
"www2.google.com.",
|
||||
"internal.local.",
|
||||
"google.",
|
||||
"maps.google.com.",
|
||||
}
|
||||
|
||||
for i, l := 0, len(domains); i < b.N; i++ {
|
||||
upsSink = config.getUpstreamsForDomain(domains[i%l])
|
||||
}
|
||||
}
|
||||
|
||||
// assertUpstreamsForDomain checks the addresses of the specified domain
|
||||
// upstreams and their number.
|
||||
func assertUpstreamsForDomain(t testing.TB, config *UpstreamConfig, domain string, address []string) {
|
||||
t.Helper()
|
||||
func assertUpstreamsForDomain(tb testing.TB, ups []upstream.Upstream, want []string) {
|
||||
tb.Helper()
|
||||
|
||||
u := config.getUpstreamsForDomain(domain)
|
||||
require.Len(t, u, len(address))
|
||||
|
||||
for i, up := range u {
|
||||
assert.Equalf(t, address[i], up.Address(), "bad upstream at index %d", i)
|
||||
require.Len(tb, ups, len(want))
|
||||
for i, up := range ups {
|
||||
assert.Equalf(tb, want[i], up.Address(), "bad upstream at index %d", i)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user