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 {
dialer.Timeout = time.Duration(options.ConnectTimeout)
} else {
dialer.Timeout = C.DefaultTCPTimeout
dialer.Timeout = C.TCPTimeout
}
if options.TCPFastOpen {
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 {
tcpConn.SetKeepAlive(true)
tcpConn.SetKeepAlivePeriod(C.DefaultTCPKeepAlivePeriod)
tcpConn.SetKeepAlivePeriod(C.TCPKeepAlivePeriod)
}
return conn, nil
}

View file

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

View file

@ -3,9 +3,12 @@ package constant
import "time"
const (
DefaultTCPTimeout = 5 * time.Second
DefaultTCPKeepAlivePeriod = 20 * time.Second
TCPTimeout = 5 * time.Second
TCPKeepAlivePeriod = 20 * time.Second
ReadPayloadTimeout = 300 * time.Millisecond
URLTestTimeout = DefaultTCPTimeout
URLTestTimeout = TCPTimeout
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/logrusorgru/aurora v2.0.3+incompatible
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-shadowsocks v0.0.0-20220717063942-45a2ad9cd41f
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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
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-20220725031620-096223b346f3/go.mod h1:GbtQfZSpmtD3cXeD1qX2LCMwY8dH+bnnInDTqd92IsM=
github.com/sagernet/sing v0.0.0-20220725141316-c15de13f4f68 h1:EQ80ogJM1Xk4zgYI5lioDc15SCFeP0vdM5DMlmAOqnc=
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/go.mod h1:2A34p89do4H4E9Ke046cJCMTdVqmvsXGWXzRwgeO2TQ=
github.com/sagernet/sing-shadowsocks v0.0.0-20220717063942-45a2ad9cd41f h1:F6yiuKbBoXgWiuoP7R0YA14pDEl3emxA1mL7M16Q7gc=

View file

@ -7,6 +7,7 @@ import (
"time"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/canceler"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing/common"
@ -78,6 +79,16 @@ func NewPacketConnection(ctx context.Context, this N.Dialer, conn N.PacketConn,
if err != nil {
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))
}

View file

@ -6,10 +6,9 @@ import (
"io"
"net"
"os"
"sync"
"time"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/canceler"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"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 {
defer conn.Close()
ctx = adapter.WithContext(ctx, &metadata)
fastClose, cancel := context.WithCancel(ctx)
timeout := canceler.New(fastClose, cancel, C.DNSTimeout)
return task.Run(fastClose, func() error {
defer cancel()
_buffer := buf.StackNewSize(1024)
defer common.KeepAlive(_buffer)
buffer := common.Dup(_buffer)
defer buffer.Release()
var wg sync.WaitGroup
fastClose, cancel := context.WithCancel(ctx)
err := task.Run(fastClose, func() error {
var count int
for {
buffer.FullReset()
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])
d.logger.DebugContext(ctx, "inbound dns query ", formatDNSQuestion(question), " from ", metadata.Source)
}
wg.Add(1)
timeout.Update()
go func() error {
defer wg.Done()
response, err := d.router.Exchange(ctx, &message)
if err != nil {
return err
}
timeout.Update()
_responseBuffer := buf.StackNewSize(1024)
defer common.KeepAlive(_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)
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 {

View file

@ -3,6 +3,7 @@ package outbound
import (
"context"
"net"
"sort"
"time"
"github.com/sagernet/sing-box/adapter"
@ -167,6 +168,29 @@ func (g *URLTestGroup) Select() adapter.Outbound {
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() {
go g.checkOutbounds()
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) {
ctx, transport := r.matchDNS(ctx)
ctx, cancel := context.WithTimeout(ctx, C.DNSTimeout)
defer cancel()
return r.dnsClient.Exchange(ctx, transport, message)
}
func (r *Router) Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error) {
ctx, transport := r.matchDNS(ctx)
ctx, cancel := context.WithTimeout(ctx, C.DNSTimeout)
defer cancel()
return r.dnsClient.Lookup(ctx, transport, domain, strategy)
}
func (r *Router) LookupDefault(ctx context.Context, domain string) ([]netip.Addr, error) {
ctx, transport := r.matchDNS(ctx)
ctx, cancel := context.WithTimeout(ctx, C.DNSTimeout)
defer cancel()
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/go-connections v0.4.0
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/stretchr/testify v1.8.0
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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
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-20220725031620-096223b346f3/go.mod h1:GbtQfZSpmtD3cXeD1qX2LCMwY8dH+bnnInDTqd92IsM=
github.com/sagernet/sing v0.0.0-20220725141316-c15de13f4f68 h1:EQ80ogJM1Xk4zgYI5lioDc15SCFeP0vdM5DMlmAOqnc=
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/go.mod h1:2A34p89do4H4E9Ke046cJCMTdVqmvsXGWXzRwgeO2TQ=
github.com/sagernet/sing-shadowsocks v0.0.0-20220717063942-45a2ad9cd41f h1:F6yiuKbBoXgWiuoP7R0YA14pDEl3emxA1mL7M16Q7gc=