Pull request: 4403-internal-proxy

Updates #4403.

* commit 'd519929988575439c2238924ae5b1d96091445f7':
  dnsforward: imp code, fmt
  Also honor the user-defined UpstreamMode for the internal DNS proxy
This commit is contained in:
Ainar Garipov 2022-08-29 16:23:03 +03:00
commit a0c8aee3f7
7 changed files with 123 additions and 96 deletions

View file

@ -163,10 +163,10 @@ type TLSConfig struct {
// DNSCryptConfig is the DNSCrypt server configuration struct. // DNSCryptConfig is the DNSCrypt server configuration struct.
type DNSCryptConfig struct { type DNSCryptConfig struct {
ResolverCert *dnscrypt.Cert
ProviderName string
UDPListenAddrs []*net.UDPAddr UDPListenAddrs []*net.UDPAddr
TCPListenAddrs []*net.TCPAddr TCPListenAddrs []*net.TCPAddr
ProviderName string
ResolverCert *dnscrypt.Cert
Enabled bool Enabled bool
} }
@ -214,68 +214,67 @@ var defaultValues = ServerConfig{
FilteringConfig: FilteringConfig{BlockedResponseTTL: 3600}, FilteringConfig: FilteringConfig{BlockedResponseTTL: 3600},
} }
// createProxyConfig creates and validates configuration for the main proxy // createProxyConfig creates and validates configuration for the main proxy.
func (s *Server) createProxyConfig() (proxy.Config, error) { func (s *Server) createProxyConfig() (conf proxy.Config, err error) {
proxyConfig := proxy.Config{ srvConf := s.conf
UDPListenAddr: s.conf.UDPListenAddrs, conf = proxy.Config{
TCPListenAddr: s.conf.TCPListenAddrs, UDPListenAddr: srvConf.UDPListenAddrs,
Ratelimit: int(s.conf.Ratelimit), TCPListenAddr: srvConf.TCPListenAddrs,
RatelimitWhitelist: s.conf.RatelimitWhitelist, Ratelimit: int(srvConf.Ratelimit),
RefuseAny: s.conf.RefuseAny, RatelimitWhitelist: srvConf.RatelimitWhitelist,
TrustedProxies: s.conf.TrustedProxies, RefuseAny: srvConf.RefuseAny,
CacheMinTTL: s.conf.CacheMinTTL, TrustedProxies: srvConf.TrustedProxies,
CacheMaxTTL: s.conf.CacheMaxTTL, CacheMinTTL: srvConf.CacheMinTTL,
CacheOptimistic: s.conf.CacheOptimistic, CacheMaxTTL: srvConf.CacheMaxTTL,
UpstreamConfig: s.conf.UpstreamConfig, CacheOptimistic: srvConf.CacheOptimistic,
UpstreamConfig: srvConf.UpstreamConfig,
BeforeRequestHandler: s.beforeRequestHandler, BeforeRequestHandler: s.beforeRequestHandler,
RequestHandler: s.handleDNSRequest, RequestHandler: s.handleDNSRequest,
EnableEDNSClientSubnet: s.conf.EnableEDNSClientSubnet, EnableEDNSClientSubnet: srvConf.EnableEDNSClientSubnet,
MaxGoroutines: int(s.conf.MaxGoroutines), MaxGoroutines: int(srvConf.MaxGoroutines),
} }
if s.conf.CacheSize != 0 { if srvConf.CacheSize != 0 {
proxyConfig.CacheEnabled = true conf.CacheEnabled = true
proxyConfig.CacheSizeBytes = int(s.conf.CacheSize) conf.CacheSizeBytes = int(srvConf.CacheSize)
} }
proxyConfig.UpstreamMode = proxy.UModeLoadBalance setProxyUpstreamMode(
if s.conf.AllServers { &conf,
proxyConfig.UpstreamMode = proxy.UModeParallel srvConf.AllServers,
} else if s.conf.FastestAddr { srvConf.FastestAddr,
proxyConfig.UpstreamMode = proxy.UModeFastestAddr srvConf.FastestTimeout.Duration,
proxyConfig.FastestPingTimeout = s.conf.FastestTimeout.Duration )
}
for i, s := range s.conf.BogusNXDomain { for i, s := range srvConf.BogusNXDomain {
subnet, err := netutil.ParseSubnet(s) var subnet *net.IPNet
subnet, err = netutil.ParseSubnet(s)
if err != nil { if err != nil {
log.Error("subnet at index %d: %s", i, err) log.Error("subnet at index %d: %s", i, err)
continue continue
} }
proxyConfig.BogusNXDomain = append(proxyConfig.BogusNXDomain, subnet) conf.BogusNXDomain = append(conf.BogusNXDomain, subnet)
} }
// TLS settings err = s.prepareTLS(&conf)
err := s.prepareTLS(&proxyConfig)
if err != nil { if err != nil {
return proxyConfig, err return conf, fmt.Errorf("validating tls: %w", err)
} }
if s.conf.DNSCryptConfig.Enabled { if c := srvConf.DNSCryptConfig; c.Enabled {
proxyConfig.DNSCryptUDPListenAddr = s.conf.DNSCryptConfig.UDPListenAddrs conf.DNSCryptUDPListenAddr = c.UDPListenAddrs
proxyConfig.DNSCryptTCPListenAddr = s.conf.DNSCryptConfig.TCPListenAddrs conf.DNSCryptTCPListenAddr = c.TCPListenAddrs
proxyConfig.DNSCryptProviderName = s.conf.DNSCryptConfig.ProviderName conf.DNSCryptProviderName = c.ProviderName
proxyConfig.DNSCryptResolverCert = s.conf.DNSCryptConfig.ResolverCert conf.DNSCryptResolverCert = c.ResolverCert
} }
// Validate proxy config if conf.UpstreamConfig == nil || len(conf.UpstreamConfig.Upstreams) == 0 {
if proxyConfig.UpstreamConfig == nil || len(proxyConfig.UpstreamConfig.Upstreams) == 0 { return conf, errors.Error("no default upstream servers configured")
return proxyConfig, errors.Error("no default upstream servers configured")
} }
return proxyConfig, nil return conf, nil
} }
const ( const (
@ -381,14 +380,45 @@ func (s *Server) prepareUpstreamSettings() error {
return nil return nil
} }
// prepareIntlProxy - initializes DNS proxy that we use for internal DNS queries // prepareInternalProxy initializes the DNS proxy that is used for internal DNS
func (s *Server) prepareIntlProxy() { // queries, such at client PTR resolving and udpater hostname resolving.
func (s *Server) prepareInternalProxy() {
conf := &proxy.Config{
CacheEnabled: true,
CacheSizeBytes: 4096,
UpstreamConfig: s.conf.UpstreamConfig,
MaxGoroutines: int(s.conf.MaxGoroutines),
}
srvConf := s.conf
setProxyUpstreamMode(
conf,
srvConf.AllServers,
srvConf.FastestAddr,
srvConf.FastestTimeout.Duration,
)
// TODO(a.garipov): Make a proper constructor for proxy.Proxy.
s.internalProxy = &proxy.Proxy{ s.internalProxy = &proxy.Proxy{
Config: proxy.Config{ Config: *conf,
CacheEnabled: true, }
CacheSizeBytes: 4096, }
UpstreamConfig: s.conf.UpstreamConfig,
}, // setProxyUpstreamMode sets the upstream mode and related settings in conf
// based on provided parameters.
func setProxyUpstreamMode(
conf *proxy.Config,
allServers bool,
fastestAddr bool,
fastestTimeout time.Duration,
) {
if allServers {
conf.UpstreamMode = proxy.UModeParallel
} else if fastestAddr {
conf.UpstreamMode = proxy.UModeFastestAddr
conf.FastestPingTimeout = fastestTimeout
} else {
conf.UpstreamMode = proxy.UModeLoadBalance
} }
} }

View file

@ -49,11 +49,12 @@ type hostToIPTable = map[string]net.IP
// Server is the main way to start a DNS server. // Server is the main way to start a DNS server.
// //
// Example: // Example:
// s := dnsforward.Server{} //
// err := s.Start(nil) // will start a DNS server listening on default port 53, in a goroutine // s := dnsforward.Server{}
// err := s.Reconfigure(ServerConfig{UDPListenAddr: &net.UDPAddr{Port: 53535}}) // will reconfigure running DNS server to listen on UDP port 53535 // err := s.Start(nil) // will start a DNS server listening on default port 53, in a goroutine
// err := s.Stop() // will stop listening on port 53535 and cancel all goroutines // err := s.Reconfigure(ServerConfig{UDPListenAddr: &net.UDPAddr{Port: 53535}}) // will reconfigure running DNS server to listen on UDP port 53535
// err := s.Start(nil) // will start listening again, on port 53535, in a goroutine // err := s.Stop() // will stop listening on port 53535 and cancel all goroutines
// err := s.Start(nil) // will start listening again, on port 53535, in a goroutine
// //
// The zero Server is empty and ready for use. // The zero Server is empty and ready for use.
type Server struct { type Server struct {
@ -176,18 +177,6 @@ func NewServer(p DNSCreateParams) (s *Server, err error) {
return s, nil return s, nil
} }
// NewCustomServer creates a new instance of *Server with custom internal proxy.
func NewCustomServer(internalProxy *proxy.Proxy) *Server {
s := &Server{
recDetector: newRecursionDetector(0, 1),
}
if internalProxy != nil {
s.internalProxy = internalProxy
}
return s
}
// Close gracefully closes the server. It is safe for concurrent use. // Close gracefully closes the server. It is safe for concurrent use.
// //
// TODO(e.burkov): A better approach would be making Stop method waiting for all // TODO(e.burkov): A better approach would be making Stop method waiting for all
@ -488,7 +477,7 @@ func (s *Server) Prepare(config *ServerConfig) error {
// Prepare a DNS proxy instance that we use for internal DNS queries // Prepare a DNS proxy instance that we use for internal DNS queries
// -- // --
s.prepareIntlProxy() s.prepareInternalProxy()
s.access, err = newAccessCtx(s.conf.AllowedClients, s.conf.DisallowedClients, s.conf.BlockedHosts) s.access, err = newAccessCtx(s.conf.AllowedClients, s.conf.DisallowedClients, s.conf.BlockedHosts)
if err != nil { if err != nil {

View file

@ -1217,13 +1217,17 @@ func TestServer_Exchange(t *testing.T) {
errUpstream := aghtest.NewErrorUpstream() errUpstream := aghtest.NewErrorUpstream()
nonPtrUpstream := aghtest.NewBlockUpstream("some-host", true) nonPtrUpstream := aghtest.NewBlockUpstream("some-host", true)
srv := NewCustomServer(&proxy.Proxy{ srv := &Server{
Config: proxy.Config{ recDetector: newRecursionDetector(0, 1),
UpstreamConfig: &proxy.UpstreamConfig{ internalProxy: &proxy.Proxy{
Upstreams: []upstream.Upstream{extUpstream}, Config: proxy.Config{
UpstreamConfig: &proxy.UpstreamConfig{
Upstreams: []upstream.Upstream{extUpstream},
},
}, },
}, },
}) }
srv.conf.ResolveClients = true srv.conf.ResolveClients = true
srv.conf.UsePrivateRDNS = true srv.conf.UsePrivateRDNS = true

View file

@ -711,11 +711,16 @@ func (s *Server) handleTestUpstreamDNS(w http.ResponseWriter, r *http.Request) {
} }
} }
// handleDoH is the DNS-over-HTTPs handler.
//
// Control flow: // Control flow:
// web //
// -> dnsforward.handleDoH -> dnsforward.ServeHTTP // HTTP server
// -> proxy.ServeHTTP -> proxy.handleDNSRequest // -> dnsforward.handleDoH
// -> dnsforward.handleDNSRequest // -> dnsforward.ServeHTTP
// -> proxy.ServeHTTP
// -> proxy.handleDNSRequest
// -> dnsforward.handleDNSRequest
func (s *Server) handleDoH(w http.ResponseWriter, r *http.Request) { func (s *Server) handleDoH(w http.ResponseWriter, r *http.Request) {
if !s.conf.TLSAllowUnencryptedDoH && r.TLS == nil { if !s.conf.TLSAllowUnencryptedDoH && r.TLS == nil {
aghhttp.Error(r, w, http.StatusNotFound, "Not Found") aghhttp.Error(r, w, http.StatusNotFound, "Not Found")

View file

@ -157,10 +157,10 @@ var svcbKeyHandlers = map[string]svcbKeyHandler{
// Firstly, the parsing of non-contiguous values isn't supported. Secondly, the // Firstly, the parsing of non-contiguous values isn't supported. Secondly, the
// parsing of value-lists is not supported either. // parsing of value-lists is not supported either.
// //
// ipv4hint=127.0.0.1 // Supported. // ipv4hint=127.0.0.1 // Supported.
// ipv4hint="127.0.0.1" // Unsupported. // ipv4hint="127.0.0.1" // Unsupported.
// ipv4hint=127.0.0.1,127.0.0.2 // Unsupported. // ipv4hint=127.0.0.1,127.0.0.2 // Unsupported.
// ipv4hint="127.0.0.1,127.0.0.2" // Unsupported. // ipv4hint="127.0.0.1,127.0.0.2" // Unsupported.
// //
// TODO(a.garipov): Support all of these. // TODO(a.garipov): Support all of these.
func (s *Server) genAnswerSVCB(req *dns.Msg, svcb *rules.DNSSVCB) (ans *dns.SVCB) { func (s *Server) genAnswerSVCB(req *dns.Msg, svcb *rules.DNSSVCB) (ans *dns.SVCB) {

View file

@ -280,10 +280,10 @@ func newDNSCrypt(hosts []net.IP, tlsConf tlsConfigSettings) (dnscc dnsforward.DN
} }
return dnsforward.DNSCryptConfig{ return dnsforward.DNSCryptConfig{
UDPListenAddrs: ipsToUDPAddrs(hosts, tlsConf.PortDNSCrypt),
TCPListenAddrs: ipsToTCPAddrs(hosts, tlsConf.PortDNSCrypt),
ResolverCert: cert, ResolverCert: cert,
ProviderName: rc.ProviderName, ProviderName: rc.ProviderName,
UDPListenAddrs: ipsToUDPAddrs(hosts, tlsConf.PortDNSCrypt),
TCPListenAddrs: ipsToTCPAddrs(hosts, tlsConf.PortDNSCrypt),
Enabled: true, Enabled: true,
}, nil }, nil
} }

View file

@ -73,8 +73,7 @@ const (
// fmtModule returns formatted information about module. The result looks like: // fmtModule returns formatted information about module. The result looks like:
// //
// github.com/Username/module@v1.2.3 (sum: someHASHSUM=) // github.com/Username/module@v1.2.3 (sum: someHASHSUM=)
//
func fmtModule(m *debug.Module) (formatted string) { func fmtModule(m *debug.Module) (formatted string) {
if m == nil { if m == nil {
return "" return ""
@ -118,18 +117,18 @@ const (
// Verbose returns formatted build information. Output example: // Verbose returns formatted build information. Output example:
// //
// AdGuard Home // AdGuard Home
// Version: v0.105.3 // Version: v0.105.3
// Channel: development // Channel: development
// Go version: go1.15.3 // Go version: go1.15.3
// Build time: 2021-03-30T16:26:08Z+0300 // Build time: 2021-03-30T16:26:08Z+0300
// GOOS: darwin // GOOS: darwin
// GOARCH: amd64 // GOARCH: amd64
// Race: false // Race: false
// Main module: // Main module:
// ... // ...
// Dependencies: // Dependencies:
// ... // ...
// //
// TODO(e.burkov): Make it write into passed io.Writer. // TODO(e.burkov): Make it write into passed io.Writer.
func Verbose() (v string) { func Verbose() (v string) {