mirror of
https://github.com/AdguardTeam/dnsproxy.git
synced 2026-10-12 02:01:26 +00:00
Pull request 394: bind-retry
Updates AdGuardTeam/AdGuardDNSClient#11. Squashed commit of the following: commit ce7cbf5dbfec6e075a2bc4da1d84aefcb90b2899 Author: Eugene Burkov <[email protected]> Date: Tue Mar 18 16:10:43 2025 +0300 proxy: imp code commit cb0f42b79a44b581597d98374fbf0155a2684360 Author: Eugene Burkov <[email protected]> Date: Mon Mar 17 20:16:08 2025 +0300 proxy: imp docs commit 87bb259cbfd166d1d58dc4e471163b5c5ea9261f Author: Eugene Burkov <[email protected]> Date: Mon Mar 17 13:55:56 2025 +0300 proxy: imp alignment commit 88e883fedff4b558f28e61cef3ce67004bf59a34 Author: Eugene Burkov <[email protected]> Date: Fri Mar 14 19:03:31 2025 +0300 proxy: upd golibs commit f4edce5a400c98f4ff6d369f47477f4868bcf8f9 Author: Eugene Burkov <[email protected]> Date: Fri Mar 14 18:36:31 2025 +0300 proxy: imp names, doc commit aafb4e004c79f79179e93f455cab41c2f17d5f81 Author: Eugene Burkov <[email protected]> Date: Fri Mar 14 18:06:32 2025 +0300 proxy: imp code commit c3db3d09369e39cf45edba8396692c57a9335187 Author: Eugene Burkov <[email protected]> Date: Thu Mar 13 20:57:39 2025 +0300 proxy: add bind retry
This commit is contained in:
@@ -3,7 +3,7 @@ module github.com/AdguardTeam/dnsproxy
|
||||
go 1.24.1
|
||||
|
||||
require (
|
||||
github.com/AdguardTeam/golibs v0.31.0
|
||||
github.com/AdguardTeam/golibs v0.32.6
|
||||
github.com/ameshkov/dnscrypt/v2 v2.3.0
|
||||
github.com/ameshkov/dnsstamps v1.0.3
|
||||
github.com/beefsack/go-rate v0.0.0-20220214233405-116f4ca011a0
|
||||
@@ -12,15 +12,17 @@ require (
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/quic-go/quic-go v0.48.2
|
||||
github.com/stretchr/testify v1.10.0
|
||||
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394
|
||||
golang.org/x/net v0.37.0
|
||||
golang.org/x/sys v0.31.0
|
||||
// TODO(e.burkov): Either find a better maintained dependency or implement
|
||||
// the weighted random algorithm yourself.
|
||||
gonum.org/v1/gonum v0.15.1
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.118.3 // indirect
|
||||
cloud.google.com/go v0.119.0 // indirect
|
||||
cloud.google.com/go/ai v0.10.0 // indirect
|
||||
cloud.google.com/go/auth v0.15.0 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect
|
||||
@@ -52,6 +54,7 @@ require (
|
||||
github.com/onsi/ginkgo/v2 v2.22.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/quic-go/qpack v0.5.1 // indirect
|
||||
github.com/robfig/cron/v3 v3.0.1 // indirect
|
||||
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
||||
github.com/securego/gosec/v2 v2.22.2 // indirect
|
||||
github.com/uudashr/gocognit v1.2.0 // indirect
|
||||
@@ -68,15 +71,15 @@ require (
|
||||
golang.org/x/mod v0.24.0 // indirect
|
||||
golang.org/x/oauth2 v0.28.0 // indirect
|
||||
golang.org/x/sync v0.12.0 // indirect
|
||||
golang.org/x/telemetry v0.0.0-20250305155315-2a181eac97a3 // indirect
|
||||
golang.org/x/telemetry v0.0.0-20250310203348-fdfaad844314 // indirect
|
||||
golang.org/x/term v0.30.0 // indirect
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
golang.org/x/tools v0.31.0 // indirect
|
||||
golang.org/x/vuln v1.1.4 // indirect
|
||||
google.golang.org/api v0.224.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb // indirect
|
||||
google.golang.org/api v0.225.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250311190419-81fb87f6b8bf // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250311190419-81fb87f6b8bf // indirect
|
||||
google.golang.org/grpc v1.71.0 // indirect
|
||||
google.golang.org/protobuf v1.36.5 // indirect
|
||||
honnef.co/go/tools v0.6.1 // indirect
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
cloud.google.com/go v0.118.3 h1:jsypSnrE/w4mJysioGdMBg4MiW/hHx/sArFpaBWHdME=
|
||||
cloud.google.com/go v0.118.3/go.mod h1:Lhs3YLnBlwJ4KA6nuObNMZ/fCbOQBPuWKPoE0Wa/9Vc=
|
||||
cloud.google.com/go v0.119.0 h1:tw7OjErMzJKbbjaEHkrt60KQrK5Wus/boCZ7tm5/RNE=
|
||||
cloud.google.com/go v0.119.0/go.mod h1:fwB8QLzTcNevxqi8dcpR+hoMIs3jBherGS9VUBDAW08=
|
||||
cloud.google.com/go/ai v0.10.0 h1:hwj6CI6sMKubXodoJJGTy/c2T1RbbLGM6TL3QoAvzU8=
|
||||
cloud.google.com/go/ai v0.10.0/go.mod h1:kvnt2KeHqX8+41PVeMRBETDyQAp/RFvBWGdx/aGjNMo=
|
||||
cloud.google.com/go/auth v0.15.0 h1:Ly0u4aA5vG/fsSsxu98qCQBemXtAtJf+95z9HK+cxps=
|
||||
@@ -10,8 +10,8 @@ cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4
|
||||
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
|
||||
cloud.google.com/go/longrunning v0.6.5 h1:sD+t8DO8j4HKW4QfouCklg7ZC1qC4uzVZt8iz3uTW+Q=
|
||||
cloud.google.com/go/longrunning v0.6.5/go.mod h1:Et04XK+0TTLKa5IPYryKf5DkpwImy6TluQ1QTLwlKmY=
|
||||
github.com/AdguardTeam/golibs v0.31.0 h1:Z0oPfLTLw6iZmpE58dePy2Bel0MaX+lnDwtFEE5EmIo=
|
||||
github.com/AdguardTeam/golibs v0.31.0/go.mod h1:wIkZ9o2UnppeW6/YD7yJB71dYbMhiuC1Fh/I2ElW7GQ=
|
||||
github.com/AdguardTeam/golibs v0.32.6 h1:J/s6BiDHIJa5VsumGAzgI6S1SVLOaVB3YIvFlBK79HU=
|
||||
github.com/AdguardTeam/golibs v0.32.6/go.mod h1:bE8KV1zqTzgZjmjFyBJ9f9O5DEKO717r7e57j1HclJA=
|
||||
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs=
|
||||
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
|
||||
@@ -94,6 +94,8 @@ github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||
github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE=
|
||||
github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/securego/gosec/v2 v2.22.2 h1:IXbuI7cJninj0nRpZSLCUlotsj8jGusohfONMrHoF6g=
|
||||
@@ -129,8 +131,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 h1:1UoZQm6f0P/ZO0w1Ri+f+ifG/gXhegadRdwBIXEFWDo=
|
||||
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
|
||||
golang.org/x/exp/typeparams v0.0.0-20250305212735-054e65f0b394 h1:VI4qDpTkfFaCXEPrbojidLgVQhj2x4nzTccG0hjaLlU=
|
||||
golang.org/x/exp/typeparams v0.0.0-20250305212735-054e65f0b394/go.mod h1:LKZHyeOpPuZcMgxeHjJp4p5yvxrCX1xDvH10zYHhjjQ=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
@@ -154,8 +156,8 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/telemetry v0.0.0-20250305155315-2a181eac97a3 h1:k+pofz4/0MRETtVtItAwfDgPUvNlWrUrFw+8dtUVUa8=
|
||||
golang.org/x/telemetry v0.0.0-20250305155315-2a181eac97a3/go.mod h1:16eI1RtbPZAEm3u7hpIh7JM/w5AbmlDtnrdKYaREic8=
|
||||
golang.org/x/telemetry v0.0.0-20250310203348-fdfaad844314 h1:UY+gQAskx5vohcvUlJDKkJPt9lALCgtZs3rs8msRatU=
|
||||
golang.org/x/telemetry v0.0.0-20250310203348-fdfaad844314/go.mod h1:16eI1RtbPZAEm3u7hpIh7JM/w5AbmlDtnrdKYaREic8=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
|
||||
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
|
||||
@@ -177,12 +179,12 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gonum.org/v1/gonum v0.15.1 h1:FNy7N6OUZVUaWG9pTiD+jlhdQ3lMP+/LcTpJ6+a8sQ0=
|
||||
gonum.org/v1/gonum v0.15.1/go.mod h1:eZTZuRFrzu5pcyjN5wJhcIhnUdNijYxX1T2IcrOGY0o=
|
||||
google.golang.org/api v0.224.0 h1:Ir4UPtDsNiwIOHdExr3fAj4xZ42QjK7uQte3lORLJwU=
|
||||
google.golang.org/api v0.224.0/go.mod h1:3V39my2xAGkodXy0vEqcEtkqgw2GtrFL5WuBZlCTCOQ=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb h1:TLPQVbx1GJ8VKZxz52VAxl1EBgKXXbTiU9Fc5fZeLn4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
|
||||
google.golang.org/api v0.225.0 h1:+4/IVqBQm0MV5S+JW3kdEGC1WtOmM2mXN1LKH1LdNlw=
|
||||
google.golang.org/api v0.225.0/go.mod h1:WP/0Xm4LVvMOCldfvOISnWquSRWbG2kArDZcg+W2DbY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250311190419-81fb87f6b8bf h1:BdIVRm+fyDUn8lrZLPSlBCfM/YKDwUBYgDoLv9+DYo0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250311190419-81fb87f6b8bf/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250311190419-81fb87f6b8bf h1:dHDlF3CWxQkefK9IJx+O8ldY0gLygvrlYRBNbPqDWuY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250311190419-81fb87f6b8bf/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
|
||||
google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=
|
||||
google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
|
||||
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
package proxy
|
||||
|
||||
import "time"
|
||||
|
||||
// clock is the interface for provider of current time. It's used to simplify
|
||||
// testing.
|
||||
//
|
||||
// TODO(e.burkov): Move to golibs.
|
||||
type clock interface {
|
||||
// Now returns the current local time.
|
||||
Now() (now time.Time)
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ clock = realClock{}
|
||||
|
||||
// realClock is the [clock] which actually uses the [time] package.
|
||||
type realClock struct{}
|
||||
|
||||
// Now implements the [clock] interface for RealClock.
|
||||
func (realClock) Now() (now time.Time) { return time.Now() }
|
||||
@@ -97,6 +97,10 @@ type Config struct {
|
||||
// DNSCrypt server.
|
||||
DNSCryptResolverCert *dnscrypt.Cert
|
||||
|
||||
// BindRetryConfig configures the listeners binding retrying. If nil,
|
||||
// retries are disabled.
|
||||
BindRetryConfig *BindRetryConfig
|
||||
|
||||
// DNSCryptProviderName is the DNSCrypt provider name. Required for
|
||||
// DNSCrypt server.
|
||||
DNSCryptProviderName string
|
||||
|
||||
+4
-5
@@ -34,7 +34,7 @@ func (p *Proxy) exchangeUpstreams(
|
||||
|
||||
if len(ups) == 1 {
|
||||
u = ups[0]
|
||||
resp, _, err = p.exchange(u, req, p.time)
|
||||
resp, _, err = p.exchange(u, req)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -50,7 +50,7 @@ func (p *Proxy) exchangeUpstreams(
|
||||
u = ups[i]
|
||||
|
||||
var elapsed time.Duration
|
||||
resp, elapsed, err = p.exchange(u, req, p.time)
|
||||
resp, elapsed, err = p.exchange(u, req)
|
||||
if err == nil {
|
||||
p.updateRTT(u.Address(), elapsed)
|
||||
|
||||
@@ -75,13 +75,12 @@ func (p *Proxy) exchangeUpstreams(
|
||||
func (p *Proxy) exchange(
|
||||
u upstream.Upstream,
|
||||
req *dns.Msg,
|
||||
c clock,
|
||||
) (resp *dns.Msg, dur time.Duration, err error) {
|
||||
startTime := c.Now()
|
||||
startTime := p.time.Now()
|
||||
resp, err = u.Exchange(req)
|
||||
|
||||
// Don't use [time.Since] because it uses [time.Now].
|
||||
dur = c.Now().Sub(startTime)
|
||||
dur = p.time.Now().Sub(startTime)
|
||||
|
||||
addr := u.Address()
|
||||
q := &req.Question[0]
|
||||
|
||||
@@ -10,22 +10,15 @@ import (
|
||||
"github.com/AdguardTeam/dnsproxy/upstream"
|
||||
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/AdguardTeam/golibs/testutil/faketime"
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
//lint:ignore SA1019 See TODO for the gonum.org/v1/gonum import in go.mod.
|
||||
"golang.org/x/exp/rand"
|
||||
)
|
||||
|
||||
// fakeClock is the function-based implementation of the [clock] interface.
|
||||
type fakeClock struct {
|
||||
onNow func() (now time.Time)
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ clock = (*fakeClock)(nil)
|
||||
|
||||
// Now implements the [clock] interface for *fakeClock.
|
||||
func (c *fakeClock) Now() (now time.Time) { return c.onNow() }
|
||||
|
||||
// newUpstreamWithErrorRate returns an [upstream.Upstream] that responds with an
|
||||
// error every [rate] requests. The returned upstream isn't safe for concurrent
|
||||
// use.
|
||||
@@ -80,15 +73,15 @@ func TestProxy_Exchange_loadBalance(t *testing.T) {
|
||||
// value until currentNow is modified elsewhere.
|
||||
zeroTime := time.Unix(0, 0)
|
||||
currentNow := zeroTime
|
||||
zeroingClock := &fakeClock{
|
||||
onNow: func() (now time.Time) {
|
||||
zeroingClock := &faketime.Clock{
|
||||
OnNow: func() (now time.Time) {
|
||||
now, currentNow = currentNow, zeroTime
|
||||
|
||||
return now
|
||||
},
|
||||
}
|
||||
constClock := &fakeClock{
|
||||
onNow: func() (now time.Time) {
|
||||
constClock := &faketime.Clock{
|
||||
OnNow: func() (now time.Time) {
|
||||
now, currentNow = currentNow, currentNow.Add(testRTT/50)
|
||||
|
||||
return now
|
||||
@@ -153,7 +146,7 @@ func TestProxy_Exchange_loadBalance(t *testing.T) {
|
||||
|
||||
testCases := []struct {
|
||||
wantStat map[string]int64
|
||||
clock clock
|
||||
clock timeutil.Clock
|
||||
name string
|
||||
servers []upstream.Upstream
|
||||
}{{
|
||||
|
||||
+28
-5
@@ -25,11 +25,14 @@ import (
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/AdguardTeam/golibs/service"
|
||||
"github.com/AdguardTeam/golibs/syncutil"
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
"github.com/ameshkov/dnscrypt/v2"
|
||||
"github.com/miekg/dns"
|
||||
gocache "github.com/patrickmn/go-cache"
|
||||
"github.com/quic-go/quic-go"
|
||||
"github.com/quic-go/quic-go/http3"
|
||||
|
||||
//lint:ignore SA1019 See TODO for the gonum.org/v1/gonum import in go.mod.
|
||||
"golang.org/x/exp/rand"
|
||||
)
|
||||
|
||||
@@ -77,7 +80,7 @@ type Proxy struct {
|
||||
// time provides the current time.
|
||||
//
|
||||
// TODO(e.burkov): Consider configuring it.
|
||||
time clock
|
||||
time timeutil.Clock
|
||||
|
||||
// randSrc provides the source of randomness.
|
||||
//
|
||||
@@ -178,6 +181,14 @@ type Proxy struct {
|
||||
// udpOOBSize is the size of the out-of-band data for UDP connections.
|
||||
udpOOBSize int
|
||||
|
||||
// bindRetryNum is the number of retries for binding to an address for
|
||||
// listening. Zero means one attempt and no retries.
|
||||
bindRetryNum uint
|
||||
|
||||
// bindRetryIvl is the interval between attempts to bind to an address for
|
||||
// listening.
|
||||
bindRetryIvl time.Duration
|
||||
|
||||
// counter counts message contexts created with [Proxy.newDNSContext].
|
||||
counter atomic.Uint64
|
||||
|
||||
@@ -226,7 +237,7 @@ func New(c *Config) (p *Proxy, err error) {
|
||||
},
|
||||
},
|
||||
udpOOBSize: proxynetutil.UDPGetOOBSize(),
|
||||
time: realClock{},
|
||||
time: timeutil.SystemClock{},
|
||||
messages: cmp.Or[MessageConstructor](
|
||||
c.MessageConstructor,
|
||||
dnsmsg.DefaultMessageConstructor{},
|
||||
@@ -263,15 +274,19 @@ func New(c *Config) (p *Proxy, err error) {
|
||||
p.requestsSema = syncutil.EmptySemaphore{}
|
||||
}
|
||||
|
||||
if p.UpstreamMode == "" {
|
||||
p.UpstreamMode = UpstreamModeLoadBalance
|
||||
} else 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,
|
||||
})
|
||||
}
|
||||
|
||||
if bindRetries := c.BindRetryConfig; bindRetries != nil && bindRetries.Enabled {
|
||||
p.bindRetryNum = bindRetries.Count
|
||||
p.bindRetryIvl = bindRetries.Interval
|
||||
}
|
||||
|
||||
err = p.setupDNS64()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("setting up DNS64: %w", err)
|
||||
@@ -337,6 +352,14 @@ func (p *Proxy) Start(ctx context.Context) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// logClose closes the closer and logs the error at the specified level if it
|
||||
// occurs.
|
||||
func (p *Proxy) logClose(ctx context.Context, l slog.Level, c io.Closer, msg string, args ...any) {
|
||||
if err := c.Close(); err != nil {
|
||||
p.logger.Log(ctx, l, msg, append(args, slogutil.KeyError, err)...)
|
||||
}
|
||||
}
|
||||
|
||||
// closeAll closes all closers and appends the occurred errors to errs.
|
||||
func closeAll[C io.Closer](errs []error, closers ...C) (appended []error) {
|
||||
for _, c := range closers {
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||
)
|
||||
|
||||
// BindRetryConfig contains configuration for the listeners binding retry
|
||||
// mechanism.
|
||||
type BindRetryConfig struct {
|
||||
// Interval is the minimum time to wait after the latest failure. It must
|
||||
// not be negative if Enabled is true.
|
||||
Interval time.Duration
|
||||
|
||||
// Count is the maximum number of retries after the first attempt.
|
||||
Count uint
|
||||
|
||||
// Enabled indicates whether the binding should be retried.
|
||||
Enabled bool
|
||||
}
|
||||
|
||||
// bindWithRetry calls f until it returns no error or the retries limit is
|
||||
// reached, sleeping for configured interval between attempts. bindFunc must
|
||||
// not be nil and should carry the result of the binding operation itself.
|
||||
func (p *Proxy) bindWithRetry(ctx context.Context, bindFunc func() (err error)) (err error) {
|
||||
err = bindFunc()
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
p.logger.WarnContext(ctx, "binding", "attempt", 1, slogutil.KeyError, err)
|
||||
|
||||
for attempt := uint(1); attempt <= p.bindRetryNum; attempt++ {
|
||||
time.Sleep(p.bindRetryIvl)
|
||||
|
||||
retryErr := bindFunc()
|
||||
if retryErr == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
p.logger.WarnContext(ctx, "binding", "attempt", attempt+1, slogutil.KeyError, retryErr)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestWithRetry(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
errA errors.Error = "error about a"
|
||||
errB errors.Error = "error about b"
|
||||
)
|
||||
|
||||
var (
|
||||
good = func() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
badOne = func() (err error) {
|
||||
return errA
|
||||
}
|
||||
|
||||
// Don't protect against concurrent access since the closure is expected
|
||||
// to be used in a single case.
|
||||
returnedA = false
|
||||
badBoth = func() (err error) {
|
||||
if !returnedA {
|
||||
returnedA = true
|
||||
|
||||
return errA
|
||||
}
|
||||
|
||||
return errB
|
||||
}
|
||||
|
||||
// Don't protect against concurrent access since the closure is expected
|
||||
// to be used in a single case.
|
||||
returnedErr = false
|
||||
badThenOk = func() (err error) {
|
||||
if !returnedErr {
|
||||
returnedErr = true
|
||||
|
||||
return assert.AnError
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
)
|
||||
|
||||
testCases := []struct {
|
||||
f func() (err error)
|
||||
wantErr error
|
||||
name string
|
||||
}{{
|
||||
f: good,
|
||||
wantErr: nil,
|
||||
name: "no_error",
|
||||
}, {
|
||||
f: badOne,
|
||||
wantErr: errA,
|
||||
name: "one_error",
|
||||
}, {
|
||||
f: badBoth,
|
||||
wantErr: errA,
|
||||
name: "two_errors",
|
||||
}, {
|
||||
f: badThenOk,
|
||||
wantErr: nil,
|
||||
name: "error_then_ok",
|
||||
}}
|
||||
|
||||
p := &Proxy{
|
||||
logger: slogutil.NewDiscardLogger(),
|
||||
bindRetryNum: 1,
|
||||
bindRetryIvl: 0,
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||
|
||||
err := p.bindWithRetry(ctx, tc.f)
|
||||
assert.ErrorIs(t, err, tc.wantErr)
|
||||
})
|
||||
}
|
||||
}
|
||||
+6
-6
@@ -17,32 +17,32 @@ import (
|
||||
|
||||
// configureListeners configures listeners.
|
||||
func (p *Proxy) configureListeners(ctx context.Context) (err error) {
|
||||
err = p.createUDPListeners(ctx)
|
||||
err = p.initUDPListeners(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = p.createTCPListeners(ctx)
|
||||
err = p.initTCPListeners(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = p.createTLSListeners()
|
||||
err = p.initTLSListeners(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = p.createHTTPSListeners()
|
||||
err = p.initHTTPSListeners(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = p.createQUICListeners()
|
||||
err = p.initQUICListeners(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = p.createDNSCryptListeners()
|
||||
err = p.initDNSCryptListeners(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+56
-16
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func (p *Proxy) createDNSCryptListeners() (err error) {
|
||||
func (p *Proxy) initDNSCryptListeners(ctx context.Context) (err error) {
|
||||
if len(p.DNSCryptUDPListenAddr) == 0 && len(p.DNSCryptTCPListenAddr) == 0 {
|
||||
// Do nothing if DNSCrypt listen addresses are not specified.
|
||||
return nil
|
||||
@@ -23,42 +23,82 @@ func (p *Proxy) createDNSCryptListeners() (err error) {
|
||||
return errors.Error("invalid dnscrypt configuration: no certificate or provider name")
|
||||
}
|
||||
|
||||
p.logger.Info("initializing dnscrypt", "provider", p.DNSCryptProviderName)
|
||||
p.logger.InfoContext(ctx, "initializing dnscrypt", "provider", p.DNSCryptProviderName)
|
||||
p.dnsCryptServer = &dnscrypt.Server{
|
||||
ProviderName: p.DNSCryptProviderName,
|
||||
ResolverCert: p.DNSCryptResolverCert,
|
||||
Handler: &dnsCryptHandler{
|
||||
proxy: p,
|
||||
|
||||
proxy: p,
|
||||
reqSema: p.requestsSema,
|
||||
},
|
||||
}
|
||||
|
||||
for _, a := range p.DNSCryptUDPListenAddr {
|
||||
p.logger.Info("creating dnscrypt udp listener")
|
||||
udpListen, lErr := net.ListenUDP(bootstrap.NetworkUDP, a)
|
||||
for _, addr := range p.DNSCryptUDPListenAddr {
|
||||
udp, lErr := p.listenDNSCryptUDP(ctx, addr)
|
||||
if lErr != nil {
|
||||
return fmt.Errorf("listening to dnscrypt udp socket: %w", lErr)
|
||||
return fmt.Errorf("listening to dnscrypt udp on addr %s: %w", addr, lErr)
|
||||
}
|
||||
|
||||
p.dnsCryptUDPListen = append(p.dnsCryptUDPListen, udpListen)
|
||||
p.logger.Info("listening for dnscrypt messages on udp", "addr", udpListen.LocalAddr())
|
||||
p.dnsCryptUDPListen = append(p.dnsCryptUDPListen, udp)
|
||||
}
|
||||
|
||||
for _, a := range p.DNSCryptTCPListenAddr {
|
||||
p.logger.Info("creating a dnscrypt tcp listener")
|
||||
tcpListen, lErr := net.ListenTCP(bootstrap.NetworkTCP, a)
|
||||
for _, addr := range p.DNSCryptTCPListenAddr {
|
||||
tcp, lErr := p.listenDNSCryptTCP(ctx, addr)
|
||||
if lErr != nil {
|
||||
return fmt.Errorf("listening to dnscrypt tcp socket: %w", lErr)
|
||||
return fmt.Errorf("listening to dnscrypt tcp on addr %s: %w", addr, lErr)
|
||||
}
|
||||
|
||||
p.dnsCryptTCPListen = append(p.dnsCryptTCPListen, tcpListen)
|
||||
p.logger.Info("listening for dnscrypt messages on tcp", "addr", tcpListen.Addr())
|
||||
p.dnsCryptTCPListen = append(p.dnsCryptTCPListen, tcp)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// listenDNSCryptUDP returns a new UDP connection for DNSCrypt listening on
|
||||
// addr.
|
||||
func (p *Proxy) listenDNSCryptUDP(
|
||||
ctx context.Context,
|
||||
addr *net.UDPAddr,
|
||||
) (conn *net.UDPConn, err error) {
|
||||
addrStr := addr.String()
|
||||
p.logger.InfoContext(ctx, "creating dnscrypt udp server socket", "addr", addrStr)
|
||||
|
||||
err = p.bindWithRetry(ctx, func() (listenErr error) {
|
||||
conn, listenErr = net.ListenUDP(bootstrap.NetworkUDP, addr)
|
||||
|
||||
return listenErr
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("listening to udp socket: %w", err)
|
||||
}
|
||||
|
||||
p.logger.InfoContext(ctx, "listening for dnscrypt messages on udp", "addr", conn.LocalAddr())
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// listenDNSCryptTCP returns a new TCP listener for DNSCrypt listening on addr.
|
||||
func (p *Proxy) listenDNSCryptTCP(
|
||||
ctx context.Context,
|
||||
addr *net.TCPAddr,
|
||||
) (conn *net.TCPListener, err error) {
|
||||
addrStr := addr.String()
|
||||
p.logger.InfoContext(ctx, "creating dnscrypt tcp server socket", "addr", addrStr)
|
||||
|
||||
err = p.bindWithRetry(ctx, func() (listenErr error) {
|
||||
conn, listenErr = net.ListenTCP(bootstrap.NetworkTCP, addr)
|
||||
|
||||
return listenErr
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("listening to tcp socket: %w", err)
|
||||
}
|
||||
|
||||
p.logger.InfoContext(ctx, "listening for dnscrypt messages on tcp", "addr", conn.Addr())
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// dnsCryptHandler - dnscrypt.Handler implementation
|
||||
type dnsCryptHandler struct {
|
||||
proxy *Proxy
|
||||
|
||||
+37
-17
@@ -25,42 +25,56 @@ import (
|
||||
// listenHTTP creates instances of TLS listeners that will be used to run an
|
||||
// H1/H2 server. Returns the address the listener actually listens to (useful
|
||||
// in the case if port 0 is specified).
|
||||
func (p *Proxy) listenHTTP(addr *net.TCPAddr) (laddr *net.TCPAddr, err error) {
|
||||
tcpListen, err := net.ListenTCP(bootstrap.NetworkTCP, addr)
|
||||
func (p *Proxy) listenHTTP(
|
||||
ctx context.Context,
|
||||
addr *net.TCPAddr,
|
||||
) (ln net.Listener, tcpAddr *net.TCPAddr, err error) {
|
||||
var tcpListen *net.TCPListener
|
||||
err = p.bindWithRetry(ctx, func() (listenErr error) {
|
||||
tcpListen, listenErr = net.ListenTCP(bootstrap.NetworkTCP, addr)
|
||||
|
||||
return listenErr
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("tcp listener: %w", err)
|
||||
return nil, nil, fmt.Errorf("tcp listener: %w", err)
|
||||
}
|
||||
|
||||
p.logger.Info("listening to https", "addr", tcpListen.Addr())
|
||||
laddr := tcpListen.Addr()
|
||||
tcpAddr, ok := laddr.(*net.TCPAddr)
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("bad listener address type: %T", laddr)
|
||||
}
|
||||
|
||||
p.logger.InfoContext(ctx, "listening to https", "addr", tcpAddr)
|
||||
|
||||
tlsConfig := p.TLSConfig.Clone()
|
||||
tlsConfig.NextProtos = []string{http2.NextProtoTLS, "http/1.1"}
|
||||
|
||||
tlsListen := tls.NewListener(tcpListen, tlsConfig)
|
||||
p.httpsListen = append(p.httpsListen, tlsListen)
|
||||
|
||||
return tcpListen.Addr().(*net.TCPAddr), nil
|
||||
return tlsListen, tcpAddr, nil
|
||||
}
|
||||
|
||||
// listenH3 creates instances of QUIC listeners that will be used for running
|
||||
// an HTTP/3 server.
|
||||
func (p *Proxy) listenH3(addr *net.UDPAddr) (err error) {
|
||||
func (p *Proxy) listenH3(
|
||||
ctx context.Context,
|
||||
addr *net.UDPAddr,
|
||||
) (ln *quic.EarlyListener, err error) {
|
||||
tlsConfig := p.TLSConfig.Clone()
|
||||
tlsConfig.NextProtos = []string{"h3"}
|
||||
quicListen, err := quic.ListenAddrEarly(addr.String(), tlsConfig, newServerQUICConfig())
|
||||
if err != nil {
|
||||
return fmt.Errorf("quic listener: %w", err)
|
||||
return nil, fmt.Errorf("quic listener: %w", err)
|
||||
}
|
||||
|
||||
p.logger.Info("listening to h3", "addr", quicListen.Addr())
|
||||
p.logger.InfoContext(ctx, "listening to h3", "addr", quicListen.Addr())
|
||||
|
||||
p.h3Listen = append(p.h3Listen, quicListen)
|
||||
|
||||
return nil
|
||||
return quicListen, nil
|
||||
}
|
||||
|
||||
// createHTTPSListeners creates TCP/UDP listeners and HTTP/H3 servers.
|
||||
func (p *Proxy) createHTTPSListeners() (err error) {
|
||||
// initHTTPSListeners creates TCP/UDP listeners and HTTP/H3 servers.
|
||||
func (p *Proxy) initHTTPSListeners(ctx context.Context) (err error) {
|
||||
p.httpsServer = &http.Server{
|
||||
Handler: p,
|
||||
ReadHeaderTimeout: defaultTimeout,
|
||||
@@ -74,21 +88,27 @@ func (p *Proxy) createHTTPSListeners() (err error) {
|
||||
}
|
||||
|
||||
for _, addr := range p.HTTPSListenAddr {
|
||||
p.logger.Info("creating an https server")
|
||||
p.logger.InfoContext(ctx, "creating an https server")
|
||||
|
||||
tcpAddr, lErr := p.listenHTTP(addr)
|
||||
ln, tcpAddr, lErr := p.listenHTTP(ctx, addr)
|
||||
if lErr != nil {
|
||||
return fmt.Errorf("failed to start HTTPS server on %s: %w", addr, lErr)
|
||||
}
|
||||
|
||||
p.httpsListen = append(p.httpsListen, ln)
|
||||
|
||||
if p.HTTP3 {
|
||||
// HTTP/3 server listens to the same pair IP:port as the one HTTP/2
|
||||
// server listens to.
|
||||
udpAddr := &net.UDPAddr{IP: tcpAddr.IP, Port: tcpAddr.Port}
|
||||
err = p.listenH3(udpAddr)
|
||||
|
||||
var quicListen *quic.EarlyListener
|
||||
quicListen, err = p.listenH3(ctx, udpAddr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start HTTP/3 server on %s: %w", udpAddr, err)
|
||||
}
|
||||
|
||||
p.h3Listen = append(p.h3Listen, quicListen)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+47
-27
@@ -62,43 +62,63 @@ const (
|
||||
DoQCodeProtocolError quic.ApplicationErrorCode = 2
|
||||
)
|
||||
|
||||
// createQUICListeners creates QUIC listeners for the DoQ server.
|
||||
func (p *Proxy) createQUICListeners() error {
|
||||
// initQUICListeners creates QUIC listeners for the DoQ server.
|
||||
func (p *Proxy) initQUICListeners(ctx context.Context) (err error) {
|
||||
for _, a := range p.QUICListenAddr {
|
||||
p.logger.Info("creating quic listener", "addr", a)
|
||||
|
||||
conn, err := net.ListenUDP(bootstrap.NetworkUDP, a)
|
||||
var conn *net.UDPConn
|
||||
var ln *quic.EarlyListener
|
||||
var tr *quic.Transport
|
||||
conn, ln, tr, err = p.listenQUIC(ctx, a)
|
||||
if err != nil {
|
||||
return fmt.Errorf("listening to %s: %w", a, err)
|
||||
return fmt.Errorf("listening on quic addr %s: %w", a, err)
|
||||
}
|
||||
|
||||
p.quicConns = append(p.quicConns, conn)
|
||||
|
||||
v := newQUICAddrValidator(quicAddrValidatorCacheSize, quicAddrValidatorCacheTTL)
|
||||
transport := &quic.Transport{
|
||||
Conn: conn,
|
||||
VerifySourceAddress: v.requiresValidation,
|
||||
}
|
||||
|
||||
tlsConfig := p.TLSConfig.Clone()
|
||||
tlsConfig.NextProtos = compatProtoDQ
|
||||
quicListen, err := transport.ListenEarly(
|
||||
tlsConfig,
|
||||
newServerQUICConfig(),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("quic listener: %w", err)
|
||||
}
|
||||
|
||||
p.quicTransports = append(p.quicTransports, transport)
|
||||
p.quicListen = append(p.quicListen, quicListen)
|
||||
|
||||
p.logger.Info("listening quic", "addr", quicListen.Addr())
|
||||
p.quicTransports = append(p.quicTransports, tr)
|
||||
p.quicListen = append(p.quicListen, ln)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// listenQUIC returns a new UDP connection listening on addr, the QUIC listener
|
||||
// utilizing it, and the associated QUIC transport.
|
||||
func (p *Proxy) listenQUIC(
|
||||
ctx context.Context,
|
||||
addr *net.UDPAddr,
|
||||
) (conn *net.UDPConn, l *quic.EarlyListener, tr *quic.Transport, err error) {
|
||||
p.logger.InfoContext(ctx, "creating quic listener", "addr", addr)
|
||||
|
||||
err = p.bindWithRetry(ctx, func() (listenErr error) {
|
||||
conn, listenErr = net.ListenUDP(bootstrap.NetworkUDP, addr)
|
||||
|
||||
return listenErr
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("listening to udp socket: %w", err)
|
||||
}
|
||||
|
||||
v := newQUICAddrValidator(quicAddrValidatorCacheSize, quicAddrValidatorCacheTTL)
|
||||
tr = &quic.Transport{
|
||||
Conn: conn,
|
||||
VerifySourceAddress: v.requiresValidation,
|
||||
}
|
||||
|
||||
tlsConfig := p.TLSConfig.Clone()
|
||||
tlsConfig.NextProtos = compatProtoDQ
|
||||
l, err = tr.ListenEarly(
|
||||
tlsConfig,
|
||||
newServerQUICConfig(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("listening early: %w", err)
|
||||
}
|
||||
|
||||
p.logger.InfoContext(ctx, "listening quic", "addr", l.Addr())
|
||||
|
||||
return conn, l, tr, nil
|
||||
}
|
||||
|
||||
// quicPacketLoop listens for incoming QUIC packets.
|
||||
//
|
||||
// See also the comment on Proxy.requestsSema.
|
||||
|
||||
+49
-25
@@ -18,46 +18,70 @@ import (
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func (p *Proxy) createTCPListeners(ctx context.Context) (err error) {
|
||||
for _, a := range p.TCPListenAddr {
|
||||
p.logger.Info("creating tcp server socket", "addr", a)
|
||||
|
||||
lsnr, lErr := proxynetutil.ListenConfig(p.logger).Listen(
|
||||
ctx,
|
||||
bootstrap.NetworkTCP,
|
||||
a.String(),
|
||||
)
|
||||
if lErr != nil {
|
||||
return fmt.Errorf("listening to tcp socket: %w", lErr)
|
||||
// initTCPListeners initializes TCP listeners with configured addresses.
|
||||
func (p *Proxy) initTCPListeners(ctx context.Context) (err error) {
|
||||
for _, addr := range p.TCPListenAddr {
|
||||
var ln *net.TCPListener
|
||||
ln, err = p.listenTCP(ctx, addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("listening on tcp addr %s: %w", addr, err)
|
||||
}
|
||||
|
||||
tcpListener, ok := lsnr.(*net.TCPListener)
|
||||
if !ok {
|
||||
return fmt.Errorf("wrong listener type on tcp addr %s: %T", a, lsnr)
|
||||
}
|
||||
|
||||
p.tcpListen = append(p.tcpListen, tcpListener)
|
||||
|
||||
p.logger.Info("listening to tcp", "addr", tcpListener.Addr())
|
||||
p.tcpListen = append(p.tcpListen, ln)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Proxy) createTLSListeners() (err error) {
|
||||
for _, a := range p.TLSListenAddr {
|
||||
p.logger.Info("creating tls server socket", "addr", a)
|
||||
// listenTCP returns a new TCP listener listening on addr.
|
||||
func (p *Proxy) listenTCP(ctx context.Context, addr *net.TCPAddr) (ln *net.TCPListener, err error) {
|
||||
addrStr := addr.String()
|
||||
p.logger.InfoContext(ctx, "creating tcp server socket", "addr", addrStr)
|
||||
|
||||
conf := proxynetutil.ListenConfig(p.logger)
|
||||
|
||||
var listener net.Listener
|
||||
err = p.bindWithRetry(ctx, func() (listenErr error) {
|
||||
listener, listenErr = conf.Listen(ctx, bootstrap.NetworkTCP, addrStr)
|
||||
|
||||
return listenErr
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("listening to tcp socket: %w", err)
|
||||
}
|
||||
|
||||
var ok bool
|
||||
ln, ok = listener.(*net.TCPListener)
|
||||
if !ok {
|
||||
// TODO(e.burkov): Close the listener.
|
||||
|
||||
return nil, fmt.Errorf("bad listener type: %T", listener)
|
||||
}
|
||||
|
||||
p.logger.InfoContext(ctx, "listening to tcp", "addr", ln.Addr())
|
||||
|
||||
return ln, nil
|
||||
}
|
||||
|
||||
// initTLSListeners initializes TLS listeners with configured addresses.
|
||||
func (p *Proxy) initTLSListeners(ctx context.Context) (err error) {
|
||||
for _, addr := range p.TLSListenAddr {
|
||||
p.logger.InfoContext(ctx, "creating tls server socket", "addr", addr)
|
||||
|
||||
var tcpListen *net.TCPListener
|
||||
tcpListen, err = net.ListenTCP("tcp", a)
|
||||
err = p.bindWithRetry(ctx, func() (listenErr error) {
|
||||
tcpListen, listenErr = net.ListenTCP("tcp", addr)
|
||||
|
||||
return listenErr
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("listening on tls addr %s: %w", a, err)
|
||||
return fmt.Errorf("listening on tls addr %s: %w", addr, err)
|
||||
}
|
||||
|
||||
l := tls.NewListener(tcpListen, p.TLSConfig)
|
||||
p.tlsListen = append(p.tlsListen, l)
|
||||
|
||||
p.logger.Info("listening to tls", "addr", l.Addr())
|
||||
p.logger.InfoContext(ctx, "listening to tls", "addr", l.Addr())
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
+31
-17
@@ -16,10 +16,11 @@ import (
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func (p *Proxy) createUDPListeners(ctx context.Context) (err error) {
|
||||
// initUDPListeners initializes UDP listeners with configured addresses.
|
||||
func (p *Proxy) initUDPListeners(ctx context.Context) (err error) {
|
||||
for _, a := range p.UDPListenAddr {
|
||||
var pc *net.UDPConn
|
||||
pc, sErr := p.udpCreate(ctx, a)
|
||||
pc, sErr := p.listenUDP(ctx, a)
|
||||
if sErr != nil {
|
||||
return fmt.Errorf("listening on udp addr %s: %w", a, sErr)
|
||||
}
|
||||
@@ -30,39 +31,52 @@ func (p *Proxy) createUDPListeners(ctx context.Context) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// udpCreate - create a UDP listening socket
|
||||
func (p *Proxy) udpCreate(ctx context.Context, udpAddr *net.UDPAddr) (*net.UDPConn, error) {
|
||||
p.logger.InfoContext(ctx, "creating udp server socket", "addr", udpAddr)
|
||||
// listenUDP returns a new UDP connection listening on addr.
|
||||
func (p *Proxy) listenUDP(ctx context.Context, addr *net.UDPAddr) (conn *net.UDPConn, err error) {
|
||||
addrStr := addr.String()
|
||||
p.logger.InfoContext(ctx, "creating udp server socket", "addr", addrStr)
|
||||
|
||||
packetConn, err := proxynetutil.ListenConfig(p.logger).ListenPacket(
|
||||
ctx,
|
||||
bootstrap.NetworkUDP,
|
||||
udpAddr.String(),
|
||||
)
|
||||
conf := proxynetutil.ListenConfig(p.logger)
|
||||
|
||||
var packetConn net.PacketConn
|
||||
err = p.bindWithRetry(ctx, func() (listenErr error) {
|
||||
packetConn, listenErr = conf.ListenPacket(ctx, bootstrap.NetworkUDP, addrStr)
|
||||
|
||||
return listenErr
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("listening to udp socket: %w", err)
|
||||
}
|
||||
|
||||
udpListen := packetConn.(*net.UDPConn)
|
||||
// TODO(e.burkov): Use [errors.WithDeferred] for closing errors.
|
||||
|
||||
var ok bool
|
||||
conn, ok = packetConn.(*net.UDPConn)
|
||||
if !ok {
|
||||
// TODO(e.burkov): Close the connection.
|
||||
|
||||
return nil, fmt.Errorf("bad conn type: %T(%[1]v)", packetConn)
|
||||
}
|
||||
|
||||
if p.Config.UDPBufferSize > 0 {
|
||||
err = udpListen.SetReadBuffer(p.Config.UDPBufferSize)
|
||||
err = conn.SetReadBuffer(p.Config.UDPBufferSize)
|
||||
if err != nil {
|
||||
_ = udpListen.Close()
|
||||
p.logClose(ctx, slog.LevelDebug, conn, "closing after failed read buffer size setting")
|
||||
|
||||
return nil, fmt.Errorf("setting udp buf size: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
err = proxynetutil.UDPSetOptions(udpListen)
|
||||
err = proxynetutil.UDPSetOptions(conn)
|
||||
if err != nil {
|
||||
_ = udpListen.Close()
|
||||
p.logClose(ctx, slog.LevelDebug, conn, "closing after failed options setting")
|
||||
|
||||
return nil, fmt.Errorf("setting udp opts: %w", err)
|
||||
}
|
||||
|
||||
p.logger.InfoContext(ctx, "listening to udp", "addr", udpListen.LocalAddr())
|
||||
p.logger.InfoContext(ctx, "listening to udp", "addr", conn.LocalAddr())
|
||||
|
||||
return udpListen, nil
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// udpPacketLoop listens for incoming UDP packets.
|
||||
|
||||
Reference in New Issue
Block a user