Pull request 1891: 5902-bootstrap-hosts

Merge in DNS/adguard-home from 5902-bootstrap-hosts to master

Updates #5902.

Squashed commit of the following:

commit fcc65d3a8d7566acc361f54b18d1af85045225e2
Merge: 0c336af07 1fd6cf1a2
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Jun 30 12:29:06 2023 +0300

    Merge branch 'master' into 5902-bootstrap-hosts

commit 0c336af07d2864533e1f10029b4321d7cd210a47
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Jun 29 15:40:28 2023 +0300

    all: imp & simplify

commit 45aae90035b98b30199cc7fc92991528f4e968c0
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jun 28 20:24:43 2023 +0300

    all: imp code, docs

commit e3dbb5bfe5dfbde7af00f39adcc15e9711e5feb0
Merge: a33a8e93c 2069eddf9
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jun 28 18:27:36 2023 +0300

    Merge branch 'master' into 5902-bootstrap-hosts

commit a33a8e93cb36f7d0c4472e524e44de6ff0ab6653
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jun 28 13:27:11 2023 +0300

    aghos: add type check

commit 781a3a248871df2ea37a936c8d6b0b11e2d2f3a4
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jun 28 13:09:37 2023 +0300

    all: log changes

commit 4575368655356f84992fad2bfb78cbc1c88da25a
Merge: 636c440fc cf7c12c97
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jun 28 13:08:11 2023 +0300

    Merge branch 'master' into 5902-bootstrap-hosts

commit 636c440fca9cbdfd5c12b7f89432fb9323e01d86
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jun 28 13:06:32 2023 +0300

    all: imp tests

commit 0eff7a747e32216d78abf9db9460cb9d48f31f96
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Jun 26 18:40:22 2023 +0300

    dnsforward: imp code

commit 7489a30971e3c76b8f62fd4ca11a977eeabe2cf5
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Jun 26 17:04:10 2023 +0300

    all: resolve upstreams with hosts
This commit is contained in:
Eugene Burkov
2023-06-30 12:41:10 +03:00
parent 1fd6cf1a2f
commit 91f3e29c08
8 changed files with 502 additions and 205 deletions

View File

@@ -633,61 +633,70 @@ func (err domainSpecificTestError) Error() (msg string) {
return fmt.Sprintf("WARNING: %s", err.error)
}
// checkDNS checks the upstream server defined by upstreamConfigStr using
// healthCheck for actually exchange messages. It uses bootstrap to resolve the
// upstream's address.
func checkDNS(
upstreamConfigStr string,
bootstrap []string,
bootstrapPrefIPv6 bool,
timeout time.Duration,
healthCheck healthCheckFunc,
) (err error) {
if IsCommentOrEmpty(upstreamConfigStr) {
return nil
// parseUpstreamLine parses line and creates the [upstream.Upstream] using opts
// and information from [s.dnsFilter.EtcHosts]. It returns an error if the line
// is not a valid upstream line, see [upstream.AddressToUpstream]. It's a
// caller's responsibility to close u.
func (s *Server) parseUpstreamLine(
line string,
opts *upstream.Options,
) (u upstream.Upstream, specific bool, err error) {
// Separate upstream from domains list.
upstreamAddr, domains, err := separateUpstream(line)
if err != nil {
return nil, false, fmt.Errorf("wrong upstream format: %w", err)
}
// Separate upstream from domains list.
upstreamAddr, domains, err := separateUpstream(upstreamConfigStr)
if err != nil {
return fmt.Errorf("wrong upstream format: %w", err)
}
specific = len(domains) > 0
useDefault, err := validateUpstream(upstreamAddr, domains)
if err != nil {
return fmt.Errorf("wrong upstream format: %w", err)
return nil, specific, fmt.Errorf("wrong upstream format: %w", err)
} else if useDefault {
return nil
}
if len(bootstrap) == 0 {
bootstrap = defaultBootstrap
return nil, specific, nil
}
log.Debug("dnsforward: checking if upstream %q works", upstreamAddr)
u, err := upstream.AddressToUpstream(upstreamAddr, &upstream.Options{
Bootstrap: bootstrap,
Timeout: timeout,
PreferIPv6: bootstrapPrefIPv6,
})
opts = &upstream.Options{
Bootstrap: opts.Bootstrap,
Timeout: opts.Timeout,
PreferIPv6: opts.PreferIPv6,
}
if s.dnsFilter != nil && s.dnsFilter.EtcHosts != nil {
resolved := s.resolveUpstreamHost(extractUpstreamHost(upstreamAddr))
sortNetIPAddrs(resolved, opts.PreferIPv6)
opts.ServerIPAddrs = resolved
}
u, err = upstream.AddressToUpstream(upstreamAddr, opts)
if err != nil {
return fmt.Errorf("failed to choose upstream for %q: %w", upstreamAddr, err)
return nil, specific, fmt.Errorf("creating upstream for %q: %w", upstreamAddr, err)
}
return u, specific, nil
}
func (s *Server) checkDNS(line string, opts *upstream.Options, check healthCheckFunc) (err error) {
if IsCommentOrEmpty(line) {
return nil
}
var u upstream.Upstream
var specific bool
defer func() {
if err != nil && specific {
err = domainSpecificTestError{error: err}
}
}()
u, specific, err = s.parseUpstreamLine(line, opts)
if err != nil || u == nil {
return err
}
defer func() { err = errors.WithDeferred(err, u.Close()) }()
if err = healthCheck(u); err != nil {
err = fmt.Errorf("upstream %q fails to exchange: %w", upstreamAddr, err)
if domains != nil {
return domainSpecificTestError{error: err}
}
return err
}
log.Debug("dnsforward: upstream %q is ok", upstreamAddr)
return nil
return check(u)
}
func (s *Server) handleTestUpstreamDNS(w http.ResponseWriter, r *http.Request) {
@@ -699,47 +708,54 @@ func (s *Server) handleTestUpstreamDNS(w http.ResponseWriter, r *http.Request) {
return
}
result := map[string]string{}
bootstraps := req.BootstrapDNS
bootstrapPrefIPv6 := s.conf.BootstrapPreferIPv6
timeout := s.conf.UpstreamTimeout
opts := &upstream.Options{
Bootstrap: req.BootstrapDNS,
Timeout: s.conf.UpstreamTimeout,
PreferIPv6: s.conf.BootstrapPreferIPv6,
}
if len(opts.Bootstrap) == 0 {
opts.Bootstrap = defaultBootstrap
}
type upsCheckResult = struct {
res string
err error
host string
}
req.Upstreams = stringutil.FilterOut(req.Upstreams, IsCommentOrEmpty)
req.PrivateUpstreams = stringutil.FilterOut(req.PrivateUpstreams, IsCommentOrEmpty)
upsNum := len(req.Upstreams) + len(req.PrivateUpstreams)
result := make(map[string]string, upsNum)
resCh := make(chan upsCheckResult, upsNum)
checkUps := func(ups string, healthCheck healthCheckFunc) {
res := upsCheckResult{
host: ups,
}
defer func() { resCh <- res }()
checkErr := checkDNS(ups, bootstraps, bootstrapPrefIPv6, timeout, healthCheck)
if checkErr != nil {
res.res = checkErr.Error()
} else {
res.res = "OK"
}
}
for _, ups := range req.Upstreams {
go checkUps(ups, checkDNSUpstreamExc)
go func(ups string) {
resCh <- upsCheckResult{
host: ups,
err: s.checkDNS(ups, opts, checkDNSUpstreamExc),
}
}(ups)
}
for _, ups := range req.PrivateUpstreams {
go checkUps(ups, checkPrivateUpstreamExc)
go func(ups string) {
resCh <- upsCheckResult{
host: ups,
err: s.checkDNS(ups, opts, checkPrivateUpstreamExc),
}
}(ups)
}
for i := 0; i < upsNum; i++ {
pair := <-resCh
// TODO(e.burkov): The upstreams used for both common and private
// resolving should be reported separately.
result[pair.host] = pair.res
pair := <-resCh
if pair.err != nil {
result[pair.host] = pair.err.Error()
} else {
result[pair.host] = "OK"
}
}
close(resCh)
_ = aghhttp.WriteJSONResponse(w, r, result)
}