mirror of
https://github.com/AdguardTeam/dnsproxy.git
synced 2026-10-12 02:01:26 +00:00
AGDNS-4357 Do not embed config
This commit is contained in:
@@ -9,13 +9,13 @@ import (
|
||||
// isBogusNXDomain returns true if m contains at least a single IP address in
|
||||
// the Answer section contained in BogusNXDomain subnets of p.
|
||||
func (p *Proxy) isBogusNXDomain(m *dns.Msg) (ok bool) {
|
||||
if m == nil || len(p.BogusNXDomain) == 0 || len(m.Question) == 0 {
|
||||
if m == nil || len(p.bogusNXDomain) == 0 || len(m.Question) == 0 {
|
||||
return false
|
||||
} else if qt := m.Question[0].Qtype; qt != dns.TypeA && qt != dns.TypeAAAA {
|
||||
return false
|
||||
}
|
||||
|
||||
set := netutil.SliceSubnetSet(p.BogusNXDomain)
|
||||
set := netutil.SliceSubnetSet(p.bogusNXDomain)
|
||||
for _, rr := range m.Answer {
|
||||
ip := proxyutil.IPFromRR(rr)
|
||||
if set.Contains(ip) {
|
||||
|
||||
@@ -78,7 +78,7 @@ func TestProxy_IsBogusNXDomain(t *testing.T) {
|
||||
}}
|
||||
|
||||
u := testUpstream{}
|
||||
prx.UpstreamConfig.Upstreams = []upstream.Upstream{&u}
|
||||
prx.upstreamConf.Upstreams = []upstream.Upstream{&u}
|
||||
|
||||
servicetest.RequireRun(t, prx, testTimeout)
|
||||
|
||||
|
||||
+6
-6
@@ -165,20 +165,20 @@ func (c *cache) unpackItem(data []byte, req *dns.Msg) (ci *cacheItem, expired bo
|
||||
|
||||
// initCache initializes cache if it's enabled.
|
||||
func (p *Proxy) initCache() {
|
||||
if !p.CacheEnabled {
|
||||
if !p.cacheEnabled {
|
||||
p.logger.Info("cache disabled")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
size := p.CacheSizeBytes
|
||||
size := p.cacheSizeBytes
|
||||
p.logger.Info("cache enabled", "size", size)
|
||||
p.cache = newCache(&cacheConfig{
|
||||
size: size,
|
||||
optimisticTTL: p.CacheOptimisticAnswerTTL,
|
||||
optimisticMaxAge: p.CacheOptimisticMaxAge,
|
||||
withECS: p.EnableEDNSClientSubnet,
|
||||
optimistic: p.CacheOptimistic,
|
||||
optimisticTTL: p.cacheOptimisticAnswerTTL,
|
||||
optimisticMaxAge: p.cacheOptimisticMaxAge,
|
||||
withECS: p.enableEDNSClientSubnet,
|
||||
optimistic: p.cacheOptimistic,
|
||||
})
|
||||
p.shortFlighter = newOptimisticResolver(p)
|
||||
}
|
||||
|
||||
@@ -402,7 +402,7 @@ func TestCacheExpirationWithTTLOverride(t *testing.T) {
|
||||
assert.Equal(t, msgToKey(d.Req), key)
|
||||
|
||||
require.NotNil(t, ci)
|
||||
assert.Equal(t, dnsProxy.CacheMinTTL, ci.m.Answer[0].Header().Ttl)
|
||||
assert.Equal(t, dnsProxy.cacheMinTTL, ci.m.Answer[0].Header().Ttl)
|
||||
})
|
||||
|
||||
t.Run("replace_max", func(t *testing.T) {
|
||||
@@ -426,7 +426,7 @@ func TestCacheExpirationWithTTLOverride(t *testing.T) {
|
||||
assert.Equal(t, msgToKey(d.Req), key)
|
||||
|
||||
require.NotNil(t, ci)
|
||||
assert.Equal(t, dnsProxy.CacheMaxTTL, ci.m.Answer[0].Header().Ttl)
|
||||
assert.Equal(t, dnsProxy.cacheMaxTTL, ci.m.Answer[0].Header().Ttl)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
+30
-30
@@ -274,30 +274,30 @@ type HTTPConfig struct {
|
||||
}
|
||||
|
||||
// validateConfig verifies that the supplied configuration is valid and returns
|
||||
// an error if it's not.
|
||||
// an error if it's not. c must be non-nil and valid.
|
||||
//
|
||||
// TODO(s.chzhen): Use [validate.Interface] from golibs.
|
||||
func (p *Proxy) validateConfig() (err error) {
|
||||
err = p.UpstreamConfig.validate()
|
||||
func (p *Proxy) validateConfig(c *Config) (err error) {
|
||||
err = c.UpstreamConfig.validate()
|
||||
if err != nil {
|
||||
return fmt.Errorf("general upstreams: %w", err)
|
||||
}
|
||||
|
||||
err = ValidatePrivateConfig(p.PrivateRDNSUpstreamConfig, p.privateNets)
|
||||
err = ValidatePrivateConfig(c.PrivateRDNSUpstreamConfig, c.PrivateSubnets)
|
||||
if err != nil {
|
||||
if p.UsePrivateRDNS || errors.Is(err, upstream.ErrNoUpstreams) {
|
||||
if c.UsePrivateRDNS || errors.Is(err, upstream.ErrNoUpstreams) {
|
||||
return fmt.Errorf("private rdns upstreams: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
err = p.Fallbacks.validate()
|
||||
// Allow [Proxy.Fallbacks] to be nil, but not empty. nil means not to use
|
||||
err = c.Fallbacks.validate()
|
||||
// Allow [Config.Fallbacks] to be nil, but not empty. nil means not to use
|
||||
// fallbacks at all.
|
||||
if errors.Is(err, upstream.ErrNoUpstreams) {
|
||||
return fmt.Errorf("fallbacks: %w", err)
|
||||
}
|
||||
|
||||
switch p.UpstreamMode {
|
||||
switch c.UpstreamMode {
|
||||
case
|
||||
"",
|
||||
UpstreamModeFastestAddr,
|
||||
@@ -305,7 +305,7 @@ func (p *Proxy) validateConfig() (err error) {
|
||||
UpstreamModeParallel:
|
||||
// Go on.
|
||||
default:
|
||||
return fmt.Errorf("upstream mode: %w: %q", errors.ErrBadEnumValue, p.UpstreamMode)
|
||||
return fmt.Errorf("upstream mode: %w: %q", errors.ErrBadEnumValue, c.UpstreamMode)
|
||||
}
|
||||
|
||||
err = p.validateBasicAuth()
|
||||
@@ -320,20 +320,20 @@ func (p *Proxy) validateConfig() (err error) {
|
||||
|
||||
// logConfigInfo logs proxy configuration information.
|
||||
func (p *Proxy) logConfigInfo() {
|
||||
if p.CacheMinTTL > 0 || p.CacheMaxTTL > 0 {
|
||||
p.logger.Info("cache ttl override is enabled", "min", p.CacheMinTTL, "max", p.CacheMaxTTL)
|
||||
if p.cacheMinTTL > 0 || p.cacheMaxTTL > 0 {
|
||||
p.logger.Info("cache ttl override is enabled", "min", p.cacheMinTTL, "max", p.cacheMaxTTL)
|
||||
}
|
||||
|
||||
if p.RefuseAny {
|
||||
if p.refuseAny {
|
||||
p.logger.Info("server will refuse requests of type any")
|
||||
}
|
||||
|
||||
if len(p.BogusNXDomain) > 0 {
|
||||
p.logger.Info("bogus-nxdomain ip specified", "prefix_len", len(p.BogusNXDomain))
|
||||
if len(p.bogusNXDomain) > 0 {
|
||||
p.logger.Info("bogus-nxdomain ip specified", "prefix_len", len(p.bogusNXDomain))
|
||||
}
|
||||
|
||||
if p.UpstreamMode != "" {
|
||||
p.logger.Info("upstream mode is set", "mode", p.UpstreamMode)
|
||||
if p.upstreamMode != "" {
|
||||
p.logger.Info("upstream mode is set", "mode", p.upstreamMode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -351,12 +351,12 @@ func (p *Proxy) validateListenAddrs() (err error) {
|
||||
return fmt.Errorf("invalid tls configuration: %w", err)
|
||||
}
|
||||
|
||||
if p.DNSCryptResolverCert == nil || p.DNSCryptProviderName == "" {
|
||||
if p.DNSCryptTCPListenAddr != nil {
|
||||
if p.dnsCryptResolverCert == nil || p.dnsCryptProviderName == "" {
|
||||
if p.dnsCryptTCPListenAddr != nil {
|
||||
return errors.Error("cannot create dnscrypt tcp listener without dnscrypt config")
|
||||
}
|
||||
|
||||
if p.DNSCryptUDPListenAddr != nil {
|
||||
if p.dnsCryptUDPListenAddr != nil {
|
||||
return errors.Error("cannot create dnscrypt udp listener without dnscrypt config")
|
||||
}
|
||||
}
|
||||
@@ -367,19 +367,19 @@ func (p *Proxy) validateListenAddrs() (err error) {
|
||||
// validateTLSConfig returns an error if proxy TLS configuration parameters are
|
||||
// needed but aren't provided.
|
||||
func (p *Proxy) validateTLSConfig() (err error) {
|
||||
if p.TLSConfig != nil {
|
||||
if p.tlsConf != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if p.TLSListenAddr != nil {
|
||||
if p.tlsListenAddr != nil {
|
||||
return errors.Error("tls listener configuration not found")
|
||||
}
|
||||
|
||||
if p.HTTPConfig != nil && p.HTTPConfig.ListenAddresses != nil {
|
||||
if p.httpConf != nil && p.httpConf.ListenAddresses != nil {
|
||||
return errors.Error("https listener configuration not found")
|
||||
}
|
||||
|
||||
if p.QUICListenAddr != nil {
|
||||
if p.quicListenAddr != nil {
|
||||
return errors.Error("quic listener configuration not found")
|
||||
}
|
||||
|
||||
@@ -388,11 +388,11 @@ func (p *Proxy) validateTLSConfig() (err error) {
|
||||
|
||||
// hasListenAddrs - is there any addresses to listen to?
|
||||
func (p *Proxy) hasListenAddrs() (ok bool) {
|
||||
return p.UDPListenAddr != nil ||
|
||||
p.TCPListenAddr != nil ||
|
||||
p.TLSListenAddr != nil ||
|
||||
(p.HTTPConfig != nil && p.HTTPConfig.ListenAddresses != nil) ||
|
||||
p.QUICListenAddr != nil ||
|
||||
p.DNSCryptUDPListenAddr != nil ||
|
||||
p.DNSCryptTCPListenAddr != nil
|
||||
return p.udpListenAddr != nil ||
|
||||
p.tcpListenAddr != nil ||
|
||||
p.tlsListenAddr != nil ||
|
||||
(p.httpConf != nil && p.httpConf.ListenAddresses != nil) ||
|
||||
p.quicListenAddr != nil ||
|
||||
p.dnsCryptUDPListenAddr != nil ||
|
||||
p.dnsCryptTCPListenAddr != nil
|
||||
}
|
||||
|
||||
+6
-6
@@ -40,24 +40,24 @@ const (
|
||||
// synthesize AAAA records.
|
||||
//
|
||||
// TODO(e.burkov): Split validation and initialization.
|
||||
func (p *Proxy) setupDNS64() (err error) {
|
||||
if !p.Config.UseDNS64 {
|
||||
func (p *Proxy) setupDNS64(dns64Prefs netutil.SliceSubnetSet) (err error) {
|
||||
if !p.useDNS64 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(p.Config.DNS64Prefs) == 0 {
|
||||
if len(dns64Prefs) == 0 {
|
||||
p.dns64Prefs = netutil.SliceSubnetSet{dns64WellKnownPref}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
for i, pref := range p.Config.DNS64Prefs {
|
||||
for i, pref := range dns64Prefs {
|
||||
if !pref.Addr().Is6() {
|
||||
return fmt.Errorf("prefix at index %d: %q is not an IPv6 prefix", i, pref)
|
||||
return fmt.Errorf("prefix at index %d: %q is not an ipv6 prefix", i, pref)
|
||||
}
|
||||
|
||||
if pref.Bits() > maxNAT64PrefixBitLen {
|
||||
return fmt.Errorf("prefix at index %d: %q is too long for DNS64", i, pref)
|
||||
return fmt.Errorf("prefix at index %d: %q is too long for dns64", i, pref)
|
||||
}
|
||||
|
||||
p.dns64Prefs = append(p.dns64Prefs, pref.Masked())
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ func (p *Proxy) exchangeUpstreams(
|
||||
req *dns.Msg,
|
||||
ups []upstream.Upstream,
|
||||
) (resp *dns.Msg, u upstream.Upstream, err error) {
|
||||
switch p.UpstreamMode {
|
||||
switch p.upstreamMode {
|
||||
case UpstreamModeParallel:
|
||||
return upstream.ExchangeParallel(ups, req)
|
||||
case UpstreamModeFastestAddr:
|
||||
|
||||
+1
-1
@@ -81,7 +81,7 @@ func (p *Proxy) LookupNetIP(
|
||||
return addrs, errors.Join(errs...)
|
||||
}
|
||||
|
||||
if p.Config.PreferIPv6 {
|
||||
if p.preferIPv6 {
|
||||
slices.SortStableFunc(addrs, netutil.PreferIPv6)
|
||||
} else {
|
||||
slices.SortStableFunc(addrs, netutil.PreferIPv4)
|
||||
|
||||
+320
-131
@@ -5,6 +5,7 @@ package proxy
|
||||
import (
|
||||
"cmp"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
@@ -65,6 +66,25 @@ const logKeyProto = "proto"
|
||||
//
|
||||
// TODO(a.garipov): Consider extracting conf blocks for better fieldalignment.
|
||||
type Proxy struct {
|
||||
// messages constructs DNS messages.
|
||||
messages MessageConstructor
|
||||
|
||||
// pendingRequests is a storage for duplicated requests. It is used to
|
||||
// prevent sending the same request to upstreams multiple times.
|
||||
pendingRequests pendingRequests
|
||||
|
||||
// privateNets determines if the requested address and the client address
|
||||
// are private.
|
||||
privateNets netutil.SubnetSet
|
||||
|
||||
// randSrc provides the source of randomness.
|
||||
//
|
||||
// TODO(e.burkov): Consider configuring it.
|
||||
randSrc rand.Source
|
||||
|
||||
// requestHandler handles the DNS request. It is never nil.
|
||||
requestHandler Handler
|
||||
|
||||
// requestsSema limits the number of simultaneous requests.
|
||||
//
|
||||
// TODO(a.garipov): Currently we have to pass this exact semaphore to the
|
||||
@@ -74,113 +94,168 @@ type Proxy struct {
|
||||
// See also: https://github.com/AdguardTeam/AdGuardHome/issues/2242.
|
||||
requestsSema syncutil.Semaphore
|
||||
|
||||
// privateNets determines if the requested address and the client address
|
||||
// are private.
|
||||
privateNets netutil.SubnetSet
|
||||
// reqCtx is a constructor for the request contexts. It is never nil.
|
||||
reqCtx contextutil.Constructor
|
||||
|
||||
// time provides the current time.
|
||||
//
|
||||
// TODO(e.burkov): Consider configuring it.
|
||||
time timeutil.Clock
|
||||
|
||||
// randSrc provides the source of randomness.
|
||||
//
|
||||
// TODO(e.burkov): Consider configuring it.
|
||||
randSrc rand.Source
|
||||
// trustedProxies is the trusted list of CIDR networks to detect proxy
|
||||
// servers addresses from where the DoH requests should be handled. The
|
||||
// value of nil makes Proxy not trust any address.
|
||||
trustedProxies netutil.SubnetSet
|
||||
|
||||
// messages constructs DNS messages.
|
||||
messages MessageConstructor
|
||||
// bogusNXDomain is the set of networks used to transform responses into
|
||||
// NXDOMAIN ones if they contain at least a single IP address within these
|
||||
// networks. It's similar to dnsmasq's "bogus-nxdomain".
|
||||
bogusNXDomain []netip.Prefix
|
||||
|
||||
// requestHandler handles the DNS request. It is never nil.
|
||||
requestHandler Handler
|
||||
|
||||
// dnsCryptServers serve DNSCrypt queries.
|
||||
dnsCryptServers []*dnscrypt.Server
|
||||
|
||||
// logger is used for logging in the proxy service. It is never nil.
|
||||
logger *slog.Logger
|
||||
|
||||
// fastestAddr finds the fastest IP address for the resolved domain.
|
||||
fastestAddr *fastip.FastestAddr
|
||||
|
||||
// cache is used to cache requests. It is disabled if nil.
|
||||
//
|
||||
// TODO(d.kolyshev): Move this cache to [Proxy.UpstreamConfig] field.
|
||||
cache *cache
|
||||
|
||||
// shortFlighter is used to resolve the expired cached requests without
|
||||
// repetitions.
|
||||
shortFlighter *optimisticResolver
|
||||
|
||||
// recDetector detects recursive requests that may appear when resolving
|
||||
// requests for private addresses.
|
||||
recDetector *recursionDetector
|
||||
|
||||
// reqCtx is a constructor for the request contexts. It is never nil.
|
||||
reqCtx contextutil.Constructor
|
||||
|
||||
// pendingRequests is a storage for duplicated requests. It is used to
|
||||
// prevent sending the same request to upstreams multiple times.
|
||||
pendingRequests pendingRequests
|
||||
// bindRetryConf configures the listeners binding retrying. If nil,
|
||||
// retries are disabled.
|
||||
bindRetryConf *BindRetryConfig
|
||||
|
||||
// bytesPool is a pool of byte slices used to read DNS packets.
|
||||
bytesPool *syncutil.Pool[[]byte]
|
||||
|
||||
// udpListen are the listened UDP connections.
|
||||
udpListen []*net.UDPConn
|
||||
// cache is used to cache requests. It is disabled if nil.
|
||||
//
|
||||
// TODO(d.kolyshev): Move this cache to [Proxy.upstreamConf] field.
|
||||
cache *cache
|
||||
|
||||
// tcpListen are the listened TCP connections.
|
||||
tcpListen []net.Listener
|
||||
// dnsCryptProviderName is the DNSCrypt provider name. Required for
|
||||
// DNSCrypt server.
|
||||
dnsCryptProviderName string
|
||||
|
||||
// tlsListen are the listened TCP connections with TLS.
|
||||
tlsListen []net.Listener
|
||||
|
||||
// quicListen are the listened QUIC connections.
|
||||
quicListen []*quic.EarlyListener
|
||||
|
||||
// quicConns are UDP connections for all listened QUIC connections. These
|
||||
// should be closed on shutdown, since *quic.EarlyListener doesn't close
|
||||
// them.
|
||||
quicConns []*net.UDPConn
|
||||
|
||||
// quicTransports are transports for all listened QUIC connections. These
|
||||
// should be closed on shutdown, since *quic.EarlyListener doesn't close
|
||||
// them.
|
||||
quicTransports []*quic.Transport
|
||||
|
||||
// httpsListen are the listened HTTPS connections.
|
||||
httpsListen []net.Listener
|
||||
|
||||
// h3Listen are the listened HTTP/3 connections.
|
||||
h3Listen []*quic.EarlyListener
|
||||
|
||||
// httpsServer serves queries received over HTTPS.
|
||||
httpsServer *http.Server
|
||||
|
||||
// h3Server serves queries received over HTTP/3.
|
||||
h3Server *http3.Server
|
||||
|
||||
// upstreamRTTStats maps the upstream address to its round-trip time
|
||||
// statistics. It's holds the statistics for all upstreams to perform a
|
||||
// weighted random selection when using the load balancing mode.
|
||||
upstreamRTTStats map[string]upstreamRTTStats
|
||||
// dnsCryptResolverCert is the DNSCrypt resolver certificate. Required for
|
||||
// DNSCrypt server.
|
||||
dnsCryptResolverCert *dnscrypt.Certificate
|
||||
|
||||
// dns64Prefs is a set of NAT64 prefixes that are used to detect and
|
||||
// construct DNS64 responses. The DNS64 function is disabled if it is
|
||||
// empty.
|
||||
dns64Prefs netutil.SliceSubnetSet
|
||||
|
||||
// Config is the proxy configuration.
|
||||
// ednsAddr is the ECS IP used in request.
|
||||
ednsAddr net.IP
|
||||
|
||||
// fastestAddr finds the fastest IP address for the resolved domain.
|
||||
fastestAddr *fastip.FastestAddr
|
||||
|
||||
// fallbacks is a list of fallback resolvers. Those will be used if the
|
||||
// general set fails responding. It isn't allowed to be empty, but can be
|
||||
// nil, which means not to use fallbacks.
|
||||
//
|
||||
// TODO(a.garipov): Remove this embed and create a proper initializer.
|
||||
Config
|
||||
// TODO(e.burkov): Add explicit boolean for disabling fallbacks.
|
||||
fallbacks *UpstreamConfig
|
||||
|
||||
// udpOOBSize is the size of the out-of-band data for UDP connections.
|
||||
udpOOBSize int
|
||||
// h3Listen are the listened HTTP/3 connections.
|
||||
h3Listen []*quic.EarlyListener
|
||||
|
||||
// bindRetryCount is the number of retries for binding to an address for
|
||||
// listening. Zero means one attempt and no retries.
|
||||
bindRetryCount uint
|
||||
// h3Server serves queries received over HTTP/3.
|
||||
h3Server *http3.Server
|
||||
|
||||
// httpConf is the configuration for HTTP requests proxying. Required for
|
||||
// DoH server. If nil, the DoH server is disabled.
|
||||
httpConf *HTTPConfig
|
||||
|
||||
// httpsListen are the listened HTTPS connections.
|
||||
httpsListen []net.Listener
|
||||
|
||||
// httpsServer serves queries received over HTTPS.
|
||||
httpsServer *http.Server
|
||||
|
||||
// logger is used for logging in the proxy service. It is never nil.
|
||||
logger *slog.Logger
|
||||
|
||||
// mu protects the whole Proxy struct.
|
||||
//
|
||||
// TODO(m.kazantsev): Add a more thorough documentation on what exactly is
|
||||
// protected by this mutex.
|
||||
mu *sync.RWMutex
|
||||
|
||||
// privateRDNSUpstreamConfig is the set of upstream DNS servers for
|
||||
// resolving private IP addresses. All the requests considered private will
|
||||
// be resolved via these upstream servers. Such queries will finish with
|
||||
// [upstream.ErrNoUpstream] if it's empty.
|
||||
privateRDNSUpstreamConfig *UpstreamConfig
|
||||
|
||||
// quicConns are UDP connections for all listened QUIC connections. These
|
||||
// should be closed on shutdown, since *quic.EarlyListener doesn't close
|
||||
// them.
|
||||
quicConns []*net.UDPConn
|
||||
|
||||
// quicListen are the listened QUIC connections.
|
||||
quicListen []*quic.EarlyListener
|
||||
|
||||
// quicTransports are transports for all listened QUIC connections. These
|
||||
// should be closed on shutdown, since *quic.EarlyListener doesn't close
|
||||
// them.
|
||||
quicTransports []*quic.Transport
|
||||
|
||||
// recDetector detects recursive requests that may appear when resolving
|
||||
// requests for private addresses.
|
||||
recDetector *recursionDetector
|
||||
|
||||
// rttLock protects upstreamRTTStats.
|
||||
rttLock *sync.Mutex
|
||||
|
||||
// shortFlighter is used to resolve the expired cached requests without
|
||||
// repetitions.
|
||||
shortFlighter *optimisticResolver
|
||||
|
||||
// tcpListen are the listened TCP connections.
|
||||
tcpListen []net.Listener
|
||||
|
||||
// tlsConf is the TLS configuration. Required for DNS-over-TLS,
|
||||
// DNS-over-HTTP, and DNS-over-QUIC servers.
|
||||
tlsConf *tls.Config
|
||||
|
||||
// tlsListen are the listened TCP connections with TLS.
|
||||
tlsListen []net.Listener
|
||||
|
||||
// udpListen are the listened UDP connections.
|
||||
udpListen []*net.UDPConn
|
||||
|
||||
// upstreamConf is a general set of DNS servers to forward requests to.
|
||||
upstreamConf *UpstreamConfig
|
||||
|
||||
// upstreamMode determines the logic through which upstreams will be used.
|
||||
// If not specified the [proxy.UpstreamModeLoadBalance] is used.
|
||||
upstreamMode UpstreamMode
|
||||
|
||||
// upstreamRTTStats maps the upstream address to its round-trip time
|
||||
// statistics. It's holds the statistics for all upstreams to perform a
|
||||
// weighted random selection when using the load balancing mode.
|
||||
upstreamRTTStats map[string]upstreamRTTStats
|
||||
|
||||
// quicListenAddr is the set of UDP addresses to listen for DNS-over-QUIC
|
||||
// requests.
|
||||
quicListenAddr []*net.UDPAddr
|
||||
|
||||
// tcpListenAddr is the set of TCP addresses to listen for plain
|
||||
// DNS-over-TCP requests.
|
||||
tcpListenAddr []*net.TCPAddr
|
||||
|
||||
// tlsListenAddr is the set of TCP addresses to listen for DNS-over-TLS
|
||||
// requests.
|
||||
tlsListenAddr []*net.TCPAddr
|
||||
|
||||
// udpListenAddr is the set of UDP addresses to listen for plain
|
||||
// DNS-over-UDP requests.
|
||||
udpListenAddr []*net.UDPAddr
|
||||
|
||||
// dnsCryptServers serve DNSCrypt queries.
|
||||
dnsCryptServers []*dnscrypt.Server
|
||||
|
||||
// dnsCryptTCPListenAddr is the set of TCP addresses to listen for DNSCrypt
|
||||
// requests.
|
||||
dnsCryptTCPListenAddr []*net.TCPAddr
|
||||
|
||||
// dnsCryptUDPListenAddr is the set of UDP addresses to listen for DNSCrypt
|
||||
// requests.
|
||||
dnsCryptUDPListenAddr []*net.UDPAddr
|
||||
|
||||
// bindRetryIvl is the interval between attempts to bind to an address for
|
||||
// listening.
|
||||
@@ -189,19 +264,100 @@ type Proxy struct {
|
||||
// counter counts message contexts created with [Proxy.newDNSContext].
|
||||
counter atomic.Uint64
|
||||
|
||||
// RWMutex protects the whole proxy.
|
||||
//
|
||||
// TODO(e.burkov): Find out what exactly it protects and name it properly.
|
||||
// Also make it a pointer.
|
||||
sync.RWMutex
|
||||
// cacheOptimisticAnswerTTL is the default TTL for expired cached responses.
|
||||
// Default value is [DefaultOptimisticAnswerTTL].
|
||||
cacheOptimisticAnswerTTL time.Duration
|
||||
|
||||
// rttLock protects upstreamRTTStats.
|
||||
// cacheOptimisticMaxAge is the maximum time entries remain in the cache
|
||||
// when cache is optimistic. Default value is [DefaultOptimisticMaxAge].
|
||||
cacheOptimisticMaxAge time.Duration
|
||||
|
||||
// fastestPingTimeout is the timeout for waiting the first successful
|
||||
// dialing when the UpstreamMode is set to [UpstreamModeFastestAddr].
|
||||
// Non-positive value will be replaced with the default one.
|
||||
fastestPingTimeout time.Duration
|
||||
|
||||
// cacheSizeBytes is the maximum cache size in bytes.
|
||||
cacheSizeBytes int
|
||||
|
||||
// The size of the read buffer on the underlying socket. Larger read
|
||||
// buffers can handle larger bursts of requests before packets get dropped.
|
||||
udpBufferSize int
|
||||
|
||||
// udpOOBSize is the size of the out-of-band data for UDP connections.
|
||||
udpOOBSize int
|
||||
|
||||
// bindRetryCount is the number of retries for binding to an address for
|
||||
// listening. Zero means one attempt and no retries.
|
||||
bindRetryCount uint
|
||||
|
||||
// maxGoroutines is the maximum number of goroutines processing DNS
|
||||
// requests. Important for mobile users.
|
||||
//
|
||||
// TODO(e.burkov): Make it a pointer.
|
||||
rttLock sync.Mutex
|
||||
// TODO(a.garipov): Rename this to something like “MaxDNSRequestGoroutines”
|
||||
// in a later major version, as it doesn't actually limit all goroutines.
|
||||
maxGoroutines uint
|
||||
|
||||
// cacheMaxTTL is the maximum TTL for cached DNS responses in seconds.
|
||||
cacheMaxTTL uint32
|
||||
|
||||
// cacheMinTTL is the minimum TTL for cached DNS responses in seconds.
|
||||
cacheMinTTL uint32
|
||||
|
||||
// cacheEnabled defines if the response cache should be used.
|
||||
cacheEnabled bool
|
||||
|
||||
// cacheOptimistic defines if the optimistic cache mechanism should be used.
|
||||
cacheOptimistic bool
|
||||
|
||||
// dnsSecEnabled specifies if the proxy should set the DO bits in the
|
||||
// upstream requests.
|
||||
dnsSecEnabled bool
|
||||
|
||||
// Enable EDNS Client Subnet option DNS requests to the upstream server will
|
||||
// contain an OPT record with Client Subnet option. If the original request
|
||||
// already has this option set, we pass it through as is. Otherwise, we set
|
||||
// it ourselves using the client IP with subnet /24 (for IPv4) and /56 (for
|
||||
// IPv6).
|
||||
//
|
||||
// If the upstream server supports ECS, it sets subnet number in the
|
||||
// response. This subnet number along with the client IP and other data is
|
||||
// used as a cache key. Next time, if a client from the same subnet
|
||||
// requests this host name, we get the response from cache. If another
|
||||
// client from a different subnet requests this host name, we pass his
|
||||
// request to the upstream server.
|
||||
//
|
||||
// If the upstream server doesn't support ECS (there's no subnet number in
|
||||
// response), this response will be cached for all clients.
|
||||
//
|
||||
// If client IP is private (i.e. not public), we don't add EDNS record into
|
||||
// a request. And so there will be no EDNS record in response either. We
|
||||
// store these responses in general cache (without subnet) so they will
|
||||
// never be used for clients with public IP addresses.
|
||||
enableEDNSClientSubnet bool
|
||||
|
||||
// preferIPv6 tells the proxy to prefer IPv6 addresses when bootstrapping
|
||||
// upstreams that use hostnames.
|
||||
preferIPv6 bool
|
||||
|
||||
// refuseAny makes proxy refuse the requests of type ANY.
|
||||
refuseAny bool
|
||||
|
||||
// started indicates if the proxy has been started.
|
||||
started bool
|
||||
|
||||
// useDNS64 enables DNS64 handling. If true, proxy will translate IPv4
|
||||
// answers into IPv6 answers using first of DNS64Prefs. Note also that PTR
|
||||
// requests for addresses within the specified networks are considered
|
||||
// private and will be forwarded as PrivateRDNSUpstreamConfig specifies.
|
||||
// Those will be responded with NXDOMAIN if UsePrivateRDNS is false.
|
||||
useDNS64 bool
|
||||
|
||||
// usePrivateRDNS defines if the PTR requests for private IP addresses
|
||||
// should be resolved via PrivateRDNSUpstreamConfig. Note that it requires
|
||||
// a valid PrivateRDNSUpstreamConfig with at least a single general upstream
|
||||
// server.
|
||||
usePrivateRDNS bool
|
||||
}
|
||||
|
||||
// New creates a new Proxy with the specified configuration. c must not be nil.
|
||||
@@ -211,7 +367,40 @@ type Proxy struct {
|
||||
// TODO(e.burkov): Add context.
|
||||
func New(c *Config) (p *Proxy, err error) {
|
||||
p = &Proxy{
|
||||
Config: *c,
|
||||
quicListenAddr: c.QUICListenAddr,
|
||||
tlsListenAddr: c.TLSListenAddr,
|
||||
tcpListenAddr: c.TCPListenAddr,
|
||||
udpListenAddr: c.UDPListenAddr,
|
||||
fallbacks: c.Fallbacks,
|
||||
preferIPv6: c.PreferIPv6,
|
||||
usePrivateRDNS: c.UsePrivateRDNS,
|
||||
useDNS64: c.UseDNS64,
|
||||
cacheOptimistic: c.CacheOptimistic,
|
||||
cacheEnabled: c.CacheEnabled,
|
||||
enableEDNSClientSubnet: c.EnableEDNSClientSubnet,
|
||||
dnsSecEnabled: c.DNSSECEnabled,
|
||||
refuseAny: c.RefuseAny,
|
||||
fastestPingTimeout: c.FastestPingTimeout,
|
||||
udpBufferSize: c.UDPBufferSize,
|
||||
maxGoroutines: c.MaxGoroutines,
|
||||
cacheOptimisticMaxAge: c.CacheOptimisticMaxAge,
|
||||
cacheOptimisticAnswerTTL: c.CacheOptimisticAnswerTTL,
|
||||
cacheMaxTTL: c.CacheMaxTTL,
|
||||
cacheMinTTL: c.CacheMinTTL,
|
||||
cacheSizeBytes: c.CacheSizeBytes,
|
||||
ednsAddr: c.EDNSAddr,
|
||||
bogusNXDomain: c.BogusNXDomain,
|
||||
dnsCryptTCPListenAddr: c.DNSCryptTCPListenAddr,
|
||||
dnsCryptUDPListenAddr: c.DNSCryptUDPListenAddr,
|
||||
upstreamMode: c.UpstreamMode,
|
||||
dnsCryptProviderName: c.DNSCryptProviderName,
|
||||
dnsCryptResolverCert: c.DNSCryptResolverCert,
|
||||
tlsConf: c.TLSConfig,
|
||||
bindRetryConf: c.BindRetryConfig,
|
||||
httpConf: c.HTTPConfig,
|
||||
upstreamConf: c.UpstreamConfig,
|
||||
privateRDNSUpstreamConfig: c.PrivateRDNSUpstreamConfig,
|
||||
trustedProxies: c.TrustedProxies,
|
||||
privateNets: cmp.Or[netutil.SubnetSet](
|
||||
c.PrivateSubnets,
|
||||
netutil.SubnetSetFunc(netutil.IsLocallyServed),
|
||||
@@ -222,8 +411,8 @@ func New(c *Config) (p *Proxy, err error) {
|
||||
),
|
||||
requestHandler: cmp.Or[Handler](c.RequestHandler, DefaultHandler{}),
|
||||
upstreamRTTStats: map[string]upstreamRTTStats{},
|
||||
rttLock: sync.Mutex{},
|
||||
RWMutex: sync.RWMutex{},
|
||||
rttLock: &sync.Mutex{},
|
||||
mu: &sync.RWMutex{},
|
||||
// 2 bytes may be used to store packet length (see TCP/TLS).
|
||||
bytesPool: syncutil.NewSlicePool[byte](2 + dns.MaxMsgSize),
|
||||
udpOOBSize: proxynetutil.UDPGetOOBSize(),
|
||||
@@ -239,29 +428,29 @@ func New(c *Config) (p *Proxy, err error) {
|
||||
|
||||
// TODO(e.burkov): Validate config separately and add the contract to the
|
||||
// New function.
|
||||
err = p.validateConfig()
|
||||
err = p.validateConfig(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p.CacheOptimisticAnswerTTL = cmp.Or(p.CacheOptimisticAnswerTTL, DefaultOptimisticAnswerTTL)
|
||||
p.CacheOptimisticMaxAge = cmp.Or(p.CacheOptimisticMaxAge, DefaultOptimisticMaxAge)
|
||||
p.cacheOptimisticAnswerTTL = cmp.Or(p.cacheOptimisticAnswerTTL, DefaultOptimisticAnswerTTL)
|
||||
p.cacheOptimisticMaxAge = cmp.Or(p.cacheOptimisticMaxAge, DefaultOptimisticMaxAge)
|
||||
|
||||
p.initCache()
|
||||
|
||||
if p.MaxGoroutines > 0 {
|
||||
p.logger.Info("max goroutines is set", "count", p.MaxGoroutines)
|
||||
if p.maxGoroutines > 0 {
|
||||
p.logger.Info("max goroutines is set", "count", p.maxGoroutines)
|
||||
|
||||
p.requestsSema = syncutil.NewChanSemaphore(p.MaxGoroutines)
|
||||
p.requestsSema = syncutil.NewChanSemaphore(p.maxGoroutines)
|
||||
} else {
|
||||
p.requestsSema = syncutil.EmptySemaphore{}
|
||||
}
|
||||
|
||||
p.UpstreamMode = cmp.Or(p.UpstreamMode, UpstreamModeLoadBalance)
|
||||
if p.UpstreamMode == UpstreamModeFastestAddr {
|
||||
p.upstreamMode = cmp.Or(p.upstreamMode, UpstreamModeLoadBalance)
|
||||
if p.upstreamMode == UpstreamModeFastestAddr {
|
||||
p.fastestAddr = fastip.New(&fastip.Config{
|
||||
Logger: p.Logger,
|
||||
PingWaitTimeout: p.FastestPingTimeout,
|
||||
Logger: p.logger,
|
||||
PingWaitTimeout: p.fastestPingTimeout,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -270,9 +459,9 @@ func New(c *Config) (p *Proxy, err error) {
|
||||
p.bindRetryIvl = bindRetries.Interval
|
||||
}
|
||||
|
||||
err = p.setupDNS64()
|
||||
err = p.setupDNS64(c.DNS64Prefs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("setting up DNS64: %w", err)
|
||||
return nil, fmt.Errorf("setting up dns64: %w", err)
|
||||
}
|
||||
|
||||
return p, nil
|
||||
@@ -300,7 +489,7 @@ func loggerOrDefault(l *slog.Logger) (logger *slog.Logger) {
|
||||
|
||||
// validateBasicAuth validates the HTTP settings if HTTPConfig.Userinfo is set.
|
||||
func (p *Proxy) validateBasicAuth() (err error) {
|
||||
conf := p.Config.HTTPConfig
|
||||
conf := p.httpConf
|
||||
if conf == nil || conf.Userinfo == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -310,8 +499,8 @@ func (p *Proxy) validateBasicAuth() (err error) {
|
||||
|
||||
// Returns true if proxy is started. It is safe for concurrent use.
|
||||
func (p *Proxy) isStarted() (ok bool) {
|
||||
p.RLock()
|
||||
defer p.RUnlock()
|
||||
p.mu.RLock()
|
||||
defer p.mu.RUnlock()
|
||||
|
||||
return p.started
|
||||
}
|
||||
@@ -323,8 +512,8 @@ var _ service.Interface = (*Proxy)(nil)
|
||||
func (p *Proxy) Start(ctx context.Context) (err error) {
|
||||
p.logger.InfoContext(ctx, "starting dns proxy server")
|
||||
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
if p.started {
|
||||
return errors.Error("server has been already started")
|
||||
@@ -391,8 +580,8 @@ func closeAll[C io.Closer](errs []error, closers ...C) (appended []error) {
|
||||
func (p *Proxy) Shutdown(ctx context.Context) (err error) {
|
||||
p.logger.InfoContext(ctx, "stopping server")
|
||||
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
if !p.started {
|
||||
// TODO(a.garipov): Consider returning err.
|
||||
@@ -404,9 +593,9 @@ func (p *Proxy) Shutdown(ctx context.Context) (err error) {
|
||||
errs := p.closeListeners(nil)
|
||||
|
||||
for _, u := range []*UpstreamConfig{
|
||||
p.UpstreamConfig,
|
||||
p.PrivateRDNSUpstreamConfig,
|
||||
p.Fallbacks,
|
||||
p.upstreamConf,
|
||||
p.privateRDNSUpstreamConfig,
|
||||
p.fallbacks,
|
||||
} {
|
||||
if u != nil {
|
||||
errs = closeAll(errs, u)
|
||||
@@ -489,8 +678,8 @@ func collectAddrs[A any](addressers []A, af addrFunc[A]) (addrs []net.Addr) {
|
||||
// proxy does not listen to it. proto must be one of [Proto]: [ProtoTCP],
|
||||
// [ProtoUDP], [ProtoTLS], [ProtoHTTPS], [ProtoQUIC], or [ProtoDNSCrypt].
|
||||
func (p *Proxy) Addrs(proto Proto) (addrs []net.Addr) {
|
||||
p.RLock()
|
||||
defer p.RUnlock()
|
||||
p.mu.RLock()
|
||||
defer p.mu.RUnlock()
|
||||
|
||||
switch proto {
|
||||
case ProtoTCP:
|
||||
@@ -525,8 +714,8 @@ func firstAddr[A any](addressers []A, af addrFunc[A]) (addr net.Addr) {
|
||||
// proxy does not listen to it. proto must be one of [Proto]: [ProtoTCP],
|
||||
// [ProtoUDP], [ProtoTLS], [ProtoHTTPS], [ProtoQUIC], or [ProtoDNSCrypt].
|
||||
func (p *Proxy) Addr(proto Proto) (addr net.Addr) {
|
||||
p.RLock()
|
||||
defer p.RUnlock()
|
||||
p.mu.RLock()
|
||||
defer p.mu.RUnlock()
|
||||
|
||||
switch proto {
|
||||
case ProtoTCP:
|
||||
@@ -555,8 +744,8 @@ func (p *Proxy) selectUpstreams(d *DNSContext) (upstreams []upstream.Upstream, i
|
||||
|
||||
if d.RequestedPrivateRDNS != (netip.Prefix{}) || p.shouldStripDNS64(d.Req) {
|
||||
// Use private upstreams.
|
||||
private := p.PrivateRDNSUpstreamConfig
|
||||
if p.UsePrivateRDNS && d.IsPrivateClient && private != nil {
|
||||
private := p.privateRDNSUpstreamConfig
|
||||
if p.usePrivateRDNS && d.IsPrivateClient && private != nil {
|
||||
// This may only be a PTR, SOA, and NS request.
|
||||
upstreams = private.getUpstreamsForDomain(host)
|
||||
}
|
||||
@@ -578,7 +767,7 @@ func (p *Proxy) selectUpstreams(d *DNSContext) (upstreams []upstream.Upstream, i
|
||||
}
|
||||
|
||||
// Use configured.
|
||||
return getUpstreams(p.UpstreamConfig, host), false
|
||||
return getUpstreams(p.upstreamConf, host), false
|
||||
}
|
||||
|
||||
// replyFromUpstream tries to resolve the request via configured upstream
|
||||
@@ -610,14 +799,14 @@ func (p *Proxy) replyFromUpstream(d *DNSContext) (ok bool, err error) {
|
||||
}
|
||||
|
||||
var wrappedFallbacks []upstream.Upstream
|
||||
if err != nil && !isPrivate && p.Fallbacks != nil {
|
||||
if err != nil && !isPrivate && p.fallbacks != nil {
|
||||
p.logger.Debug("using fallback", slogutil.KeyError, err)
|
||||
|
||||
src = "fallback"
|
||||
|
||||
// upstreams mustn't appear empty since they have been validated when
|
||||
// creating proxy.
|
||||
upstreams = p.Fallbacks.getUpstreamsForDomain(req.Question[0].Name)
|
||||
upstreams = p.fallbacks.getUpstreamsForDomain(req.Question[0].Name)
|
||||
|
||||
wrappedFallbacks = upstreamsWithStats(upstreams)
|
||||
resp, u, err = upstream.ExchangeParallel(wrappedFallbacks, req)
|
||||
@@ -631,7 +820,7 @@ func (p *Proxy) replyFromUpstream(d *DNSContext) (ok bool, err error) {
|
||||
p.logger.Debug("resolved", "upstream", u.Address(), "src", src)
|
||||
}
|
||||
|
||||
unwrapped, stats := collectQueryStats(p.UpstreamMode, u, wrapped, wrappedFallbacks)
|
||||
unwrapped, stats := collectQueryStats(p.upstreamMode, u, wrapped, wrappedFallbacks)
|
||||
d.queryStatistics = stats
|
||||
|
||||
ctx := context.TODO()
|
||||
@@ -668,7 +857,7 @@ func (p *Proxy) handleExchangeResult(
|
||||
// addDO adds EDNS0 RR if needed and sets DO bit of msg to true. msg must not
|
||||
// be nil.
|
||||
func (p *Proxy) addDO(msg *dns.Msg) {
|
||||
if !p.DNSSECEnabled {
|
||||
if !p.dnsSecEnabled {
|
||||
// Do nothing if DNSSEC is disabled in the proxy.
|
||||
return
|
||||
}
|
||||
@@ -690,8 +879,8 @@ const defaultUDPBufSize = 2048
|
||||
// Resolve is the default resolving method used by the DNS proxy to query
|
||||
// upstream servers. It expects dctx is filled with the client's request.
|
||||
func (p *Proxy) Resolve(ctx context.Context, dctx *DNSContext) (err error) {
|
||||
if p.EnableEDNSClientSubnet {
|
||||
dctx.processECS(p.EDNSAddr, p.logger)
|
||||
if p.enableEDNSClientSubnet {
|
||||
dctx.processECS(p.ednsAddr, p.logger)
|
||||
}
|
||||
|
||||
dctx.calcFlagsAndSize()
|
||||
@@ -760,7 +949,7 @@ func (p *Proxy) validateRequest(d *DNSContext) (resp *dns.Msg) {
|
||||
p.logger.Debug("invalid number of questions", "req_questions_len", len(d.Req.Question))
|
||||
|
||||
return p.messages.NewMsgFORMERR(d.Req)
|
||||
case p.RefuseAny && d.Req.Question[0].Qtype == dns.TypeANY:
|
||||
case p.refuseAny && d.Req.Question[0].Qtype == dns.TypeANY:
|
||||
// Refuse requests of type ANY (anti-DDOS measure).
|
||||
p.logger.Debug("refusing dns type any request")
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"github.com/AdguardTeam/dnsproxy/internal/dnsproxytest"
|
||||
"github.com/AdguardTeam/dnsproxy/upstream"
|
||||
glcache "github.com/AdguardTeam/golibs/cache"
|
||||
"github.com/AdguardTeam/golibs/contextutil"
|
||||
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
@@ -1011,7 +1012,7 @@ func TestExchangeCustomUpstreamConfigCache(t *testing.T) {
|
||||
&UpstreamConfig{Upstreams: []upstream.Upstream{u}},
|
||||
true,
|
||||
defaultCacheSize,
|
||||
prx.EnableEDNSClientSubnet,
|
||||
prx.enableEDNSClientSubnet,
|
||||
)
|
||||
|
||||
d := &DNSContext{
|
||||
@@ -1242,7 +1243,7 @@ func TestECSProxyCacheMinMaxTTL(t *testing.T) {
|
||||
assert.False(t, expired)
|
||||
|
||||
assert.Equal(t, key, msgToKeyWithSubnet(d.Req, clientIP, 24))
|
||||
assert.True(t, ci.m.Answer[0].Header().Ttl == prx.CacheMinTTL)
|
||||
assert.True(t, ci.m.Answer[0].Header().Ttl == prx.cacheMinTTL)
|
||||
|
||||
// 2nd request
|
||||
clientIP = net.IP{1, 2, 4, 0}
|
||||
@@ -1268,7 +1269,7 @@ func TestECSProxyCacheMinMaxTTL(t *testing.T) {
|
||||
})
|
||||
assert.False(t, expired)
|
||||
assert.Equal(t, key, msgToKeyWithSubnet(d.Req, clientIP, 24))
|
||||
assert.True(t, ci.m.Answer[0].Header().Ttl == prx.CacheMaxTTL)
|
||||
assert.True(t, ci.m.Answer[0].Header().Ttl == prx.cacheMaxTTL)
|
||||
}
|
||||
|
||||
func TestProxy_Resolve_withOptimisticResolver(t *testing.T) {
|
||||
@@ -1305,16 +1306,17 @@ func TestProxy_Resolve_withOptimisticResolver(t *testing.T) {
|
||||
}
|
||||
|
||||
p := &Proxy{
|
||||
Config: Config{
|
||||
CacheEnabled: true,
|
||||
CacheOptimistic: true,
|
||||
CacheOptimisticAnswerTTL: testOptimisticTTL,
|
||||
CacheOptimisticMaxAge: testOptimisticMaxAge,
|
||||
DNSSECEnabled: true,
|
||||
// TODO(e.burkov): Set panicking upstream configuration.
|
||||
},
|
||||
reqCtx: contextutil.EmptyConstructor{},
|
||||
requestHandler: DefaultHandler{},
|
||||
cacheEnabled: true,
|
||||
cacheOptimistic: true,
|
||||
cacheOptimisticAnswerTTL: testOptimisticTTL,
|
||||
cacheOptimisticMaxAge: testOptimisticMaxAge,
|
||||
dnsSecEnabled: true,
|
||||
// TODO(e.burkov): Set panicking upstream configuration.
|
||||
logger: testLogger,
|
||||
pendingRequests: newDefaultPendingRequests(),
|
||||
mu: &sync.RWMutex{},
|
||||
}
|
||||
|
||||
p.initCache()
|
||||
|
||||
+3
-3
@@ -25,7 +25,7 @@ func (p *Proxy) replyFromCache(d *DNSContext) (hit bool) {
|
||||
var key []byte
|
||||
|
||||
// TODO(d.kolyshev): Use EnableEDNSClientSubnet from dctxCache.
|
||||
if p.Config.EnableEDNSClientSubnet && d.ReqECS != nil {
|
||||
if p.enableEDNSClientSubnet && d.ReqECS != nil {
|
||||
ci, expired, key = dctxCache.getWithSubnet(d.Req, d.ReqECS)
|
||||
cacheSource = "subnet cache"
|
||||
} else {
|
||||
@@ -43,7 +43,7 @@ func (p *Proxy) replyFromCache(d *DNSContext) (hit bool) {
|
||||
p.logger.Debug(
|
||||
"replying from cache",
|
||||
"source", cacheSource,
|
||||
"ecs_enabled", p.Config.EnableEDNSClientSubnet,
|
||||
"ecs_enabled", p.enableEDNSClientSubnet,
|
||||
)
|
||||
|
||||
if dctxCache.optimistic && expired {
|
||||
@@ -81,7 +81,7 @@ func cloneIPNet(n *net.IPNet) (clone *net.IPNet) {
|
||||
func (p *Proxy) cacheResp(d *DNSContext) {
|
||||
dctxCache := p.cacheForContext(d)
|
||||
|
||||
if !p.EnableEDNSClientSubnet {
|
||||
if !p.enableEDNSClientSubnet {
|
||||
dctxCache.set(d.Req, d.Res, d.Upstream, p.logger)
|
||||
|
||||
return
|
||||
|
||||
+4
-4
@@ -17,11 +17,11 @@ const (
|
||||
routePatternDNSQueryPost = http.MethodPost + " " + pathPatternDNSQuery
|
||||
)
|
||||
|
||||
// routeDoH registers DoH handlers in mux. p.HTTPConfig must not be nil.
|
||||
// p.HTTPConfig.Routes must be valid, if p.HTTPConfig.Routes is empty, the
|
||||
// default routes are registered.
|
||||
// routeDoH registers DoH handlers in mux. p.httpConf must not be nil.
|
||||
// p.httpConf.Routes must be valid, if p.httpConf.Routes is empty, the default
|
||||
// routes are registered.
|
||||
func (p *Proxy) routeDoH(mux *http.ServeMux) {
|
||||
routes := p.HTTPConfig.Routes
|
||||
routes := p.httpConf.Routes
|
||||
if len(routes) == 0 {
|
||||
mux.Handle(routePatternRootGet, p)
|
||||
mux.Handle(routePatternRootPost, p)
|
||||
|
||||
+1
-1
@@ -184,7 +184,7 @@ func (p *Proxy) setMinMaxTTL(ctx context.Context, r *dns.Msg) {
|
||||
for _, rrSet := range rrSets {
|
||||
for _, rr := range rrSet.Value {
|
||||
original := rr.Header().Ttl
|
||||
overridden := respectTTLOverrides(original, p.CacheMinTTL, p.CacheMaxTTL)
|
||||
overridden := respectTTLOverrides(original, p.cacheMinTTL, p.cacheMaxTTL)
|
||||
|
||||
if original == overridden {
|
||||
continue
|
||||
|
||||
@@ -14,18 +14,18 @@ import (
|
||||
|
||||
// initDNSCryptServers initializes the DNSCrypt servers.
|
||||
func (p *Proxy) initDNSCryptServers(ctx context.Context) (err error) {
|
||||
if len(p.DNSCryptUDPListenAddr) == 0 && len(p.DNSCryptTCPListenAddr) == 0 {
|
||||
if len(p.dnsCryptUDPListenAddr) == 0 && len(p.dnsCryptTCPListenAddr) == 0 {
|
||||
// Do nothing if DNSCrypt listen addresses are not specified.
|
||||
return nil
|
||||
}
|
||||
|
||||
if p.DNSCryptResolverCert == nil || p.DNSCryptProviderName == "" {
|
||||
if p.dnsCryptResolverCert == nil || p.dnsCryptProviderName == "" {
|
||||
return errors.Error("invalid dnscrypt configuration: no certificate or provider name")
|
||||
}
|
||||
|
||||
p.logger.InfoContext(ctx, "initializing dnscrypt", "provider", p.DNSCryptProviderName)
|
||||
p.logger.InfoContext(ctx, "initializing dnscrypt", "provider", p.dnsCryptProviderName)
|
||||
|
||||
for _, addr := range p.DNSCryptUDPListenAddr {
|
||||
for _, addr := range p.dnsCryptUDPListenAddr {
|
||||
s, sErr := p.newDNSCryptServer(ctx, netutil.NetAddrToAddrPort(addr), dnscrypt.ProtoUDP)
|
||||
if sErr != nil {
|
||||
return fmt.Errorf("listening to dnscrypt udp on addr %s: %w", addr, sErr)
|
||||
@@ -34,7 +34,7 @@ func (p *Proxy) initDNSCryptServers(ctx context.Context) (err error) {
|
||||
p.dnsCryptServers = append(p.dnsCryptServers, s)
|
||||
}
|
||||
|
||||
for _, addr := range p.DNSCryptTCPListenAddr {
|
||||
for _, addr := range p.dnsCryptTCPListenAddr {
|
||||
s, sErr := p.newDNSCryptServer(ctx, netutil.NetAddrToAddrPort(addr), dnscrypt.ProtoTCP)
|
||||
if sErr != nil {
|
||||
return fmt.Errorf("listening to dnscrypt tcp on addr %s: %w", addr, sErr)
|
||||
@@ -97,9 +97,9 @@ func (p *Proxy) newDNSCryptServer(
|
||||
proxy: p,
|
||||
reqSema: p.requestsSema,
|
||||
},
|
||||
ResolverCert: p.DNSCryptResolverCert,
|
||||
ResolverCert: p.dnsCryptResolverCert,
|
||||
Logger: p.logger,
|
||||
ProviderName: p.DNSCryptProviderName,
|
||||
ProviderName: p.dnsCryptProviderName,
|
||||
Addr: addr,
|
||||
Proto: proto,
|
||||
})
|
||||
|
||||
@@ -48,7 +48,7 @@ func (p *Proxy) listenHTTP(
|
||||
|
||||
p.logger.InfoContext(ctx, "listening to https", "addr", tcpAddr)
|
||||
|
||||
tlsConfig := p.TLSConfig.Clone()
|
||||
tlsConfig := p.tlsConf.Clone()
|
||||
tlsConfig.NextProtos = []string{http2.NextProtoTLS, "http/1.1"}
|
||||
|
||||
tlsListen := tls.NewListener(tcpListen, tlsConfig)
|
||||
@@ -62,7 +62,7 @@ func (p *Proxy) listenH3(
|
||||
ctx context.Context,
|
||||
addr *net.UDPAddr,
|
||||
) (ln *quic.EarlyListener, err error) {
|
||||
tlsConfig := p.TLSConfig.Clone()
|
||||
tlsConfig := p.tlsConf.Clone()
|
||||
tlsConfig.NextProtos = []string{"h3"}
|
||||
quicListen, err := quic.ListenAddrEarly(addr.String(), tlsConfig, newServerDoH3Config())
|
||||
if err != nil {
|
||||
@@ -86,7 +86,7 @@ func newServerDoH3Config() (conf *quic.Config) {
|
||||
|
||||
// initHTTPSListeners creates TCP/UDP listeners and HTTP/H3 servers.
|
||||
func (p *Proxy) initHTTPSListeners(ctx context.Context) (err error) {
|
||||
httpConf := p.HTTPConfig
|
||||
httpConf := p.httpConf
|
||||
if httpConf == nil {
|
||||
p.logger.DebugContext(ctx, "no https configuration provided")
|
||||
|
||||
@@ -212,7 +212,7 @@ func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
p.logger.DebugContext(ctx, "incoming https request", "url", r.URL)
|
||||
|
||||
if !p.HTTPConfig.InsecureEnabled && r.TLS == nil {
|
||||
if !p.httpConf.InsecureEnabled && r.TLS == nil {
|
||||
statusCode := http.StatusNotFound
|
||||
http.Error(w, http.StatusText(statusCode), statusCode)
|
||||
|
||||
@@ -240,7 +240,7 @@ func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
l.DebugContext(ctx, "request came from proxy server")
|
||||
|
||||
if !p.TrustedProxies.Contains(prx.Addr()) {
|
||||
if !p.trustedProxies.Contains(prx.Addr()) {
|
||||
l.DebugContext(ctx, "proxy is not trusted, using original remote addr")
|
||||
|
||||
// So the address of the proxy itself is used, as the remote address
|
||||
@@ -263,13 +263,13 @@ func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// checkBasicAuth checks the basic authorization data, if necessary, and if the
|
||||
// data isn't valid, it writes an error. shouldHandle is false if the request
|
||||
// has been denied. p.HTTPConfig must not be nil.
|
||||
// has been denied. p.httpConf must not be nil.
|
||||
func (p *Proxy) checkBasicAuth(
|
||||
w http.ResponseWriter,
|
||||
r *http.Request,
|
||||
raddr netip.AddrPort,
|
||||
) (shouldHandle bool) {
|
||||
ui := p.HTTPConfig.Userinfo
|
||||
ui := p.httpConf.Userinfo
|
||||
if ui == nil {
|
||||
return true
|
||||
}
|
||||
@@ -315,7 +315,7 @@ func (p *Proxy) respondHTTPS(d *DNSContext) (err error) {
|
||||
return fmt.Errorf("packing message: %w", err)
|
||||
}
|
||||
|
||||
if srvHeader := p.HTTPConfig.ServerHeader; srvHeader != "" {
|
||||
if srvHeader := p.httpConf.ServerHeader; srvHeader != "" {
|
||||
w.Header().Set(httphdr.Server, srvHeader)
|
||||
}
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ func TestProxy_trustedProxies(t *testing.T) {
|
||||
|
||||
msg := newTestMessage()
|
||||
|
||||
dnsProxy.TrustedProxies = netip.PrefixFrom(addr, addr.BitLen())
|
||||
dnsProxy.trustedProxies = netip.PrefixFrom(addr, addr.BitLen())
|
||||
|
||||
servicetest.RequireRun(t, dnsProxy, testTimeout)
|
||||
|
||||
|
||||
+2
-2
@@ -62,7 +62,7 @@ const (
|
||||
|
||||
// initQUICListeners creates QUIC listeners for the DoQ server.
|
||||
func (p *Proxy) initQUICListeners(ctx context.Context) (err error) {
|
||||
for _, a := range p.QUICListenAddr {
|
||||
for _, a := range p.quicListenAddr {
|
||||
var conn *net.UDPConn
|
||||
var ln *quic.EarlyListener
|
||||
var tr *quic.Transport
|
||||
@@ -102,7 +102,7 @@ func (p *Proxy) listenQUIC(
|
||||
VerifySourceAddress: v.requiresValidation,
|
||||
}
|
||||
|
||||
tlsConfig := p.TLSConfig.Clone()
|
||||
tlsConfig := p.tlsConf.Clone()
|
||||
tlsConfig.NextProtos = compatProtoDQ
|
||||
l, err = tr.ListenEarly(
|
||||
tlsConfig,
|
||||
|
||||
+3
-3
@@ -19,7 +19,7 @@ import (
|
||||
|
||||
// initTCPListeners initializes TCP listeners with configured addresses.
|
||||
func (p *Proxy) initTCPListeners(ctx context.Context) (err error) {
|
||||
for _, addr := range p.TCPListenAddr {
|
||||
for _, addr := range p.tcpListenAddr {
|
||||
var ln *net.TCPListener
|
||||
ln, err = p.listenTCP(ctx, addr)
|
||||
if err != nil {
|
||||
@@ -64,7 +64,7 @@ func (p *Proxy) listenTCP(ctx context.Context, addr *net.TCPAddr) (ln *net.TCPLi
|
||||
|
||||
// initTLSListeners initializes TLS listeners with configured addresses.
|
||||
func (p *Proxy) initTLSListeners(ctx context.Context) (err error) {
|
||||
for _, addr := range p.TLSListenAddr {
|
||||
for _, addr := range p.tlsListenAddr {
|
||||
p.logger.InfoContext(ctx, "creating tls server socket", "addr", addr)
|
||||
|
||||
var tcpListen *net.TCPListener
|
||||
@@ -77,7 +77,7 @@ func (p *Proxy) initTLSListeners(ctx context.Context) (err error) {
|
||||
return fmt.Errorf("listening on tls addr %s: %w", addr, err)
|
||||
}
|
||||
|
||||
l := tls.NewListener(tcpListen, p.TLSConfig)
|
||||
l := tls.NewListener(tcpListen, p.tlsConf)
|
||||
p.tlsListen = append(p.tlsListen, l)
|
||||
|
||||
p.logger.InfoContext(ctx, "listening to tls", "addr", l.Addr())
|
||||
|
||||
+3
-3
@@ -19,7 +19,7 @@ import (
|
||||
|
||||
// initUDPListeners initializes UDP listeners with configured addresses.
|
||||
func (p *Proxy) initUDPListeners(ctx context.Context) (err error) {
|
||||
for _, a := range p.UDPListenAddr {
|
||||
for _, a := range p.udpListenAddr {
|
||||
var pc *net.UDPConn
|
||||
pc, sErr := p.listenUDP(ctx, a)
|
||||
if sErr != nil {
|
||||
@@ -59,8 +59,8 @@ func (p *Proxy) listenUDP(ctx context.Context, addr *net.UDPAddr) (conn *net.UDP
|
||||
return nil, fmt.Errorf("bad conn type: %T(%[1]v)", packetConn)
|
||||
}
|
||||
|
||||
if p.Config.UDPBufferSize > 0 {
|
||||
err = conn.SetReadBuffer(p.Config.UDPBufferSize)
|
||||
if p.udpBufferSize > 0 {
|
||||
err = conn.SetReadBuffer(p.udpBufferSize)
|
||||
if err != nil {
|
||||
p.logClose(ctx, slog.LevelDebug, conn, "closing after failed read buffer size setting")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user