mirror of
https://github.com/AdguardTeam/AdGuardHome.git
synced 2025-03-14 22:48:35 +03:00
all: upstream config parser
This commit is contained in:
parent
9241393ed2
commit
15fbd229de
5 changed files with 75 additions and 84 deletions
2
go.mod
2
go.mod
|
@ -3,7 +3,7 @@ module github.com/AdguardTeam/AdGuardHome
|
|||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/AdguardTeam/dnsproxy v0.60.1
|
||||
github.com/AdguardTeam/dnsproxy v0.60.2-0.20231213112429-14cbbed218dc
|
||||
github.com/AdguardTeam/golibs v0.18.0
|
||||
github.com/AdguardTeam/urlfilter v0.17.3
|
||||
github.com/NYTimes/gziphandler v1.1.1
|
||||
|
|
4
go.sum
4
go.sum
|
@ -1,5 +1,5 @@
|
|||
github.com/AdguardTeam/dnsproxy v0.60.1 h1:YveGe7UZLaAiePkaV3orkc0IIfPX9vi/qQDIFdeO//A=
|
||||
github.com/AdguardTeam/dnsproxy v0.60.1/go.mod h1:B7FvvTFQZBfey1cJXQo732EyCLX6xj4JqrciCawATzg=
|
||||
github.com/AdguardTeam/dnsproxy v0.60.2-0.20231213112429-14cbbed218dc h1:D60Z2i4nR13VbWRXrFqO3ZuwA143xSKqNPh+1BEmn+4=
|
||||
github.com/AdguardTeam/dnsproxy v0.60.2-0.20231213112429-14cbbed218dc/go.mod h1:B7FvvTFQZBfey1cJXQo732EyCLX6xj4JqrciCawATzg=
|
||||
github.com/AdguardTeam/golibs v0.18.0 h1:ckS2YK7t2Ub6UkXl0fnreVaM15Zb07Hh1gmFqttjpWg=
|
||||
github.com/AdguardTeam/golibs v0.18.0/go.mod h1:DKhCIXHcUYtBhU8ibTLKh1paUL96n5zhQBlx763sj+U=
|
||||
github.com/AdguardTeam/urlfilter v0.17.3 h1:fg/ObbnO0Cv6aw0tW6N/ETDMhhNvmcUUOZ7HlmKC3rw=
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/AdguardTeam/dnsproxy/proxy"
|
||||
"github.com/AdguardTeam/dnsproxy/upstream"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
|
@ -63,49 +64,88 @@ func newUpstreamConfigValidator(
|
|||
) (cv *upstreamConfigValidator) {
|
||||
cv = &upstreamConfigValidator{}
|
||||
|
||||
for _, line := range general {
|
||||
cv.general = cv.insertLineResults(cv.general, line, opts)
|
||||
}
|
||||
for _, line := range fallback {
|
||||
cv.fallback = cv.insertLineResults(cv.fallback, line, opts)
|
||||
}
|
||||
for _, line := range private {
|
||||
cv.private = cv.insertLineResults(cv.private, line, opts)
|
||||
}
|
||||
cv.general = cv.insertLinesResults(cv.general, general, opts)
|
||||
cv.fallback = cv.insertLinesResults(cv.fallback, fallback, opts)
|
||||
cv.private = cv.insertLinesResults(cv.private, private, opts)
|
||||
|
||||
return cv
|
||||
}
|
||||
|
||||
// insertLineResults parses line and inserts the result into s. It can insert
|
||||
// insertLinesResults parses lines and inserts the result into s. It can insert
|
||||
// multiple results as well as none.
|
||||
func (cv *upstreamConfigValidator) insertLineResults(
|
||||
func (cv *upstreamConfigValidator) insertLinesResults(
|
||||
s []*upstreamResult,
|
||||
line string,
|
||||
lines []string,
|
||||
opts *upstream.Options,
|
||||
) (result []*upstreamResult) {
|
||||
upstreams, isSpecific, err := splitUpstreamLine(line)
|
||||
conf, err := proxy.ParseUpstreamsConfig(lines, opts)
|
||||
if err != nil {
|
||||
return cv.insert(s, &upstreamResult{
|
||||
err: err,
|
||||
original: line,
|
||||
})
|
||||
s = cv.insertErrResults(s, err)
|
||||
}
|
||||
|
||||
for _, upstreamAddr := range upstreams {
|
||||
var res *upstreamResult
|
||||
if upstreamAddr != "#" {
|
||||
res = cv.parseUpstream(upstreamAddr, opts)
|
||||
} else if !isSpecific {
|
||||
res = &upstreamResult{
|
||||
err: errNotDomainSpecific,
|
||||
original: upstreamAddr,
|
||||
}
|
||||
} else {
|
||||
return cv.insertConfResults(s, conf)
|
||||
}
|
||||
|
||||
// insertErrResults parses err and inserts the result into s. It can insert
|
||||
// multiple results as well as none.
|
||||
func (cv *upstreamConfigValidator) insertErrResults(
|
||||
s []*upstreamResult,
|
||||
err error,
|
||||
) (result []*upstreamResult) {
|
||||
wrapper, ok := err.(interface{ Unwrap() []error })
|
||||
if !ok {
|
||||
return s
|
||||
}
|
||||
|
||||
errs := wrapper.Unwrap()
|
||||
for _, e := range errs {
|
||||
var parseErr *proxy.ParseError
|
||||
if !errors.As(e, &parseErr) {
|
||||
continue
|
||||
}
|
||||
|
||||
res.isSpecific = isSpecific
|
||||
s = cv.insert(s, res)
|
||||
idx := parseErr.Idx
|
||||
s = cv.insert(s, &upstreamResult{
|
||||
err: err,
|
||||
original: fmt.Sprintf("Line: %d", idx+1),
|
||||
})
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// insertConfResults parses conf and inserts the result into s. It can insert
|
||||
// multiple results as well as none.
|
||||
func (cv *upstreamConfigValidator) insertConfResults(
|
||||
s []*upstreamResult,
|
||||
conf *proxy.UpstreamConfig,
|
||||
) (result []*upstreamResult) {
|
||||
s = cv.insertListResults(s, conf.Upstreams, false)
|
||||
|
||||
for _, ups := range conf.DomainReservedUpstreams {
|
||||
s = cv.insertListResults(s, ups, true)
|
||||
}
|
||||
|
||||
for _, ups := range conf.SpecifiedDomainUpstreams {
|
||||
s = cv.insertListResults(s, ups, true)
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// insertListResults constructs upstream results from the upstream list and
|
||||
// inserts into s. It can insert multiple results as well as none.
|
||||
func (cv *upstreamConfigValidator) insertListResults(
|
||||
s []*upstreamResult,
|
||||
ups []upstream.Upstream,
|
||||
specific bool,
|
||||
) (result []*upstreamResult) {
|
||||
for _, u := range ups {
|
||||
s = cv.insert(s, &upstreamResult{
|
||||
server: u,
|
||||
original: u.Address(),
|
||||
isSpecific: specific,
|
||||
})
|
||||
}
|
||||
|
||||
return s
|
||||
|
@ -127,34 +167,6 @@ func (cv *upstreamConfigValidator) insert(
|
|||
return slices.Insert(s, i, r)
|
||||
}
|
||||
|
||||
// parseUpstream parses addr and returns the result of parsing. It returns nil
|
||||
// if the specified server points at the default upstream server which is
|
||||
// validated separately.
|
||||
func (cv *upstreamConfigValidator) parseUpstream(
|
||||
addr string,
|
||||
opts *upstream.Options,
|
||||
) (r *upstreamResult) {
|
||||
// Check if the upstream has a valid protocol prefix.
|
||||
//
|
||||
// TODO(e.burkov): Validate the domain name.
|
||||
if proto, _, ok := strings.Cut(addr, "://"); ok {
|
||||
if !slices.Contains(protocols, proto) {
|
||||
return &upstreamResult{
|
||||
err: fmt.Errorf("bad protocol %q", proto),
|
||||
original: addr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ups, err := upstream.AddressToUpstream(addr, opts)
|
||||
|
||||
return &upstreamResult{
|
||||
server: ups,
|
||||
err: err,
|
||||
original: addr,
|
||||
}
|
||||
}
|
||||
|
||||
// check tries to exchange with each successfully parsed upstream and enriches
|
||||
// the results with the healthcheck errors. It should not be called after the
|
||||
// [upsConfValidator.close] method, since it makes no sense to check the closed
|
||||
|
|
|
@ -20,10 +20,6 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
// errNotDomainSpecific is returned when the upstream should be
|
||||
// domain-specific, but isn't.
|
||||
errNotDomainSpecific errors.Error = "not a domain-specific upstream"
|
||||
|
||||
// errMissingSeparator is returned when the domain-specific part of the
|
||||
// upstream configuration line isn't closed.
|
||||
errMissingSeparator errors.Error = "missing separator"
|
||||
|
|
|
@ -100,8 +100,7 @@ func TestUpstreamConfigValidator(t *testing.T) {
|
|||
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`,
|
||||
"/]1.2.3.4:53": `WARNING: couldn't communicate with upstream: creating dial handler: dialing "/]1.2.3.4:53": address /]1.2.3.4:53: unexpected ']' in address`,
|
||||
},
|
||||
}, {
|
||||
name: "all_different",
|
||||
|
@ -120,23 +119,7 @@ func TestUpstreamConfigValidator(t *testing.T) {
|
|||
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",
|
||||
"Line: 1": `index: 0: cannot prepare the upstream 0 "[/example/]/]` + goodUps + `": unsupported url scheme: `,
|
||||
},
|
||||
}, {
|
||||
name: "bad_proto",
|
||||
|
@ -144,7 +127,7 @@ func TestUpstreamConfigValidator(t *testing.T) {
|
|||
"bad://1.2.3.4",
|
||||
},
|
||||
want: map[string]string{
|
||||
"bad://1.2.3.4": `bad protocol "bad"`,
|
||||
"Line: 1": `index: 0: cannot prepare the upstream 0 "bad://1.2.3.4": unsupported url scheme: bad`,
|
||||
},
|
||||
}}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue