Skip to content

Commit

Permalink
dnsforward: add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
EugeneOne1 committed Nov 22, 2023
1 parent 7077597 commit 120ba4b
Show file tree
Hide file tree
Showing 3 changed files with 178 additions and 110 deletions.
116 changes: 9 additions & 107 deletions internal/dnsforward/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ func TestValidateUpstreams(t *testing.T) {
set: []string{"123.3.7m"},
}, {
name: "invalid",
wantErr: `bad upstream for domain "[/host.com]tls://dns.adguard.com": ` +
wantErr: `splitting upstream line "[/host.com]tls://dns.adguard.com": ` +
`missing separator`,
set: []string{"[/host.com]tls://dns.adguard.com"},
}, {
Expand All @@ -387,7 +387,7 @@ func TestValidateUpstreams(t *testing.T) {
},
}, {
name: "bad_domain",
wantErr: `bad upstream for domain "[/!/]8.8.8.8": domain at index 0: ` +
wantErr: `splitting upstream line "[/!/]8.8.8.8": domain at index 0: ` +
`bad domain name "!": bad top-level domain name label "!": ` +
`bad top-level domain name label rune '!'`,
set: []string{"[/!/]8.8.8.8"},
Expand Down Expand Up @@ -475,34 +475,24 @@ func newLocalUpstreamListener(t *testing.T, port uint16, handler dns.Handler) (r
}

func TestServer_HandleTestUpstreamDNS(t *testing.T) {
goodHandler := dns.HandlerFunc(func(w dns.ResponseWriter, m *dns.Msg) {
hdlr := dns.HandlerFunc(func(w dns.ResponseWriter, m *dns.Msg) {
err := w.WriteMsg(new(dns.Msg).SetReply(m))
require.NoError(testutil.PanicT{}, err)
})
badHandler := dns.HandlerFunc(func(w dns.ResponseWriter, _ *dns.Msg) {
err := w.WriteMsg(new(dns.Msg))
require.NoError(testutil.PanicT{}, err)
})

goodUps := (&url.URL{
Scheme: "tcp",
Host: newLocalUpstreamListener(t, 0, goodHandler).String(),
}).String()
badUps := (&url.URL{
ups := (&url.URL{
Scheme: "tcp",
Host: newLocalUpstreamListener(t, 0, badHandler).String(),
Host: newLocalUpstreamListener(t, 0, hdlr).String(),
}).String()

goodAndBadUps := strings.Join([]string{goodUps, badUps}, " ")

const (
upsTimeout = 100 * time.Millisecond

hostsFileName = "hosts"
upstreamHost = "custom.localhost"
)

hostsListener := newLocalUpstreamListener(t, 0, goodHandler)
hostsListener := newLocalUpstreamListener(t, 0, hdlr)
hostsUps := (&url.URL{
Scheme: "tcp",
Host: netutil.JoinHostPort(upstreamHost, hostsListener.Port()),
Expand Down Expand Up @@ -542,43 +532,6 @@ func TestServer_HandleTestUpstreamDNS(t *testing.T) {
wantResp map[string]any
name string
}{{
body: map[string]any{
"upstream_dns": []string{goodUps},
},
wantResp: map[string]any{
goodUps: "OK",
},
name: "success",
}, {
body: map[string]any{
"upstream_dns": []string{badUps},
},
wantResp: map[string]any{
badUps: `couldn't communicate with upstream: exchanging with ` +
badUps + ` over tcp: dns: id mismatch`,
},
name: "broken",
}, {
body: map[string]any{
"upstream_dns": []string{goodUps, badUps},
},
wantResp: map[string]any{
goodUps: "OK",
badUps: `couldn't communicate with upstream: exchanging with ` +
badUps + ` over tcp: dns: id mismatch`,
},
name: "both",
}, {
body: map[string]any{
"upstream_dns": []string{"[/domain.example/]" + badUps},
},
wantResp: map[string]any{
badUps: `WARNING: couldn't communicate ` +
`with upstream: exchanging with ` + badUps + ` over tcp: ` +
`dns: id mismatch`,
},
name: "domain_specific_error",
}, {
body: map[string]any{
"upstream_dns": []string{hostsUps},
},
Expand All @@ -588,63 +541,12 @@ func TestServer_HandleTestUpstreamDNS(t *testing.T) {
name: "etc_hosts",
}, {
body: map[string]any{
"fallback_dns": []string{goodUps},
},
wantResp: map[string]any{
goodUps: "OK",
},
name: "fallback_success",
}, {
body: map[string]any{
"fallback_dns": []string{badUps},
},
wantResp: map[string]any{
badUps: `couldn't communicate with upstream: exchanging with ` +
badUps + ` over tcp: dns: id mismatch`,
},
name: "fallback_broken",
}, {
body: map[string]any{
"fallback_dns": []string{goodUps, "#this.is.comment"},
},
wantResp: map[string]any{
goodUps: "OK",
},
name: "fallback_comment_mix",
}, {
body: map[string]any{
"upstream_dns": []string{"[/domain.example/]" + goodUps + " " + badUps},
},
wantResp: map[string]any{
goodUps: "OK",
badUps: `WARNING: couldn't communicate ` +
`with upstream: exchanging with ` + badUps + ` over tcp: ` +
`dns: id mismatch`,
},
name: "multiple_domain_specific_upstreams",
}, {
body: map[string]any{
"upstream_dns": []string{"[/domain.example/]/]1.2.3.4"},
},
wantResp: map[string]any{
"[/domain.example/]/]1.2.3.4": `separating upstream line: ` +
`bad upstream for domain "[/domain.example/]/]1.2.3.4": ` +
`duplicated separator`,
},
name: "bad_specification",
}, {
body: map[string]any{
"upstream_dns": []string{"[/domain.example/]" + goodAndBadUps},
"fallback_dns": []string{"[/domain.example/]" + goodAndBadUps},
"private_upstream": []string{"[/domain.example/]" + goodAndBadUps},
"upstream_dns": []string{ups, "#this.is.comment"},
},
wantResp: map[string]any{
goodUps: "OK",
badUps: `WARNING: couldn't communicate ` +
`with upstream: exchanging with ` + badUps + ` over tcp: ` +
`dns: id mismatch`,
ups: "OK",
},
name: "all_different",
name: "comment_mix",
}}

for _, tc := range testCases {
Expand Down
9 changes: 6 additions & 3 deletions internal/dnsforward/upstreams.go
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ func splitUpstreamLine(upstreamStr string) (upstreams []string, isSpecific bool,
return []string{upstreamStr}, false, nil
}

defer func() { err = errors.Annotate(err, "bad upstream for domain %q: %w", upstreamStr) }()
defer func() { err = errors.Annotate(err, "splitting upstream line %q: %w", upstreamStr) }()

doms, ups, found := strings.Cut(upstreamStr[2:], "/]")
if !found {
Expand Down Expand Up @@ -444,7 +444,7 @@ func (cv *upstreamConfigValidator) insertLineResults(
upstreams, isSpecific, err := splitUpstreamLine(line)
if err != nil {
return cv.insert(s, &upstreamResult{
err: fmt.Errorf("separating upstream line: %w", err),
err: err,
original: line,
})
}
Expand All @@ -471,7 +471,10 @@ func (cv *upstreamConfigValidator) insertLineResults(

// insert inserts r into slice in a sorted order, except duplicates. slice must
// not be nil.
func (cv *upstreamConfigValidator) insert(s []*upstreamResult, r *upstreamResult) (res []*upstreamResult) {
func (cv *upstreamConfigValidator) insert(
s []*upstreamResult,
r *upstreamResult,
) (result []*upstreamResult) {
i, has := slices.BinarySearchFunc(s, r, (*upstreamResult).compare)
if has {
log.Debug("dnsforward: duplicate configuration %q", r.original)
Expand Down
163 changes: 163 additions & 0 deletions internal/dnsforward/upstreams_internal_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package dnsforward

import (
"net"
"net/url"
"strings"
"testing"
"time"

"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/testutil"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestUpstreamConfigValidator(t *testing.T) {
goodHandler := dns.HandlerFunc(func(w dns.ResponseWriter, m *dns.Msg) {
err := w.WriteMsg(new(dns.Msg).SetReply(m))
require.NoError(testutil.PanicT{}, err)
})
badHandler := dns.HandlerFunc(func(w dns.ResponseWriter, _ *dns.Msg) {
err := w.WriteMsg(new(dns.Msg))
require.NoError(testutil.PanicT{}, err)
})

goodUps := (&url.URL{
Scheme: "tcp",
Host: newLocalUpstreamListener(t, 0, goodHandler).String(),
}).String()
badUps := (&url.URL{
Scheme: "tcp",
Host: newLocalUpstreamListener(t, 0, badHandler).String(),
}).String()

goodAndBadUps := strings.Join([]string{goodUps, badUps}, " ")

// upsTimeout restricts the checking process to prevent the test from
// hanging.
const upsTimeout = 100 * time.Millisecond

testCases := []struct {
want map[string]string
name string
general []string
fallback []string
private []string
}{{
name: "success",
general: []string{goodUps},
want: map[string]string{
goodUps: "OK",
},
}, {
name: "broken",
general: []string{badUps},
want: map[string]string{
badUps: `couldn't communicate with upstream: exchanging with ` +
badUps + ` over tcp: dns: id mismatch`,
},
}, {
name: "both",
general: []string{goodUps, badUps, goodUps},
want: map[string]string{
goodUps: "OK",
badUps: `couldn't communicate with upstream: exchanging with ` +
badUps + ` over tcp: dns: id mismatch`,
},
}, {
name: "domain_specific_error",
general: []string{"[/domain.example/]" + badUps},
want: map[string]string{
badUps: `WARNING: couldn't communicate ` +
`with upstream: exchanging with ` + badUps + ` over tcp: ` +
`dns: id mismatch`,
},
}, {
name: "fallback_success",
fallback: []string{goodUps},
want: map[string]string{
goodUps: "OK",
},
}, {
name: "fallback_broken",
fallback: []string{badUps},
want: map[string]string{
badUps: `couldn't communicate with upstream: exchanging with ` +
badUps + ` over tcp: dns: id mismatch`,
},
}, {
name: "multiple_domain_specific_upstreams",
general: []string{"[/domain.example/]" + goodAndBadUps},
want: map[string]string{
goodUps: "OK",
badUps: `WARNING: couldn't communicate ` +
`with upstream: exchanging with ` + badUps + ` over tcp: ` +
`dns: id mismatch`,
},
}, {
name: "bad_specification",
general: []string{"[/domain.example/]/]1.2.3.4"},
want: map[string]string{
"[/domain.example/]/]1.2.3.4": `splitting upstream line ` +
`"[/domain.example/]/]1.2.3.4": duplicated separator`,
},
}, {
name: "all_different",
general: []string{"[/domain.example/]" + goodAndBadUps},
fallback: []string{"[/domain.example/]" + goodAndBadUps},
private: []string{"[/domain.example/]" + goodAndBadUps},
want: map[string]string{
goodUps: "OK",
badUps: `WARNING: couldn't communicate ` +
`with upstream: exchanging with ` + badUps + ` over tcp: ` +
`dns: id mismatch`,
},
}, {
name: "bad_specific_domains",
general: []string{"[/example/]/]" + goodUps},
fallback: []string{"[/example/" + goodUps},
private: []string{"[/example//bad.123/]" + goodUps},
want: map[string]string{
`[/example/]/]` + goodUps: `splitting upstream line ` +
`"[/example/]/]` + goodUps + `": duplicated separator`,
`[/example/` + goodUps: `splitting upstream line ` +
`"[/example/` + goodUps + `": missing separator`,
`[/example//bad.123/]` + goodUps: `splitting upstream line ` +
`"[/example//bad.123/]` + goodUps + `": domain at index 2: ` +
`bad domain name "bad.123": ` +
`bad top-level domain name label "123": all octets are numeric`,
},
}, {
name: "non-specific_default",
general: []string{
"#",
"[/example/]#",
},
want: map[string]string{
"#": "not a domain-specific upstream",
},
}, {
name: "bad_proto",
general: []string{
"bad://1.2.3.4",
},
want: map[string]string{
"bad://1.2.3.4": `bad protocol "bad"`,
},
}}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
cv := newUpstreamConfigValidator(tc.general, tc.fallback, tc.private, &upstream.Options{
Timeout: upsTimeout,
Bootstrap: net.DefaultResolver,
})
cv.check()
cv.close()

assert.Equal(t, tc.want, cv.status())
})
}
}

0 comments on commit 120ba4b

Please sign in to comment.