diff --git a/proxy/bogusnxdomain.go b/proxy/bogusnxdomain.go index a5fb2ea0..9b38ddb4 100644 --- a/proxy/bogusnxdomain.go +++ b/proxy/bogusnxdomain.go @@ -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) { diff --git a/proxy/bogusnxdomain_internal_test.go b/proxy/bogusnxdomain_internal_test.go index 57ddd3a4..f1d52283 100644 --- a/proxy/bogusnxdomain_internal_test.go +++ b/proxy/bogusnxdomain_internal_test.go @@ -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) diff --git a/proxy/cache.go b/proxy/cache.go index 49a25510..a24ac72d 100644 --- a/proxy/cache.go +++ b/proxy/cache.go @@ -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) } diff --git a/proxy/cache_internal_test.go b/proxy/cache_internal_test.go index 51584658..bb2cda1b 100644 --- a/proxy/cache_internal_test.go +++ b/proxy/cache_internal_test.go @@ -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) }) } diff --git a/proxy/config.go b/proxy/config.go index a56a5043..8dcca44d 100644 --- a/proxy/config.go +++ b/proxy/config.go @@ -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 } diff --git a/proxy/dns64.go b/proxy/dns64.go index 8b302916..6006157a 100644 --- a/proxy/dns64.go +++ b/proxy/dns64.go @@ -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()) diff --git a/proxy/exchange.go b/proxy/exchange.go index a34eaf63..0a488154 100644 --- a/proxy/exchange.go +++ b/proxy/exchange.go @@ -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: diff --git a/proxy/lookup.go b/proxy/lookup.go index 8d0ed2e6..18edccac 100644 --- a/proxy/lookup.go +++ b/proxy/lookup.go @@ -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) diff --git a/proxy/proxy.go b/proxy/proxy.go index 67b53521..ffa4d642 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -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") diff --git a/proxy/proxy_internal_test.go b/proxy/proxy_internal_test.go index 2528b9e4..bbd5eba3 100644 --- a/proxy/proxy_internal_test.go +++ b/proxy/proxy_internal_test.go @@ -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() diff --git a/proxy/proxycache.go b/proxy/proxycache.go index f161d9b7..a4195fb7 100644 --- a/proxy/proxycache.go +++ b/proxy/proxycache.go @@ -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 diff --git a/proxy/route.go b/proxy/route.go index afbbf666..d23d19f1 100644 --- a/proxy/route.go +++ b/proxy/route.go @@ -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) diff --git a/proxy/server.go b/proxy/server.go index 260225df..7f35f87a 100644 --- a/proxy/server.go +++ b/proxy/server.go @@ -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 diff --git a/proxy/serverdnscrypt.go b/proxy/serverdnscrypt.go index 4a911298..58e06525 100644 --- a/proxy/serverdnscrypt.go +++ b/proxy/serverdnscrypt.go @@ -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, }) diff --git a/proxy/serverhttps.go b/proxy/serverhttps.go index 0cf957aa..5d7d8ce7 100644 --- a/proxy/serverhttps.go +++ b/proxy/serverhttps.go @@ -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) } diff --git a/proxy/serverhttps_internal_test.go b/proxy/serverhttps_internal_test.go index a0e55522..2048ebc2 100644 --- a/proxy/serverhttps_internal_test.go +++ b/proxy/serverhttps_internal_test.go @@ -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) diff --git a/proxy/serverquic.go b/proxy/serverquic.go index 6ab310d5..4c24c7d2 100644 --- a/proxy/serverquic.go +++ b/proxy/serverquic.go @@ -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, diff --git a/proxy/servertcp.go b/proxy/servertcp.go index f5968188..622f551e 100644 --- a/proxy/servertcp.go +++ b/proxy/servertcp.go @@ -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()) diff --git a/proxy/serverudp.go b/proxy/serverudp.go index 913776f5..67de7a65 100644 --- a/proxy/serverudp.go +++ b/proxy/serverudp.go @@ -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")