mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-06-03 17:57:30 +03:00
Update sing-box core, refactor MASQUE, update XHTTP
This commit is contained in:
@@ -70,10 +70,7 @@ func NewClient(options ClientOptions) *Client {
|
||||
if client.timeout == 0 {
|
||||
client.timeout = C.DNSTimeout
|
||||
}
|
||||
cacheCapacity := options.CacheCapacity
|
||||
if cacheCapacity < 1024 {
|
||||
cacheCapacity = 1024
|
||||
}
|
||||
cacheCapacity := max(options.CacheCapacity, 1024)
|
||||
if !client.disableCache {
|
||||
if !client.independentCache {
|
||||
client.cache = common.Must1(freelru.NewSharded[dns.Question, *dns.Msg](cacheCapacity, maphash.NewHasher[dns.Question]().Hash32))
|
||||
@@ -334,9 +331,10 @@ func (c *Client) Lookup(ctx context.Context, transport adapter.DNSTransport, dom
|
||||
if options.LookupStrategy != C.DomainStrategyAsIS {
|
||||
lookupOptions.Strategy = strategy
|
||||
}
|
||||
if strategy == C.DomainStrategyIPv4Only {
|
||||
switch strategy {
|
||||
case C.DomainStrategyIPv4Only:
|
||||
return c.lookupToExchange(ctx, transport, dnsName, dns.TypeA, lookupOptions, responseChecker)
|
||||
} else if strategy == C.DomainStrategyIPv6Only {
|
||||
case C.DomainStrategyIPv6Only:
|
||||
return c.lookupToExchange(ctx, transport, dnsName, dns.TypeAAAA, lookupOptions, responseChecker)
|
||||
}
|
||||
var response4 []netip.Addr
|
||||
@@ -500,10 +498,7 @@ func (c *Client) loadResponse(question dns.Question, transport adapter.DNSTransp
|
||||
}
|
||||
}
|
||||
}
|
||||
nowTTL := int(expireAt.Sub(timeNow).Seconds())
|
||||
if nowTTL < 0 {
|
||||
nowTTL = 0
|
||||
}
|
||||
nowTTL := max(int(expireAt.Sub(timeNow).Seconds()), 0)
|
||||
response = response.Copy()
|
||||
if originTTL > 0 {
|
||||
duration := uint32(originTTL - nowTTL)
|
||||
@@ -551,18 +546,6 @@ func MessageToAddresses(response *dns.Msg) []netip.Addr {
|
||||
return addresses
|
||||
}
|
||||
|
||||
func wrapError(err error) error {
|
||||
switch dnsErr := err.(type) {
|
||||
case *net.DNSError:
|
||||
if dnsErr.IsNotFound {
|
||||
return RcodeNameError
|
||||
}
|
||||
case *net.AddrError:
|
||||
return RcodeNameError
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
type transportKey struct{}
|
||||
|
||||
func contextWithTransportTag(ctx context.Context, transportTag string) context.Context {
|
||||
|
||||
@@ -4,9 +4,10 @@ import (
|
||||
"context"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing/common/x/list"
|
||||
|
||||
"golang.org/x/sync/semaphore"
|
||||
)
|
||||
|
||||
type ConnPoolMode int
|
||||
@@ -17,14 +18,18 @@ const (
|
||||
)
|
||||
|
||||
type ConnPoolOptions[T comparable] struct {
|
||||
Mode ConnPoolMode
|
||||
IsAlive func(T) bool
|
||||
Close func(T, error)
|
||||
Mode ConnPoolMode
|
||||
// MaxInflight caps concurrent in-progress dials. Only honored in ConnPoolOrdered mode.
|
||||
MaxInflight int
|
||||
IsAlive func(T) bool
|
||||
Close func(T, error)
|
||||
}
|
||||
|
||||
type ConnPool[T comparable] struct {
|
||||
options ConnPoolOptions[T]
|
||||
|
||||
sem *semaphore.Weighted
|
||||
|
||||
access sync.Mutex
|
||||
closed bool
|
||||
state *connPoolState[T]
|
||||
@@ -53,24 +58,15 @@ type connPoolConnect[T comparable] struct {
|
||||
err error
|
||||
}
|
||||
|
||||
type connPoolDialContext struct {
|
||||
context.Context
|
||||
parent context.Context
|
||||
}
|
||||
|
||||
func (c connPoolDialContext) Deadline() (time.Time, bool) {
|
||||
return c.parent.Deadline()
|
||||
}
|
||||
|
||||
func (c connPoolDialContext) Value(key any) any {
|
||||
return c.parent.Value(key)
|
||||
}
|
||||
|
||||
func NewConnPool[T comparable](options ConnPoolOptions[T]) *ConnPool[T] {
|
||||
return &ConnPool[T]{
|
||||
p := &ConnPool[T]{
|
||||
options: options,
|
||||
state: newConnPoolState[T](options.Mode),
|
||||
}
|
||||
if options.Mode == ConnPoolOrdered && options.MaxInflight > 0 {
|
||||
p.sem = semaphore.NewWeighted(int64(options.MaxInflight))
|
||||
}
|
||||
p.state = newConnPoolState[T](options.Mode)
|
||||
return p
|
||||
}
|
||||
|
||||
func newConnPoolState[T comparable](mode ConnPoolMode) *connPoolState[T] {
|
||||
@@ -108,67 +104,27 @@ func (p *ConnPool[T]) AcquireShared(ctx context.Context, dial func(context.Conte
|
||||
}
|
||||
|
||||
func (p *ConnPool[T]) Release(conn T, reuse bool) {
|
||||
var (
|
||||
closeConn bool
|
||||
closeErr error
|
||||
)
|
||||
|
||||
p.access.Lock()
|
||||
if p.closed || p.state == nil {
|
||||
closeConn = true
|
||||
closeErr = net.ErrClosed
|
||||
if p.closed {
|
||||
p.access.Unlock()
|
||||
if closeConn {
|
||||
p.options.Close(conn, closeErr)
|
||||
}
|
||||
p.options.Close(conn, net.ErrClosed)
|
||||
return
|
||||
}
|
||||
|
||||
currentState := p.state
|
||||
_, tracked := currentState.all[conn]
|
||||
if !tracked {
|
||||
closeConn = true
|
||||
closeErr = p.closeCause(currentState)
|
||||
state := p.state
|
||||
if _, tracked := state.all[conn]; !tracked {
|
||||
p.access.Unlock()
|
||||
if closeConn {
|
||||
p.options.Close(conn, closeErr)
|
||||
}
|
||||
p.options.Close(conn, net.ErrClosed)
|
||||
return
|
||||
}
|
||||
|
||||
if !reuse || !p.options.IsAlive(conn) {
|
||||
delete(currentState.all, conn)
|
||||
switch p.options.Mode {
|
||||
case ConnPoolSingle:
|
||||
if currentState.hasShared && currentState.shared == conn {
|
||||
var zero T
|
||||
currentState.shared = zero
|
||||
currentState.hasShared = false
|
||||
currentState.sharedClaimed = false
|
||||
currentState.sharedCtx = nil
|
||||
if currentState.sharedCancel != nil {
|
||||
currentState.sharedCancel(net.ErrClosed)
|
||||
currentState.sharedCancel = nil
|
||||
}
|
||||
}
|
||||
case ConnPoolOrdered:
|
||||
if element, loaded := currentState.idleElements[conn]; loaded {
|
||||
currentState.idle.Remove(element)
|
||||
delete(currentState.idleElements, conn)
|
||||
}
|
||||
}
|
||||
closeConn = true
|
||||
closeErr = net.ErrClosed
|
||||
p.removeConn(state, conn, net.ErrClosed)
|
||||
p.access.Unlock()
|
||||
if closeConn {
|
||||
p.options.Close(conn, closeErr)
|
||||
}
|
||||
p.options.Close(conn, net.ErrClosed)
|
||||
return
|
||||
}
|
||||
|
||||
if p.options.Mode == ConnPoolOrdered {
|
||||
if _, loaded := currentState.idleElements[conn]; !loaded {
|
||||
currentState.idleElements[conn] = currentState.idle.PushBack(conn)
|
||||
if _, idle := state.idleElements[conn]; !idle {
|
||||
state.idleElements[conn] = state.idle.PushBack(conn)
|
||||
}
|
||||
}
|
||||
p.access.Unlock()
|
||||
@@ -176,42 +132,68 @@ func (p *ConnPool[T]) Release(conn T, reuse bool) {
|
||||
|
||||
func (p *ConnPool[T]) Invalidate(conn T, cause error) {
|
||||
p.access.Lock()
|
||||
if p.closed || p.state == nil {
|
||||
if p.closed {
|
||||
p.access.Unlock()
|
||||
p.options.Close(conn, cause)
|
||||
return
|
||||
}
|
||||
|
||||
currentState := p.state
|
||||
_, tracked := currentState.all[conn]
|
||||
if !tracked {
|
||||
state := p.state
|
||||
if _, tracked := state.all[conn]; !tracked {
|
||||
p.access.Unlock()
|
||||
return
|
||||
}
|
||||
p.removeConn(state, conn, cause)
|
||||
p.access.Unlock()
|
||||
p.options.Close(conn, cause)
|
||||
}
|
||||
|
||||
delete(currentState.all, conn)
|
||||
func (p *ConnPool[T]) acquireSlot(ctx context.Context, state *connPoolState[T]) error {
|
||||
if p.sem == nil {
|
||||
return nil
|
||||
}
|
||||
acquireCtx, cancel := context.WithCancel(ctx)
|
||||
stopStateCancel := context.AfterFunc(state.ctx, cancel)
|
||||
err := p.sem.Acquire(acquireCtx, 1)
|
||||
stopStateCancel()
|
||||
cancel()
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
ctxErr := ctx.Err()
|
||||
if ctxErr != nil {
|
||||
return ctxErr
|
||||
}
|
||||
return context.Cause(state.ctx)
|
||||
}
|
||||
|
||||
func (p *ConnPool[T]) releaseSlot() {
|
||||
if p.sem != nil {
|
||||
p.sem.Release(1)
|
||||
}
|
||||
}
|
||||
|
||||
// removeConn must be called with p.access held.
|
||||
func (p *ConnPool[T]) removeConn(state *connPoolState[T], conn T, cause error) {
|
||||
delete(state.all, conn)
|
||||
switch p.options.Mode {
|
||||
case ConnPoolSingle:
|
||||
if currentState.hasShared && currentState.shared == conn {
|
||||
if state.hasShared && state.shared == conn {
|
||||
var zero T
|
||||
currentState.shared = zero
|
||||
currentState.hasShared = false
|
||||
currentState.sharedClaimed = false
|
||||
currentState.sharedCtx = nil
|
||||
if currentState.sharedCancel != nil {
|
||||
currentState.sharedCancel(cause)
|
||||
currentState.sharedCancel = nil
|
||||
state.shared = zero
|
||||
state.hasShared = false
|
||||
state.sharedClaimed = false
|
||||
state.sharedCtx = nil
|
||||
if state.sharedCancel != nil {
|
||||
state.sharedCancel(cause)
|
||||
state.sharedCancel = nil
|
||||
}
|
||||
}
|
||||
case ConnPoolOrdered:
|
||||
if element, loaded := currentState.idleElements[conn]; loaded {
|
||||
currentState.idle.Remove(element)
|
||||
delete(currentState.idleElements, conn)
|
||||
if element, loaded := state.idleElements[conn]; loaded {
|
||||
state.idle.Remove(element)
|
||||
delete(state.idleElements, conn)
|
||||
}
|
||||
}
|
||||
p.access.Unlock()
|
||||
|
||||
p.options.Close(conn, cause)
|
||||
}
|
||||
|
||||
func (p *ConnPool[T]) Reset() {
|
||||
@@ -220,7 +202,6 @@ func (p *ConnPool[T]) Reset() {
|
||||
p.access.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
oldState := p.state
|
||||
p.state = newConnPoolState[T](p.options.Mode)
|
||||
p.access.Unlock()
|
||||
@@ -234,7 +215,6 @@ func (p *ConnPool[T]) Close() error {
|
||||
p.access.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
p.closed = true
|
||||
oldState := p.state
|
||||
p.state = nil
|
||||
@@ -247,77 +227,83 @@ func (p *ConnPool[T]) Close() error {
|
||||
func (p *ConnPool[T]) acquireOrdered(ctx context.Context, dial func(context.Context) (T, error)) (T, bool, error) {
|
||||
var zero T
|
||||
for {
|
||||
var (
|
||||
staleConn T
|
||||
hasStale bool
|
||||
)
|
||||
|
||||
p.access.Lock()
|
||||
if p.closed {
|
||||
p.access.Unlock()
|
||||
return zero, false, net.ErrClosed
|
||||
}
|
||||
|
||||
currentState := p.state
|
||||
if element := currentState.idle.Front(); element != nil {
|
||||
conn := currentState.idle.Remove(element)
|
||||
delete(currentState.idleElements, conn)
|
||||
if p.options.IsAlive(conn) {
|
||||
current := p.state
|
||||
if element := current.idle.Front(); element != nil {
|
||||
idleConn := current.idle.Remove(element)
|
||||
delete(current.idleElements, idleConn)
|
||||
if p.options.IsAlive(idleConn) {
|
||||
p.access.Unlock()
|
||||
return conn, false, nil
|
||||
return idleConn, false, nil
|
||||
}
|
||||
delete(currentState.all, conn)
|
||||
staleConn = conn
|
||||
hasStale = true
|
||||
}
|
||||
p.access.Unlock()
|
||||
|
||||
if hasStale {
|
||||
p.options.Close(staleConn, net.ErrClosed)
|
||||
delete(current.all, idleConn)
|
||||
p.access.Unlock()
|
||||
p.options.Close(idleConn, net.ErrClosed)
|
||||
continue
|
||||
}
|
||||
|
||||
conn, err := p.dial(ctx, currentState, dial)
|
||||
if err != nil {
|
||||
return zero, false, err
|
||||
}
|
||||
|
||||
p.access.Lock()
|
||||
if p.closed {
|
||||
p.access.Unlock()
|
||||
p.options.Close(conn, net.ErrClosed)
|
||||
return zero, false, net.ErrClosed
|
||||
}
|
||||
if p.state != currentState {
|
||||
cause := p.closeCause(currentState)
|
||||
p.access.Unlock()
|
||||
p.options.Close(conn, cause)
|
||||
return zero, false, cause
|
||||
}
|
||||
currentState.all[conn] = struct{}{}
|
||||
p.access.Unlock()
|
||||
return conn, true, nil
|
||||
return p.dialAndInstall(ctx, current, dial)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ConnPool[T]) dialAndInstall(ctx context.Context, current *connPoolState[T], dial func(context.Context) (T, error)) (T, bool, error) {
|
||||
var zero T
|
||||
err := p.acquireSlot(ctx, current)
|
||||
if err != nil {
|
||||
return zero, false, err
|
||||
}
|
||||
defer p.releaseSlot()
|
||||
dialCtx, dialCancel := context.WithCancelCause(ctx)
|
||||
stopStateCancel := context.AfterFunc(current.ctx, func() {
|
||||
dialCancel(context.Cause(current.ctx))
|
||||
})
|
||||
conn, err := dial(dialCtx)
|
||||
stateCancelStopped := stopStateCancel()
|
||||
dialErr := context.Cause(dialCtx)
|
||||
if dialErr == nil && !stateCancelStopped {
|
||||
dialErr = context.Cause(current.ctx)
|
||||
}
|
||||
dialCancel(nil)
|
||||
if err != nil {
|
||||
if dialErr != nil {
|
||||
return zero, false, dialErr
|
||||
}
|
||||
return zero, false, err
|
||||
}
|
||||
if dialErr != nil {
|
||||
p.options.Close(conn, dialErr)
|
||||
return zero, false, dialErr
|
||||
}
|
||||
|
||||
p.access.Lock()
|
||||
if p.closed {
|
||||
p.access.Unlock()
|
||||
p.options.Close(conn, net.ErrClosed)
|
||||
return zero, false, net.ErrClosed
|
||||
}
|
||||
if p.state != current {
|
||||
p.access.Unlock()
|
||||
p.options.Close(conn, net.ErrClosed)
|
||||
return zero, false, net.ErrClosed
|
||||
}
|
||||
current.all[conn] = struct{}{}
|
||||
p.access.Unlock()
|
||||
return conn, true, nil
|
||||
}
|
||||
|
||||
func (p *ConnPool[T]) acquireShared(ctx context.Context, dial func(context.Context) (T, error)) (T, context.Context, bool, error) {
|
||||
var zero T
|
||||
for {
|
||||
var (
|
||||
staleConn T
|
||||
hasStale bool
|
||||
state *connPoolConnect[T]
|
||||
current *connPoolState[T]
|
||||
startDial bool
|
||||
)
|
||||
|
||||
p.access.Lock()
|
||||
if p.closed {
|
||||
p.access.Unlock()
|
||||
return zero, nil, false, net.ErrClosed
|
||||
}
|
||||
|
||||
current = p.state
|
||||
current := p.state
|
||||
if current.hasShared {
|
||||
conn := current.shared
|
||||
if p.options.IsAlive(conn) {
|
||||
@@ -327,35 +313,19 @@ func (p *ConnPool[T]) acquireShared(ctx context.Context, dial func(context.Conte
|
||||
p.access.Unlock()
|
||||
return conn, connCtx, created, nil
|
||||
}
|
||||
delete(current.all, conn)
|
||||
var zeroConn T
|
||||
current.shared = zeroConn
|
||||
current.hasShared = false
|
||||
current.sharedClaimed = false
|
||||
current.sharedCtx = nil
|
||||
if current.sharedCancel != nil {
|
||||
current.sharedCancel(net.ErrClosed)
|
||||
current.sharedCancel = nil
|
||||
}
|
||||
staleConn = conn
|
||||
hasStale = true
|
||||
p.removeConn(current, conn, net.ErrClosed)
|
||||
p.access.Unlock()
|
||||
p.options.Close(staleConn, net.ErrClosed)
|
||||
p.options.Close(conn, net.ErrClosed)
|
||||
continue
|
||||
}
|
||||
|
||||
if current.connecting == nil {
|
||||
current.connecting = &connPoolConnect[T]{
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
startDial = true
|
||||
startDial := current.connecting == nil
|
||||
if startDial {
|
||||
current.connecting = &connPoolConnect[T]{done: make(chan struct{})}
|
||||
}
|
||||
state = current.connecting
|
||||
state := current.connecting
|
||||
p.access.Unlock()
|
||||
|
||||
if hasStale {
|
||||
continue
|
||||
}
|
||||
if startDial {
|
||||
go p.connectSingle(current, state, ctx, dial)
|
||||
}
|
||||
@@ -381,35 +351,39 @@ func (p *ConnPool[T]) acquireShared(ctx context.Context, dial func(context.Conte
|
||||
}
|
||||
|
||||
func (p *ConnPool[T]) connectSingle(current *connPoolState[T], state *connPoolConnect[T], ctx context.Context, dial func(context.Context) (T, error)) {
|
||||
conn, err := p.dial(ctx, current, dial)
|
||||
if err != nil {
|
||||
p.access.Lock()
|
||||
if current.connecting == state {
|
||||
current.connecting = nil
|
||||
dialCtx, dialCancel := context.WithCancelCause(ctx)
|
||||
stopStateCancel := context.AfterFunc(current.ctx, func() {
|
||||
dialCancel(context.Cause(current.ctx))
|
||||
})
|
||||
conn, err := dial(dialCtx)
|
||||
stateCancelStopped := stopStateCancel()
|
||||
dialErr := context.Cause(dialCtx)
|
||||
if dialErr == nil && !stateCancelStopped {
|
||||
dialErr = context.Cause(current.ctx)
|
||||
}
|
||||
dialCancel(nil)
|
||||
if dialErr != nil {
|
||||
if err == nil {
|
||||
p.options.Close(conn, dialErr)
|
||||
}
|
||||
state.err = err
|
||||
p.access.Unlock()
|
||||
close(state.done)
|
||||
return
|
||||
err = dialErr
|
||||
}
|
||||
|
||||
var closeErr error
|
||||
|
||||
p.access.Lock()
|
||||
if current.connecting == state {
|
||||
current.connecting = nil
|
||||
}
|
||||
if p.closed {
|
||||
current.connecting = nil
|
||||
if err != nil {
|
||||
state.err = err
|
||||
} else if p.closed {
|
||||
closeErr = net.ErrClosed
|
||||
state.err = closeErr
|
||||
} else if p.state != current {
|
||||
closeErr = p.closeCause(current)
|
||||
closeErr = net.ErrClosed
|
||||
state.err = closeErr
|
||||
} else {
|
||||
sharedCtx, sharedCancel := context.WithCancelCause(current.ctx)
|
||||
current.shared = conn
|
||||
current.hasShared = true
|
||||
current.sharedClaimed = false
|
||||
current.sharedCtx = sharedCtx
|
||||
current.sharedCancel = sharedCancel
|
||||
current.all[conn] = struct{}{}
|
||||
@@ -439,9 +413,8 @@ func (p *ConnPool[T]) collectShared(current *connPoolState[T], state *connPoolCo
|
||||
return zero, nil, false, false, net.ErrClosed
|
||||
}
|
||||
if p.state != current {
|
||||
cause := p.closeCause(current)
|
||||
p.access.Unlock()
|
||||
return zero, nil, false, false, cause
|
||||
return zero, nil, false, false, net.ErrClosed
|
||||
}
|
||||
if !current.hasShared {
|
||||
p.access.Unlock()
|
||||
@@ -450,16 +423,7 @@ func (p *ConnPool[T]) collectShared(current *connPoolState[T], state *connPoolCo
|
||||
|
||||
conn := current.shared
|
||||
if !p.options.IsAlive(conn) {
|
||||
delete(current.all, conn)
|
||||
var zeroConn T
|
||||
current.shared = zeroConn
|
||||
current.hasShared = false
|
||||
current.sharedClaimed = false
|
||||
current.sharedCtx = nil
|
||||
if current.sharedCancel != nil {
|
||||
current.sharedCancel(net.ErrClosed)
|
||||
current.sharedCancel = nil
|
||||
}
|
||||
p.removeConn(current, conn, net.ErrClosed)
|
||||
p.access.Unlock()
|
||||
p.options.Close(conn, net.ErrClosed)
|
||||
return zero, nil, false, true, nil
|
||||
@@ -472,76 +436,9 @@ func (p *ConnPool[T]) collectShared(current *connPoolState[T], state *connPoolCo
|
||||
return conn, connCtx, created, false, nil
|
||||
}
|
||||
|
||||
func (p *ConnPool[T]) dial(ctx context.Context, current *connPoolState[T], dial func(context.Context) (T, error)) (T, error) {
|
||||
var zero T
|
||||
|
||||
if err := ctx.Err(); err != nil {
|
||||
return zero, err
|
||||
}
|
||||
if cause := context.Cause(current.ctx); cause != nil {
|
||||
return zero, cause
|
||||
}
|
||||
|
||||
dialCtx, cancel := context.WithCancelCause(current.ctx)
|
||||
var (
|
||||
stateAccess sync.Mutex
|
||||
dialComplete bool
|
||||
)
|
||||
stopCancel := context.AfterFunc(ctx, func() {
|
||||
stateAccess.Lock()
|
||||
if !dialComplete {
|
||||
cancel(context.Cause(ctx))
|
||||
}
|
||||
stateAccess.Unlock()
|
||||
})
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
stateAccess.Lock()
|
||||
dialComplete = true
|
||||
stateAccess.Unlock()
|
||||
stopCancel()
|
||||
cancel(context.Cause(ctx))
|
||||
return zero, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
conn, err := dial(connPoolDialContext{
|
||||
Context: dialCtx,
|
||||
parent: ctx,
|
||||
})
|
||||
stateAccess.Lock()
|
||||
dialComplete = true
|
||||
stateAccess.Unlock()
|
||||
stopCancel()
|
||||
if err != nil {
|
||||
if cause := context.Cause(dialCtx); cause != nil {
|
||||
return zero, cause
|
||||
}
|
||||
return zero, err
|
||||
}
|
||||
if cause := context.Cause(dialCtx); cause != nil {
|
||||
p.options.Close(conn, cause)
|
||||
return zero, cause
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (p *ConnPool[T]) closeState(state *connPoolState[T], cause error) {
|
||||
if state == nil {
|
||||
return
|
||||
}
|
||||
|
||||
state.cancel(cause)
|
||||
if state.sharedCancel != nil {
|
||||
state.sharedCancel(cause)
|
||||
}
|
||||
for conn := range state.all {
|
||||
p.options.Close(conn, cause)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ConnPool[T]) closeCause(state *connPoolState[T]) error {
|
||||
_ = state
|
||||
return net.ErrClosed
|
||||
}
|
||||
|
||||
@@ -222,7 +222,7 @@ func (t *Transport) fetchServers0(ctx context.Context, iface *control.Interface)
|
||||
packetConn net.PacketConn
|
||||
err error
|
||||
)
|
||||
for i := 0; i < 5; i++ {
|
||||
for range 5 {
|
||||
packetConn, err = listener.ListenPacket(t.ctx, "udp4", listenAddr)
|
||||
if err == nil || !errors.Is(err, syscall.EADDRINUSE) {
|
||||
break
|
||||
|
||||
@@ -72,7 +72,7 @@ func (t *Transport) tryOneName(ctx context.Context, servers []M.Socksaddr, fqdn
|
||||
sLen := len(servers)
|
||||
var lastErr error
|
||||
for i := 0; i < t.attempts; i++ {
|
||||
for j := 0; j < sLen; j++ {
|
||||
for j := range sLen {
|
||||
server := servers[j]
|
||||
question := message.Question[0]
|
||||
question.Name = fqdn
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//go:build !linux
|
||||
|
||||
//nolint:unused
|
||||
package local
|
||||
|
||||
import (
|
||||
|
||||
@@ -82,7 +82,7 @@ func (t *Transport) tryOneName(ctx context.Context, config *dnsConfig, fqdn stri
|
||||
sLen := uint32(len(config.servers))
|
||||
var lastErr error
|
||||
for i := 0; i < config.attempts; i++ {
|
||||
for j := uint32(0); j < sLen; j++ {
|
||||
for j := range sLen {
|
||||
server := config.servers[(serverOffset+j)%sLen]
|
||||
question := message.Question[0]
|
||||
question.Name = fqdn
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//nolint:unused
|
||||
package local
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//nolint:unused
|
||||
package local
|
||||
|
||||
import (
|
||||
|
||||
@@ -100,7 +100,7 @@ func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg,
|
||||
err error
|
||||
response *mDNS.Msg
|
||||
)
|
||||
for i := 0; i < 2; i++ {
|
||||
for range 2 {
|
||||
conn, _, err = t.connection.Acquire(ctx, func(ctx context.Context) (*quic.Conn, error) {
|
||||
rawConn, err := t.dialer.DialContext(ctx, N.NetworkUDP, t.serverAddr)
|
||||
if err != nil {
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
@@ -13,6 +15,7 @@ import (
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio/deadline"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
@@ -71,6 +74,7 @@ func (t *TCPTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.M
|
||||
return nil, E.Cause(err, "dial TCP connection")
|
||||
}
|
||||
defer conn.Close()
|
||||
defer setConnDeadline(ctx, conn, deadline.NeedAdditionalReadDeadline(conn))()
|
||||
err = WriteMessage(conn, 0, message)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "write request")
|
||||
@@ -82,6 +86,20 @@ func (t *TCPTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.M
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func setConnDeadline(ctx context.Context, conn net.Conn, needClose bool) func() {
|
||||
if needClose {
|
||||
stop := context.AfterFunc(ctx, func() {
|
||||
conn.Close()
|
||||
})
|
||||
return func() { stop() }
|
||||
}
|
||||
if d, ok := ctx.Deadline(); ok {
|
||||
conn.SetDeadline(d)
|
||||
return func() { conn.SetDeadline(time.Time{}) }
|
||||
}
|
||||
return func() {}
|
||||
}
|
||||
|
||||
func ReadMessage(reader io.Reader) (*mDNS.Msg, error) {
|
||||
var responseLen uint16
|
||||
err := binary.Read(reader, binary.BigEndian, &responseLen)
|
||||
|
||||
@@ -2,7 +2,6 @@ package transport
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
@@ -12,6 +11,7 @@ import (
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/bufio/deadline"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
@@ -22,6 +22,8 @@ import (
|
||||
|
||||
var _ adapter.DNSTransport = (*TLSTransport)(nil)
|
||||
|
||||
const tlsDNSMaxInflight = 8
|
||||
|
||||
func RegisterTLS(registry *dns.TransportRegistry) {
|
||||
dns.RegisterTransport[option.RemoteTLSDNSServerOptions](registry, C.DNSTypeTLS, NewTLS)
|
||||
}
|
||||
@@ -38,7 +40,8 @@ type TLSTransport struct {
|
||||
|
||||
type tlsDNSConn struct {
|
||||
tls.Conn
|
||||
queryId uint16
|
||||
queryId uint16
|
||||
needDeadlineClose bool
|
||||
}
|
||||
|
||||
func NewTLS(ctx context.Context, logger log.ContextLogger, tag string, options option.RemoteTLSDNSServerOptions) (adapter.DNSTransport, error) {
|
||||
@@ -70,7 +73,8 @@ func NewTLSRaw(logger logger.ContextLogger, adapter dns.TransportAdapter, dialer
|
||||
serverAddr: serverAddr,
|
||||
tlsConfig: tlsConfig,
|
||||
connections: NewConnPool(ConnPoolOptions[*tlsDNSConn]{
|
||||
Mode: ConnPoolOrdered,
|
||||
Mode: ConnPoolOrdered,
|
||||
MaxInflight: tlsDNSMaxInflight,
|
||||
IsAlive: func(conn *tlsDNSConn) bool {
|
||||
return conn != nil
|
||||
},
|
||||
@@ -98,13 +102,16 @@ func (t *TLSTransport) Reset() {
|
||||
|
||||
func (t *TLSTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
var lastErr error
|
||||
for attempt := 0; attempt < 2; attempt++ {
|
||||
for range 2 {
|
||||
conn, created, err := t.connections.Acquire(ctx, func(ctx context.Context) (*tlsDNSConn, error) {
|
||||
tlsConn, err := t.dialer.DialTLSContext(ctx, t.serverAddr)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "dial TLS connection")
|
||||
}
|
||||
return &tlsDNSConn{Conn: tlsConn}, nil
|
||||
return &tlsDNSConn{
|
||||
Conn: tlsConn,
|
||||
needDeadlineClose: deadline.NeedAdditionalReadDeadline(tlsConn.NetConn()),
|
||||
}, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -125,9 +132,7 @@ func (t *TLSTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.M
|
||||
}
|
||||
|
||||
func (t *TLSTransport) exchange(ctx context.Context, message *mDNS.Msg, conn *tlsDNSConn) (*mDNS.Msg, error) {
|
||||
if deadline, ok := ctx.Deadline(); ok {
|
||||
conn.SetDeadline(deadline)
|
||||
}
|
||||
defer setConnDeadline(ctx, conn, conn.needDeadlineClose)()
|
||||
conn.queryId++
|
||||
err := WriteMessage(conn, conn.queryId, message)
|
||||
if err != nil {
|
||||
@@ -137,6 +142,5 @@ func (t *TLSTransport) exchange(ctx context.Context, message *mDNS.Msg, conn *tl
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "read response")
|
||||
}
|
||||
conn.SetDeadline(time.Time{})
|
||||
return response, nil
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio/deadline"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
@@ -130,6 +131,7 @@ func (t *UDPTransport) exchangeTCP(ctx context.Context, message *mDNS.Msg) (*mDN
|
||||
return nil, E.Cause(err, "dial TCP connection")
|
||||
}
|
||||
defer conn.Close()
|
||||
defer setConnDeadline(ctx, conn, deadline.NeedAdditionalReadDeadline(conn))()
|
||||
err = WriteMessage(conn, message.Id, message)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "write request")
|
||||
|
||||
Reference in New Issue
Block a user