Pull request 2064: AG-23599 Upd proxy

Merge in DNS/adguard-home from AG-23599-upd-proxy to master

Squashed commit of the following:

commit 31a4da2fe425d648a94f13060e8786ffae0be3ca
Merge: 2c2fb253d 94bceaa84
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Nov 16 13:37:55 2023 +0300

    Merge branch 'master' into AG-23599-upd-proxy

commit 2c2fb253d489baa6b97a524b7e3327676ee6aa6f
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Nov 15 19:03:20 2023 +0300

    dnsforward: imp code

commit 7384365758f80753cc4234184e7bd7311a85435d
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Nov 14 17:02:07 2023 +0300

    all: imp code

commit 9c0be82285eed0602f593f805cfb7d02ace17a64
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Nov 10 20:21:00 2023 +0300

    all: imp code, docs

commit 5a47875882b5afd0264e4d473e884843745ff3f4
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Nov 9 16:50:51 2023 +0300

    all: upd proxy
This commit is contained in:
Eugene Burkov
2023-11-16 14:05:10 +03:00
parent 94bceaa84d
commit 9b91a87406
19 changed files with 443 additions and 346 deletions

View File

@@ -3,6 +3,7 @@ package dnsforward
import (
"fmt"
"io"
"net"
"net/http"
"net/netip"
@@ -135,8 +136,21 @@ type Server struct {
// PTR resolving.
sysResolvers SystemResolvers
// recDetector is a cache for recursive requests. It is used to detect
// and prevent recursive requests only for private upstreams.
// etcHosts contains the data from the system's hosts files.
etcHosts upstream.Resolver
// bootstrap is the resolver for upstreams' hostnames.
bootstrap upstream.Resolver
// bootResolvers are the resolvers that should be used for
// bootstrapping along with [etcHosts].
//
// TODO(e.burkov): Use [proxy.UpstreamConfig] when it will implement the
// [upstream.Resolver] interface.
bootResolvers []*upstream.UpstreamResolver
// recDetector is a cache for recursive requests. It is used to detect and
// prevent recursive requests only for private upstreams.
//
// See https://github.com/adguardTeam/adGuardHome/issues/3185#issuecomment-851048135.
recDetector *recursionDetector
@@ -153,8 +167,8 @@ type Server struct {
// during the BeforeRequestHandler stage.
clientIDCache cache.Cache
// DNS proxy instance for internal usage
// We don't Start() it and so no listen port is required.
// internalProxy resolves internal requests from the application itself. It
// isn't started and so no listen ports are required.
internalProxy *proxy.Proxy
// isRunning is true if the DNS server is running.
@@ -185,6 +199,7 @@ type DNSCreateParams struct {
DHCPServer DHCP
PrivateNets netutil.SubnetSet
Anonymizer *aghnet.IPMut
EtcHosts *aghnet.HostsContainer
LocalDomain string
}
@@ -224,6 +239,7 @@ func NewServer(p DNSCreateParams) (s *Server, err error) {
privateNets: p.PrivateNets,
// TODO(e.burkov): Use some case-insensitive string comparison.
localDomainSuffix: strings.ToLower(localDomainSuffix),
etcHosts: p.EtcHosts,
recDetector: newRecursionDetector(recursionTTL, cachedRecurrentReqNum),
clientIDCache: cache.New(cache.Config{
EnableLRU: true,
@@ -421,7 +437,7 @@ func hostFromPTR(resp *dns.Msg) (host string, ttl time.Duration, err error) {
return "", 0, ErrRDNSNoData
}
// Start starts the DNS server.
// Start starts the DNS server. It must only be called after [Server.Prepare].
func (s *Server) Start() error {
s.serverLock.Lock()
defer s.serverLock.Unlock()
@@ -429,12 +445,14 @@ func (s *Server) Start() error {
return s.startLocked()
}
// startLocked starts the DNS server without locking. For internal use only.
// startLocked starts the DNS server without locking. s.serverLock is expected
// to be locked.
func (s *Server) startLocked() error {
err := s.dnsProxy.Start()
if err == nil {
s.isRunning = true
}
return err
}
@@ -443,21 +461,20 @@ func (s *Server) startLocked() error {
// faster than ordinary upstreams.
const defaultLocalTimeout = 1 * time.Second
// setupLocalResolvers initializes the resolvers for local addresses. For
// internal use only.
func (s *Server) setupLocalResolvers() (err error) {
matcher, err := s.conf.ourAddrsMatcher()
// setupLocalResolvers initializes the resolvers for local addresses. It
// assumes s.serverLock is locked or the Server not running.
func (s *Server) setupLocalResolvers(boot upstream.Resolver) (err error) {
set, err := s.conf.ourAddrsSet()
if err != nil {
// Don't wrap the error because it's informative enough as is.
return err
}
bootstraps := s.conf.BootstrapDNS
resolvers := s.conf.LocalPTRResolvers
filterConfig := false
if len(resolvers) == 0 {
sysResolvers := slices.DeleteFunc(s.sysResolvers.Addrs(), matcher)
sysResolvers := slices.DeleteFunc(slices.Clone(s.sysResolvers.Addrs()), set.Has)
resolvers = make([]string, 0, len(sysResolvers))
for _, r := range sysResolvers {
resolvers = append(resolvers, r.String())
@@ -470,7 +487,7 @@ func (s *Server) setupLocalResolvers() (err error) {
log.Debug("dnsforward: upstreams to resolve ptr for local addresses: %v", resolvers)
uc, err := s.prepareUpstreamConfig(resolvers, nil, &upstream.Options{
Bootstrap: bootstraps,
Bootstrap: boot,
Timeout: defaultLocalTimeout,
// TODO(e.burkov): Should we verify server's certificates?
PreferIPv6: s.conf.BootstrapPreferIPv6,
@@ -480,7 +497,7 @@ func (s *Server) setupLocalResolvers() (err error) {
}
if filterConfig {
if err = matcher.filterOut(uc); err != nil {
if err = filterOutAddrs(uc, set); err != nil {
return fmt.Errorf("filtering private upstreams: %w", err)
}
}
@@ -491,6 +508,7 @@ func (s *Server) setupLocalResolvers() (err error) {
},
}
// TODO(e.burkov): Should we also consider the DNS64 usage?
if s.conf.UsePrivateRDNS &&
// Only set the upstream config if there are any upstreams. It's safe
// to put nil into [proxy.Config.PrivateRDNSUpstreamConfig].
@@ -517,31 +535,19 @@ func (s *Server) Prepare(conf *ServerConfig) (err error) {
s.initDefaultSettings()
err = s.prepareIpsetListSettings()
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return fmt.Errorf("preparing ipset settings: %w", err)
}
err = s.prepareUpstreamSettings()
boot, err := s.prepareInternalDNS()
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
}
var proxyConfig proxy.Config
proxyConfig, err = s.createProxyConfig()
proxyConfig, err := s.createProxyConfig()
if err != nil {
return fmt.Errorf("preparing proxy: %w", err)
}
s.setupDNS64()
err = s.prepareInternalProxy()
if err != nil {
return fmt.Errorf("preparing internal proxy: %w", err)
}
s.access, err = newAccessCtx(
s.conf.AllowedClients,
s.conf.DisallowedClients,
@@ -556,7 +562,7 @@ func (s *Server) Prepare(conf *ServerConfig) (err error) {
// TODO(e.burkov): Remove once the local resolvers logic moved to dnsproxy.
s.dnsProxy = &proxy.Proxy{Config: proxyConfig}
err = s.setupLocalResolvers()
err = s.setupLocalResolvers(boot)
if err != nil {
return fmt.Errorf("setting up resolvers: %w", err)
}
@@ -575,6 +581,38 @@ func (s *Server) Prepare(conf *ServerConfig) (err error) {
return nil
}
// prepareInternalDNS initializes the internal state of s before initializing
// the primary DNS proxy instance. It assumes s.serverLock is locked or the
// Server not running.
func (s *Server) prepareInternalDNS() (boot upstream.Resolver, err error) {
err = s.prepareIpsetListSettings()
if err != nil {
return nil, fmt.Errorf("preparing ipset settings: %w", err)
}
s.bootstrap, s.bootResolvers, err = s.createBootstrap(s.conf.BootstrapDNS, &upstream.Options{
Timeout: DefaultTimeout,
HTTPVersions: UpstreamHTTPVersions(s.conf.UseHTTP3Upstreams),
})
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return nil, err
}
err = s.prepareUpstreamSettings(boot)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return s.bootstrap, err
}
err = s.prepareInternalProxy()
if err != nil {
return s.bootstrap, fmt.Errorf("preparing internal proxy: %w", err)
}
return s.bootstrap, nil
}
// setupFallbackDNS initializes the fallback DNS servers.
func (s *Server) setupFallbackDNS() (err error) {
fallbacks := s.conf.FallbackDNS
@@ -598,7 +636,8 @@ func (s *Server) setupFallbackDNS() (err error) {
return nil
}
// setupAddrProc initializes the address processor. For internal use only.
// setupAddrProc initializes the address processor. It assumes s.serverLock is
// locked or the Server not running.
func (s *Server) setupAddrProc() {
// TODO(a.garipov): This is a crutch for tests; remove.
if s.conf.AddrProcConf == nil {
@@ -687,7 +726,8 @@ func (s *Server) Stop() error {
return s.stopLocked()
}
// stopLocked stops the DNS server without locking. For internal use only.
// stopLocked stops the DNS server without locking. s.serverLock is expected to
// be locked.
func (s *Server) stopLocked() (err error) {
// TODO(e.burkov, a.garipov): Return critical errors, not just log them.
// This will require filtering all the non-critical errors in
@@ -700,18 +740,11 @@ func (s *Server) stopLocked() (err error) {
}
}
if upsConf := s.internalProxy.UpstreamConfig; upsConf != nil {
err = upsConf.Close()
if err != nil {
log.Error("dnsforward: closing internal resolvers: %s", err)
}
}
logCloserErr(s.internalProxy.UpstreamConfig, "dnsforward: closing internal resolvers: %s")
logCloserErr(s.localResolvers.UpstreamConfig, "dnsforward: closing local resolvers: %s")
if upsConf := s.localResolvers.UpstreamConfig; upsConf != nil {
err = upsConf.Close()
if err != nil {
log.Error("dnsforward: closing local resolvers: %s", err)
}
for _, b := range s.bootResolvers {
logCloserErr(b, "dnsforward: closing bootstrap %s: %s", b.Address())
}
s.isRunning = false
@@ -719,6 +752,18 @@ func (s *Server) stopLocked() (err error) {
return nil
}
// logCloserErr logs the error returned by c, if any.
func logCloserErr(c io.Closer, format string, args ...any) {
if c == nil {
return
}
err := c.Close()
if err != nil {
log.Error(format, append(args, err)...)
}
}
// IsRunning returns true if the DNS server is running.
func (s *Server) IsRunning() bool {
s.serverLock.RLock()