Improve udp timeout

This commit is contained in:
世界 2022-07-25 22:02:39 +08:00
parent 5491895a60
commit 816d7b734c
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
13 changed files with 157 additions and 41 deletions

View file

@ -0,0 +1,48 @@
package canceler
import (
"context"
"time"
)
type Instance struct {
ctx context.Context
cancelFunc context.CancelFunc
timer *time.Timer
timeout time.Duration
}
func New(ctx context.Context, cancelFunc context.CancelFunc, timeout time.Duration) *Instance {
instance := &Instance{
ctx,
cancelFunc,
time.NewTimer(timeout),
timeout,
}
go instance.wait()
return instance
}
func (i *Instance) Update() bool {
if !i.timer.Stop() {
return false
}
if !i.timer.Reset(i.timeout) {
return false
}
return true
}
func (i *Instance) wait() {
select {
case <-i.timer.C:
case <-i.ctx.Done():
}
i.Close()
}
func (i *Instance) Close() error {
i.timer.Stop()
i.cancelFunc()
return nil
}

41
common/canceler/packet.go Normal file
View file

@ -0,0 +1,41 @@
package canceler
import (
"context"
"time"
"github.com/sagernet/sing/common/buf"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
)
type PacketConn struct {
N.PacketConn
instance *Instance
}
func NewPacketConn(ctx context.Context, conn N.PacketConn, timeout time.Duration) (context.Context, N.PacketConn) {
ctx, cancel := context.WithCancel(ctx)
instance := New(ctx, cancel, timeout)
return ctx, &PacketConn{conn, instance}
}
func (c *PacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {
destination, err = c.PacketConn.ReadPacket(buffer)
if err == nil {
c.instance.Update()
}
return
}
func (c *PacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
err := c.PacketConn.WritePacket(buffer, destination)
if err == nil {
c.instance.Update()
}
return err
}
func (c *PacketConn) Upstream() any {
return c.PacketConn
}

View file

@ -103,7 +103,7 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
if options.ConnectTimeout != 0 { if options.ConnectTimeout != 0 {
dialer.Timeout = time.Duration(options.ConnectTimeout) dialer.Timeout = time.Duration(options.ConnectTimeout)
} else { } else {
dialer.Timeout = C.DefaultTCPTimeout dialer.Timeout = C.TCPTimeout
} }
if options.TCPFastOpen { if options.TCPFastOpen {
warnTFOOnUnsupportedPlatform.Check() warnTFOOnUnsupportedPlatform.Check()
@ -118,7 +118,7 @@ func (d *DefaultDialer) DialContext(ctx context.Context, network string, address
} }
if tcpConn, isTCP := common.Cast[*net.TCPConn](conn); isTCP { if tcpConn, isTCP := common.Cast[*net.TCPConn](conn); isTCP {
tcpConn.SetKeepAlive(true) tcpConn.SetKeepAlive(true)
tcpConn.SetKeepAlivePeriod(C.DefaultTCPKeepAlivePeriod) tcpConn.SetKeepAlivePeriod(C.TCPKeepAlivePeriod)
} }
return conn, nil return conn, nil
} }

View file

@ -129,7 +129,7 @@ func (d *TLSDialer) DialContext(ctx context.Context, network string, destination
return nil, err return nil, err
} }
tlsConn := tls.Client(conn, d.config) tlsConn := tls.Client(conn, d.config)
ctx, cancel := context.WithTimeout(ctx, C.DefaultTCPTimeout) ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout)
defer cancel() defer cancel()
err = tlsConn.HandshakeContext(ctx) err = tlsConn.HandshakeContext(ctx)
return tlsConn, err return tlsConn, err

View file

@ -3,9 +3,12 @@ package constant
import "time" import "time"
const ( const (
DefaultTCPTimeout = 5 * time.Second TCPTimeout = 5 * time.Second
DefaultTCPKeepAlivePeriod = 20 * time.Second TCPKeepAlivePeriod = 20 * time.Second
ReadPayloadTimeout = 300 * time.Millisecond ReadPayloadTimeout = 300 * time.Millisecond
URLTestTimeout = DefaultTCPTimeout URLTestTimeout = TCPTimeout
DefaultURLTestInterval = 1 * time.Minute DefaultURLTestInterval = 1 * time.Minute
DNSTimeout = 10 * time.Second
QUICTimeout = 30 * time.Second
STUNTimeout = 15 * time.Second
) )

2
go.mod
View file

@ -12,7 +12,7 @@ require (
github.com/gorilla/websocket v1.5.0 github.com/gorilla/websocket v1.5.0
github.com/logrusorgru/aurora v2.0.3+incompatible github.com/logrusorgru/aurora v2.0.3+incompatible
github.com/oschwald/maxminddb-golang v1.9.0 github.com/oschwald/maxminddb-golang v1.9.0
github.com/sagernet/sing v0.0.0-20220725031620-096223b346f3 github.com/sagernet/sing v0.0.0-20220725141316-c15de13f4f68
github.com/sagernet/sing-dns v0.0.0-20220724053927-eb8d0d542175 github.com/sagernet/sing-dns v0.0.0-20220724053927-eb8d0d542175
github.com/sagernet/sing-shadowsocks v0.0.0-20220717063942-45a2ad9cd41f github.com/sagernet/sing-shadowsocks v0.0.0-20220717063942-45a2ad9cd41f
github.com/sagernet/sing-tun v0.0.0-20220720051454-d35c334b46c9 github.com/sagernet/sing-tun v0.0.0-20220720051454-d35c334b46c9

4
go.sum
View file

@ -37,8 +37,8 @@ github.com/oschwald/maxminddb-golang v1.9.0/go.mod h1:TK+s/Z2oZq0rSl4PSeAEoP0bgm
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagernet/sing v0.0.0-20220725031620-096223b346f3 h1:Qm57CtqzaZ6Cq0ZDz1dX4vSNUoTYKn7qfXnpleKE0z0= github.com/sagernet/sing v0.0.0-20220725141316-c15de13f4f68 h1:EQ80ogJM1Xk4zgYI5lioDc15SCFeP0vdM5DMlmAOqnc=
github.com/sagernet/sing v0.0.0-20220725031620-096223b346f3/go.mod h1:GbtQfZSpmtD3cXeD1qX2LCMwY8dH+bnnInDTqd92IsM= github.com/sagernet/sing v0.0.0-20220725141316-c15de13f4f68/go.mod h1:GbtQfZSpmtD3cXeD1qX2LCMwY8dH+bnnInDTqd92IsM=
github.com/sagernet/sing-dns v0.0.0-20220724053927-eb8d0d542175 h1:YpacS9+rDFcLG8lSkmwU21capz2gewk4LQfCE/x73U0= github.com/sagernet/sing-dns v0.0.0-20220724053927-eb8d0d542175 h1:YpacS9+rDFcLG8lSkmwU21capz2gewk4LQfCE/x73U0=
github.com/sagernet/sing-dns v0.0.0-20220724053927-eb8d0d542175/go.mod h1:2A34p89do4H4E9Ke046cJCMTdVqmvsXGWXzRwgeO2TQ= github.com/sagernet/sing-dns v0.0.0-20220724053927-eb8d0d542175/go.mod h1:2A34p89do4H4E9Ke046cJCMTdVqmvsXGWXzRwgeO2TQ=
github.com/sagernet/sing-shadowsocks v0.0.0-20220717063942-45a2ad9cd41f h1:F6yiuKbBoXgWiuoP7R0YA14pDEl3emxA1mL7M16Q7gc= github.com/sagernet/sing-shadowsocks v0.0.0-20220717063942-45a2ad9cd41f h1:F6yiuKbBoXgWiuoP7R0YA14pDEl3emxA1mL7M16Q7gc=

View file

@ -7,6 +7,7 @@ import (
"time" "time"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/canceler"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
@ -78,6 +79,16 @@ func NewPacketConnection(ctx context.Context, this N.Dialer, conn N.PacketConn,
if err != nil { if err != nil {
return err return err
} }
if metadata.Protocol != "" {
switch metadata.Protocol {
case C.ProtocolQUIC:
ctx, conn = canceler.NewPacketConn(ctx, conn, C.QUICTimeout)
case C.ProtocolDNS:
ctx, conn = canceler.NewPacketConn(ctx, conn, C.DNSTimeout)
case C.ProtocolSTUN:
ctx, conn = canceler.NewPacketConn(ctx, conn, C.STUNTimeout)
}
}
return bufio.CopyPacketConn(ctx, conn, bufio.NewPacketConn(outConn)) return bufio.CopyPacketConn(ctx, conn, bufio.NewPacketConn(outConn))
} }

View file

@ -6,10 +6,9 @@ import (
"io" "io"
"net" "net"
"os" "os"
"sync"
"time"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/canceler"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
@ -103,14 +102,14 @@ func (d *DNS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter
func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
defer conn.Close() defer conn.Close()
ctx = adapter.WithContext(ctx, &metadata) ctx = adapter.WithContext(ctx, &metadata)
_buffer := buf.StackNewSize(1024)
defer common.KeepAlive(_buffer)
buffer := common.Dup(_buffer)
defer buffer.Release()
var wg sync.WaitGroup
fastClose, cancel := context.WithCancel(ctx) fastClose, cancel := context.WithCancel(ctx)
err := task.Run(fastClose, func() error { timeout := canceler.New(fastClose, cancel, C.DNSTimeout)
var count int return task.Run(fastClose, func() error {
defer cancel()
_buffer := buf.StackNewSize(1024)
defer common.KeepAlive(_buffer)
buffer := common.Dup(_buffer)
defer buffer.Release()
for { for {
buffer.FullReset() buffer.FullReset()
destination, err := conn.ReadPacket(buffer) destination, err := conn.ReadPacket(buffer)
@ -127,13 +126,13 @@ func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metada
metadata.Domain = string(question.Name.Data[:question.Name.Length-1]) metadata.Domain = string(question.Name.Data[:question.Name.Length-1])
d.logger.DebugContext(ctx, "inbound dns query ", formatDNSQuestion(question), " from ", metadata.Source) d.logger.DebugContext(ctx, "inbound dns query ", formatDNSQuestion(question), " from ", metadata.Source)
} }
wg.Add(1) timeout.Update()
go func() error { go func() error {
defer wg.Done()
response, err := d.router.Exchange(ctx, &message) response, err := d.router.Exchange(ctx, &message)
if err != nil { if err != nil {
return err return err
} }
timeout.Update()
_responseBuffer := buf.StackNewSize(1024) _responseBuffer := buf.StackNewSize(1024)
defer common.KeepAlive(_responseBuffer) defer common.KeepAlive(_responseBuffer)
responseBuffer := common.Dup(_responseBuffer) responseBuffer := common.Dup(_responseBuffer)
@ -146,24 +145,8 @@ func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metada
err = conn.WritePacket(responseBuffer, destination) err = conn.WritePacket(responseBuffer, destination)
return err return err
}() }()
count++
if count == 2 {
break
}
} }
cancel()
return nil
}, func() error {
timer := time.NewTimer(5 * time.Second)
select {
case <-timer.C:
cancel()
case <-fastClose.Done():
}
return nil
}) })
wg.Wait()
return err
} }
func formatDNSQuestion(question dnsmessage.Question) string { func formatDNSQuestion(question dnsmessage.Question) string {

View file

@ -3,6 +3,7 @@ package outbound
import ( import (
"context" "context"
"net" "net"
"sort"
"time" "time"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
@ -167,6 +168,29 @@ func (g *URLTestGroup) Select() adapter.Outbound {
return minOutbound return minOutbound
} }
func (g *URLTestGroup) Fallback(used adapter.Outbound) []adapter.Outbound {
outbounds := make([]adapter.Outbound, 0, len(g.outbounds)-1)
for _, detour := range g.outbounds {
if detour != used {
outbounds = append(outbounds, detour)
}
}
sort.Slice(outbounds, func(i, j int) bool {
oi := outbounds[i]
oj := outbounds[j]
hi := g.router.URLTestHistoryStorage(false).LoadURLTestHistory(RealTag(oi))
if hi == nil {
return false
}
hj := g.router.URLTestHistoryStorage(false).LoadURLTestHistory(RealTag(oj))
if hj == nil {
return false
}
return hi.Delay < hj.Delay
})
return outbounds
}
func (g *URLTestGroup) loopCheck() { func (g *URLTestGroup) loopCheck() {
go g.checkOutbounds() go g.checkOutbounds()
for { for {

View file

@ -576,16 +576,22 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
func (r *Router) Exchange(ctx context.Context, message *dnsmessage.Message) (*dnsmessage.Message, error) { func (r *Router) Exchange(ctx context.Context, message *dnsmessage.Message) (*dnsmessage.Message, error) {
ctx, transport := r.matchDNS(ctx) ctx, transport := r.matchDNS(ctx)
ctx, cancel := context.WithTimeout(ctx, C.DNSTimeout)
defer cancel()
return r.dnsClient.Exchange(ctx, transport, message) return r.dnsClient.Exchange(ctx, transport, message)
} }
func (r *Router) Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error) { func (r *Router) Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error) {
ctx, transport := r.matchDNS(ctx) ctx, transport := r.matchDNS(ctx)
ctx, cancel := context.WithTimeout(ctx, C.DNSTimeout)
defer cancel()
return r.dnsClient.Lookup(ctx, transport, domain, strategy) return r.dnsClient.Lookup(ctx, transport, domain, strategy)
} }
func (r *Router) LookupDefault(ctx context.Context, domain string) ([]netip.Addr, error) { func (r *Router) LookupDefault(ctx context.Context, domain string) ([]netip.Addr, error) {
ctx, transport := r.matchDNS(ctx) ctx, transport := r.matchDNS(ctx)
ctx, cancel := context.WithTimeout(ctx, C.DNSTimeout)
defer cancel()
return r.dnsClient.Lookup(ctx, transport, domain, r.defaultDomainStrategy) return r.dnsClient.Lookup(ctx, transport, domain, r.defaultDomainStrategy)
} }

View file

@ -10,7 +10,7 @@ require (
github.com/docker/docker v20.10.17+incompatible github.com/docker/docker v20.10.17+incompatible
github.com/docker/go-connections v0.4.0 github.com/docker/go-connections v0.4.0
github.com/gofrs/uuid v4.2.0+incompatible github.com/gofrs/uuid v4.2.0+incompatible
github.com/sagernet/sing v0.0.0-20220725031620-096223b346f3 github.com/sagernet/sing v0.0.0-20220725141316-c15de13f4f68
github.com/spyzhov/ajson v0.7.1 github.com/spyzhov/ajson v0.7.1
github.com/stretchr/testify v1.8.0 github.com/stretchr/testify v1.8.0
golang.org/x/net v0.0.0-20220722155237-a158d28d115b golang.org/x/net v0.0.0-20220722155237-a158d28d115b

View file

@ -64,8 +64,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sagernet/sing v0.0.0-20220725031620-096223b346f3 h1:Qm57CtqzaZ6Cq0ZDz1dX4vSNUoTYKn7qfXnpleKE0z0= github.com/sagernet/sing v0.0.0-20220725141316-c15de13f4f68 h1:EQ80ogJM1Xk4zgYI5lioDc15SCFeP0vdM5DMlmAOqnc=
github.com/sagernet/sing v0.0.0-20220725031620-096223b346f3/go.mod h1:GbtQfZSpmtD3cXeD1qX2LCMwY8dH+bnnInDTqd92IsM= github.com/sagernet/sing v0.0.0-20220725141316-c15de13f4f68/go.mod h1:GbtQfZSpmtD3cXeD1qX2LCMwY8dH+bnnInDTqd92IsM=
github.com/sagernet/sing-dns v0.0.0-20220724053927-eb8d0d542175 h1:YpacS9+rDFcLG8lSkmwU21capz2gewk4LQfCE/x73U0= github.com/sagernet/sing-dns v0.0.0-20220724053927-eb8d0d542175 h1:YpacS9+rDFcLG8lSkmwU21capz2gewk4LQfCE/x73U0=
github.com/sagernet/sing-dns v0.0.0-20220724053927-eb8d0d542175/go.mod h1:2A34p89do4H4E9Ke046cJCMTdVqmvsXGWXzRwgeO2TQ= github.com/sagernet/sing-dns v0.0.0-20220724053927-eb8d0d542175/go.mod h1:2A34p89do4H4E9Ke046cJCMTdVqmvsXGWXzRwgeO2TQ=
github.com/sagernet/sing-shadowsocks v0.0.0-20220717063942-45a2ad9cd41f h1:F6yiuKbBoXgWiuoP7R0YA14pDEl3emxA1mL7M16Q7gc= github.com/sagernet/sing-shadowsocks v0.0.0-20220717063942-45a2ad9cd41f h1:F6yiuKbBoXgWiuoP7R0YA14pDEl3emxA1mL7M16Q7gc=