Update sing-box core, refactor MASQUE, update XHTTP

This commit is contained in:
Shtorm
2026-05-29 01:31:57 +03:00
parent 1cb7950810
commit b953954b60
111 changed files with 1291 additions and 1660 deletions

View File

@@ -13,6 +13,7 @@ import (
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/dialer"
"github.com/sagernet/sing-box/common/sniff"
tf "github.com/sagernet/sing-box/common/tlsfragment"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing/common"
@@ -128,11 +129,12 @@ func (m *ConnectionManager) NewConnection(ctx context.Context, this N.Dialer, co
if metadata.TLSFragment || metadata.TLSRecordFragment {
remoteConn = tf.NewConn(remoteConn, ctx, metadata.TLSFragment, metadata.TLSRecordFragment, metadata.TLSFragmentFallbackDelay)
}
serverFirst := sniff.Skip(&metadata)
var done atomic.Bool
if m.kickWriteHandshake(ctx, conn, remoteConn, false, &done, onClose) {
if m.kickWriteHandshake(ctx, conn, remoteConn, serverFirst, false, &done, onClose) {
return
}
if m.kickWriteHandshake(ctx, remoteConn, conn, true, &done, onClose) {
if m.kickWriteHandshake(ctx, remoteConn, conn, serverFirst, true, &done, onClose) {
return
}
go m.connectionCopy(ctx, conn, remoteConn, false, &done, onClose)
@@ -293,37 +295,43 @@ func (m *ConnectionManager) connectionCopy(ctx context.Context, source net.Conn,
}
}
func (m *ConnectionManager) kickWriteHandshake(ctx context.Context, source net.Conn, destination net.Conn, direction bool, done *atomic.Bool, onClose N.CloseHandlerFunc) bool {
func (m *ConnectionManager) kickWriteHandshake(ctx context.Context, source net.Conn, destination net.Conn, serverFirst bool, direction bool, done *atomic.Bool, onClose N.CloseHandlerFunc) bool {
if !N.NeedHandshakeForWrite(destination) {
return false
}
var (
cachedBuffer *buf.Buffer
err error
wrotePayload bool
)
sourceReader, readCounters := N.UnwrapCountReader(source, nil)
destinationWriter, writeCounters := N.UnwrapCountWriter(destination, nil)
if cachedReader, ok := sourceReader.(N.CachedReader); ok {
cachedBuffer = cachedReader.ReadCached()
}
var err error
if cachedBuffer != nil {
wrotePayload = true
dataLen := cachedBuffer.Len()
_, err = destinationWriter.Write(cachedBuffer.Bytes())
cachedBuffer.Release()
if err == nil {
for _, counter := range readCounters {
counter(int64(dataLen))
}
for _, counter := range writeCounters {
counter(int64(dataLen))
}
}
} else {
if serverFirst {
_ = destination.SetWriteDeadline(time.Now().Add(C.ReadPayloadTimeout))
_, err = destinationWriter.Write(nil)
_, err = destination.Write(nil)
_ = destination.SetWriteDeadline(time.Time{})
} else {
var cachedBuffer *buf.Buffer
sourceReader, readCounters := N.UnwrapCountReader(source, nil)
destinationWriter, writeCounters := N.UnwrapCountWriter(destination, nil)
if cachedReader, ok := sourceReader.(N.CachedReader); ok {
cachedBuffer = cachedReader.ReadCached()
}
if cachedBuffer != nil {
wrotePayload = true
dataLen := cachedBuffer.Len()
_, err = destinationWriter.Write(cachedBuffer.Bytes())
cachedBuffer.Release()
if err == nil {
for _, counter := range readCounters {
counter(int64(dataLen))
}
for _, counter := range writeCounters {
counter(int64(dataLen))
}
}
} else {
_ = destination.SetWriteDeadline(time.Now().Add(C.ReadPayloadTimeout))
_, err = destinationWriter.Write(nil)
_ = destination.SetWriteDeadline(time.Time{})
}
}
if err == nil {
return false

View File

@@ -3,6 +3,7 @@ package route
import (
"context"
"net/netip"
"slices"
"strings"
"github.com/sagernet/sing-box/adapter"
@@ -78,10 +79,8 @@ func (r *Router) isLocalSource(source netip.Addr) bool {
return true
}
if r.platformInterface != nil {
for _, addr := range r.platformInterface.MyInterfaceAddress() {
if addr == source {
return true
}
if slices.Contains(r.platformInterface.MyInterfaceAddress(), source) {
return true
}
}
for _, netInterface := range r.network.InterfaceFinder().Interfaces() {

View File

@@ -31,7 +31,7 @@ import (
// Deprecated: use RouteConnectionEx instead.
func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
done := make(chan interface{})
done := make(chan any)
err := r.routeConnection(ctx, conn, metadata, N.OnceClose(func(it error) {
close(done)
}))
@@ -160,7 +160,7 @@ func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata ad
}
func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
done := make(chan interface{})
done := make(chan any)
err := r.routePacketConnection(ctx, conn, metadata, N.OnceClose(func(it error) {
close(done)
}))

View File

@@ -42,11 +42,11 @@ func (s ruleMatchStateSet) combine(other ruleMatchStateSet) ruleMatchStateSet {
return 0
}
var combined ruleMatchStateSet
for left := ruleMatchState(0); left < 16; left++ {
for left := range ruleMatchState(16) {
if !s.contains(left) {
continue
}
for right := ruleMatchState(0); right < 16; right++ {
for right := range ruleMatchState(16) {
if !other.contains(right) {
continue
}
@@ -61,7 +61,7 @@ func (s ruleMatchStateSet) withBase(base ruleMatchState) ruleMatchStateSet {
return 0
}
var withBase ruleMatchStateSet
for state := ruleMatchState(0); state < 16; state++ {
for state := range ruleMatchState(16) {
if !s.contains(state) {
continue
}
@@ -72,7 +72,7 @@ func (s ruleMatchStateSet) withBase(base ruleMatchState) ruleMatchStateSet {
func (s ruleMatchStateSet) filter(allowed func(ruleMatchState) bool) ruleMatchStateSet {
var filtered ruleMatchStateSet
for state := ruleMatchState(0); state < 16; state++ {
for state := range ruleMatchState(16) {
if !s.contains(state) {
continue
}
@@ -91,10 +91,6 @@ type ruleStateMatcherWithBase interface {
matchStatesWithBase(metadata *adapter.InboundContext, base ruleMatchState) ruleMatchStateSet
}
func matchHeadlessRuleStates(rule adapter.HeadlessRule, metadata *adapter.InboundContext) ruleMatchStateSet {
return matchHeadlessRuleStatesWithBase(rule, metadata, 0)
}
func matchHeadlessRuleStatesWithBase(rule adapter.HeadlessRule, metadata *adapter.InboundContext, base ruleMatchState) ruleMatchStateSet {
if matcher, isStateMatcher := rule.(ruleStateMatcherWithBase); isStateMatcher {
return matcher.matchStatesWithBase(metadata, base)
@@ -108,10 +104,6 @@ func matchHeadlessRuleStatesWithBase(rule adapter.HeadlessRule, metadata *adapte
return 0
}
func matchRuleItemStates(item RuleItem, metadata *adapter.InboundContext) ruleMatchStateSet {
return matchRuleItemStatesWithBase(item, metadata, 0)
}
func matchRuleItemStatesWithBase(item RuleItem, metadata *adapter.InboundContext, base ruleMatchState) ruleMatchStateSet {
if matcher, isStateMatcher := item.(ruleStateMatcherWithBase); isStateMatcher {
return matcher.matchStatesWithBase(metadata, base)

View File

@@ -141,7 +141,6 @@ func TestAbstractLogicalRule_And_WithRuleSetInvert(t *testing.T) {
},
}
for _, testCase := range testCases {
testCase := testCase
t.Run(testCase.name, func(t *testing.T) {
t.Parallel()
logicalRule := &abstractLogicalRule{

View File

@@ -2,6 +2,7 @@ package rule
import (
"net/netip"
"slices"
"strings"
"github.com/sagernet/sing-box/adapter"
@@ -80,12 +81,7 @@ func (r *IPCIDRItem) Match(metadata *adapter.InboundContext) bool {
return r.ipSet.Contains(metadata.Destination.Addr)
}
if len(metadata.DestinationAddresses) > 0 {
for _, address := range metadata.DestinationAddresses {
if r.ipSet.Contains(address) {
return true
}
}
return false
return slices.ContainsFunc(metadata.DestinationAddresses, r.ipSet.Contains)
}
return metadata.IPCIDRAcceptEmpty
}

View File

@@ -1,6 +1,7 @@
package rule
import (
"slices"
"strings"
"github.com/sagernet/sing-box/adapter"
@@ -16,15 +17,11 @@ type DomainItem struct {
}
func NewDomainItem(domains []string, domainSuffixes []string) (*DomainItem, error) {
for _, domainItem := range domains {
if domainItem == "" {
return nil, E.New("domain: empty item is not allowed")
}
if slices.Contains(domains, "") {
return nil, E.New("domain: empty item is not allowed")
}
for _, domainSuffixItem := range domainSuffixes {
if domainSuffixItem == "" {
return nil, E.New("domain_suffix: empty item is not allowed")
}
if slices.Contains(domainSuffixes, "") {
return nil, E.New("domain_suffix: empty item is not allowed")
}
var description string
if dLen := len(domains); dLen > 0 {

View File

@@ -57,7 +57,6 @@ func TestRouteRuleSetMergeDestinationAddressGroup(t *testing.T) {
},
}
for _, testCase := range testCases {
testCase := testCase
t.Run(testCase.name, func(t *testing.T) {
t.Parallel()
ruleSet := newLocalRuleSetForTest("merge-destination", testCase.inner)
@@ -223,7 +222,6 @@ func TestRouteRuleSetOuterGroupedStateMergesIntoSameGroup(t *testing.T) {
},
}
for _, testCase := range testCases {
testCase := testCase
t.Run(testCase.name, func(t *testing.T) {
t.Parallel()
ruleSet := newLocalRuleSetForTest("outer-merge-"+testCase.name, headlessDefaultRule(t, func(rule *abstractDefaultRule) {
@@ -652,7 +650,6 @@ func TestDNSInvertAddressLimitPreLookupRegression(t *testing.T) {
},
}
for _, testCase := range testCases {
testCase := testCase
t.Run(testCase.name, func(t *testing.T) {
t.Parallel()
rule := dnsRuleForTest(func(rule *abstractDefaultRule) {