AdGuardHome/internal/dnsforward/upstreams.go
Dimitry Kolyshev c0a33ce708 Pull request: upd-dnsproxy
Squashed commit of the following:

commit 463811748fa5a1f52e084c782e94f268b00b3abc
Merge: 3de62244e 130560b10
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Jul 10 15:06:01 2024 +0300

    Merge remote-tracking branch 'origin/master' into upd-dnsproxy

commit 3de62244ee10fce9fb97c73c2955479883ce34eb
Merge: e2de50bf9 e269260fb
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Jul 10 09:13:40 2024 +0300

    Merge remote-tracking branch 'origin/master' into upd-dnsproxy

commit e2de50bf9cf4eddaa0d87c20c8c1605bf4630fce
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Jul 10 09:11:25 2024 +0300

    home: todos

commit 58fe497eec
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Tue Jul 9 13:29:19 2024 +0300

    home: imp code

commit 4db7cdc0c4
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Tue Jul 9 11:31:12 2024 +0300

    all: imp code

commit 7e8d3b50e7
Merge: 559c3b79d 9a6dd0dc5
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Mon Jul 8 10:56:14 2024 +0300

    Merge remote-tracking branch 'origin/master' into upd-dnsproxy

commit 559c3b79d7
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Mon Jul 8 10:54:03 2024 +0300

    dnsforward: imp code

commit ba4a7e1c70
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Mon Jul 8 10:49:46 2024 +0300

    aghos: imp code

commit cdf9ccd371
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri Jul 5 16:19:27 2024 +0300

    all: partial revert slog logger usage

commit f16cddbb8c
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri Jul 5 13:01:37 2024 +0300

    all: upd dnsproxy

commit 5932c8d102
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri Jul 5 11:49:37 2024 +0300

    dnsforward: slog logger

commit 3d7f734ac9
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri Jul 5 11:05:14 2024 +0300

    all: slog logger

commit 9a74d5d98b
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Thu Jul 4 12:16:21 2024 +0300

    all: upd dnsproxy

commit 537bdacec8
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Thu Jul 4 12:10:30 2024 +0300

    all: upd dnsproxy

commit 38e10dee48
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Thu Jul 4 10:37:50 2024 +0300

    dnsforward: upstream mode
2024-07-10 15:18:46 +03:00

171 lines
5.1 KiB
Go

package dnsforward
import (
"fmt"
"slices"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/stringutil"
)
// newBootstrap returns a bootstrap resolver based on the configuration of s.
// boots are the upstream resolvers that should be closed after use. r is the
// actual bootstrap resolver, which may include the system hosts.
//
// TODO(e.burkov): This function currently returns a resolver and a slice of
// the upstream resolvers, which are essentially the same. boots are returned
// for being able to close them afterwards, but it introduces an implicit
// contract that r could only be used before that. Anyway, this code should
// improve when the [proxy.UpstreamConfig] will become an [upstream.Resolver]
// and be used here.
func newBootstrap(
addrs []string,
etcHosts upstream.Resolver,
opts *upstream.Options,
) (r upstream.Resolver, boots []*upstream.UpstreamResolver, err error) {
if len(addrs) == 0 {
addrs = defaultBootstrap
}
boots, err = aghnet.ParseBootstraps(addrs, opts)
if err != nil {
// Don't wrap the error, since it's informative enough as is.
return nil, nil, err
}
var parallel upstream.ParallelResolver
for _, b := range boots {
parallel = append(parallel, upstream.NewCachingResolver(b))
}
if etcHosts != nil {
r = upstream.ConsequentResolver{etcHosts, parallel}
} else {
r = parallel
}
return r, boots, nil
}
// newUpstreamConfig returns the upstream configuration based on upstreams. If
// upstreams slice specifies no default upstreams, defaultUpstreams are used to
// create upstreams with no domain specifications. opts are used when creating
// upstream configuration.
func newUpstreamConfig(
upstreams []string,
defaultUpstreams []string,
opts *upstream.Options,
) (uc *proxy.UpstreamConfig, err error) {
uc, err = proxy.ParseUpstreamsConfig(upstreams, opts)
if err != nil {
return uc, fmt.Errorf("parsing upstreams: %w", err)
}
if len(uc.Upstreams) == 0 && len(defaultUpstreams) > 0 {
log.Info("dnsforward: warning: no default upstreams specified, using %v", defaultUpstreams)
var defaultUpstreamConfig *proxy.UpstreamConfig
defaultUpstreamConfig, err = proxy.ParseUpstreamsConfig(defaultUpstreams, opts)
if err != nil {
return uc, fmt.Errorf("parsing default upstreams: %w", err)
}
uc.Upstreams = defaultUpstreamConfig.Upstreams
}
return uc, nil
}
// newPrivateConfig creates an upstream configuration for resolving PTR records
// for local addresses. The configuration is built either from the provided
// addresses or from the system resolvers. unwanted filters the resulting
// upstream configuration.
func newPrivateConfig(
addrs []string,
unwanted addrPortSet,
sysResolvers SystemResolvers,
privateNets netutil.SubnetSet,
opts *upstream.Options,
) (uc *proxy.UpstreamConfig, err error) {
confNeedsFiltering := len(addrs) > 0
if confNeedsFiltering {
addrs = stringutil.FilterOut(addrs, IsCommentOrEmpty)
} else {
sysResolvers := slices.DeleteFunc(slices.Clone(sysResolvers.Addrs()), unwanted.Has)
addrs = make([]string, 0, len(sysResolvers))
for _, r := range sysResolvers {
addrs = append(addrs, r.String())
}
}
log.Debug("dnsforward: private-use upstreams: %v", addrs)
uc, err = proxy.ParseUpstreamsConfig(addrs, opts)
if err != nil {
return uc, fmt.Errorf("preparing private upstreams: %w", err)
}
if confNeedsFiltering {
err = filterOutAddrs(uc, unwanted)
if err != nil {
return uc, fmt.Errorf("filtering private upstreams: %w", err)
}
}
// Prevalidate the config to catch the exact error before creating proxy.
// See TODO on [PrivateRDNSError].
err = proxy.ValidatePrivateConfig(uc, privateNets)
if err != nil {
return uc, &PrivateRDNSError{err: err}
}
return uc, nil
}
// UpstreamHTTPVersions returns the HTTP versions for upstream configuration
// depending on configuration.
func UpstreamHTTPVersions(http3 bool) (v []upstream.HTTPVersion) {
if !http3 {
return upstream.DefaultHTTPVersions
}
return []upstream.HTTPVersion{
upstream.HTTPVersion3,
upstream.HTTPVersion2,
upstream.HTTPVersion11,
}
}
// setProxyUpstreamMode sets the upstream mode and related settings in conf
// based on provided parameters.
func setProxyUpstreamMode(
conf *proxy.Config,
upstreamMode UpstreamMode,
fastestTimeout time.Duration,
) (err error) {
switch upstreamMode {
case UpstreamModeParallel:
conf.UpstreamMode = proxy.UpstreamModeParallel
case UpstreamModeFastestAddr:
conf.UpstreamMode = proxy.UpstreamModeFastestAddr
conf.FastestPingTimeout = fastestTimeout
case UpstreamModeLoadBalance:
conf.UpstreamMode = proxy.UpstreamModeLoadBalance
default:
return fmt.Errorf("unexpected value %q", upstreamMode)
}
return nil
}
// IsCommentOrEmpty returns true if s starts with a "#" character or is empty.
// This function is useful for filtering out non-upstream lines from upstream
// configs.
func IsCommentOrEmpty(s string) (ok bool) {
return len(s) == 0 || s[0] == '#'
}