diff --git a/.github/update.log b/.github/update.log index 3cee21c6be..da34d1f4d9 100644 --- a/.github/update.log +++ b/.github/update.log @@ -1089,3 +1089,4 @@ Update On Sun Aug 10 20:40:02 CEST 2025 Update On Mon Aug 11 20:43:53 CEST 2025 Update On Tue Aug 12 20:40:42 CEST 2025 Update On Wed Aug 13 20:43:46 CEST 2025 +Update On Thu Aug 14 20:40:52 CEST 2025 diff --git a/clash-meta/common/atomic/value.go b/clash-meta/common/atomic/value.go index e63d231096..6480ee4766 100644 --- a/clash-meta/common/atomic/value.go +++ b/clash-meta/common/atomic/value.go @@ -5,58 +5,50 @@ import ( "sync/atomic" ) -func DefaultValue[T any]() T { - var defaultValue T - return defaultValue -} - type TypedValue[T any] struct { - _ noCopy - value atomic.Value + value atomic.Pointer[T] } -// tValue is a struct with determined type to resolve atomic.Value usages with interface types -// https://github.com/golang/go/issues/22550 -// -// The intention to have an atomic value store for errors. However, running this code panics: -// panic: sync/atomic: store of inconsistently typed value into Value -// This is because atomic.Value requires that the underlying concrete type be the same (which is a reasonable expectation for its implementation). -// When going through the atomic.Value.Store method call, the fact that both these are of the error interface is lost. -type tValue[T any] struct { - value T +func (t *TypedValue[T]) Load() (v T) { + v, _ = t.LoadOk() + return } -func (t *TypedValue[T]) Load() T { - value, _ := t.LoadOk() - return value -} - -func (t *TypedValue[T]) LoadOk() (_ T, ok bool) { +func (t *TypedValue[T]) LoadOk() (v T, ok bool) { value := t.value.Load() if value == nil { - return DefaultValue[T](), false + return } - return value.(tValue[T]).value, true + return *value, true } func (t *TypedValue[T]) Store(value T) { - t.value.Store(tValue[T]{value}) + t.value.Store(&value) } -func (t *TypedValue[T]) Swap(new T) T { - old := t.value.Swap(tValue[T]{new}) +func (t *TypedValue[T]) Swap(new T) (v T) { + old := t.value.Swap(&new) if old == nil { - return DefaultValue[T]() + return } - return old.(tValue[T]).value + return *old } func (t *TypedValue[T]) CompareAndSwap(old, new T) bool { - return t.value.CompareAndSwap(tValue[T]{old}, tValue[T]{new}) || - // In the edge-case where [atomic.Value.Store] is uninitialized - // and trying to compare with the zero value of T, - // then compare-and-swap with the nil any value. - (any(old) == any(DefaultValue[T]()) && t.value.CompareAndSwap(any(nil), tValue[T]{new})) + for { + currentP := t.value.Load() + var currentValue T + if currentP != nil { + currentValue = *currentP + } + // Compare old and current via runtime equality check. + if any(currentValue) != any(old) { + return false + } + if t.value.CompareAndSwap(currentP, &new) { + return true + } + } } func (t *TypedValue[T]) MarshalJSON() ([]byte, error) { @@ -89,9 +81,3 @@ func NewTypedValue[T any](t T) (v TypedValue[T]) { v.Store(t) return } - -type noCopy struct{} - -// Lock is a no-op used by -copylocks checker from `go vet`. -func (*noCopy) Lock() {} -func (*noCopy) Unlock() {} diff --git a/clash-meta/common/atomic/value_test.go b/clash-meta/common/atomic/value_test.go index 881b3e8d8e..1697309d46 100644 --- a/clash-meta/common/atomic/value_test.go +++ b/clash-meta/common/atomic/value_test.go @@ -7,20 +7,6 @@ import ( ) func TestTypedValue(t *testing.T) { - { - // Always wrapping should not allocate for simple values - // because tValue[T] has the same memory layout as T. - var v TypedValue[bool] - bools := []bool{true, false} - if n := int(testing.AllocsPerRun(1000, func() { - for _, b := range bools { - v.Store(b) - } - })); n != 0 { - t.Errorf("AllocsPerRun = %d, want 0", n) - } - } - { var v TypedValue[int] got, gotOk := v.LoadOk() @@ -58,20 +44,126 @@ func TestTypedValue(t *testing.T) { } } + { + e1, e2, e3 := io.EOF, &os.PathError{}, &os.PathError{} + var v TypedValue[error] + if v.CompareAndSwap(e1, e2) != false { + t.Fatalf("CompareAndSwap = true, want false") + } + if value := v.Load(); value != nil { + t.Fatalf("Load = (%v), want (%v)", value, nil) + } + if v.CompareAndSwap(nil, e1) != true { + t.Fatalf("CompareAndSwap = false, want true") + } + if value := v.Load(); value != e1 { + t.Fatalf("Load = (%v), want (%v)", value, e1) + } + if v.CompareAndSwap(e2, e3) != false { + t.Fatalf("CompareAndSwap = true, want false") + } + if value := v.Load(); value != e1 { + t.Fatalf("Load = (%v), want (%v)", value, e1) + } + if v.CompareAndSwap(e1, e2) != true { + t.Fatalf("CompareAndSwap = false, want true") + } + if value := v.Load(); value != e2 { + t.Fatalf("Load = (%v), want (%v)", value, e2) + } + if v.CompareAndSwap(e3, e2) != false { + t.Fatalf("CompareAndSwap = true, want false") + } + if value := v.Load(); value != e2 { + t.Fatalf("Load = (%v), want (%v)", value, e2) + } + if v.CompareAndSwap(nil, e3) != false { + t.Fatalf("CompareAndSwap = true, want false") + } + if value := v.Load(); value != e2 { + t.Fatalf("Load = (%v), want (%v)", value, e2) + } + } + { c1, c2, c3 := make(chan struct{}), make(chan struct{}), make(chan struct{}) var v TypedValue[chan struct{}] if v.CompareAndSwap(c1, c2) != false { t.Fatalf("CompareAndSwap = true, want false") } + if value := v.Load(); value != nil { + t.Fatalf("Load = (%v), want (%v)", value, nil) + } if v.CompareAndSwap(nil, c1) != true { t.Fatalf("CompareAndSwap = false, want true") } + if value := v.Load(); value != c1 { + t.Fatalf("Load = (%v), want (%v)", value, c1) + } if v.CompareAndSwap(c2, c3) != false { t.Fatalf("CompareAndSwap = true, want false") } + if value := v.Load(); value != c1 { + t.Fatalf("Load = (%v), want (%v)", value, c1) + } if v.CompareAndSwap(c1, c2) != true { t.Fatalf("CompareAndSwap = false, want true") } + if value := v.Load(); value != c2 { + t.Fatalf("Load = (%v), want (%v)", value, c2) + } + if v.CompareAndSwap(c3, c2) != false { + t.Fatalf("CompareAndSwap = true, want false") + } + if value := v.Load(); value != c2 { + t.Fatalf("Load = (%v), want (%v)", value, c2) + } + if v.CompareAndSwap(nil, c3) != false { + t.Fatalf("CompareAndSwap = true, want false") + } + if value := v.Load(); value != c2 { + t.Fatalf("Load = (%v), want (%v)", value, c2) + } + } + + { + c1, c2, c3 := &io.LimitedReader{}, &io.SectionReader{}, &io.SectionReader{} + var v TypedValue[io.Reader] + if v.CompareAndSwap(c1, c2) != false { + t.Fatalf("CompareAndSwap = true, want false") + } + if value := v.Load(); value != nil { + t.Fatalf("Load = (%v), want (%v)", value, nil) + } + if v.CompareAndSwap(nil, c1) != true { + t.Fatalf("CompareAndSwap = false, want true") + } + if value := v.Load(); value != c1 { + t.Fatalf("Load = (%v), want (%v)", value, c1) + } + if v.CompareAndSwap(c2, c3) != false { + t.Fatalf("CompareAndSwap = true, want false") + } + if value := v.Load(); value != c1 { + t.Fatalf("Load = (%v), want (%v)", value, c1) + } + if v.CompareAndSwap(c1, c2) != true { + t.Fatalf("CompareAndSwap = false, want true") + } + if value := v.Load(); value != c2 { + t.Fatalf("Load = (%v), want (%v)", value, c2) + } + if v.CompareAndSwap(c3, c2) != false { + t.Fatalf("CompareAndSwap = true, want false") + } + if value := v.Load(); value != c2 { + t.Fatalf("Load = (%v), want (%v)", value, c2) + } + if v.CompareAndSwap(nil, c3) != false { + t.Fatalf("CompareAndSwap = true, want false") + } + if value := v.Load(); value != c2 { + t.Fatalf("Load = (%v), want (%v)", value, c2) + } } } diff --git a/clash-meta/component/keepalive/tcp_keepalive.go b/clash-meta/component/keepalive/tcp_keepalive.go index ddb853e916..fee06a94e1 100644 --- a/clash-meta/component/keepalive/tcp_keepalive.go +++ b/clash-meta/component/keepalive/tcp_keepalive.go @@ -10,27 +10,27 @@ import ( ) var ( - keepAliveIdle = atomic.NewTypedValue[time.Duration](0 * time.Second) - keepAliveInterval = atomic.NewTypedValue[time.Duration](0 * time.Second) + keepAliveIdle = atomic.NewInt64(0) + keepAliveInterval = atomic.NewInt64(0) disableKeepAlive = atomic.NewBool(false) SetDisableKeepAliveCallback = utils.NewCallback[bool]() ) func SetKeepAliveIdle(t time.Duration) { - keepAliveIdle.Store(t) + keepAliveIdle.Store(int64(t)) } func SetKeepAliveInterval(t time.Duration) { - keepAliveInterval.Store(t) + keepAliveInterval.Store(int64(t)) } func KeepAliveIdle() time.Duration { - return keepAliveIdle.Load() + return time.Duration(keepAliveIdle.Load()) } func KeepAliveInterval() time.Duration { - return keepAliveInterval.Load() + return time.Duration(keepAliveInterval.Load()) } func SetDisableKeepAlive(disable bool) { diff --git a/clash-meta/go.mod b/clash-meta/go.mod index c218528072..d28ac4e8b6 100644 --- a/clash-meta/go.mod +++ b/clash-meta/go.mod @@ -3,7 +3,6 @@ module github.com/metacubex/mihomo go 1.20 require ( - github.com/3andne/restls-client-go v0.1.6 github.com/bahlo/generic-list-go v0.2.0 github.com/coreos/go-iptables v0.8.0 github.com/dlclark/regexp2 v1.11.5 @@ -19,18 +18,20 @@ require ( github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab github.com/metacubex/bart v0.20.5 github.com/metacubex/bbolt v0.0.0-20250725135710-010dbbbb7a5b + github.com/metacubex/blake3 v0.1.0 github.com/metacubex/chacha v0.1.5 github.com/metacubex/fswatch v0.1.1 github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 github.com/metacubex/quic-go v0.54.1-0.20250730114134-a1ae705fe295 github.com/metacubex/randv2 v0.2.0 - github.com/metacubex/sing v0.5.4 + github.com/metacubex/restls-client-go v0.1.7 + github.com/metacubex/sing v0.5.5 github.com/metacubex/sing-mux v0.3.3-0.20250813083925-d7c9aeaeeaac github.com/metacubex/sing-quic v0.0.0-20250718154553-1b193bec4cbb - github.com/metacubex/sing-shadowsocks v0.2.11 - github.com/metacubex/sing-shadowsocks2 v0.2.5 + github.com/metacubex/sing-shadowsocks v0.2.12 + github.com/metacubex/sing-shadowsocks2 v0.2.6 github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 - github.com/metacubex/sing-tun v0.4.7-0.20250801130030-308b828865ae + github.com/metacubex/sing-tun v0.4.7 github.com/metacubex/sing-vmess v0.2.4-0.20250731011226-ea28d589924d github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f github.com/metacubex/smux v0.0.0-20250503055512-501391591dee @@ -59,7 +60,6 @@ require ( golang.org/x/sys v0.30.0 // lastest version compatible with golang1.20 google.golang.org/protobuf v1.34.2 // lastest version compatible with golang1.20 gopkg.in/yaml.v3 v3.0.1 - lukechampine.com/blake3 v1.3.0 // lastest version compatible with golang1.20 ) require ( @@ -85,11 +85,11 @@ require ( github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/hashicorp/yamux v0.1.2 // indirect github.com/josharian/native v1.1.0 // indirect - github.com/klauspost/cpuid/v2 v2.2.9 // indirect github.com/kr/text v0.2.0 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mdlayher/socket v0.4.1 // indirect + github.com/metacubex/ascon v0.1.0 // indirect github.com/metacubex/gvisor v0.0.0-20250324165734-5857f47bd43b // indirect github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793 // indirect github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect diff --git a/clash-meta/go.sum b/clash-meta/go.sum index 4acd1b8706..374463a99a 100644 --- a/clash-meta/go.sum +++ b/clash-meta/go.sum @@ -1,5 +1,3 @@ -github.com/3andne/restls-client-go v0.1.6 h1:tRx/YilqW7iHpgmEL4E1D8dAsuB0tFF3uvncS+B6I08= -github.com/3andne/restls-client-go v0.1.6/go.mod h1:iEdTZNt9kzPIxjIGSMScUFSBrUH6bFRNg0BWlP4orEY= github.com/RyuaNerin/go-krypto v1.3.0 h1:smavTzSMAx8iuVlGb4pEwl9MD2qicqMzuXR2QWp2/Pg= github.com/RyuaNerin/go-krypto v1.3.0/go.mod h1:9R9TU936laAIqAmjcHo/LsaXYOZlymudOAxjaBf62UM= github.com/RyuaNerin/testingutil v0.1.0 h1:IYT6JL57RV3U2ml3dLHZsVtPOP6yNK7WUVdzzlpNrss= @@ -81,8 +79,6 @@ github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtL github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= -github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= -github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= @@ -98,10 +94,14 @@ github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab h1:Chbw+/31UC14YFNr78pESt5Vowlc62zziw05JCUqoL4= github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab/go.mod h1:xVKK8jC5Sd3hfh7WjmCq+HorehIbrBijaUWmcuKjPcI= +github.com/metacubex/ascon v0.1.0 h1:6ZWxmXYszT1XXtwkf6nxfFhc/OTtQ9R3Vyj1jN32lGM= +github.com/metacubex/ascon v0.1.0/go.mod h1:eV5oim4cVPPdEL8/EYaTZ0iIKARH9pnhAK/fcT5Kacc= github.com/metacubex/bart v0.20.5 h1:XkgLZ17QxfxkqKdGsojoM2Zu01mmHyyQSFzt2/calTM= github.com/metacubex/bart v0.20.5/go.mod h1:DCcyfP4MC+Zy7sLK7XeGuMw+P5K9mIRsYOBgiE8icsI= github.com/metacubex/bbolt v0.0.0-20250725135710-010dbbbb7a5b h1:j7dadXD8I2KTmMt8jg1JcaP1ANL3JEObJPdANKcSYPY= github.com/metacubex/bbolt v0.0.0-20250725135710-010dbbbb7a5b/go.mod h1:+WmP0VJZDkDszvpa83HzfUp6QzARl/IKkMorH4+nODw= +github.com/metacubex/blake3 v0.1.0 h1:KGnjh/56REO7U+cgZA8dnBhxdP7jByrG7hTP+bu6cqY= +github.com/metacubex/blake3 v0.1.0/go.mod h1:CCkLdzFrqf7xmxCdhQFvJsRRV2mwOLDoSPg6vUTB9Uk= github.com/metacubex/chacha v0.1.5 h1:fKWMb/5c7ZrY8Uoqi79PPFxl+qwR7X/q0OrsAubyX2M= github.com/metacubex/chacha v0.1.5/go.mod h1:Djn9bPZxLTXbJFSeyo0/qzEzQI+gUSSzttuzZM75GH8= github.com/metacubex/fswatch v0.1.1 h1:jqU7C/v+g0qc2RUFgmAOPoVvfl2BXXUXEumn6oQuxhU= @@ -116,21 +116,23 @@ github.com/metacubex/quic-go v0.54.1-0.20250730114134-a1ae705fe295 h1:8JVlYuE8uS github.com/metacubex/quic-go v0.54.1-0.20250730114134-a1ae705fe295/go.mod h1:1lktQFtCD17FZliVypbrDHwbsFSsmz2xz2TRXydvB5c= github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs= github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= +github.com/metacubex/restls-client-go v0.1.7 h1:eCwiXCTQb5WJu9IlgYvDBA1OgrINv58dEe7hcN5H15k= +github.com/metacubex/restls-client-go v0.1.7/go.mod h1:BN/U52vPw7j8VTSh2vleD/MnmVKCov84mS5VcjVHH4g= github.com/metacubex/sing v0.5.2/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w= -github.com/metacubex/sing v0.5.4 h1:a4kAOZmF+OXosbzPEcrSc5QD35/ex+MNuZsrcuWskHk= -github.com/metacubex/sing v0.5.4/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w= +github.com/metacubex/sing v0.5.5 h1:m5U8iHvRAUxlme3FZlE/LPIGHjU8oMCUzXWGbQQAC1E= +github.com/metacubex/sing v0.5.5/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w= github.com/metacubex/sing-mux v0.3.3-0.20250813083925-d7c9aeaeeaac h1:wDH/Jh/yqWbzPktqJP+Y1cUG8hchcrzKzUxJiSpnaQs= github.com/metacubex/sing-mux v0.3.3-0.20250813083925-d7c9aeaeeaac/go.mod h1:3rt1soewn0O6j89GCLmwAQFsq257u0jf2zQSPhTL3Bw= github.com/metacubex/sing-quic v0.0.0-20250718154553-1b193bec4cbb h1:U/m3h8lp/j7i8zFgfvScLdZa1/Y8dd74oO7iZaQq80s= github.com/metacubex/sing-quic v0.0.0-20250718154553-1b193bec4cbb/go.mod h1:B60FxaPHjR1SeQB0IiLrgwgvKsaoASfOWdiqhLjmMGA= -github.com/metacubex/sing-shadowsocks v0.2.11 h1:p2NGNOdF95e6XvdDKipLj1FRRqR8dnbfC/7pw2CCTlw= -github.com/metacubex/sing-shadowsocks v0.2.11/go.mod h1:bT1PCTV316zFnlToRMk5zt9HmIQYRBveiT71mplYPfc= -github.com/metacubex/sing-shadowsocks2 v0.2.5 h1:MnPn0hbcDkSJt6TlpI15XImHKK6IqaOwBUGPKyMnJnE= -github.com/metacubex/sing-shadowsocks2 v0.2.5/go.mod h1:Zyh+rAQRyevYfG/COCvDs1c/YMhGqCuknn7QrGmoQIw= +github.com/metacubex/sing-shadowsocks v0.2.12 h1:Wqzo8bYXrK5aWqxu/TjlTnYZzAKtKsaFQBdr6IHFaBE= +github.com/metacubex/sing-shadowsocks v0.2.12/go.mod h1:2e5EIaw0rxKrm1YTRmiMnDulwbGxH9hAFlrwQLQMQkU= +github.com/metacubex/sing-shadowsocks2 v0.2.6 h1:ZR1kYT0f0Vi64iQSS09OdhFfppiNkh7kjgRdMm0SB98= +github.com/metacubex/sing-shadowsocks2 v0.2.6/go.mod h1:vOEbfKC60txi0ca+yUlqEwOGc3Obl6cnSgx9Gf45KjE= github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 h1:gXU+MYPm7Wme3/OAY2FFzVq9d9GxPHOqu5AQfg/ddhI= github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2/go.mod h1:mbfboaXauKJNIHJYxQRa+NJs4JU9NZfkA+I33dS2+9E= -github.com/metacubex/sing-tun v0.4.7-0.20250801130030-308b828865ae h1:hKYGuBaJBLqNmySrYrOGDXXsyxXNaR8omO96QDsRI2s= -github.com/metacubex/sing-tun v0.4.7-0.20250801130030-308b828865ae/go.mod h1:2YywXPWW8Z97kTH7RffOeykKzU+l0aiKlglWV1PAS64= +github.com/metacubex/sing-tun v0.4.7 h1:ZDY/W+1c7PeWWKeKRyUo18fySF/TWjB0i5ui81Ar778= +github.com/metacubex/sing-tun v0.4.7/go.mod h1:xHecZRwBnKWe6zG9amAK9cXf91lF6blgjBqm+VvOrmU= github.com/metacubex/sing-vmess v0.2.4-0.20250731011226-ea28d589924d h1:koSVAQiYqU/qtJ527e+wbsEPvTaM39HW75LSvh04IT0= github.com/metacubex/sing-vmess v0.2.4-0.20250731011226-ea28d589924d/go.mod h1:21R5R1u90uUvBQF0owoooEu96/SAYYD56nDrwm6nFaM= github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f h1:Sr/DYKYofKHKc4GF3qkRGNuj6XA6c0eqPgEDN+VAsYU= @@ -279,5 +281,3 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= -lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= diff --git a/clash-meta/transport/restls/restls.go b/clash-meta/transport/restls/restls.go index 95ac698907..200c9dad83 100644 --- a/clash-meta/transport/restls/restls.go +++ b/clash-meta/transport/restls/restls.go @@ -4,7 +4,7 @@ import ( "context" "net" - tls "github.com/3andne/restls-client-go" + tls "github.com/metacubex/restls-client-go" ) const ( diff --git a/clash-meta/transport/tuic/v4/protocol.go b/clash-meta/transport/tuic/v4/protocol.go index cb04fad595..bea1b2f3bb 100644 --- a/clash-meta/transport/tuic/v4/protocol.go +++ b/clash-meta/transport/tuic/v4/protocol.go @@ -8,8 +8,8 @@ import ( "net/netip" "strconv" + "github.com/metacubex/blake3" "github.com/metacubex/quic-go" - "lukechampine.com/blake3" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/transport/socks5" diff --git a/clash-meta/transport/vless/conn.go b/clash-meta/transport/vless/conn.go index 02224892e2..180a9969d2 100644 --- a/clash-meta/transport/vless/conn.go +++ b/clash-meta/transport/vless/conn.go @@ -105,26 +105,20 @@ func (vc *Conn) sendRequest(p []byte) bool { } } - var buffer *buf.Buffer - if vc.IsXTLSVisionEnabled() { - buffer = buf.New() - defer buffer.Release() - } else { - requestLen := 1 // protocol version - requestLen += 16 // UUID - requestLen += 1 // addons length - requestLen += len(addonsBytes) - requestLen += 1 // command - if !vc.dst.Mux { - requestLen += 2 // port - requestLen += 1 // addr type - requestLen += len(vc.dst.Addr) - } - requestLen += len(p) - - buffer = buf.NewSize(requestLen) - defer buffer.Release() + requestLen := 1 // protocol version + requestLen += 16 // UUID + requestLen += 1 // addons length + requestLen += len(addonsBytes) + requestLen += 1 // command + if !vc.dst.Mux { + requestLen += 2 // port + requestLen += 1 // addr type + requestLen += len(vc.dst.Addr) } + requestLen += len(p) + + buffer := buf.NewSize(requestLen) + defer buffer.Release() buf.Must( buffer.WriteByte(Version), // protocol version @@ -182,10 +176,6 @@ func (vc *Conn) NeedHandshake() bool { return vc.needHandshake } -func (vc *Conn) IsXTLSVisionEnabled() bool { - return vc.addons != nil && vc.addons.Flow == XRV -} - // newConn return a Conn instance func newConn(conn net.Conn, client *Client, dst *DstAddr) (net.Conn, error) { c := &Conn{ diff --git a/clash-meta/transport/vless/encryption/client.go b/clash-meta/transport/vless/encryption/client.go index 3bef4d94eb..b6e2c19dfd 100644 --- a/clash-meta/transport/vless/encryption/client.go +++ b/clash-meta/transport/vless/encryption/client.go @@ -4,11 +4,13 @@ import ( "bytes" "crypto/cipher" "crypto/rand" + "crypto/sha256" "errors" "fmt" "io" "net" "runtime" + "strings" "sync" "time" @@ -36,13 +38,12 @@ func init() { type ClientInstance struct { sync.RWMutex - nfsEKey *mlkem.EncapsulationKey768 - nfsEKeyBytes []byte - xor uint32 - minutes time.Duration - expire time.Time - baseKey []byte - ticket []byte + nfsEKey *mlkem.EncapsulationKey768 + xorKey []byte + minutes time.Duration + expire time.Time + baseKey []byte + ticket []byte } type ClientConn struct { @@ -59,10 +60,17 @@ type ClientConn struct { } func (i *ClientInstance) Init(nfsEKeyBytes []byte, xor uint32, minutes time.Duration) (err error) { + if i.nfsEKey != nil { + err = errors.New("already initialized") + return + } i.nfsEKey, err = mlkem.NewEncapsulationKey768(nfsEKeyBytes) + if err != nil { + return + } if xor > 0 { - i.nfsEKeyBytes = nfsEKeyBytes - i.xor = xor + xorKey := sha256.Sum256(nfsEKeyBytes) + i.xorKey = xorKey[:] } i.minutes = minutes return @@ -72,8 +80,8 @@ func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) { if i.nfsEKey == nil { return nil, errors.New("uninitialized") } - if i.xor > 0 { - conn = NewXorConn(conn, i.nfsEKeyBytes) + if i.xorKey != nil { + conn = NewXorConn(conn, i.xorKey) } c := &ClientConn{Conn: conn} @@ -107,16 +115,16 @@ func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) { if _, err := c.Conn.Write(clientHello); err != nil { return nil, err } - // client can send more padding / NFS AEAD messages if needed + // client can send more paddings / NFS AEAD messages if needed - _, t, l, err := ReadAndDecodeHeader(c.Conn) + _, t, l, err := ReadAndDiscardPaddings(c.Conn) // allow paddings before server hello if err != nil { return nil, err } + if t != 1 { return nil, fmt.Errorf("unexpected type %v, expect random hello", t) } - peerRandomHello := make([]byte, 1088+21) if l != len(peerRandomHello) { return nil, fmt.Errorf("unexpected length %v for random hello", l) @@ -193,27 +201,9 @@ func (c *ClientConn) Read(b []byte) (int, error) { return 0, nil } if c.peerAead == nil { - var t byte - var l int - var err error - if c.instance == nil { // from 1-RTT - for { - if _, t, l, err = ReadAndDecodeHeader(c.Conn); err != nil { - return 0, err - } - if t != 23 { - break - } - if _, err := io.ReadFull(c.Conn, make([]byte, l)); err != nil { - return 0, err - } - } - } else { - h := make([]byte, 5) - if _, err := io.ReadFull(c.Conn, h); err != nil { - return 0, err - } - if t, l, err = DecodeHeader(h); err != nil { + _, t, l, err := ReadAndDiscardPaddings(c.Conn) // allow paddings before random hello + if err != nil { + if c.instance != nil && strings.HasPrefix(err.Error(), "invalid header: ") { // 0-RTT's 0-RTT c.instance.Lock() if bytes.Equal(c.ticket, c.instance.ticket) { c.instance.expire = time.Now() // expired @@ -221,6 +211,7 @@ func (c *ClientConn) Read(b []byte) (int, error) { c.instance.Unlock() return 0, errors.New("new handshake needed") } + return 0, err } if t != 0 { return 0, fmt.Errorf("unexpected type %v, expect server random", t) diff --git a/clash-meta/transport/vless/encryption/common.go b/clash-meta/transport/vless/encryption/common.go index cd4a93c5de..151a48e40a 100644 --- a/clash-meta/transport/vless/encryption/common.go +++ b/clash-meta/transport/vless/encryption/common.go @@ -45,10 +45,10 @@ func DecodeHeader(h []byte) (t byte, l int, err error) { } else if h[0] == 1 && h[1] == 1 && h[2] == 1 { t = 1 } else { - h = nil + l = 0 } - if h == nil || l < 17 || l > 17000 { // TODO: TLSv1.3 max length - err = fmt.Errorf("invalid header: %v", h[:5]) + if l < 17 || l > 17000 { // TODO: TLSv1.3 max length + err = fmt.Errorf("invalid header: %v", h[:5]) // DO NOT CHANGE: relied by client's Read() } return } @@ -62,6 +62,17 @@ func ReadAndDecodeHeader(conn net.Conn) (h []byte, t byte, l int, err error) { return } +func ReadAndDiscardPaddings(conn net.Conn) (h []byte, t byte, l int, err error) { + for { + if h, t, l, err = ReadAndDecodeHeader(conn); err != nil || t != 23 { + return + } + if _, err = io.ReadFull(conn, make([]byte, l)); err != nil { + return + } + } +} + func NewAead(c byte, secret, salt, info []byte) (aead cipher.AEAD) { key := make([]byte, 32) hkdf.New(sha256.New, secret, salt, info).Read(key) diff --git a/clash-meta/transport/vless/encryption/doc.go b/clash-meta/transport/vless/encryption/doc.go index 96976ed920..900866ec05 100644 --- a/clash-meta/transport/vless/encryption/doc.go +++ b/clash-meta/transport/vless/encryption/doc.go @@ -9,4 +9,6 @@ // https://github.com/XTLS/Xray-core/commit/1720be168fa069332c418503d30341fc6e01df7f // https://github.com/XTLS/Xray-core/commit/0fd7691d6b28e05922d7a5a9313d97745a51ea63 // https://github.com/XTLS/Xray-core/commit/09cc92c61d9067e0d65c1cae9124664ecfc78f43 +// https://github.com/XTLS/Xray-core/commit/2807ee432a1fbeb301815647189eacd650b12a8b +// https://github.com/XTLS/Xray-core/commit/bfe4820f2f086daf639b1957eb23dc13c843cad1 package encryption diff --git a/clash-meta/transport/vless/encryption/server.go b/clash-meta/transport/vless/encryption/server.go index 2accabe7ca..f67c9720ce 100644 --- a/clash-meta/transport/vless/encryption/server.go +++ b/clash-meta/transport/vless/encryption/server.go @@ -4,6 +4,7 @@ import ( "bytes" "crypto/cipher" "crypto/rand" + "crypto/sha256" "errors" "fmt" "io" @@ -23,12 +24,11 @@ type ServerSession struct { type ServerInstance struct { sync.RWMutex - nfsDKey *mlkem.DecapsulationKey768 - nfsEKeyBytes []byte - xor uint32 - minutes time.Duration - sessions map[[21]byte]*ServerSession - closed bool + nfsDKey *mlkem.DecapsulationKey768 + xorKey []byte + minutes time.Duration + sessions map[[21]byte]*ServerSession + closed bool } type ServerConn struct { @@ -45,10 +45,17 @@ type ServerConn struct { } func (i *ServerInstance) Init(nfsDKeySeed []byte, xor uint32, minutes time.Duration) (err error) { + if i.nfsDKey != nil { + err = errors.New("already initialized") + return + } i.nfsDKey, err = mlkem.NewDecapsulationKey768(nfsDKeySeed) + if err != nil { + return + } if xor > 0 { - i.nfsEKeyBytes = i.nfsDKey.EncapsulationKey().Bytes() - i.xor = xor + xorKey := sha256.Sum256(i.nfsDKey.EncapsulationKey().Bytes()) + i.xorKey = xorKey[:] } if minutes > 0 { i.minutes = minutes @@ -85,18 +92,15 @@ func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) { if i.nfsDKey == nil { return nil, errors.New("uninitialized") } - if i.xor > 0 { - conn = NewXorConn(conn, i.nfsEKeyBytes) + if i.xorKey != nil { + conn = NewXorConn(conn, i.xorKey) } c := &ServerConn{Conn: conn} - _, t, l, err := ReadAndDecodeHeader(c.Conn) + _, t, l, err := ReadAndDiscardPaddings(c.Conn) // allow paddings before client/ticket hello if err != nil { return nil, err } - if t == 23 { - return nil, errors.New("unexpected data") - } if t == 0 { if i.minutes == 0 { @@ -113,9 +117,13 @@ func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) { s := i.sessions[[21]byte(peerTicketHello)] i.RUnlock() if s == nil { - noise := make([]byte, randBetween(100, 1000)) - rand.Read(noise) - c.Conn.Write(noise) // make client do new handshake + noises := make([]byte, randBetween(100, 1000)) + var err error + for err == nil { + rand.Read(noises) + _, _, err = DecodeHeader(noises) + } + c.Conn.Write(noises) // make client do new handshake return nil, errors.New("expired ticket") } if _, replay := s.randoms.LoadOrStore([32]byte(peerTicketHello[21:]), true); replay { @@ -165,7 +173,7 @@ func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) { if _, err := c.Conn.Write(serverHello); err != nil { return nil, err } - // server can send more padding / PFS AEAD messages if needed + // server can send more paddings / PFS AEAD messages if needed if i.minutes > 0 { i.Lock() @@ -185,20 +193,10 @@ func (c *ServerConn) Read(b []byte) (int, error) { return 0, nil } if c.peerAead == nil { - if c.peerRandom == nil { // from 1-RTT - var t byte - var l int - var err error - for { - if _, t, l, err = ReadAndDecodeHeader(c.Conn); err != nil { - return 0, err - } - if t != 23 { - break - } - if _, err := io.ReadFull(c.Conn, make([]byte, l)); err != nil { - return 0, err - } + if c.peerRandom == nil { // 1-RTT's 0-RTT + _, t, l, err := ReadAndDiscardPaddings(c.Conn) // allow paddings before ticket hello + if err != nil { + return 0, err } if t != 0 { return 0, fmt.Errorf("unexpected type %v, expect ticket hello", t) diff --git a/clash-meta/transport/vless/encryption/xor.go b/clash-meta/transport/vless/encryption/xor.go index 696702bc69..64828eaa26 100644 --- a/clash-meta/transport/vless/encryption/xor.go +++ b/clash-meta/transport/vless/encryption/xor.go @@ -18,11 +18,11 @@ type XorConn struct { } func NewXorConn(conn net.Conn, key []byte) *XorConn { - return &XorConn{Conn: conn, key: key[:16]} + return &XorConn{Conn: conn, key: key} //chacha20.NewUnauthenticatedCipher() } -func (c *XorConn) Write(b []byte) (int, error) { // two records at most +func (c *XorConn) Write(b []byte) (int, error) { // whole one/two records if len(b) == 0 { return 0, nil } @@ -34,10 +34,10 @@ func (c *XorConn) Write(b []byte) (int, error) { // two records at most c.ctr = cipher.NewCTR(block, iv) } t, l, _ := DecodeHeader(b) - if t != 23 { - l += 10 // 5+l+5 - } else { + if t == 23 { // single 23 l = 5 + } else { // 1/0 + 23, or noises only + l += 10 } c.ctr.XORKeyStream(b[:l], b[:l]) // caller MUST discard b if iv != nil { @@ -73,8 +73,8 @@ func (c *XorConn) Read(b []byte) (int, error) { // 5-bytes, data, 5-bytes... return len(b), nil } c.peerCtr.XORKeyStream(b, b) - if c.isHeader { - if t, _, _ := DecodeHeader(b); t == 23 { // always 5-bytes + if c.isHeader { // always 5-bytes + if t, _, _ := DecodeHeader(b); t == 23 { c.skipNext = true } else { c.isHeader = false diff --git a/clash-meta/transport/vless/vision/conn.go b/clash-meta/transport/vless/vision/conn.go index 56684808cb..a0e83f7145 100644 --- a/clash-meta/transport/vless/vision/conn.go +++ b/clash-meta/transport/vless/vision/conn.go @@ -3,7 +3,6 @@ package vision import ( "bytes" "crypto/subtle" - gotls "crypto/tls" "encoding/binary" "errors" "fmt" @@ -12,7 +11,6 @@ import ( "github.com/metacubex/mihomo/common/buf" N "github.com/metacubex/mihomo/common/net" - tlsC "github.com/metacubex/mihomo/component/tls" "github.com/metacubex/mihomo/log" "github.com/gofrs/uuid/v5" @@ -181,17 +179,10 @@ func (vc *Conn) WriteBuffer(buffer *buf.Buffer) (err error) { buffer.Release() return err } - switch underlying := vc.tlsConn.(type) { - case *gotls.Conn: - if underlying.ConnectionState().Version != gotls.VersionTLS13 { - buffer.Release() - return ErrNotTLS13 - } - case *tlsC.UConn: - if underlying.ConnectionState().Version != tlsC.VersionTLS13 { - buffer.Release() - return ErrNotTLS13 - } + err = vc.checkTLSVersion() + if err != nil { + buffer.Release() + return err } vc.tlsConn = nil return nil diff --git a/clash-meta/transport/vless/vision/vision.go b/clash-meta/transport/vless/vision/vision.go index 00d0bf09dd..570a569c34 100644 --- a/clash-meta/transport/vless/vision/vision.go +++ b/clash-meta/transport/vless/vision/vision.go @@ -67,3 +67,21 @@ func NewConn(conn connWithUpstream, userUUID *uuid.UUID) (*Conn, error) { c.rawInput = (*bytes.Buffer)(unsafe.Add(p, r.Offset)) return c, nil } + +func (vc *Conn) checkTLSVersion() error { + switch underlying := vc.tlsConn.(type) { + case *gotls.Conn: + if underlying.ConnectionState().Version != gotls.VersionTLS13 { + return ErrNotTLS13 + } + case *tlsC.Conn: + if underlying.ConnectionState().Version != tlsC.VersionTLS13 { + return ErrNotTLS13 + } + case *tlsC.UConn: + if underlying.ConnectionState().Version != tlsC.VersionTLS13 { + return ErrNotTLS13 + } + } + return nil +} diff --git a/clash-nyanpasu/frontend/nyanpasu/package.json b/clash-nyanpasu/frontend/nyanpasu/package.json index 2995820881..88ace5835d 100644 --- a/clash-nyanpasu/frontend/nyanpasu/package.json +++ b/clash-nyanpasu/frontend/nyanpasu/package.json @@ -56,12 +56,12 @@ "@csstools/normalize.css": "12.1.1", "@emotion/babel-plugin": "11.13.5", "@emotion/react": "11.14.0", - "@iconify/json": "2.2.371", + "@iconify/json": "2.2.373", "@monaco-editor/react": "4.7.0", "@tanstack/react-query": "5.84.2", - "@tanstack/react-router": "1.131.5", - "@tanstack/react-router-devtools": "1.131.5", - "@tanstack/router-plugin": "1.131.5", + "@tanstack/react-router": "1.131.10", + "@tanstack/react-router-devtools": "1.131.10", + "@tanstack/router-plugin": "1.131.11", "@tauri-apps/plugin-clipboard-manager": "2.3.0", "@tauri-apps/plugin-dialog": "2.3.0", "@tauri-apps/plugin-fs": "2.4.0", diff --git a/clash-nyanpasu/manifest/version.json b/clash-nyanpasu/manifest/version.json index 9a935369b3..d88a93c7ae 100644 --- a/clash-nyanpasu/manifest/version.json +++ b/clash-nyanpasu/manifest/version.json @@ -2,7 +2,7 @@ "manifest_version": 1, "latest": { "mihomo": "v1.19.12", - "mihomo_alpha": "alpha-0e9102d", + "mihomo_alpha": "alpha-0408da2", "clash_rs": "v0.8.2", "clash_premium": "2023-09-05-gdcc8d87", "clash_rs_alpha": "0.8.2-alpha+sha.df9f591" @@ -69,5 +69,5 @@ "linux-armv7hf": "clash-armv7-unknown-linux-gnueabihf" } }, - "updated_at": "2025-08-12T22:21:09.244Z" + "updated_at": "2025-08-13T22:21:26.184Z" } diff --git a/clash-nyanpasu/package.json b/clash-nyanpasu/package.json index 8e9786c23c..867360a293 100644 --- a/clash-nyanpasu/package.json +++ b/clash-nyanpasu/package.json @@ -105,7 +105,7 @@ "stylelint-order": "7.0.0", "stylelint-scss": "6.12.1", "tailwindcss": "4.1.11", - "tsx": "4.20.3", + "tsx": "4.20.4", "typescript": "5.9.2", "typescript-eslint": "8.39.1" }, diff --git a/clash-nyanpasu/pnpm-lock.yaml b/clash-nyanpasu/pnpm-lock.yaml index d1a5dbdc01..ea0cab2076 100644 --- a/clash-nyanpasu/pnpm-lock.yaml +++ b/clash-nyanpasu/pnpm-lock.yaml @@ -164,8 +164,8 @@ importers: specifier: 4.1.11 version: 4.1.11 tsx: - specifier: 4.20.3 - version: 4.20.3 + specifier: 4.20.4 + version: 4.20.4 typescript: specifier: 5.9.2 version: 5.9.2 @@ -250,7 +250,7 @@ importers: version: 4.1.11 '@tanstack/router-zod-adapter': specifier: 1.81.5 - version: 1.81.5(@tanstack/react-router@1.131.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(zod@4.0.17) + version: 1.81.5(@tanstack/react-router@1.131.10(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(zod@4.0.17) '@tauri-apps/api': specifier: 2.6.0 version: 2.6.0 @@ -343,8 +343,8 @@ importers: specifier: 11.14.0 version: 11.14.0(@types/react@19.1.10)(react@19.1.1) '@iconify/json': - specifier: 2.2.371 - version: 2.2.371 + specifier: 2.2.373 + version: 2.2.373 '@monaco-editor/react': specifier: 4.7.0 version: 4.7.0(monaco-editor@0.52.2)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -352,14 +352,14 @@ importers: specifier: 5.84.2 version: 5.84.2(react@19.1.1) '@tanstack/react-router': - specifier: 1.131.5 - version: 1.131.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + specifier: 1.131.10 + version: 1.131.10(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@tanstack/react-router-devtools': - specifier: 1.131.5 - version: 1.131.5(@tanstack/react-router@1.131.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@tanstack/router-core@1.131.5)(csstype@3.1.3)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(solid-js@1.9.5)(tiny-invariant@1.3.3) + specifier: 1.131.10 + version: 1.131.10(@tanstack/react-router@1.131.10(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@tanstack/router-core@1.131.7)(csstype@3.1.3)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(solid-js@1.9.5)(tiny-invariant@1.3.3) '@tanstack/router-plugin': - specifier: 1.131.5 - version: 1.131.5(@tanstack/react-router@1.131.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.1)) + specifier: 1.131.11 + version: 1.131.11(@tanstack/react-router@1.131.10(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1)) '@tauri-apps/plugin-clipboard-manager': specifier: 2.3.0 version: 2.3.0 @@ -395,13 +395,13 @@ importers: version: 13.15.2 '@vitejs/plugin-legacy': specifier: 7.2.1 - version: 7.2.1(terser@5.36.0)(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.1)) + version: 7.2.1(terser@5.36.0)(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1)) '@vitejs/plugin-react': specifier: 4.7.0 - version: 4.7.0(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.1)) + version: 4.7.0(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1)) '@vitejs/plugin-react-swc': specifier: 3.11.0 - version: 3.11.0(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.1)) + version: 3.11.0(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1)) change-case: specifier: 5.4.4 version: 5.4.4 @@ -440,19 +440,19 @@ importers: version: 13.15.15 vite: specifier: 7.1.1 - version: 7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.1) + version: 7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1) vite-plugin-html: specifier: 3.2.2 - version: 3.2.2(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.1)) + version: 3.2.2(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1)) vite-plugin-sass-dts: specifier: 1.3.31 - version: 1.3.31(postcss@8.5.6)(prettier@3.6.2)(sass-embedded@1.90.0)(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.1)) + version: 1.3.31(postcss@8.5.6)(prettier@3.6.2)(sass-embedded@1.90.0)(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1)) vite-plugin-svgr: specifier: 4.3.0 - version: 4.3.0(rollup@4.46.2)(typescript@5.9.2)(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.1)) + version: 4.3.0(rollup@4.46.2)(typescript@5.9.2)(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1)) vite-tsconfig-paths: specifier: 5.1.4 - version: 5.1.4(typescript@5.9.2)(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.1)) + version: 5.1.4(typescript@5.9.2)(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1)) zod: specifier: 4.0.17 version: 4.0.17 @@ -488,7 +488,7 @@ importers: version: 19.1.10 '@vitejs/plugin-react': specifier: 4.7.0 - version: 4.7.0(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.1)) + version: 4.7.0(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1)) ahooks: specifier: 3.9.0 version: 3.9.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -518,10 +518,10 @@ importers: version: 4.1.11 vite: specifier: 7.1.1 - version: 7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.1) + version: 7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1) vite-tsconfig-paths: specifier: 5.1.4 - version: 5.1.4(typescript@5.9.2)(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.1)) + version: 5.1.4(typescript@5.9.2)(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1)) devDependencies: '@emotion/react': specifier: 11.14.0 @@ -546,7 +546,7 @@ importers: version: 5.2.0(typescript@5.9.2) vite-plugin-dts: specifier: 4.5.4 - version: 4.5.4(@types/node@22.17.1)(rollup@4.46.2)(typescript@5.9.2)(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.1)) + version: 4.5.4(@types/node@22.17.1)(rollup@4.46.2)(typescript@5.9.2)(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1)) scripts: dependencies: @@ -1759,8 +1759,8 @@ packages: prettier-plugin-ember-template-tag: optional: true - '@iconify/json@2.2.371': - resolution: {integrity: sha512-dnamUrgw8aDWz4STJOkQqqCz6GB2s7SHGPIY84EVQmlVXzM7T1V7L/csdL7r9bkA3AHF80+OXhx7o4FXn5L8pQ==} + '@iconify/json@2.2.373': + resolution: {integrity: sha512-ZgDwDLgbzf/nEyDoSdC75A1XA0L7Q4rfz0ZKGiq/ZWTztrlM7ASBvjKb6/dhs+gtvepnOc5CLUrBcDUYKZblLg==} '@iconify/types@2.0.0': resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} @@ -2945,16 +2945,16 @@ packages: peerDependencies: react: ^18 || ^19 - '@tanstack/react-router-devtools@1.131.5': - resolution: {integrity: sha512-3LaEbWDYGnzw4J8DM7KX32qRslGrSt67Cg7uoAYReBfDlLpWVRnjpyG2fef7nHDqn5HaAuV0IbY1n6Duwp5IyQ==} + '@tanstack/react-router-devtools@1.131.10': + resolution: {integrity: sha512-D9DlyPTQt0bkM1YcTykB9yYAiw6GhsvhvzOuprO0xKz3lMXbyuaQ5QO1xBfzhL+2Wv5nlnCLu68Ra3skE8TQTA==} engines: {node: '>=12'} peerDependencies: - '@tanstack/react-router': ^1.131.5 + '@tanstack/react-router': ^1.131.10 react: '>=18.0.0 || >=19.0.0' react-dom: '>=18.0.0 || >=19.0.0' - '@tanstack/react-router@1.131.5': - resolution: {integrity: sha512-71suJGuCmrHN9PLLRUDB3CGnW5RNcEEfgfX616TOpKamHs977H8P4/75BgWPRWcLHCga/1kkA6c7bddCwZ35Fw==} + '@tanstack/react-router@1.131.10': + resolution: {integrity: sha512-RjfCgHEHChmW3icKjLQSRKWKCJyzVd8qQXW2jF+cil5XXFeKvyuqm3R92uRIF/ONUUJ8b/V9/ET4/+dH/U8FPA==} engines: {node: '>=12'} peerDependencies: react: '>=18.0.0 || >=19.0.0' @@ -2979,15 +2979,15 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - '@tanstack/router-core@1.131.5': - resolution: {integrity: sha512-XVfZdnKNQbWfkQ6G7I9ml2wHp98Wy7wgTboP5SfrJHfOE+kPeHeZRJqF/pp5oqLZ2feBJqsDDKNWo9323L7sWQ==} + '@tanstack/router-core@1.131.7': + resolution: {integrity: sha512-NpFfAG1muv4abrCij6sEtRrVzlU+xYpY30NAgquHNhMMMNIiN7djzsaGV+vCJdR4u5mi13+f0c3f+f9MdekY5A==} engines: {node: '>=12'} - '@tanstack/router-devtools-core@1.131.5': - resolution: {integrity: sha512-kH3cZz7UfnVQW9vMZJ/CAx15pu+iGkn10N4rRKBVWCEJZFPX3GZvYEwkeALHASsV0Io7yUvKDcWfPsc+UowyzQ==} + '@tanstack/router-devtools-core@1.131.7': + resolution: {integrity: sha512-1GHWILJr69Ej/c8UUMhT7Srx392FbsDqRrPhCWWtrjmYOv6Fdx3HdKDJt/YdJGBc8z6x+V7EE41j+LZggD+70Q==} engines: {node: '>=12'} peerDependencies: - '@tanstack/router-core': ^1.131.5 + '@tanstack/router-core': ^1.131.7 csstype: ^3.0.10 solid-js: '>=1.9.5' tiny-invariant: ^1.3.3 @@ -2995,16 +2995,16 @@ packages: csstype: optional: true - '@tanstack/router-generator@1.131.5': - resolution: {integrity: sha512-5+/zyp/R9WN8tHNVIEYQZpRMzcsOrNH06HoPnPMiLiB9T4WsOLFJCcHdyso9ofGQq+hoxB4M9SUBXVBbJVWbSw==} + '@tanstack/router-generator@1.131.7': + resolution: {integrity: sha512-djwY5O1LdJo300EOZiYog5RsjB1DYzFtgX6a3uOkAmii7LHX9k9mhFXx2KrI4dLHLQsnlKexV9QvU6cSTFmsag==} engines: {node: '>=12'} - '@tanstack/router-plugin@1.131.5': - resolution: {integrity: sha512-Px+GSijNv1cbSm74+U+kEbuSFsspL+/BakzytAJFdh2O4342G32tha1cMFMlzXbDd9SW1FaLWgu3VqNHjGIpOg==} + '@tanstack/router-plugin@1.131.11': + resolution: {integrity: sha512-cPDWynVPHpST2UcdEqUAmpqXh50IuSenTPZj7aDhUkw1q8Oeerv6ixYy6GTfH2JDdgQ2IgzJpPBIWkvJ+mOYHA==} engines: {node: '>=12'} peerDependencies: '@rsbuild/core': '>=1.0.2' - '@tanstack/react-router': ^1.131.5 + '@tanstack/react-router': ^1.131.10 vite: '>=5.0.0 || >=6.0.0' vite-plugin-solid: ^2.11.2 webpack: '>=5.92.0' @@ -5282,9 +5282,6 @@ packages: get-tsconfig@4.10.1: resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} - get-tsconfig@4.8.1: - resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==} - git-raw-commits@4.0.0: resolution: {integrity: sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==} engines: {node: '>=16'} @@ -8037,8 +8034,8 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - tsx@4.20.3: - resolution: {integrity: sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==} + tsx@4.20.4: + resolution: {integrity: sha512-yyxBKfORQ7LuRt/BQKBXrpcq59ZvSW0XxwfjAt3w2/8PmdxaFzijtMhTawprSHhpzeM5BgU2hXHG3lklIERZXg==} engines: {node: '>=18.0.0'} hasBin: true @@ -9970,7 +9967,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@iconify/json@2.2.371': + '@iconify/json@2.2.373': dependencies: '@iconify/types': 2.0.0 pathe: 1.1.2 @@ -11057,10 +11054,10 @@ snapshots: '@tanstack/query-core': 5.83.1 react: 19.1.1 - '@tanstack/react-router-devtools@1.131.5(@tanstack/react-router@1.131.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@tanstack/router-core@1.131.5)(csstype@3.1.3)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(solid-js@1.9.5)(tiny-invariant@1.3.3)': + '@tanstack/react-router-devtools@1.131.10(@tanstack/react-router@1.131.10(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@tanstack/router-core@1.131.7)(csstype@3.1.3)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(solid-js@1.9.5)(tiny-invariant@1.3.3)': dependencies: - '@tanstack/react-router': 1.131.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@tanstack/router-devtools-core': 1.131.5(@tanstack/router-core@1.131.5)(csstype@3.1.3)(solid-js@1.9.5)(tiny-invariant@1.3.3) + '@tanstack/react-router': 1.131.10(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@tanstack/router-devtools-core': 1.131.7(@tanstack/router-core@1.131.7)(csstype@3.1.3)(solid-js@1.9.5)(tiny-invariant@1.3.3) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) transitivePeerDependencies: @@ -11069,11 +11066,11 @@ snapshots: - solid-js - tiny-invariant - '@tanstack/react-router@1.131.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@tanstack/react-router@1.131.10(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: '@tanstack/history': 1.131.2 '@tanstack/react-store': 0.7.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@tanstack/router-core': 1.131.5 + '@tanstack/router-core': 1.131.7 isbot: 5.1.28 react: 19.1.1 react-dom: 19.1.1(react@19.1.1) @@ -11099,7 +11096,7 @@ snapshots: react: 19.1.1 react-dom: 19.1.1(react@19.1.1) - '@tanstack/router-core@1.131.5': + '@tanstack/router-core@1.131.7': dependencies: '@tanstack/history': 1.131.2 '@tanstack/store': 0.7.0 @@ -11109,9 +11106,9 @@ snapshots: tiny-invariant: 1.3.3 tiny-warning: 1.0.3 - '@tanstack/router-devtools-core@1.131.5(@tanstack/router-core@1.131.5)(csstype@3.1.3)(solid-js@1.9.5)(tiny-invariant@1.3.3)': + '@tanstack/router-devtools-core@1.131.7(@tanstack/router-core@1.131.7)(csstype@3.1.3)(solid-js@1.9.5)(tiny-invariant@1.3.3)': dependencies: - '@tanstack/router-core': 1.131.5 + '@tanstack/router-core': 1.131.7 clsx: 2.1.1 goober: 2.1.16(csstype@3.1.3) solid-js: 1.9.5 @@ -11119,20 +11116,20 @@ snapshots: optionalDependencies: csstype: 3.1.3 - '@tanstack/router-generator@1.131.5': + '@tanstack/router-generator@1.131.7': dependencies: - '@tanstack/router-core': 1.131.5 + '@tanstack/router-core': 1.131.7 '@tanstack/router-utils': 1.131.2 '@tanstack/virtual-file-routes': 1.131.2 prettier: 3.6.2 recast: 0.23.11 source-map: 0.7.4 - tsx: 4.20.3 + tsx: 4.20.4 zod: 3.25.76 transitivePeerDependencies: - supports-color - '@tanstack/router-plugin@1.131.5(@tanstack/react-router@1.131.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.1))': + '@tanstack/router-plugin@1.131.11(@tanstack/react-router@1.131.10(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1))': dependencies: '@babel/core': 7.28.0 '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.0) @@ -11140,8 +11137,8 @@ snapshots: '@babel/template': 7.27.2 '@babel/traverse': 7.28.0 '@babel/types': 7.28.1 - '@tanstack/router-core': 1.131.5 - '@tanstack/router-generator': 1.131.5 + '@tanstack/router-core': 1.131.7 + '@tanstack/router-generator': 1.131.7 '@tanstack/router-utils': 1.131.2 '@tanstack/virtual-file-routes': 1.131.2 babel-dead-code-elimination: 1.0.10 @@ -11149,8 +11146,8 @@ snapshots: unplugin: 2.3.5 zod: 3.25.76 optionalDependencies: - '@tanstack/react-router': 1.131.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - vite: 7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.1) + '@tanstack/react-router': 1.131.10(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + vite: 7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1) transitivePeerDependencies: - supports-color @@ -11165,9 +11162,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@tanstack/router-zod-adapter@1.81.5(@tanstack/react-router@1.131.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(zod@4.0.17)': + '@tanstack/router-zod-adapter@1.81.5(@tanstack/react-router@1.131.10(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(zod@4.0.17)': dependencies: - '@tanstack/react-router': 1.131.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@tanstack/react-router': 1.131.10(react-dom@19.1.1(react@19.1.1))(react@19.1.1) zod: 4.0.17 '@tanstack/store@0.7.0': {} @@ -11762,7 +11759,7 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.10.1': optional: true - '@vitejs/plugin-legacy@7.2.1(terser@5.36.0)(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.1))': + '@vitejs/plugin-legacy@7.2.1(terser@5.36.0)(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1))': dependencies: '@babel/core': 7.28.0 '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.28.0) @@ -11777,19 +11774,19 @@ snapshots: regenerator-runtime: 0.14.1 systemjs: 6.15.1 terser: 5.36.0 - vite: 7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.1) + vite: 7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1) transitivePeerDependencies: - supports-color - '@vitejs/plugin-react-swc@3.11.0(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.1))': + '@vitejs/plugin-react-swc@3.11.0(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1))': dependencies: '@rolldown/pluginutils': 1.0.0-beta.27 '@swc/core': 1.13.0 - vite: 7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.1) + vite: 7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1) transitivePeerDependencies: - '@swc/helpers' - '@vitejs/plugin-react@4.7.0(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.1))': + '@vitejs/plugin-react@4.7.0(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1))': dependencies: '@babel/core': 7.28.0 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.0) @@ -11797,7 +11794,7 @@ snapshots: '@rolldown/pluginutils': 1.0.0-beta.27 '@types/babel__core': 7.20.5 react-refresh: 0.17.0 - vite: 7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.1) + vite: 7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1) transitivePeerDependencies: - supports-color @@ -13834,10 +13831,6 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 - get-tsconfig@4.8.1: - dependencies: - resolve-pkg-maps: 1.0.0 - git-raw-commits@4.0.0: dependencies: dargs: 8.1.0 @@ -16798,10 +16791,10 @@ snapshots: tslib@2.8.1: {} - tsx@4.20.3: + tsx@4.20.4: dependencies: esbuild: 0.25.0 - get-tsconfig: 4.8.1 + get-tsconfig: 4.10.1 optionalDependencies: fsevents: 2.3.3 @@ -17168,7 +17161,7 @@ snapshots: - rollup - supports-color - vite-plugin-dts@4.5.4(@types/node@22.17.1)(rollup@4.46.2)(typescript@5.9.2)(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.1)): + vite-plugin-dts@4.5.4(@types/node@22.17.1)(rollup@4.46.2)(typescript@5.9.2)(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1)): dependencies: '@microsoft/api-extractor': 7.51.0(@types/node@22.17.1) '@rollup/pluginutils': 5.1.4(rollup@4.46.2) @@ -17181,13 +17174,13 @@ snapshots: magic-string: 0.30.17 typescript: 5.9.2 optionalDependencies: - vite: 7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.1) + vite: 7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1) transitivePeerDependencies: - '@types/node' - rollup - supports-color - vite-plugin-html@3.2.2(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.1)): + vite-plugin-html@3.2.2(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1)): dependencies: '@rollup/pluginutils': 4.2.1 colorette: 2.0.20 @@ -17201,39 +17194,39 @@ snapshots: html-minifier-terser: 6.1.0 node-html-parser: 5.4.2 pathe: 0.2.0 - vite: 7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.1) + vite: 7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1) - vite-plugin-sass-dts@1.3.31(postcss@8.5.6)(prettier@3.6.2)(sass-embedded@1.90.0)(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.1)): + vite-plugin-sass-dts@1.3.31(postcss@8.5.6)(prettier@3.6.2)(sass-embedded@1.90.0)(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1)): dependencies: postcss: 8.5.6 postcss-js: 4.0.1(postcss@8.5.6) prettier: 3.6.2 sass-embedded: 1.90.0 - vite: 7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.1) + vite: 7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1) - vite-plugin-svgr@4.3.0(rollup@4.46.2)(typescript@5.9.2)(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.1)): + vite-plugin-svgr@4.3.0(rollup@4.46.2)(typescript@5.9.2)(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1)): dependencies: '@rollup/pluginutils': 5.1.3(rollup@4.46.2) '@svgr/core': 8.1.0(typescript@5.9.2) '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.9.2)) - vite: 7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.1) + vite: 7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1) transitivePeerDependencies: - rollup - supports-color - typescript - vite-tsconfig-paths@5.1.4(typescript@5.9.2)(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.1)): + vite-tsconfig-paths@5.1.4(typescript@5.9.2)(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1)): dependencies: debug: 4.3.7 globrex: 0.1.2 tsconfck: 3.0.3(typescript@5.9.2) optionalDependencies: - vite: 7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.1) + vite: 7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1) transitivePeerDependencies: - supports-color - typescript - vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.1): + vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1): dependencies: esbuild: 0.25.0 fdir: 6.4.6(picomatch@4.0.3) @@ -17251,7 +17244,7 @@ snapshots: sass-embedded: 1.90.0 stylus: 0.62.0 terser: 5.36.0 - tsx: 4.20.3 + tsx: 4.20.4 yaml: 2.8.1 void-elements@3.1.0: {} diff --git a/geoip/.github/workflows/build.yml b/geoip/.github/workflows/build.yml index c941f2589a..a535cae24f 100644 --- a/geoip/.github/workflows/build.yml +++ b/geoip/.github/workflows/build.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout codebase - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 diff --git a/lede/target/linux/qualcommax/patches-6.12/0151-arm64-qcom-ipq6018-nss_port5.patch b/lede/target/linux/qualcommax/patches-6.12/0151-arm64-qcom-ipq6018-nss_port5.patch new file mode 100644 index 0000000000..9e4372a878 --- /dev/null +++ b/lede/target/linux/qualcommax/patches-6.12/0151-arm64-qcom-ipq6018-nss_port5.patch @@ -0,0 +1,125 @@ +From 9989fcd49c52500a2bf1f6d49411690dec45d2dc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= +Date: Sat, 2 Aug 2025 12:47:08 +0300 +Subject: [PATCH] clk: qcom: gcc-ipq6018: rework nss_port5 clock to multiple + conf + +Rework nss_port5 to use the new multiple configuration implementation +and correctly fix the clocks for this port under some corner case. + +In OpenWrt, this patch avoids intermittent dmesg errors of the form +nss_port5_rx_clk_src: rcg didn't update its configuration. + +This is a mechanical, straightforward port of +commit e88f03230dc07aa3293b6aeb078bd27370bb2594 +("clk: qcom: gcc-ipq8074: rework nss_port5/6 clock to multiple conf") +to gcc-ipq6018, with two conflicts resolved: different frequency of the +P_XO clock source, and only 5 Ethernet ports. + +This was originally developed by JiaY-shi . + +Link: https://lore.kernel.org/all/20231220221724.3822-4-ansuelsmth@gmail.com/ +Signed-off-by: Marko Mäkelä +Tested-by: Marko Mäkelä +--- + drivers/clk/qcom/gcc-ipq6018.c | 60 +++++++++++++++++++++------------- + 1 file changed, 38 insertions(+), 22 deletions(-) + +--- a/drivers/clk/qcom/gcc-ipq6018.c ++++ b/drivers/clk/qcom/gcc-ipq6018.c +@@ -511,15 +511,23 @@ static struct clk_rcg2 apss_ahb_clk_src + }, + }; + +-static const struct freq_tbl ftbl_nss_port5_rx_clk_src[] = { +- F(24000000, P_XO, 1, 0, 0), +- F(25000000, P_UNIPHY1_RX, 12.5, 0, 0), +- F(25000000, P_UNIPHY0_RX, 5, 0, 0), +- F(78125000, P_UNIPHY1_RX, 4, 0, 0), +- F(125000000, P_UNIPHY1_RX, 2.5, 0, 0), +- F(125000000, P_UNIPHY0_RX, 1, 0, 0), +- F(156250000, P_UNIPHY1_RX, 2, 0, 0), +- F(312500000, P_UNIPHY1_RX, 1, 0, 0), ++static const struct freq_conf ftbl_nss_port5_rx_clk_src_25[] = { ++ C(P_UNIPHY1_RX, 12.5, 0, 0), ++ C(P_UNIPHY0_RX, 5, 0, 0), ++}; ++ ++static const struct freq_conf ftbl_nss_port5_rx_clk_src_125[] = { ++ C(P_UNIPHY1_RX, 2.5, 0, 0), ++ C(P_UNIPHY0_RX, 1, 0, 0), ++}; ++ ++static const struct freq_multi_tbl ftbl_nss_port5_rx_clk_src[] = { ++ FMS(24000000, P_XO, 1, 0, 0), ++ FM(25000000, ftbl_nss_port5_rx_clk_src_25), ++ FMS(78125000, P_UNIPHY1_RX, 4, 0, 0), ++ FM(125000000, ftbl_nss_port5_rx_clk_src_125), ++ FMS(156250000, P_UNIPHY1_RX, 2, 0, 0), ++ FMS(312500000, P_UNIPHY1_RX, 1, 0, 0), + { } + }; + +@@ -547,26 +555,34 @@ gcc_xo_uniphy0_rx_tx_uniphy1_rx_tx_ubi32 + + static struct clk_rcg2 nss_port5_rx_clk_src = { + .cmd_rcgr = 0x68060, +- .freq_tbl = ftbl_nss_port5_rx_clk_src, ++ .freq_multi_tbl = ftbl_nss_port5_rx_clk_src, + .hid_width = 5, + .parent_map = gcc_xo_uniphy0_rx_tx_uniphy1_rx_tx_ubi32_bias_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_port5_rx_clk_src", + .parent_data = gcc_xo_uniphy0_rx_tx_uniphy1_rx_tx_ubi32_bias, + .num_parents = 7, +- .ops = &clk_rcg2_ops, ++ .ops = &clk_rcg2_fm_ops, + }, + }; + +-static const struct freq_tbl ftbl_nss_port5_tx_clk_src[] = { +- F(24000000, P_XO, 1, 0, 0), +- F(25000000, P_UNIPHY1_TX, 12.5, 0, 0), +- F(25000000, P_UNIPHY0_TX, 5, 0, 0), +- F(78125000, P_UNIPHY1_TX, 4, 0, 0), +- F(125000000, P_UNIPHY1_TX, 2.5, 0, 0), +- F(125000000, P_UNIPHY0_TX, 1, 0, 0), +- F(156250000, P_UNIPHY1_TX, 2, 0, 0), +- F(312500000, P_UNIPHY1_TX, 1, 0, 0), ++static const struct freq_conf ftbl_nss_port5_tx_clk_src_25[] = { ++ C(P_UNIPHY1_TX, 12.5, 0, 0), ++ C(P_UNIPHY0_TX, 5, 0, 0), ++}; ++ ++static const struct freq_conf ftbl_nss_port5_tx_clk_src_125[] = { ++ C(P_UNIPHY1_TX, 2.5, 0, 0), ++ C(P_UNIPHY0_TX, 1, 0, 0), ++}; ++ ++static const struct freq_multi_tbl ftbl_nss_port5_tx_clk_src[] = { ++ FMS(24000000, P_XO, 1, 0, 0), ++ FM(25000000, ftbl_nss_port5_tx_clk_src_25), ++ FMS(78125000, P_UNIPHY1_TX, 4, 0, 0), ++ FM(125000000, ftbl_nss_port5_tx_clk_src_125), ++ FMS(156250000, P_UNIPHY1_TX, 2, 0, 0), ++ FMS(312500000, P_UNIPHY1_TX, 1, 0, 0), + { } + }; + +@@ -594,14 +610,14 @@ gcc_xo_uniphy0_tx_rx_uniphy1_tx_rx_ubi32 + + static struct clk_rcg2 nss_port5_tx_clk_src = { + .cmd_rcgr = 0x68068, +- .freq_tbl = ftbl_nss_port5_tx_clk_src, ++ .freq_multi_tbl = ftbl_nss_port5_tx_clk_src, + .hid_width = 5, + .parent_map = gcc_xo_uniphy0_tx_rx_uniphy1_tx_rx_ubi32_bias_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_port5_tx_clk_src", + .parent_data = gcc_xo_uniphy0_tx_rx_uniphy1_tx_rx_ubi32_bias, + .num_parents = 7, +- .ops = &clk_rcg2_ops, ++ .ops = &clk_rcg2_fm_ops, + }, + }; + diff --git a/lede/target/linux/qualcommax/patches-6.12/0904-clk-qcom-ipq6018-workaround-networking-clock-parenti.patch b/lede/target/linux/qualcommax/patches-6.12/0904-clk-qcom-ipq6018-workaround-networking-clock-parenti.patch index 2e7a56f728..30c6eced96 100644 --- a/lede/target/linux/qualcommax/patches-6.12/0904-clk-qcom-ipq6018-workaround-networking-clock-parenti.patch +++ b/lede/target/linux/qualcommax/patches-6.12/0904-clk-qcom-ipq6018-workaround-networking-clock-parenti.patch @@ -28,7 +28,7 @@ Signed-off-by: Robert Marko { .hw = &gpll0.clkr.hw }, { .hw = &gpll4.clkr.hw }, { .hw = &nss_crypto_pll.clkr.hw }, -@@ -526,12 +526,12 @@ static const struct freq_tbl ftbl_nss_po +@@ -534,12 +534,12 @@ static const struct freq_multi_tbl ftbl_ static const struct clk_parent_data gcc_xo_uniphy0_rx_tx_uniphy1_rx_tx_ubi32_bias[] = { { .fw_name = "xo" }, @@ -46,7 +46,7 @@ Signed-off-by: Robert Marko }; static const struct parent_map -@@ -573,12 +573,12 @@ static const struct freq_tbl ftbl_nss_po +@@ -589,12 +589,12 @@ static const struct freq_multi_tbl ftbl_ static const struct clk_parent_data gcc_xo_uniphy0_tx_rx_uniphy1_tx_rx_ubi32_bias[] = { { .fw_name = "xo" }, @@ -64,7 +64,7 @@ Signed-off-by: Robert Marko }; static const struct parent_map -@@ -714,10 +714,10 @@ static const struct freq_tbl ftbl_nss_po +@@ -730,10 +730,10 @@ static const struct freq_tbl ftbl_nss_po static const struct clk_parent_data gcc_xo_uniphy0_rx_tx_ubi32_bias[] = { { .fw_name = "xo" }, @@ -78,7 +78,7 @@ Signed-off-by: Robert Marko }; static const struct parent_map gcc_xo_uniphy0_rx_tx_ubi32_bias_map[] = { -@@ -750,10 +750,10 @@ static const struct freq_tbl ftbl_nss_po +@@ -766,10 +766,10 @@ static const struct freq_tbl ftbl_nss_po static const struct clk_parent_data gcc_xo_uniphy0_tx_rx_ubi32_bias[] = { { .fw_name = "xo" }, @@ -92,7 +92,7 @@ Signed-off-by: Robert Marko }; static const struct parent_map gcc_xo_uniphy0_tx_rx_ubi32_bias_map[] = { -@@ -1899,12 +1899,11 @@ static const struct freq_tbl ftbl_ubi32_ +@@ -1915,12 +1915,11 @@ static const struct freq_tbl ftbl_ubi32_ { } }; diff --git a/mieru/docs/client-install.md b/mieru/docs/client-install.md index f7654e9539..39c450b097 100644 --- a/mieru/docs/client-install.md +++ b/mieru/docs/client-install.md @@ -47,7 +47,7 @@ An example of client configuration is as follows. "multiplexing": { "level": "MULTIPLEXING_HIGH" }, - "handshakeMode": "HANDSHAKE_NO_WAIT" + "handshakeMode": "HANDSHAKE_STANDARD" } ], "activeProfile": "default", @@ -69,7 +69,7 @@ Please use a text editor to modify the following fields. 5. Fill in `profiles` -> `servers` -> `portBindings` -> `port` with the TCP or UDP port number that mita is listening to. The port number must be the same as the one set in the proxy server. If you want to listen to a range of consecutive port numbers, you can also use the `portRange` property instead. 6. [Optional] Specify a value between 1280 and 1400 for the `profiles` -> `mtu` property. The default value is 1400. This value must be the same as proxy server. 7. [Optional] If you want to adjust the frequency of multiplexing, you can set a value for the `profiles` -> `multiplexing` -> `level` property. The values you can use here include `MULTIPLEXING_OFF`, `MULTIPLEXING_LOW`, `MULTIPLEXING_MIDDLE`, and `MULTIPLEXING_HIGH`. `MULTIPLEXING_OFF` will disable multiplexing. The default value is `MULTIPLEXING_LOW`. -8. [Optional] If you want to enable 0-RTT handshake, you can set the value of `profiles` -> `handshakeMode` property to `HANDSHAKE_NO_WAIT`, otherwise set it to `HANDSHAKE_STANDARD`. The default value is `HANDSHAKE_STANDARD`. +8. [Optional] If you want to enable 0-RTT handshake (experimental feature), you can set the value of `profiles` -> `handshakeMode` property to `HANDSHAKE_NO_WAIT`, otherwise set it to `HANDSHAKE_STANDARD`. The default value is `HANDSHAKE_STANDARD`. 9. Please specify a value between 1025 and 65535 for the `rpcPort` property. 10. Please specify a value between 1025 and 65535 for the `socks5Port` property. This port cannot be the same as `rpcPort`. 11. [Optional] If the client needs to provide proxy services to other devices on the LAN, set the `socks5ListenLAN` property to `true`. diff --git a/mieru/docs/client-install.zh_CN.md b/mieru/docs/client-install.zh_CN.md index a3409ae3e4..de847a185f 100644 --- a/mieru/docs/client-install.zh_CN.md +++ b/mieru/docs/client-install.zh_CN.md @@ -47,7 +47,7 @@ mieru apply config "multiplexing": { "level": "MULTIPLEXING_HIGH" }, - "handshakeMode": "HANDSHAKE_NO_WAIT" + "handshakeMode": "HANDSHAKE_STANDARD" } ], "activeProfile": "default", @@ -69,7 +69,7 @@ mieru apply config 5. 在 `profiles` -> `servers` -> `portBindings` -> `port` 中填写 mita 监听的 TCP 或 UDP 端口号。这个端口号必须与代理服务器中的设置相同。如果想要监听连续的端口号,也可以改为使用 `portRange` 属性。 6. 【可选】请为 `profiles` -> `mtu` 属性中指定一个从 1280 到 1400 之间的值。默认值为 1400。这个值必须与代理服务器相同。 7. 【可选】如果想要调整多路复用的频率,是更多地创建新连接,还是更多地重用旧连接,可以为 `profiles` -> `multiplexing` -> `level` 属性设定一个值。这里可以使用的值包括 `MULTIPLEXING_OFF`, `MULTIPLEXING_LOW`, `MULTIPLEXING_MIDDLE`, `MULTIPLEXING_HIGH`。其中 `MULTIPLEXING_OFF` 会关闭多路复用功能。默认值为 `MULTIPLEXING_LOW`。 -8. 【可选】如果想开启 0-RTT 握手,请将 `profiles` -> `handshakeMode` 属性的值设置为 `HANDSHAKE_NO_WAIT`,否则请设置为 `HANDSHAKE_STANDARD`。默认值为 `HANDSHAKE_STANDARD`。 +8. 【可选】如果想开启 0-RTT 握手(实验性功能),请将 `profiles` -> `handshakeMode` 属性的值设置为 `HANDSHAKE_NO_WAIT`,否则请设置为 `HANDSHAKE_STANDARD`。默认值为 `HANDSHAKE_STANDARD`。 9. 请为 `rpcPort` 属性指定一个从 1025 到 65535 之间的数值。 10. 请为 `socks5Port` 属性指定一个从 1025 到 65535 之间的数值。该端口不能与 `rpcPort` 相同。 11. 【可选】如果客户端需要为局域网中的其他设备提供代理服务,请将 `socks5ListenLAN` 属性设置为 `true`。 diff --git a/mieru/tools/setup.py b/mieru/tools/setup.py index fc813f0b6a..4b6d54a122 100755 --- a/mieru/tools/setup.py +++ b/mieru/tools/setup.py @@ -462,7 +462,7 @@ class ClientConfig: 'ipAddress': '', 'portBindings': [{}], }], - 'handshakeMode': 'HANDSHAKE_NO_WAIT' + 'handshakeMode': 'HANDSHAKE_STANDARD' }], 'activeProfile': 'default', 'rpcPort': 0, diff --git a/mihomo/common/atomic/value.go b/mihomo/common/atomic/value.go index e63d231096..6480ee4766 100644 --- a/mihomo/common/atomic/value.go +++ b/mihomo/common/atomic/value.go @@ -5,58 +5,50 @@ import ( "sync/atomic" ) -func DefaultValue[T any]() T { - var defaultValue T - return defaultValue -} - type TypedValue[T any] struct { - _ noCopy - value atomic.Value + value atomic.Pointer[T] } -// tValue is a struct with determined type to resolve atomic.Value usages with interface types -// https://github.com/golang/go/issues/22550 -// -// The intention to have an atomic value store for errors. However, running this code panics: -// panic: sync/atomic: store of inconsistently typed value into Value -// This is because atomic.Value requires that the underlying concrete type be the same (which is a reasonable expectation for its implementation). -// When going through the atomic.Value.Store method call, the fact that both these are of the error interface is lost. -type tValue[T any] struct { - value T +func (t *TypedValue[T]) Load() (v T) { + v, _ = t.LoadOk() + return } -func (t *TypedValue[T]) Load() T { - value, _ := t.LoadOk() - return value -} - -func (t *TypedValue[T]) LoadOk() (_ T, ok bool) { +func (t *TypedValue[T]) LoadOk() (v T, ok bool) { value := t.value.Load() if value == nil { - return DefaultValue[T](), false + return } - return value.(tValue[T]).value, true + return *value, true } func (t *TypedValue[T]) Store(value T) { - t.value.Store(tValue[T]{value}) + t.value.Store(&value) } -func (t *TypedValue[T]) Swap(new T) T { - old := t.value.Swap(tValue[T]{new}) +func (t *TypedValue[T]) Swap(new T) (v T) { + old := t.value.Swap(&new) if old == nil { - return DefaultValue[T]() + return } - return old.(tValue[T]).value + return *old } func (t *TypedValue[T]) CompareAndSwap(old, new T) bool { - return t.value.CompareAndSwap(tValue[T]{old}, tValue[T]{new}) || - // In the edge-case where [atomic.Value.Store] is uninitialized - // and trying to compare with the zero value of T, - // then compare-and-swap with the nil any value. - (any(old) == any(DefaultValue[T]()) && t.value.CompareAndSwap(any(nil), tValue[T]{new})) + for { + currentP := t.value.Load() + var currentValue T + if currentP != nil { + currentValue = *currentP + } + // Compare old and current via runtime equality check. + if any(currentValue) != any(old) { + return false + } + if t.value.CompareAndSwap(currentP, &new) { + return true + } + } } func (t *TypedValue[T]) MarshalJSON() ([]byte, error) { @@ -89,9 +81,3 @@ func NewTypedValue[T any](t T) (v TypedValue[T]) { v.Store(t) return } - -type noCopy struct{} - -// Lock is a no-op used by -copylocks checker from `go vet`. -func (*noCopy) Lock() {} -func (*noCopy) Unlock() {} diff --git a/mihomo/common/atomic/value_test.go b/mihomo/common/atomic/value_test.go index 881b3e8d8e..1697309d46 100644 --- a/mihomo/common/atomic/value_test.go +++ b/mihomo/common/atomic/value_test.go @@ -7,20 +7,6 @@ import ( ) func TestTypedValue(t *testing.T) { - { - // Always wrapping should not allocate for simple values - // because tValue[T] has the same memory layout as T. - var v TypedValue[bool] - bools := []bool{true, false} - if n := int(testing.AllocsPerRun(1000, func() { - for _, b := range bools { - v.Store(b) - } - })); n != 0 { - t.Errorf("AllocsPerRun = %d, want 0", n) - } - } - { var v TypedValue[int] got, gotOk := v.LoadOk() @@ -58,20 +44,126 @@ func TestTypedValue(t *testing.T) { } } + { + e1, e2, e3 := io.EOF, &os.PathError{}, &os.PathError{} + var v TypedValue[error] + if v.CompareAndSwap(e1, e2) != false { + t.Fatalf("CompareAndSwap = true, want false") + } + if value := v.Load(); value != nil { + t.Fatalf("Load = (%v), want (%v)", value, nil) + } + if v.CompareAndSwap(nil, e1) != true { + t.Fatalf("CompareAndSwap = false, want true") + } + if value := v.Load(); value != e1 { + t.Fatalf("Load = (%v), want (%v)", value, e1) + } + if v.CompareAndSwap(e2, e3) != false { + t.Fatalf("CompareAndSwap = true, want false") + } + if value := v.Load(); value != e1 { + t.Fatalf("Load = (%v), want (%v)", value, e1) + } + if v.CompareAndSwap(e1, e2) != true { + t.Fatalf("CompareAndSwap = false, want true") + } + if value := v.Load(); value != e2 { + t.Fatalf("Load = (%v), want (%v)", value, e2) + } + if v.CompareAndSwap(e3, e2) != false { + t.Fatalf("CompareAndSwap = true, want false") + } + if value := v.Load(); value != e2 { + t.Fatalf("Load = (%v), want (%v)", value, e2) + } + if v.CompareAndSwap(nil, e3) != false { + t.Fatalf("CompareAndSwap = true, want false") + } + if value := v.Load(); value != e2 { + t.Fatalf("Load = (%v), want (%v)", value, e2) + } + } + { c1, c2, c3 := make(chan struct{}), make(chan struct{}), make(chan struct{}) var v TypedValue[chan struct{}] if v.CompareAndSwap(c1, c2) != false { t.Fatalf("CompareAndSwap = true, want false") } + if value := v.Load(); value != nil { + t.Fatalf("Load = (%v), want (%v)", value, nil) + } if v.CompareAndSwap(nil, c1) != true { t.Fatalf("CompareAndSwap = false, want true") } + if value := v.Load(); value != c1 { + t.Fatalf("Load = (%v), want (%v)", value, c1) + } if v.CompareAndSwap(c2, c3) != false { t.Fatalf("CompareAndSwap = true, want false") } + if value := v.Load(); value != c1 { + t.Fatalf("Load = (%v), want (%v)", value, c1) + } if v.CompareAndSwap(c1, c2) != true { t.Fatalf("CompareAndSwap = false, want true") } + if value := v.Load(); value != c2 { + t.Fatalf("Load = (%v), want (%v)", value, c2) + } + if v.CompareAndSwap(c3, c2) != false { + t.Fatalf("CompareAndSwap = true, want false") + } + if value := v.Load(); value != c2 { + t.Fatalf("Load = (%v), want (%v)", value, c2) + } + if v.CompareAndSwap(nil, c3) != false { + t.Fatalf("CompareAndSwap = true, want false") + } + if value := v.Load(); value != c2 { + t.Fatalf("Load = (%v), want (%v)", value, c2) + } + } + + { + c1, c2, c3 := &io.LimitedReader{}, &io.SectionReader{}, &io.SectionReader{} + var v TypedValue[io.Reader] + if v.CompareAndSwap(c1, c2) != false { + t.Fatalf("CompareAndSwap = true, want false") + } + if value := v.Load(); value != nil { + t.Fatalf("Load = (%v), want (%v)", value, nil) + } + if v.CompareAndSwap(nil, c1) != true { + t.Fatalf("CompareAndSwap = false, want true") + } + if value := v.Load(); value != c1 { + t.Fatalf("Load = (%v), want (%v)", value, c1) + } + if v.CompareAndSwap(c2, c3) != false { + t.Fatalf("CompareAndSwap = true, want false") + } + if value := v.Load(); value != c1 { + t.Fatalf("Load = (%v), want (%v)", value, c1) + } + if v.CompareAndSwap(c1, c2) != true { + t.Fatalf("CompareAndSwap = false, want true") + } + if value := v.Load(); value != c2 { + t.Fatalf("Load = (%v), want (%v)", value, c2) + } + if v.CompareAndSwap(c3, c2) != false { + t.Fatalf("CompareAndSwap = true, want false") + } + if value := v.Load(); value != c2 { + t.Fatalf("Load = (%v), want (%v)", value, c2) + } + if v.CompareAndSwap(nil, c3) != false { + t.Fatalf("CompareAndSwap = true, want false") + } + if value := v.Load(); value != c2 { + t.Fatalf("Load = (%v), want (%v)", value, c2) + } } } diff --git a/mihomo/component/keepalive/tcp_keepalive.go b/mihomo/component/keepalive/tcp_keepalive.go index ddb853e916..fee06a94e1 100644 --- a/mihomo/component/keepalive/tcp_keepalive.go +++ b/mihomo/component/keepalive/tcp_keepalive.go @@ -10,27 +10,27 @@ import ( ) var ( - keepAliveIdle = atomic.NewTypedValue[time.Duration](0 * time.Second) - keepAliveInterval = atomic.NewTypedValue[time.Duration](0 * time.Second) + keepAliveIdle = atomic.NewInt64(0) + keepAliveInterval = atomic.NewInt64(0) disableKeepAlive = atomic.NewBool(false) SetDisableKeepAliveCallback = utils.NewCallback[bool]() ) func SetKeepAliveIdle(t time.Duration) { - keepAliveIdle.Store(t) + keepAliveIdle.Store(int64(t)) } func SetKeepAliveInterval(t time.Duration) { - keepAliveInterval.Store(t) + keepAliveInterval.Store(int64(t)) } func KeepAliveIdle() time.Duration { - return keepAliveIdle.Load() + return time.Duration(keepAliveIdle.Load()) } func KeepAliveInterval() time.Duration { - return keepAliveInterval.Load() + return time.Duration(keepAliveInterval.Load()) } func SetDisableKeepAlive(disable bool) { diff --git a/mihomo/go.mod b/mihomo/go.mod index c218528072..d28ac4e8b6 100644 --- a/mihomo/go.mod +++ b/mihomo/go.mod @@ -3,7 +3,6 @@ module github.com/metacubex/mihomo go 1.20 require ( - github.com/3andne/restls-client-go v0.1.6 github.com/bahlo/generic-list-go v0.2.0 github.com/coreos/go-iptables v0.8.0 github.com/dlclark/regexp2 v1.11.5 @@ -19,18 +18,20 @@ require ( github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab github.com/metacubex/bart v0.20.5 github.com/metacubex/bbolt v0.0.0-20250725135710-010dbbbb7a5b + github.com/metacubex/blake3 v0.1.0 github.com/metacubex/chacha v0.1.5 github.com/metacubex/fswatch v0.1.1 github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 github.com/metacubex/quic-go v0.54.1-0.20250730114134-a1ae705fe295 github.com/metacubex/randv2 v0.2.0 - github.com/metacubex/sing v0.5.4 + github.com/metacubex/restls-client-go v0.1.7 + github.com/metacubex/sing v0.5.5 github.com/metacubex/sing-mux v0.3.3-0.20250813083925-d7c9aeaeeaac github.com/metacubex/sing-quic v0.0.0-20250718154553-1b193bec4cbb - github.com/metacubex/sing-shadowsocks v0.2.11 - github.com/metacubex/sing-shadowsocks2 v0.2.5 + github.com/metacubex/sing-shadowsocks v0.2.12 + github.com/metacubex/sing-shadowsocks2 v0.2.6 github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 - github.com/metacubex/sing-tun v0.4.7-0.20250801130030-308b828865ae + github.com/metacubex/sing-tun v0.4.7 github.com/metacubex/sing-vmess v0.2.4-0.20250731011226-ea28d589924d github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f github.com/metacubex/smux v0.0.0-20250503055512-501391591dee @@ -59,7 +60,6 @@ require ( golang.org/x/sys v0.30.0 // lastest version compatible with golang1.20 google.golang.org/protobuf v1.34.2 // lastest version compatible with golang1.20 gopkg.in/yaml.v3 v3.0.1 - lukechampine.com/blake3 v1.3.0 // lastest version compatible with golang1.20 ) require ( @@ -85,11 +85,11 @@ require ( github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/hashicorp/yamux v0.1.2 // indirect github.com/josharian/native v1.1.0 // indirect - github.com/klauspost/cpuid/v2 v2.2.9 // indirect github.com/kr/text v0.2.0 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mdlayher/socket v0.4.1 // indirect + github.com/metacubex/ascon v0.1.0 // indirect github.com/metacubex/gvisor v0.0.0-20250324165734-5857f47bd43b // indirect github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793 // indirect github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect diff --git a/mihomo/go.sum b/mihomo/go.sum index 4acd1b8706..374463a99a 100644 --- a/mihomo/go.sum +++ b/mihomo/go.sum @@ -1,5 +1,3 @@ -github.com/3andne/restls-client-go v0.1.6 h1:tRx/YilqW7iHpgmEL4E1D8dAsuB0tFF3uvncS+B6I08= -github.com/3andne/restls-client-go v0.1.6/go.mod h1:iEdTZNt9kzPIxjIGSMScUFSBrUH6bFRNg0BWlP4orEY= github.com/RyuaNerin/go-krypto v1.3.0 h1:smavTzSMAx8iuVlGb4pEwl9MD2qicqMzuXR2QWp2/Pg= github.com/RyuaNerin/go-krypto v1.3.0/go.mod h1:9R9TU936laAIqAmjcHo/LsaXYOZlymudOAxjaBf62UM= github.com/RyuaNerin/testingutil v0.1.0 h1:IYT6JL57RV3U2ml3dLHZsVtPOP6yNK7WUVdzzlpNrss= @@ -81,8 +79,6 @@ github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtL github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= -github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= -github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= @@ -98,10 +94,14 @@ github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab h1:Chbw+/31UC14YFNr78pESt5Vowlc62zziw05JCUqoL4= github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab/go.mod h1:xVKK8jC5Sd3hfh7WjmCq+HorehIbrBijaUWmcuKjPcI= +github.com/metacubex/ascon v0.1.0 h1:6ZWxmXYszT1XXtwkf6nxfFhc/OTtQ9R3Vyj1jN32lGM= +github.com/metacubex/ascon v0.1.0/go.mod h1:eV5oim4cVPPdEL8/EYaTZ0iIKARH9pnhAK/fcT5Kacc= github.com/metacubex/bart v0.20.5 h1:XkgLZ17QxfxkqKdGsojoM2Zu01mmHyyQSFzt2/calTM= github.com/metacubex/bart v0.20.5/go.mod h1:DCcyfP4MC+Zy7sLK7XeGuMw+P5K9mIRsYOBgiE8icsI= github.com/metacubex/bbolt v0.0.0-20250725135710-010dbbbb7a5b h1:j7dadXD8I2KTmMt8jg1JcaP1ANL3JEObJPdANKcSYPY= github.com/metacubex/bbolt v0.0.0-20250725135710-010dbbbb7a5b/go.mod h1:+WmP0VJZDkDszvpa83HzfUp6QzARl/IKkMorH4+nODw= +github.com/metacubex/blake3 v0.1.0 h1:KGnjh/56REO7U+cgZA8dnBhxdP7jByrG7hTP+bu6cqY= +github.com/metacubex/blake3 v0.1.0/go.mod h1:CCkLdzFrqf7xmxCdhQFvJsRRV2mwOLDoSPg6vUTB9Uk= github.com/metacubex/chacha v0.1.5 h1:fKWMb/5c7ZrY8Uoqi79PPFxl+qwR7X/q0OrsAubyX2M= github.com/metacubex/chacha v0.1.5/go.mod h1:Djn9bPZxLTXbJFSeyo0/qzEzQI+gUSSzttuzZM75GH8= github.com/metacubex/fswatch v0.1.1 h1:jqU7C/v+g0qc2RUFgmAOPoVvfl2BXXUXEumn6oQuxhU= @@ -116,21 +116,23 @@ github.com/metacubex/quic-go v0.54.1-0.20250730114134-a1ae705fe295 h1:8JVlYuE8uS github.com/metacubex/quic-go v0.54.1-0.20250730114134-a1ae705fe295/go.mod h1:1lktQFtCD17FZliVypbrDHwbsFSsmz2xz2TRXydvB5c= github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs= github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= +github.com/metacubex/restls-client-go v0.1.7 h1:eCwiXCTQb5WJu9IlgYvDBA1OgrINv58dEe7hcN5H15k= +github.com/metacubex/restls-client-go v0.1.7/go.mod h1:BN/U52vPw7j8VTSh2vleD/MnmVKCov84mS5VcjVHH4g= github.com/metacubex/sing v0.5.2/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w= -github.com/metacubex/sing v0.5.4 h1:a4kAOZmF+OXosbzPEcrSc5QD35/ex+MNuZsrcuWskHk= -github.com/metacubex/sing v0.5.4/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w= +github.com/metacubex/sing v0.5.5 h1:m5U8iHvRAUxlme3FZlE/LPIGHjU8oMCUzXWGbQQAC1E= +github.com/metacubex/sing v0.5.5/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w= github.com/metacubex/sing-mux v0.3.3-0.20250813083925-d7c9aeaeeaac h1:wDH/Jh/yqWbzPktqJP+Y1cUG8hchcrzKzUxJiSpnaQs= github.com/metacubex/sing-mux v0.3.3-0.20250813083925-d7c9aeaeeaac/go.mod h1:3rt1soewn0O6j89GCLmwAQFsq257u0jf2zQSPhTL3Bw= github.com/metacubex/sing-quic v0.0.0-20250718154553-1b193bec4cbb h1:U/m3h8lp/j7i8zFgfvScLdZa1/Y8dd74oO7iZaQq80s= github.com/metacubex/sing-quic v0.0.0-20250718154553-1b193bec4cbb/go.mod h1:B60FxaPHjR1SeQB0IiLrgwgvKsaoASfOWdiqhLjmMGA= -github.com/metacubex/sing-shadowsocks v0.2.11 h1:p2NGNOdF95e6XvdDKipLj1FRRqR8dnbfC/7pw2CCTlw= -github.com/metacubex/sing-shadowsocks v0.2.11/go.mod h1:bT1PCTV316zFnlToRMk5zt9HmIQYRBveiT71mplYPfc= -github.com/metacubex/sing-shadowsocks2 v0.2.5 h1:MnPn0hbcDkSJt6TlpI15XImHKK6IqaOwBUGPKyMnJnE= -github.com/metacubex/sing-shadowsocks2 v0.2.5/go.mod h1:Zyh+rAQRyevYfG/COCvDs1c/YMhGqCuknn7QrGmoQIw= +github.com/metacubex/sing-shadowsocks v0.2.12 h1:Wqzo8bYXrK5aWqxu/TjlTnYZzAKtKsaFQBdr6IHFaBE= +github.com/metacubex/sing-shadowsocks v0.2.12/go.mod h1:2e5EIaw0rxKrm1YTRmiMnDulwbGxH9hAFlrwQLQMQkU= +github.com/metacubex/sing-shadowsocks2 v0.2.6 h1:ZR1kYT0f0Vi64iQSS09OdhFfppiNkh7kjgRdMm0SB98= +github.com/metacubex/sing-shadowsocks2 v0.2.6/go.mod h1:vOEbfKC60txi0ca+yUlqEwOGc3Obl6cnSgx9Gf45KjE= github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 h1:gXU+MYPm7Wme3/OAY2FFzVq9d9GxPHOqu5AQfg/ddhI= github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2/go.mod h1:mbfboaXauKJNIHJYxQRa+NJs4JU9NZfkA+I33dS2+9E= -github.com/metacubex/sing-tun v0.4.7-0.20250801130030-308b828865ae h1:hKYGuBaJBLqNmySrYrOGDXXsyxXNaR8omO96QDsRI2s= -github.com/metacubex/sing-tun v0.4.7-0.20250801130030-308b828865ae/go.mod h1:2YywXPWW8Z97kTH7RffOeykKzU+l0aiKlglWV1PAS64= +github.com/metacubex/sing-tun v0.4.7 h1:ZDY/W+1c7PeWWKeKRyUo18fySF/TWjB0i5ui81Ar778= +github.com/metacubex/sing-tun v0.4.7/go.mod h1:xHecZRwBnKWe6zG9amAK9cXf91lF6blgjBqm+VvOrmU= github.com/metacubex/sing-vmess v0.2.4-0.20250731011226-ea28d589924d h1:koSVAQiYqU/qtJ527e+wbsEPvTaM39HW75LSvh04IT0= github.com/metacubex/sing-vmess v0.2.4-0.20250731011226-ea28d589924d/go.mod h1:21R5R1u90uUvBQF0owoooEu96/SAYYD56nDrwm6nFaM= github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f h1:Sr/DYKYofKHKc4GF3qkRGNuj6XA6c0eqPgEDN+VAsYU= @@ -279,5 +281,3 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= -lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= diff --git a/mihomo/transport/restls/restls.go b/mihomo/transport/restls/restls.go index 95ac698907..200c9dad83 100644 --- a/mihomo/transport/restls/restls.go +++ b/mihomo/transport/restls/restls.go @@ -4,7 +4,7 @@ import ( "context" "net" - tls "github.com/3andne/restls-client-go" + tls "github.com/metacubex/restls-client-go" ) const ( diff --git a/mihomo/transport/tuic/v4/protocol.go b/mihomo/transport/tuic/v4/protocol.go index cb04fad595..bea1b2f3bb 100644 --- a/mihomo/transport/tuic/v4/protocol.go +++ b/mihomo/transport/tuic/v4/protocol.go @@ -8,8 +8,8 @@ import ( "net/netip" "strconv" + "github.com/metacubex/blake3" "github.com/metacubex/quic-go" - "lukechampine.com/blake3" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/transport/socks5" diff --git a/mihomo/transport/vless/conn.go b/mihomo/transport/vless/conn.go index 02224892e2..180a9969d2 100644 --- a/mihomo/transport/vless/conn.go +++ b/mihomo/transport/vless/conn.go @@ -105,26 +105,20 @@ func (vc *Conn) sendRequest(p []byte) bool { } } - var buffer *buf.Buffer - if vc.IsXTLSVisionEnabled() { - buffer = buf.New() - defer buffer.Release() - } else { - requestLen := 1 // protocol version - requestLen += 16 // UUID - requestLen += 1 // addons length - requestLen += len(addonsBytes) - requestLen += 1 // command - if !vc.dst.Mux { - requestLen += 2 // port - requestLen += 1 // addr type - requestLen += len(vc.dst.Addr) - } - requestLen += len(p) - - buffer = buf.NewSize(requestLen) - defer buffer.Release() + requestLen := 1 // protocol version + requestLen += 16 // UUID + requestLen += 1 // addons length + requestLen += len(addonsBytes) + requestLen += 1 // command + if !vc.dst.Mux { + requestLen += 2 // port + requestLen += 1 // addr type + requestLen += len(vc.dst.Addr) } + requestLen += len(p) + + buffer := buf.NewSize(requestLen) + defer buffer.Release() buf.Must( buffer.WriteByte(Version), // protocol version @@ -182,10 +176,6 @@ func (vc *Conn) NeedHandshake() bool { return vc.needHandshake } -func (vc *Conn) IsXTLSVisionEnabled() bool { - return vc.addons != nil && vc.addons.Flow == XRV -} - // newConn return a Conn instance func newConn(conn net.Conn, client *Client, dst *DstAddr) (net.Conn, error) { c := &Conn{ diff --git a/mihomo/transport/vless/encryption/client.go b/mihomo/transport/vless/encryption/client.go index 3bef4d94eb..b6e2c19dfd 100644 --- a/mihomo/transport/vless/encryption/client.go +++ b/mihomo/transport/vless/encryption/client.go @@ -4,11 +4,13 @@ import ( "bytes" "crypto/cipher" "crypto/rand" + "crypto/sha256" "errors" "fmt" "io" "net" "runtime" + "strings" "sync" "time" @@ -36,13 +38,12 @@ func init() { type ClientInstance struct { sync.RWMutex - nfsEKey *mlkem.EncapsulationKey768 - nfsEKeyBytes []byte - xor uint32 - minutes time.Duration - expire time.Time - baseKey []byte - ticket []byte + nfsEKey *mlkem.EncapsulationKey768 + xorKey []byte + minutes time.Duration + expire time.Time + baseKey []byte + ticket []byte } type ClientConn struct { @@ -59,10 +60,17 @@ type ClientConn struct { } func (i *ClientInstance) Init(nfsEKeyBytes []byte, xor uint32, minutes time.Duration) (err error) { + if i.nfsEKey != nil { + err = errors.New("already initialized") + return + } i.nfsEKey, err = mlkem.NewEncapsulationKey768(nfsEKeyBytes) + if err != nil { + return + } if xor > 0 { - i.nfsEKeyBytes = nfsEKeyBytes - i.xor = xor + xorKey := sha256.Sum256(nfsEKeyBytes) + i.xorKey = xorKey[:] } i.minutes = minutes return @@ -72,8 +80,8 @@ func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) { if i.nfsEKey == nil { return nil, errors.New("uninitialized") } - if i.xor > 0 { - conn = NewXorConn(conn, i.nfsEKeyBytes) + if i.xorKey != nil { + conn = NewXorConn(conn, i.xorKey) } c := &ClientConn{Conn: conn} @@ -107,16 +115,16 @@ func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) { if _, err := c.Conn.Write(clientHello); err != nil { return nil, err } - // client can send more padding / NFS AEAD messages if needed + // client can send more paddings / NFS AEAD messages if needed - _, t, l, err := ReadAndDecodeHeader(c.Conn) + _, t, l, err := ReadAndDiscardPaddings(c.Conn) // allow paddings before server hello if err != nil { return nil, err } + if t != 1 { return nil, fmt.Errorf("unexpected type %v, expect random hello", t) } - peerRandomHello := make([]byte, 1088+21) if l != len(peerRandomHello) { return nil, fmt.Errorf("unexpected length %v for random hello", l) @@ -193,27 +201,9 @@ func (c *ClientConn) Read(b []byte) (int, error) { return 0, nil } if c.peerAead == nil { - var t byte - var l int - var err error - if c.instance == nil { // from 1-RTT - for { - if _, t, l, err = ReadAndDecodeHeader(c.Conn); err != nil { - return 0, err - } - if t != 23 { - break - } - if _, err := io.ReadFull(c.Conn, make([]byte, l)); err != nil { - return 0, err - } - } - } else { - h := make([]byte, 5) - if _, err := io.ReadFull(c.Conn, h); err != nil { - return 0, err - } - if t, l, err = DecodeHeader(h); err != nil { + _, t, l, err := ReadAndDiscardPaddings(c.Conn) // allow paddings before random hello + if err != nil { + if c.instance != nil && strings.HasPrefix(err.Error(), "invalid header: ") { // 0-RTT's 0-RTT c.instance.Lock() if bytes.Equal(c.ticket, c.instance.ticket) { c.instance.expire = time.Now() // expired @@ -221,6 +211,7 @@ func (c *ClientConn) Read(b []byte) (int, error) { c.instance.Unlock() return 0, errors.New("new handshake needed") } + return 0, err } if t != 0 { return 0, fmt.Errorf("unexpected type %v, expect server random", t) diff --git a/mihomo/transport/vless/encryption/common.go b/mihomo/transport/vless/encryption/common.go index cd4a93c5de..151a48e40a 100644 --- a/mihomo/transport/vless/encryption/common.go +++ b/mihomo/transport/vless/encryption/common.go @@ -45,10 +45,10 @@ func DecodeHeader(h []byte) (t byte, l int, err error) { } else if h[0] == 1 && h[1] == 1 && h[2] == 1 { t = 1 } else { - h = nil + l = 0 } - if h == nil || l < 17 || l > 17000 { // TODO: TLSv1.3 max length - err = fmt.Errorf("invalid header: %v", h[:5]) + if l < 17 || l > 17000 { // TODO: TLSv1.3 max length + err = fmt.Errorf("invalid header: %v", h[:5]) // DO NOT CHANGE: relied by client's Read() } return } @@ -62,6 +62,17 @@ func ReadAndDecodeHeader(conn net.Conn) (h []byte, t byte, l int, err error) { return } +func ReadAndDiscardPaddings(conn net.Conn) (h []byte, t byte, l int, err error) { + for { + if h, t, l, err = ReadAndDecodeHeader(conn); err != nil || t != 23 { + return + } + if _, err = io.ReadFull(conn, make([]byte, l)); err != nil { + return + } + } +} + func NewAead(c byte, secret, salt, info []byte) (aead cipher.AEAD) { key := make([]byte, 32) hkdf.New(sha256.New, secret, salt, info).Read(key) diff --git a/mihomo/transport/vless/encryption/doc.go b/mihomo/transport/vless/encryption/doc.go index 96976ed920..900866ec05 100644 --- a/mihomo/transport/vless/encryption/doc.go +++ b/mihomo/transport/vless/encryption/doc.go @@ -9,4 +9,6 @@ // https://github.com/XTLS/Xray-core/commit/1720be168fa069332c418503d30341fc6e01df7f // https://github.com/XTLS/Xray-core/commit/0fd7691d6b28e05922d7a5a9313d97745a51ea63 // https://github.com/XTLS/Xray-core/commit/09cc92c61d9067e0d65c1cae9124664ecfc78f43 +// https://github.com/XTLS/Xray-core/commit/2807ee432a1fbeb301815647189eacd650b12a8b +// https://github.com/XTLS/Xray-core/commit/bfe4820f2f086daf639b1957eb23dc13c843cad1 package encryption diff --git a/mihomo/transport/vless/encryption/server.go b/mihomo/transport/vless/encryption/server.go index 2accabe7ca..f67c9720ce 100644 --- a/mihomo/transport/vless/encryption/server.go +++ b/mihomo/transport/vless/encryption/server.go @@ -4,6 +4,7 @@ import ( "bytes" "crypto/cipher" "crypto/rand" + "crypto/sha256" "errors" "fmt" "io" @@ -23,12 +24,11 @@ type ServerSession struct { type ServerInstance struct { sync.RWMutex - nfsDKey *mlkem.DecapsulationKey768 - nfsEKeyBytes []byte - xor uint32 - minutes time.Duration - sessions map[[21]byte]*ServerSession - closed bool + nfsDKey *mlkem.DecapsulationKey768 + xorKey []byte + minutes time.Duration + sessions map[[21]byte]*ServerSession + closed bool } type ServerConn struct { @@ -45,10 +45,17 @@ type ServerConn struct { } func (i *ServerInstance) Init(nfsDKeySeed []byte, xor uint32, minutes time.Duration) (err error) { + if i.nfsDKey != nil { + err = errors.New("already initialized") + return + } i.nfsDKey, err = mlkem.NewDecapsulationKey768(nfsDKeySeed) + if err != nil { + return + } if xor > 0 { - i.nfsEKeyBytes = i.nfsDKey.EncapsulationKey().Bytes() - i.xor = xor + xorKey := sha256.Sum256(i.nfsDKey.EncapsulationKey().Bytes()) + i.xorKey = xorKey[:] } if minutes > 0 { i.minutes = minutes @@ -85,18 +92,15 @@ func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) { if i.nfsDKey == nil { return nil, errors.New("uninitialized") } - if i.xor > 0 { - conn = NewXorConn(conn, i.nfsEKeyBytes) + if i.xorKey != nil { + conn = NewXorConn(conn, i.xorKey) } c := &ServerConn{Conn: conn} - _, t, l, err := ReadAndDecodeHeader(c.Conn) + _, t, l, err := ReadAndDiscardPaddings(c.Conn) // allow paddings before client/ticket hello if err != nil { return nil, err } - if t == 23 { - return nil, errors.New("unexpected data") - } if t == 0 { if i.minutes == 0 { @@ -113,9 +117,13 @@ func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) { s := i.sessions[[21]byte(peerTicketHello)] i.RUnlock() if s == nil { - noise := make([]byte, randBetween(100, 1000)) - rand.Read(noise) - c.Conn.Write(noise) // make client do new handshake + noises := make([]byte, randBetween(100, 1000)) + var err error + for err == nil { + rand.Read(noises) + _, _, err = DecodeHeader(noises) + } + c.Conn.Write(noises) // make client do new handshake return nil, errors.New("expired ticket") } if _, replay := s.randoms.LoadOrStore([32]byte(peerTicketHello[21:]), true); replay { @@ -165,7 +173,7 @@ func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) { if _, err := c.Conn.Write(serverHello); err != nil { return nil, err } - // server can send more padding / PFS AEAD messages if needed + // server can send more paddings / PFS AEAD messages if needed if i.minutes > 0 { i.Lock() @@ -185,20 +193,10 @@ func (c *ServerConn) Read(b []byte) (int, error) { return 0, nil } if c.peerAead == nil { - if c.peerRandom == nil { // from 1-RTT - var t byte - var l int - var err error - for { - if _, t, l, err = ReadAndDecodeHeader(c.Conn); err != nil { - return 0, err - } - if t != 23 { - break - } - if _, err := io.ReadFull(c.Conn, make([]byte, l)); err != nil { - return 0, err - } + if c.peerRandom == nil { // 1-RTT's 0-RTT + _, t, l, err := ReadAndDiscardPaddings(c.Conn) // allow paddings before ticket hello + if err != nil { + return 0, err } if t != 0 { return 0, fmt.Errorf("unexpected type %v, expect ticket hello", t) diff --git a/mihomo/transport/vless/encryption/xor.go b/mihomo/transport/vless/encryption/xor.go index 696702bc69..64828eaa26 100644 --- a/mihomo/transport/vless/encryption/xor.go +++ b/mihomo/transport/vless/encryption/xor.go @@ -18,11 +18,11 @@ type XorConn struct { } func NewXorConn(conn net.Conn, key []byte) *XorConn { - return &XorConn{Conn: conn, key: key[:16]} + return &XorConn{Conn: conn, key: key} //chacha20.NewUnauthenticatedCipher() } -func (c *XorConn) Write(b []byte) (int, error) { // two records at most +func (c *XorConn) Write(b []byte) (int, error) { // whole one/two records if len(b) == 0 { return 0, nil } @@ -34,10 +34,10 @@ func (c *XorConn) Write(b []byte) (int, error) { // two records at most c.ctr = cipher.NewCTR(block, iv) } t, l, _ := DecodeHeader(b) - if t != 23 { - l += 10 // 5+l+5 - } else { + if t == 23 { // single 23 l = 5 + } else { // 1/0 + 23, or noises only + l += 10 } c.ctr.XORKeyStream(b[:l], b[:l]) // caller MUST discard b if iv != nil { @@ -73,8 +73,8 @@ func (c *XorConn) Read(b []byte) (int, error) { // 5-bytes, data, 5-bytes... return len(b), nil } c.peerCtr.XORKeyStream(b, b) - if c.isHeader { - if t, _, _ := DecodeHeader(b); t == 23 { // always 5-bytes + if c.isHeader { // always 5-bytes + if t, _, _ := DecodeHeader(b); t == 23 { c.skipNext = true } else { c.isHeader = false diff --git a/mihomo/transport/vless/vision/conn.go b/mihomo/transport/vless/vision/conn.go index 56684808cb..a0e83f7145 100644 --- a/mihomo/transport/vless/vision/conn.go +++ b/mihomo/transport/vless/vision/conn.go @@ -3,7 +3,6 @@ package vision import ( "bytes" "crypto/subtle" - gotls "crypto/tls" "encoding/binary" "errors" "fmt" @@ -12,7 +11,6 @@ import ( "github.com/metacubex/mihomo/common/buf" N "github.com/metacubex/mihomo/common/net" - tlsC "github.com/metacubex/mihomo/component/tls" "github.com/metacubex/mihomo/log" "github.com/gofrs/uuid/v5" @@ -181,17 +179,10 @@ func (vc *Conn) WriteBuffer(buffer *buf.Buffer) (err error) { buffer.Release() return err } - switch underlying := vc.tlsConn.(type) { - case *gotls.Conn: - if underlying.ConnectionState().Version != gotls.VersionTLS13 { - buffer.Release() - return ErrNotTLS13 - } - case *tlsC.UConn: - if underlying.ConnectionState().Version != tlsC.VersionTLS13 { - buffer.Release() - return ErrNotTLS13 - } + err = vc.checkTLSVersion() + if err != nil { + buffer.Release() + return err } vc.tlsConn = nil return nil diff --git a/mihomo/transport/vless/vision/vision.go b/mihomo/transport/vless/vision/vision.go index 00d0bf09dd..570a569c34 100644 --- a/mihomo/transport/vless/vision/vision.go +++ b/mihomo/transport/vless/vision/vision.go @@ -67,3 +67,21 @@ func NewConn(conn connWithUpstream, userUUID *uuid.UUID) (*Conn, error) { c.rawInput = (*bytes.Buffer)(unsafe.Add(p, r.Offset)) return c, nil } + +func (vc *Conn) checkTLSVersion() error { + switch underlying := vc.tlsConn.(type) { + case *gotls.Conn: + if underlying.ConnectionState().Version != gotls.VersionTLS13 { + return ErrNotTLS13 + } + case *tlsC.Conn: + if underlying.ConnectionState().Version != tlsC.VersionTLS13 { + return ErrNotTLS13 + } + case *tlsC.UConn: + if underlying.ConnectionState().Version != tlsC.VersionTLS13 { + return ErrNotTLS13 + } + } + return nil +} diff --git a/sing-box/.github/setup_legacy_go.sh b/sing-box/.github/setup_legacy_go.sh index 3f1a31ad65..d4617e7956 100755 --- a/sing-box/.github/setup_legacy_go.sh +++ b/sing-box/.github/setup_legacy_go.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -VERSION="1.23.6" +VERSION="1.23.12" mkdir -p $HOME/go cd $HOME/go diff --git a/sing-box/.github/workflows/build.yml b/sing-box/.github/workflows/build.yml index 2ccc81a8d3..25114dff51 100644 --- a/sing-box/.github/workflows/build.yml +++ b/sing-box/.github/workflows/build.yml @@ -40,13 +40,13 @@ jobs: version: ${{ steps.outputs.outputs.version }} steps: - name: Checkout - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 with: fetch-depth: 0 - name: Setup Go uses: actions/setup-go@v5 with: - go-version: ^1.24.6 + go-version: ^1.25.0 - name: Check input version if: github.event_name == 'workflow_dispatch' run: |- @@ -88,13 +88,14 @@ jobs: - { os: linux, arch: loong64, debian: loongarch64, rpm: loongarch64, openwrt: "loongarch64_generic" } - { os: windows, arch: amd64 } - - { os: windows, arch: amd64, legacy_go: true } + - { os: windows, arch: amd64, legacy_go123: true, legacy_name: "windows-7" } - { os: windows, arch: "386" } - - { os: windows, arch: "386", legacy_go: true } + - { os: windows, arch: "386", legacy_go123: true, legacy_name: "windows-7" } - { os: windows, arch: arm64 } - { os: darwin, arch: amd64 } - { os: darwin, arch: arm64 } + - { os: darwin, arch: amd64, legacy_go124: true, legacy_name: "macos-11" } - { os: android, arch: arm64, ndk: "aarch64-linux-android21" } - { os: android, arch: arm, ndk: "armv7a-linux-androideabi21" } @@ -102,28 +103,33 @@ jobs: - { os: android, arch: "386", ndk: "i686-linux-android21" } steps: - name: Checkout - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 with: fetch-depth: 0 - name: Setup Go - if: ${{ ! matrix.legacy_go }} + if: ${{ ! (matrix.legacy_go123 || matrix.legacy_go124) }} uses: actions/setup-go@v5 with: - go-version: ^1.24.6 - - name: Cache Legacy Go - if: matrix.require_legacy_go + go-version: ^1.25.0 + - name: Setup Go 1.24 + if: matrix.legacy_go124 + uses: actions/setup-go@v5 + with: + go-version: ~1.24.6 + - name: Cache Go 1.23 + if: matrix.legacy_go123 id: cache-legacy-go uses: actions/cache@v4 with: path: | ~/go/go_legacy - key: go_legacy_1236 - - name: Setup Legacy Go - if: matrix.legacy_go && steps.cache-legacy-go.outputs.cache-hit != 'true' + key: go_legacy_12312 + - name: Setup Go 1.23 + if: matrix.legacy_go123 && steps.cache-legacy-go.outputs.cache-hit != 'true' run: |- .github/setup_legacy_go.sh - - name: Setup Legacy Go 2 - if: matrix.legacy_go + - name: Setup Go 1.23 + if: matrix.legacy_go123 run: |- echo "PATH=$HOME/go/go_legacy/bin:$PATH" >> $GITHUB_ENV echo "GOROOT=$HOME/go/go_legacy" >> $GITHUB_ENV @@ -184,8 +190,8 @@ jobs: DIR_NAME="${DIR_NAME}-${{ matrix.go386 }}" elif [[ -n "${{ matrix.gomips }}" && "${{ matrix.gomips }}" != 'hardfloat' ]]; then DIR_NAME="${DIR_NAME}-${{ matrix.gomips }}" - elif [[ "${{ matrix.legacy_go }}" == 'true' ]]; then - DIR_NAME="${DIR_NAME}-legacy" + elif [[ -n "${{ matrix.legacy_name }}" ]]; then + DIR_NAME="${DIR_NAME}-legacy-${{ matrix.legacy_name }}" fi echo "DIR_NAME=${DIR_NAME}" >> "${GITHUB_ENV}" PKG_VERSION="${{ needs.calculate_version.outputs.version }}" @@ -277,7 +283,7 @@ jobs: - name: Upload artifact uses: actions/upload-artifact@v4 with: - name: binary-${{ matrix.os }}_${{ matrix.arch }}${{ matrix.goarm && format('v{0}', matrix.goarm) }}${{ matrix.go386 && format('_{0}', matrix.go386) }}${{ matrix.gomips && format('_{0}', matrix.gomips) }}${{ matrix.legacy_go && '-legacy' || '' }} + name: binary-${{ matrix.os }}_${{ matrix.arch }}${{ matrix.goarm && format('v{0}', matrix.goarm) }}${{ matrix.go386 && format('_{0}', matrix.go386) }}${{ matrix.gomips && format('_{0}', matrix.gomips) }}${{ matrix.legacy_name && format('-legacy-{0}', matrix.legacy_name) }} path: "dist" build_android: name: Build Android @@ -287,14 +293,14 @@ jobs: - calculate_version steps: - name: Checkout - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 with: fetch-depth: 0 submodules: 'recursive' - name: Setup Go uses: actions/setup-go@v5 with: - go-version: ^1.24.6 + go-version: ^1.25.0 - name: Setup Android NDK id: setup-ndk uses: nttld/setup-ndk@v1 @@ -367,14 +373,14 @@ jobs: - calculate_version steps: - name: Checkout - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 with: fetch-depth: 0 submodules: 'recursive' - name: Setup Go uses: actions/setup-go@v5 with: - go-version: ^1.24.6 + go-version: ^1.25.0 - name: Setup Android NDK id: setup-ndk uses: nttld/setup-ndk@v1 @@ -464,7 +470,7 @@ jobs: steps: - name: Checkout if: matrix.if - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 with: fetch-depth: 0 submodules: 'recursive' @@ -472,7 +478,7 @@ jobs: if: matrix.if uses: actions/setup-go@v5 with: - go-version: ^1.24.6 + go-version: ^1.25.0 - name: Setup Xcode stable if: matrix.if && github.ref == 'refs/heads/main-next' run: |- @@ -624,7 +630,7 @@ jobs: - build_apple steps: - name: Checkout - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 with: fetch-depth: 0 - name: Cache ghr @@ -647,7 +653,7 @@ jobs: git tag v${{ needs.calculate_version.outputs.version }} -f echo "VERSION=${{ needs.calculate_version.outputs.version }}" >> "$GITHUB_ENV" - name: Download builds - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: path: dist merge-multiple: true diff --git a/sing-box/.github/workflows/docker.yml b/sing-box/.github/workflows/docker.yml index bcf210ab1a..a4432a21d0 100644 --- a/sing-box/.github/workflows/docker.yml +++ b/sing-box/.github/workflows/docker.yml @@ -39,7 +39,7 @@ jobs: echo "ref=$ref" echo "ref=$ref" >> $GITHUB_OUTPUT - name: Checkout - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 with: ref: ${{ steps.ref.outputs.ref }} fetch-depth: 0 @@ -107,7 +107,7 @@ jobs: echo "latest=$latest" echo "latest=$latest" >> $GITHUB_OUTPUT - name: Download digests - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: path: /tmp/digests pattern: digests-* diff --git a/sing-box/.github/workflows/lint.yml b/sing-box/.github/workflows/lint.yml index 08d5440215..0a8cbd1ae0 100644 --- a/sing-box/.github/workflows/lint.yml +++ b/sing-box/.github/workflows/lint.yml @@ -22,15 +22,15 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 with: fetch-depth: 0 - name: Setup Go uses: actions/setup-go@v5 with: - go-version: ^1.24.6 + go-version: ~1.24.6 - name: golangci-lint - uses: golangci/golangci-lint-action@v6 + uses: golangci/golangci-lint-action@v8 with: version: latest args: --timeout=30m diff --git a/sing-box/.github/workflows/linux.yml b/sing-box/.github/workflows/linux.yml index ad0c8af594..a705d7ff19 100644 --- a/sing-box/.github/workflows/linux.yml +++ b/sing-box/.github/workflows/linux.yml @@ -24,13 +24,13 @@ jobs: version: ${{ steps.outputs.outputs.version }} steps: - name: Checkout - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 with: fetch-depth: 0 - name: Setup Go uses: actions/setup-go@v5 with: - go-version: ^1.24.6 + go-version: ^1.25.0 - name: Check input version if: github.event_name == 'workflow_dispatch' run: |- @@ -65,13 +65,13 @@ jobs: - { os: linux, arch: loong64, debian: loongarch64, rpm: loongarch64 } steps: - name: Checkout - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 with: fetch-depth: 0 - name: Setup Go uses: actions/setup-go@v5 with: - go-version: ^1.24.6 + go-version: ^1.25.0 - name: Setup Android NDK if: matrix.os == 'android' uses: nttld/setup-ndk@v1 @@ -171,7 +171,7 @@ jobs: - build steps: - name: Checkout - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 with: fetch-depth: 0 - name: Set tag @@ -180,7 +180,7 @@ jobs: git tag v${{ needs.calculate_version.outputs.version }} -f echo "VERSION=${{ needs.calculate_version.outputs.version }}" >> "$GITHUB_ENV" - name: Download builds - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: path: dist merge-multiple: true diff --git a/sing-box/.golangci.yml b/sing-box/.golangci.yml index 9cb20ee801..de2aa5a6eb 100644 --- a/sing-box/.golangci.yml +++ b/sing-box/.golangci.yml @@ -1,27 +1,6 @@ -linters: - disable-all: true - enable: - - gofumpt - - govet - - gci - - staticcheck - - paralleltest - - ineffassign - -linters-settings: - gci: - custom-order: true - sections: - - standard - - prefix(github.com/sagernet/) - - default - staticcheck: - checks: - - all - - -SA1003 - +version: "2" run: - go: "1.23" + go: "1.24" build-tags: - with_gvisor - with_quic @@ -30,7 +9,51 @@ run: - with_utls - with_acme - with_clash_api - -issues: - exclude-dirs: - - transport/simple-obfs +linters: + default: none + enable: + - govet + - ineffassign + - paralleltest + - staticcheck + settings: + staticcheck: + checks: + - all + - -S1000 + - -S1008 + - -S1017 + - -ST1003 + - -QF1001 + - -QF1003 + - -QF1008 + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + paths: + - transport/simple-obfs + - third_party$ + - builtin$ + - examples$ +formatters: + enable: + - gci + - gofumpt + settings: + gci: + sections: + - standard + - prefix(github.com/sagernet/) + - default + custom-order: true + exclusions: + generated: lax + paths: + - transport/simple-obfs + - third_party$ + - builtin$ + - examples$ diff --git a/sing-box/Dockerfile b/sing-box/Dockerfile index fecd98a6f2..359ae293eb 100644 --- a/sing-box/Dockerfile +++ b/sing-box/Dockerfile @@ -1,4 +1,4 @@ -FROM --platform=$BUILDPLATFORM golang:1.24-alpine AS builder +FROM --platform=$BUILDPLATFORM golang:1.25-alpine AS builder LABEL maintainer="nekohasekai " COPY . /go/src/github.com/sagernet/sing-box WORKDIR /go/src/github.com/sagernet/sing-box diff --git a/sing-box/Makefile b/sing-box/Makefile index 3c0c152d79..43f93c8cef 100644 --- a/sing-box/Makefile +++ b/sing-box/Makefile @@ -45,7 +45,7 @@ lint: GOOS=freebsd golangci-lint run ./... lint_install: - go install -v github.com/golangci/golangci-lint/cmd/golangci-lint@latest + go install -v github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest proto: @go run ./cmd/internal/protogen @@ -245,8 +245,8 @@ lib: go run ./cmd/internal/build_libbox -target ios lib_install: - go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.1.7 - go install -v github.com/sagernet/gomobile/cmd/gobind@v0.1.7 + go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.1.8 + go install -v github.com/sagernet/gomobile/cmd/gobind@v0.1.8 docs: venv/bin/mkdocs serve diff --git a/sing-box/adapter/inbound.go b/sing-box/adapter/inbound.go index 0de4c79905..edba84472b 100644 --- a/sing-box/adapter/inbound.go +++ b/sing-box/adapter/inbound.go @@ -135,8 +135,7 @@ func ExtendContext(ctx context.Context) (context.Context, *InboundContext) { func OverrideContext(ctx context.Context) context.Context { if metadata := ContextFrom(ctx); metadata != nil { - var newMetadata InboundContext - newMetadata = *metadata + newMetadata := *metadata return WithContext(ctx, &newMetadata) } return ctx diff --git a/sing-box/cmd/internal/tun_bench/main.go b/sing-box/cmd/internal/tun_bench/main.go index abad8c34f1..e62841dc9e 100644 --- a/sing-box/cmd/internal/tun_bench/main.go +++ b/sing-box/cmd/internal/tun_bench/main.go @@ -151,9 +151,7 @@ func testOnce(boxPath string, stackName string, mtu int, multiThread bool, flags var sudoArgs []string if len(flags) > 0 { sudoArgs = append(sudoArgs, "env") - for _, flag := range flags { - sudoArgs = append(sudoArgs, flag) - } + sudoArgs = append(sudoArgs, flags...) } sudoArgs = append(sudoArgs, boxPath, "run", "-c", tempConfig.Name()) boxProcess := shell.Exec("sudo", sudoArgs...) diff --git a/sing-box/cmd/sing-box/cmd_rule_set_compile.go b/sing-box/cmd/sing-box/cmd_rule_set_compile.go index 0c44a2a1ed..73655b12ea 100644 --- a/sing-box/cmd/sing-box/cmd_rule_set_compile.go +++ b/sing-box/cmd/sing-box/cmd_rule_set_compile.go @@ -6,8 +6,10 @@ import ( "strings" "github.com/sagernet/sing-box/common/srs" + C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" + "github.com/sagernet/sing-box/route/rule" "github.com/sagernet/sing/common/json" "github.com/spf13/cobra" @@ -69,7 +71,7 @@ func compileRuleSet(sourcePath string) error { if err != nil { return err } - err = srs.Write(outputFile, plainRuleSet.Options, plainRuleSet.Version) + err = srs.Write(outputFile, plainRuleSet.Options, downgradeRuleSetVersion(plainRuleSet.Version, plainRuleSet.Options)) if err != nil { outputFile.Close() os.Remove(outputPath) @@ -78,3 +80,18 @@ func compileRuleSet(sourcePath string) error { outputFile.Close() return nil } + +func downgradeRuleSetVersion(version uint8, options option.PlainRuleSet) uint8 { + if version == C.RuleSetVersion4 && !rule.HasHeadlessRule(options.Rules, func(rule option.DefaultHeadlessRule) bool { + return rule.NetworkInterfaceAddress != nil && rule.NetworkInterfaceAddress.Size() > 0 || + len(rule.DefaultInterfaceAddress) > 0 + }) { + version = C.RuleSetVersion3 + } + if version == C.RuleSetVersion3 && !rule.HasHeadlessRule(options.Rules, func(rule option.DefaultHeadlessRule) bool { + return len(rule.NetworkType) > 0 || rule.NetworkIsExpensive || rule.NetworkIsConstrained + }) { + version = C.RuleSetVersion2 + } + return version +} diff --git a/sing-box/common/dialer/default.go b/sing-box/common/dialer/default.go index 4b9cf71c65..5337934afa 100644 --- a/sing-box/common/dialer/default.go +++ b/sing-box/common/dialer/default.go @@ -271,7 +271,7 @@ func (d *DefaultDialer) DialParallelInterface(ctx context.Context, network strin } else { dialer = d.udpDialer4 } - fastFallback := time.Now().Sub(d.networkLastFallback.Load()) < C.TCPTimeout + fastFallback := time.Since(d.networkLastFallback.Load()) < C.TCPTimeout var ( conn net.Conn isPrimary bool diff --git a/sing-box/common/dialer/tfo.go b/sing-box/common/dialer/tfo.go index 8ea59ca62b..cd1a2a2226 100644 --- a/sing-box/common/dialer/tfo.go +++ b/sing-box/common/dialer/tfo.go @@ -10,6 +10,8 @@ import ( "sync" "time" + "github.com/sagernet/sing/common" + "github.com/sagernet/sing/common/atomic" "github.com/sagernet/sing/common/bufio" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" @@ -22,7 +24,7 @@ type slowOpenConn struct { ctx context.Context network string destination M.Socksaddr - conn net.Conn + conn atomic.Pointer[net.TCPConn] create chan struct{} done chan struct{} access sync.Mutex @@ -50,22 +52,25 @@ func DialSlowContext(dialer *tcpDialer, ctx context.Context, network string, des } func (c *slowOpenConn) Read(b []byte) (n int, err error) { - if c.conn == nil { - select { - case <-c.create: - if c.err != nil { - return 0, c.err - } - case <-c.done: - return 0, os.ErrClosed - } + conn := c.conn.Load() + if conn != nil { + return conn.Read(b) + } + select { + case <-c.create: + if c.err != nil { + return 0, c.err + } + return c.conn.Load().Read(b) + case <-c.done: + return 0, os.ErrClosed } - return c.conn.Read(b) } func (c *slowOpenConn) Write(b []byte) (n int, err error) { - if c.conn != nil { - return c.conn.Write(b) + tcpConn := c.conn.Load() + if tcpConn != nil { + return tcpConn.Write(b) } c.access.Lock() defer c.access.Unlock() @@ -74,7 +79,7 @@ func (c *slowOpenConn) Write(b []byte) (n int, err error) { if c.err != nil { return 0, c.err } - return c.conn.Write(b) + return c.conn.Load().Write(b) case <-c.done: return 0, os.ErrClosed default: @@ -83,7 +88,7 @@ func (c *slowOpenConn) Write(b []byte) (n int, err error) { if err != nil { c.err = err } else { - c.conn = conn + c.conn.Store(conn.(*net.TCPConn)) } n = len(b) close(c.create) @@ -93,70 +98,77 @@ func (c *slowOpenConn) Write(b []byte) (n int, err error) { func (c *slowOpenConn) Close() error { c.closeOnce.Do(func() { close(c.done) - if c.conn != nil { - c.conn.Close() + conn := c.conn.Load() + if conn != nil { + conn.Close() } }) return nil } func (c *slowOpenConn) LocalAddr() net.Addr { - if c.conn == nil { + conn := c.conn.Load() + if conn == nil { return M.Socksaddr{} } - return c.conn.LocalAddr() + return conn.LocalAddr() } func (c *slowOpenConn) RemoteAddr() net.Addr { - if c.conn == nil { + conn := c.conn.Load() + if conn == nil { return M.Socksaddr{} } - return c.conn.RemoteAddr() + return conn.RemoteAddr() } func (c *slowOpenConn) SetDeadline(t time.Time) error { - if c.conn == nil { + conn := c.conn.Load() + if conn == nil { return os.ErrInvalid } - return c.conn.SetDeadline(t) + return conn.SetDeadline(t) } func (c *slowOpenConn) SetReadDeadline(t time.Time) error { - if c.conn == nil { + conn := c.conn.Load() + if conn == nil { return os.ErrInvalid } - return c.conn.SetReadDeadline(t) + return conn.SetReadDeadline(t) } func (c *slowOpenConn) SetWriteDeadline(t time.Time) error { - if c.conn == nil { + conn := c.conn.Load() + if conn == nil { return os.ErrInvalid } - return c.conn.SetWriteDeadline(t) + return conn.SetWriteDeadline(t) } func (c *slowOpenConn) Upstream() any { - return c.conn + return common.PtrOrNil(c.conn.Load()) } func (c *slowOpenConn) ReaderReplaceable() bool { - return c.conn != nil + return c.conn.Load() != nil } func (c *slowOpenConn) WriterReplaceable() bool { - return c.conn != nil + return c.conn.Load() != nil } func (c *slowOpenConn) LazyHeadroom() bool { - return c.conn == nil + return c.conn.Load() == nil } func (c *slowOpenConn) NeedHandshake() bool { - return c.conn == nil + return c.conn.Load() == nil } func (c *slowOpenConn) WriteTo(w io.Writer) (n int64, err error) { - if c.conn == nil { + conn := c.conn.Load() + if conn == nil { select { case <-c.create: if c.err != nil { @@ -166,5 +178,5 @@ func (c *slowOpenConn) WriteTo(w io.Writer) (n int64, err error) { return 0, c.err } } - return bufio.Copy(w, c.conn) + return bufio.Copy(w, c.conn.Load()) } diff --git a/sing-box/common/sniff/quic_blacklist.go b/sing-box/common/sniff/quic_blacklist.go index 7d5f91e79b..d5ff7bcf5a 100644 --- a/sing-box/common/sniff/quic_blacklist.go +++ b/sing-box/common/sniff/quic_blacklist.go @@ -17,8 +17,5 @@ var uQUICChrome115 = &ja3.ClientHello{ } func maybeUQUIC(fingerprint *ja3.ClientHello) bool { - if uQUICChrome115.Equals(fingerprint, true) { - return true - } - return false + return !uQUICChrome115.Equals(fingerprint, true) } diff --git a/sing-box/common/srs/binary.go b/sing-box/common/srs/binary.go index d7cda6eb83..96b578f5dd 100644 --- a/sing-box/common/srs/binary.go +++ b/sing-box/common/srs/binary.go @@ -12,6 +12,8 @@ import ( "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/domain" E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/json/badjson" + "github.com/sagernet/sing/common/json/badoption" "github.com/sagernet/sing/common/varbin" "go4.org/netipx" @@ -41,6 +43,8 @@ const ( ruleItemNetworkType ruleItemNetworkIsExpensive ruleItemNetworkIsConstrained + ruleItemNetworkInterfaceAddress + ruleItemDefaultInterfaceAddress ruleItemFinal uint8 = 0xFF ) @@ -230,6 +234,51 @@ func readDefaultRule(reader varbin.Reader, recover bool) (rule option.DefaultHea rule.NetworkIsExpensive = true case ruleItemNetworkIsConstrained: rule.NetworkIsConstrained = true + case ruleItemNetworkInterfaceAddress: + rule.NetworkInterfaceAddress = new(badjson.TypedMap[option.InterfaceType, badoption.Listable[badoption.Prefixable]]) + var size uint64 + size, err = binary.ReadUvarint(reader) + if err != nil { + return + } + for i := uint64(0); i < size; i++ { + var key uint8 + err = binary.Read(reader, binary.BigEndian, &key) + if err != nil { + return + } + var value []badoption.Prefixable + var prefixCount uint64 + prefixCount, err = binary.ReadUvarint(reader) + if err != nil { + return + } + for j := uint64(0); j < prefixCount; j++ { + var prefix netip.Prefix + prefix, err = readPrefix(reader) + if err != nil { + return + } + value = append(value, badoption.Prefixable(prefix)) + } + rule.NetworkInterfaceAddress.Put(option.InterfaceType(key), value) + } + case ruleItemDefaultInterfaceAddress: + var value []badoption.Prefixable + var prefixCount uint64 + prefixCount, err = binary.ReadUvarint(reader) + if err != nil { + return + } + for j := uint64(0); j < prefixCount; j++ { + var prefix netip.Prefix + prefix, err = readPrefix(reader) + if err != nil { + return + } + value = append(value, badoption.Prefixable(prefix)) + } + rule.DefaultInterfaceAddress = value case ruleItemFinal: err = binary.Read(reader, binary.BigEndian, &rule.Invert) return @@ -346,7 +395,7 @@ func writeDefaultRule(writer varbin.Writer, rule option.DefaultHeadlessRule, gen } if len(rule.NetworkType) > 0 { if generateVersion < C.RuleSetVersion3 { - return E.New("network_type rule item is only supported in version 3 or later") + return E.New("`network_type` rule item is only supported in version 3 or later") } err = writeRuleItemUint8(writer, ruleItemNetworkType, rule.NetworkType) if err != nil { @@ -354,17 +403,67 @@ func writeDefaultRule(writer varbin.Writer, rule option.DefaultHeadlessRule, gen } } if rule.NetworkIsExpensive { + if generateVersion < C.RuleSetVersion3 { + return E.New("`network_is_expensive` rule item is only supported in version 3 or later") + } err = binary.Write(writer, binary.BigEndian, ruleItemNetworkIsExpensive) if err != nil { return err } } if rule.NetworkIsConstrained { + if generateVersion < C.RuleSetVersion3 { + return E.New("`network_is_constrained` rule item is only supported in version 3 or later") + } err = binary.Write(writer, binary.BigEndian, ruleItemNetworkIsConstrained) if err != nil { return err } } + if rule.NetworkInterfaceAddress != nil && rule.NetworkInterfaceAddress.Size() > 0 { + if generateVersion < C.RuleSetVersion4 { + return E.New("`network_interface_address` rule item is only supported in version 4 or later") + } + err = writer.WriteByte(ruleItemNetworkInterfaceAddress) + if err != nil { + return err + } + _, err = varbin.WriteUvarint(writer, uint64(rule.NetworkInterfaceAddress.Size())) + if err != nil { + return err + } + for _, entry := range rule.NetworkInterfaceAddress.Entries() { + err = binary.Write(writer, binary.BigEndian, uint8(entry.Key.Build())) + if err != nil { + return err + } + for _, rawPrefix := range entry.Value { + err = writePrefix(writer, rawPrefix.Build(netip.Prefix{})) + if err != nil { + return err + } + } + } + } + if len(rule.DefaultInterfaceAddress) > 0 { + if generateVersion < C.RuleSetVersion4 { + return E.New("`default_interface_address` rule item is only supported in version 4 or later") + } + err = writer.WriteByte(ruleItemDefaultInterfaceAddress) + if err != nil { + return err + } + _, err = varbin.WriteUvarint(writer, uint64(len(rule.DefaultInterfaceAddress))) + if err != nil { + return err + } + for _, rawPrefix := range rule.DefaultInterfaceAddress { + err = writePrefix(writer, rawPrefix.Build(netip.Prefix{})) + if err != nil { + return err + } + } + } if len(rule.WIFISSID) > 0 { err = writeRuleItemString(writer, ruleItemWIFISSID, rule.WIFISSID) if err != nil { diff --git a/sing-box/common/srs/ip_cidr.go b/sing-box/common/srs/ip_cidr.go new file mode 100644 index 0000000000..93ae84ad32 --- /dev/null +++ b/sing-box/common/srs/ip_cidr.go @@ -0,0 +1,33 @@ +package srs + +import ( + "encoding/binary" + "net/netip" + + M "github.com/sagernet/sing/common/metadata" + "github.com/sagernet/sing/common/varbin" +) + +func readPrefix(reader varbin.Reader) (netip.Prefix, error) { + addrSlice, err := varbin.ReadValue[[]byte](reader, binary.BigEndian) + if err != nil { + return netip.Prefix{}, err + } + prefixBits, err := varbin.ReadValue[uint8](reader, binary.BigEndian) + if err != nil { + return netip.Prefix{}, err + } + return netip.PrefixFrom(M.AddrFromIP(addrSlice), int(prefixBits)), nil +} + +func writePrefix(writer varbin.Writer, prefix netip.Prefix) error { + err := varbin.Write(writer, binary.BigEndian, prefix.Addr().AsSlice()) + if err != nil { + return err + } + err = binary.Write(writer, binary.BigEndian, uint8(prefix.Bits())) + if err != nil { + return err + } + return nil +} diff --git a/sing-box/common/tls/client.go b/sing-box/common/tls/client.go index 868e039d35..d45d6173b8 100644 --- a/sing-box/common/tls/client.go +++ b/sing-box/common/tls/client.go @@ -7,7 +7,6 @@ import ( "net" "os" - "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/common/badtls" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/option" @@ -16,7 +15,7 @@ import ( aTLS "github.com/sagernet/sing/common/tls" ) -func NewDialerFromOptions(ctx context.Context, router adapter.Router, dialer N.Dialer, serverAddress string, options option.OutboundTLSOptions) (N.Dialer, error) { +func NewDialerFromOptions(ctx context.Context, dialer N.Dialer, serverAddress string, options option.OutboundTLSOptions) (N.Dialer, error) { if !options.Enabled { return dialer, nil } @@ -43,12 +42,6 @@ func ClientHandshake(ctx context.Context, conn net.Conn, config Config) (Conn, e ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout) defer cancel() tlsConn, err := aTLS.ClientHandshake(ctx, conn, config) - var echErr *tls.ECHRejectionError - if errors.As(err, &echErr) && len(echErr.RetryConfigList) > 0 { - if echConfig, isECH := config.(ECHCapableConfig); isECH { - echConfig.SetECHConfigList(echErr.RetryConfigList) - } - } if err != nil { return nil, err } @@ -61,26 +54,57 @@ func ClientHandshake(ctx context.Context, conn net.Conn, config Config) (Conn, e return tlsConn, nil } -type Dialer struct { +type Dialer interface { + N.Dialer + DialTLSContext(ctx context.Context, destination M.Socksaddr) (Conn, error) +} + +type defaultDialer struct { dialer N.Dialer config Config } -func NewDialer(dialer N.Dialer, config Config) N.Dialer { - return &Dialer{dialer, config} +func NewDialer(dialer N.Dialer, config Config) Dialer { + return &defaultDialer{dialer, config} } -func (d *Dialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { - if network != N.NetworkTCP { +func (d *defaultDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { + if N.NetworkName(network) != N.NetworkTCP { return nil, os.ErrInvalid } - conn, err := d.dialer.DialContext(ctx, network, destination) + return d.DialTLSContext(ctx, destination) +} + +func (d *defaultDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { + return nil, os.ErrInvalid +} + +func (d *defaultDialer) DialTLSContext(ctx context.Context, destination M.Socksaddr) (Conn, error) { + return d.dialContext(ctx, destination, true) +} + +func (d *defaultDialer) dialContext(ctx context.Context, destination M.Socksaddr, echRetry bool) (Conn, error) { + conn, err := d.dialer.DialContext(ctx, N.NetworkTCP, destination) if err != nil { return nil, err } - return ClientHandshake(ctx, conn, d.config) + tlsConn, err := ClientHandshake(ctx, conn, d.config) + if err == nil { + return tlsConn, nil + } + conn.Close() + if echRetry { + var echErr *tls.ECHRejectionError + if errors.As(err, &echErr) && len(echErr.RetryConfigList) > 0 { + if echConfig, isECH := d.config.(ECHCapableConfig); isECH { + echConfig.SetECHConfigList(echErr.RetryConfigList) + } + } + return d.dialContext(ctx, destination, false) + } + return nil, err } -func (d *Dialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { - return nil, os.ErrInvalid +func (d *defaultDialer) Upstream() any { + return d.dialer } diff --git a/sing-box/common/tls/ech.go b/sing-box/common/tls/ech.go index f4434604cc..830a8d0823 100644 --- a/sing-box/common/tls/ech.go +++ b/sing-box/common/tls/ech.go @@ -125,7 +125,7 @@ func (s *ECHClientConfig) ClientHandshake(ctx context.Context, conn net.Conn) (a func (s *ECHClientConfig) fetchAndHandshake(ctx context.Context, conn net.Conn) (aTLS.Conn, error) { s.access.Lock() defer s.access.Unlock() - if len(s.ECHConfigList()) == 0 || s.lastTTL == 0 || time.Now().Sub(s.lastUpdate) > s.lastTTL { + if len(s.ECHConfigList()) == 0 || s.lastTTL == 0 || time.Since(s.lastUpdate) > s.lastTTL { message := &mDNS.Msg{ MsgHdr: mDNS.MsgHdr{ RecursionDesired: true, diff --git a/sing-box/constant/rule.go b/sing-box/constant/rule.go index 336c3b389a..7144145033 100644 --- a/sing-box/constant/rule.go +++ b/sing-box/constant/rule.go @@ -22,7 +22,8 @@ const ( RuleSetVersion1 = 1 + iota RuleSetVersion2 RuleSetVersion3 - RuleSetVersionCurrent = RuleSetVersion3 + RuleSetVersion4 + RuleSetVersionCurrent = RuleSetVersion4 ) const ( diff --git a/sing-box/dns/transport/local/resolv.go b/sing-box/dns/transport/local/resolv.go index 754d453927..afe679a211 100644 --- a/sing-box/dns/transport/local/resolv.go +++ b/sing-box/dns/transport/local/resolv.go @@ -104,7 +104,7 @@ func (c *dnsConfig) serverOffset() uint32 { return 0 } -func (conf *dnsConfig) nameList(name string) []string { +func (c *dnsConfig) nameList(name string) []string { l := len(name) rooted := l > 0 && name[l-1] == '.' if l > 254 || l == 254 && !rooted { @@ -118,15 +118,15 @@ func (conf *dnsConfig) nameList(name string) []string { return []string{name} } - hasNdots := strings.Count(name, ".") >= conf.ndots + hasNdots := strings.Count(name, ".") >= c.ndots name += "." // l++ - names := make([]string, 0, 1+len(conf.search)) + names := make([]string, 0, 1+len(c.search)) if hasNdots && !avoidDNS(name) { names = append(names, name) } - for _, suffix := range conf.search { + for _, suffix := range c.search { fqdn := name + suffix if !avoidDNS(fqdn) && len(fqdn) <= 254 { names = append(names, fqdn) diff --git a/sing-box/dns/transport/tls.go b/sing-box/dns/transport/tls.go index afa988cc73..9cb35fd9af 100644 --- a/sing-box/dns/transport/tls.go +++ b/sing-box/dns/transport/tls.go @@ -30,7 +30,7 @@ func RegisterTLS(registry *dns.TransportRegistry) { type TLSTransport struct { dns.TransportAdapter logger logger.ContextLogger - dialer N.Dialer + dialer tls.Dialer serverAddr M.Socksaddr tlsConfig tls.Config access sync.Mutex @@ -67,7 +67,7 @@ func NewTLSRaw(logger logger.ContextLogger, adapter dns.TransportAdapter, dialer return &TLSTransport{ TransportAdapter: adapter, logger: logger, - dialer: dialer, + dialer: tls.NewDialer(dialer, tlsConfig), serverAddr: serverAddr, tlsConfig: tlsConfig, } @@ -100,15 +100,10 @@ func (t *TLSTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.M return response, nil } } - tcpConn, err := t.dialer.DialContext(ctx, N.NetworkTCP, t.serverAddr) + tlsConn, err := t.dialer.DialTLSContext(ctx, t.serverAddr) if err != nil { return nil, err } - tlsConn, err := tls.ClientHandshake(ctx, tcpConn, t.tlsConfig) - if err != nil { - tcpConn.Close() - return nil, err - } return t.exchange(message, &tlsDNSConn{Conn: tlsConn}) } diff --git a/sing-box/docs/changelog.md b/sing-box/docs/changelog.md index e54a9d170d..67a94b28ec 100644 --- a/sing-box/docs/changelog.md +++ b/sing-box/docs/changelog.md @@ -2,11 +2,23 @@ icon: material/alert-decagram --- -#### 1.12.1 +#### 1.13.0-alpha.1 + +* Add interface address rule items **1** +* Fixes and improvements + +**1**: + +New interface address rules allow you to dynamically adjust rules based on your network environment. + +See [Route Rule](/configuration/route/rule/), [DNS Route Rule](/configuration/dns/rule/) +and [Headless Rule](/configuration/rule-set/headless-rule/). + +### 1.12.1 * Fixes and improvements -#### 1.12.0 +### 1.12.0 * Refactor DNS servers **1** * Add domain resolver options**2** diff --git a/sing-box/docs/configuration/dns/rule.md b/sing-box/docs/configuration/dns/rule.md index 5d5cb7c3a1..524b70a2c2 100644 --- a/sing-box/docs/configuration/dns/rule.md +++ b/sing-box/docs/configuration/dns/rule.md @@ -2,6 +2,12 @@ icon: material/alert-decagram --- +!!! quote "Changes in sing-box 1.13.0" + + :material-plus: [interface_address](#interface_address) + :material-plus: [network_interface_address](#network_interface_address) + :material-plus: [default_interface_address](#default_interface_address) + !!! quote "Changes in sing-box 1.12.0" :material-plus: [ip_accept_any](#ip_accept_any) @@ -130,6 +136,19 @@ icon: material/alert-decagram ], "network_is_expensive": false, "network_is_constrained": false, + "interface_address": { + "en0": [ + "2000::/3" + ] + }, + "network_interface_address": { + "wifi": [ + "2000::/3" + ] + }, + "default_interface_address": [ + "2000::/3" + ], "wifi_ssid": [ "My WIFI" ], @@ -359,6 +378,36 @@ such as Cellular or a Personal Hotspot (on Apple platforms). Match if network is in Low Data Mode. +#### interface_address + +!!! question "Since sing-box 1.13.0" + +!!! quote "" + + Only supported on Linux, Windows, and macOS. + +Match interface address. + +#### network_interface_address + +!!! question "Since sing-box 1.13.0" + +!!! quote "" + + Only supported in graphical clients on Android and Apple platforms. + +Matches network interface (same values as `network_type`) address. + +#### default_interface_address + +!!! question "Since sing-box 1.13.0" + +!!! quote "" + + Only supported on Linux, Windows, and macOS. + +Match default interface address. + #### wifi_ssid !!! quote "" diff --git a/sing-box/docs/configuration/dns/rule.zh.md b/sing-box/docs/configuration/dns/rule.zh.md index 8973eba2bc..bc812c77df 100644 --- a/sing-box/docs/configuration/dns/rule.zh.md +++ b/sing-box/docs/configuration/dns/rule.zh.md @@ -2,6 +2,12 @@ icon: material/alert-decagram --- +!!! quote "sing-box 1.13.0 中的更改" + + :material-plus: [interface_address](#interface_address) + :material-plus: [network_interface_address](#network_interface_address) + :material-plus: [default_interface_address](#default_interface_address) + !!! quote "sing-box 1.12.0 中的更改" :material-plus: [ip_accept_any](#ip_accept_any) @@ -130,6 +136,19 @@ icon: material/alert-decagram ], "network_is_expensive": false, "network_is_constrained": false, + "interface_address": { + "en0": [ + "2000::/3" + ] + }, + "network_interface_address": { + "wifi": [ + "2000::/3" + ] + }, + "default_interface_address": [ + "2000::/3" + ], "wifi_ssid": [ "My WIFI" ], @@ -358,6 +377,36 @@ Available values: `wifi`, `cellular`, `ethernet` and `other`. 匹配如果网络在低数据模式下。 +#### interface_address + +!!! question "自 sing-box 1.13.0 起" + +!!! quote "" + + 仅支持 Linux、Windows 和 macOS. + +匹配接口地址。 + +#### network_interface_address + +!!! question "自 sing-box 1.13.0 起" + +!!! quote "" + + 仅在 Android 与 Apple 平台图形客户端中支持。 + +匹配网络接口(可用值同 `network_type`)地址。 + +#### default_interface_address + +!!! question "自 sing-box 1.13.0 起" + +!!! quote "" + + 仅支持 Linux、Windows 和 macOS. + +匹配默认接口地址。 + #### wifi_ssid !!! quote "" diff --git a/sing-box/docs/configuration/route/rule.md b/sing-box/docs/configuration/route/rule.md index 43954a78c9..a4864e442e 100644 --- a/sing-box/docs/configuration/route/rule.md +++ b/sing-box/docs/configuration/route/rule.md @@ -2,6 +2,12 @@ icon: material/new-box --- +!!! quote "Changes in sing-box 1.13.0" + + :material-plus: [interface_address](#interface_address) + :material-plus: [network_interface_address](#network_interface_address) + :material-plus: [default_interface_address](#default_interface_address) + !!! quote "Changes in sing-box 1.11.0" :material-plus: [action](#action) @@ -128,6 +134,19 @@ icon: material/new-box ], "network_is_expensive": false, "network_is_constrained": false, + "interface_address": { + "en0": [ + "2000::/3" + ] + }, + "network_interface_address": { + "wifi": [ + "2000::/3" + ] + }, + "default_interface_address": [ + "2000::/3" + ], "wifi_ssid": [ "My WIFI" ], @@ -363,6 +382,36 @@ such as Cellular or a Personal Hotspot (on Apple platforms). Match if network is in Low Data Mode. +#### interface_address + +!!! question "Since sing-box 1.13.0" + +!!! quote "" + + Only supported on Linux, Windows, and macOS. + +Match interface address. + +#### network_interface_address + +!!! question "Since sing-box 1.13.0" + +!!! quote "" + + Only supported in graphical clients on Android and Apple platforms. + +Matches network interface (same values as `network_type`) address. + +#### default_interface_address + +!!! question "Since sing-box 1.13.0" + +!!! quote "" + + Only supported on Linux, Windows, and macOS. + +Match default interface address. + #### wifi_ssid !!! quote "" diff --git a/sing-box/docs/configuration/route/rule.zh.md b/sing-box/docs/configuration/route/rule.zh.md index 8deab2f332..d30dcad985 100644 --- a/sing-box/docs/configuration/route/rule.zh.md +++ b/sing-box/docs/configuration/route/rule.zh.md @@ -2,6 +2,12 @@ icon: material/new-box --- +!!! quote "sing-box 1.13.0 中的更改" + + :material-plus: [interface_address](#interface_address) + :material-plus: [network_interface_address](#network_interface_address) + :material-plus: [default_interface_address](#default_interface_address) + !!! quote "sing-box 1.11.0 中的更改" :material-plus: [action](#action) @@ -125,6 +131,19 @@ icon: material/new-box ], "network_is_expensive": false, "network_is_constrained": false, + "interface_address": { + "en0": [ + "2000::/3" + ] + }, + "network_interface_address": { + "wifi": [ + "2000::/3" + ] + }, + "default_interface_address": [ + "2000::/3" + ], "wifi_ssid": [ "My WIFI" ], @@ -337,7 +356,7 @@ icon: material/new-box 匹配网络类型。 -Available values: `wifi`, `cellular`, `ethernet` and `other`. +可用值: `wifi`, `cellular`, `ethernet` and `other`. #### network_is_expensive @@ -360,6 +379,36 @@ Available values: `wifi`, `cellular`, `ethernet` and `other`. 匹配如果网络在低数据模式下。 +#### interface_address + +!!! question "自 sing-box 1.13.0 起" + +!!! quote "" + + 仅支持 Linux、Windows 和 macOS. + +匹配接口地址。 + +#### network_interface_address + +!!! question "自 sing-box 1.13.0 起" + +!!! quote "" + + 仅在 Android 与 Apple 平台图形客户端中支持。 + +匹配网络接口(可用值同 `network_type`)地址。 + +#### default_interface_address + +!!! question "自 sing-box 1.13.0 起" + +!!! quote "" + + 仅支持 Linux、Windows 和 macOS. + +匹配默认接口地址。 + #### wifi_ssid !!! quote "" diff --git a/sing-box/docs/configuration/rule-set/headless-rule.md b/sing-box/docs/configuration/rule-set/headless-rule.md index bdad22f0dc..89cccd394e 100644 --- a/sing-box/docs/configuration/rule-set/headless-rule.md +++ b/sing-box/docs/configuration/rule-set/headless-rule.md @@ -2,6 +2,11 @@ icon: material/new-box --- +!!! quote "Changes in sing-box 1.13.0" + + :material-plus: [network_interface_address](#network_interface_address) + :material-plus: [default_interface_address](#default_interface_address) + !!! quote "Changes in sing-box 1.11.0" :material-plus: [network_type](#network_type) @@ -78,6 +83,14 @@ icon: material/new-box ], "network_is_expensive": false, "network_is_constrained": false, + "network_interface_address": { + "wifi": [ + "2000::/3" + ] + }, + "default_interface_address": [ + "2000::/3" + ], "wifi_ssid": [ "My WIFI" ], @@ -225,6 +238,26 @@ such as Cellular or a Personal Hotspot (on Apple platforms). Match if network is in Low Data Mode. +#### network_interface_address + +!!! question "Since sing-box 1.13.0" + +!!! quote "" + + Only supported in graphical clients on Android and Apple platforms. + +Matches network interface (same values as `network_type`) address. + +#### default_interface_address + +!!! question "Since sing-box 1.13.0" + +!!! quote "" + + Only supported on Linux, Windows, and macOS. + +Match default interface address. + #### wifi_ssid !!! quote "" diff --git a/sing-box/docs/configuration/rule-set/headless-rule.zh.md b/sing-box/docs/configuration/rule-set/headless-rule.zh.md index c5281504f6..d539d71091 100644 --- a/sing-box/docs/configuration/rule-set/headless-rule.zh.md +++ b/sing-box/docs/configuration/rule-set/headless-rule.zh.md @@ -2,6 +2,11 @@ icon: material/new-box --- +!!! quote "sing-box 1.13.0 中的更改" + + :material-plus: [network_interface_address](#network_interface_address) + :material-plus: [default_interface_address](#default_interface_address) + !!! quote "sing-box 1.11.0 中的更改" :material-plus: [network_type](#network_type) @@ -78,6 +83,14 @@ icon: material/new-box ], "network_is_expensive": false, "network_is_constrained": false, + "network_interface_address": { + "wifi": [ + "2000::/3" + ] + }, + "default_interface_address": [ + "2000::/3" + ], "wifi_ssid": [ "My WIFI" ], @@ -221,6 +234,26 @@ Available values: `wifi`, `cellular`, `ethernet` and `other`. 匹配如果网络在低数据模式下。 +#### network_interface_address + +!!! question "自 sing-box 1.13.0 起" + +!!! quote "" + + 仅在 Android 与 Apple 平台图形客户端中支持。 + +匹配网络接口(可用值同 `network_type`)地址。 + +#### default_interface_address + +!!! question "自 sing-box 1.13.0 起" + +!!! quote "" + + 仅支持 Linux、Windows 和 macOS. + +匹配默认接口地址。 + #### wifi_ssid !!! quote "" diff --git a/sing-box/docs/configuration/rule-set/source-format.md b/sing-box/docs/configuration/rule-set/source-format.md index 1dcc1d4470..47d620b1c6 100644 --- a/sing-box/docs/configuration/rule-set/source-format.md +++ b/sing-box/docs/configuration/rule-set/source-format.md @@ -2,6 +2,10 @@ icon: material/new-box --- +!!! quote "Changes in sing-box 1.13.0" + + :material-plus: version `4` + !!! quote "Changes in sing-box 1.11.0" :material-plus: version `3` @@ -36,6 +40,7 @@ Version of rule-set. * 1: sing-box 1.8.0: Initial rule-set version. * 2: sing-box 1.10.0: Optimized memory usages of `domain_suffix` rules in binary rule-sets. * 3: sing-box 1.11.0: Added `network_type`, `network_is_expensive` and `network_is_constrainted` rule items. +* 4: sing-box 1.13.0: Added `network_interface_address` and `default_interface_address` rule items. #### rules diff --git a/sing-box/docs/configuration/rule-set/source-format.zh.md b/sing-box/docs/configuration/rule-set/source-format.zh.md index 3dacaea7c8..30c0679f6e 100644 --- a/sing-box/docs/configuration/rule-set/source-format.zh.md +++ b/sing-box/docs/configuration/rule-set/source-format.zh.md @@ -2,6 +2,10 @@ icon: material/new-box --- +!!! quote "sing-box 1.13.0 中的更改" + + :material-plus: version `4` + !!! quote "sing-box 1.11.0 中的更改" :material-plus: version `3` @@ -36,6 +40,7 @@ icon: material/new-box * 1: sing-box 1.8.0: 初始规则集版本。 * 2: sing-box 1.10.0: 优化了二进制规则集中 `domain_suffix` 规则的内存使用。 * 3: sing-box 1.11.0: 添加了 `network_type`、 `network_is_expensive` 和 `network_is_constrainted` 规则项。 +* 4: sing-box 1.13.0: 添加了 `network_interface_address` 和 `default_interface_address` 规则项。 #### rules diff --git a/sing-box/experimental/libbox/command_urltest.go b/sing-box/experimental/libbox/command_urltest.go index 5dcb3d6797..907d16998c 100644 --- a/sing-box/experimental/libbox/command_urltest.go +++ b/sing-box/experimental/libbox/command_urltest.go @@ -62,10 +62,7 @@ func (s *CommandServer) handleURLTest(conn net.Conn) error { return false } _, isGroup := it.(adapter.OutboundGroup) - if isGroup { - return false - } - return true + return !isGroup }) b, _ := batch.New(serviceNow.ctx, batch.WithConcurrencyNum[any](10)) for _, detour := range outbounds { diff --git a/sing-box/experimental/libbox/iterator.go b/sing-box/experimental/libbox/iterator.go index 50d7385b36..b71ab886f9 100644 --- a/sing-box/experimental/libbox/iterator.go +++ b/sing-box/experimental/libbox/iterator.go @@ -18,6 +18,7 @@ func newIterator[T any](values []T) *iterator[T] { return &iterator[T]{values} } +//go:noinline func newPtrIterator[T any](values []T) *iterator[*T] { return &iterator[*T]{common.Map(values, func(value T) *T { return &value })} } diff --git a/sing-box/experimental/v2rayapi/stats.go b/sing-box/experimental/v2rayapi/stats.go index 6c44518fd5..16d441141a 100644 --- a/sing-box/experimental/v2rayapi/stats.go +++ b/sing-box/experimental/v2rayapi/stats.go @@ -115,7 +115,7 @@ func (s *StatsService) RoutedPacketConnection(ctx context.Context, conn N.Packet writeCounter = append(writeCounter, s.loadOrCreateCounter("user>>>"+user+">>>traffic>>>downlink")) } s.access.Unlock() - return bufio.NewInt64CounterPacketConn(conn, readCounter, writeCounter) + return bufio.NewInt64CounterPacketConn(conn, readCounter, nil, writeCounter, nil) } func (s *StatsService) GetStats(ctx context.Context, request *GetStatsRequest) (*GetStatsResponse, error) { @@ -192,7 +192,7 @@ func (s *StatsService) GetSysStats(ctx context.Context, request *SysStatsRequest var rtm runtime.MemStats runtime.ReadMemStats(&rtm) response := &SysStatsResponse{ - Uptime: uint32(time.Now().Sub(s.createdAt).Seconds()), + Uptime: uint32(time.Since(s.createdAt).Seconds()), NumGoroutine: uint32(runtime.NumGoroutine()), Alloc: rtm.Alloc, TotalAlloc: rtm.TotalAlloc, diff --git a/sing-box/go.mod b/sing-box/go.mod index fef5f03ba0..d023000163 100644 --- a/sing-box/go.mod +++ b/sing-box/go.mod @@ -24,12 +24,12 @@ require ( github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a github.com/sagernet/cors v1.2.1 github.com/sagernet/fswatch v0.1.1 - github.com/sagernet/gomobile v0.1.7 + github.com/sagernet/gomobile v0.1.8 github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb github.com/sagernet/quic-go v0.52.0-beta.1 - github.com/sagernet/sing v0.7.5 - github.com/sagernet/sing-mux v0.3.2 - github.com/sagernet/sing-quic v0.5.0-beta.3 + github.com/sagernet/sing v0.7.6-0.20250814022454-614cd851ca6d + github.com/sagernet/sing-mux v0.3.3 + github.com/sagernet/sing-quic v0.5.0 github.com/sagernet/sing-shadowsocks v0.2.8 github.com/sagernet/sing-shadowsocks2 v0.2.1 github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 @@ -44,11 +44,11 @@ require ( github.com/vishvananda/netns v0.0.5 go.uber.org/zap v1.27.0 go4.org/netipx v0.0.0-20231129151722-fdeea329fbba - golang.org/x/crypto v0.40.0 + golang.org/x/crypto v0.41.0 golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 - golang.org/x/mod v0.26.0 - golang.org/x/net v0.42.0 - golang.org/x/sys v0.34.0 + golang.org/x/mod v0.27.0 + golang.org/x/net v0.43.0 + golang.org/x/sys v0.35.0 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10 google.golang.org/grpc v1.73.0 google.golang.org/protobuf v1.36.6 @@ -123,10 +123,10 @@ require ( go.uber.org/zap/exp v0.3.0 // indirect go4.org/mem v0.0.0-20240501181205-ae6ca9944745 // indirect golang.org/x/sync v0.16.0 // indirect - golang.org/x/term v0.33.0 // indirect - golang.org/x/text v0.27.0 // indirect + golang.org/x/term v0.34.0 // indirect + golang.org/x/text v0.28.0 // indirect golang.org/x/time v0.9.0 // indirect - golang.org/x/tools v0.34.0 // indirect + golang.org/x/tools v0.36.0 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect golang.zx2c4.com/wireguard/windows v0.5.3 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect diff --git a/sing-box/go.sum b/sing-box/go.sum index e83a0b05b6..ad1cd1f7be 100644 --- a/sing-box/go.sum +++ b/sing-box/go.sum @@ -156,8 +156,8 @@ github.com/sagernet/cors v1.2.1 h1:Cv5Z8y9YSD6Gm+qSpNrL3LO4lD3eQVvbFYJSG7JCMHQ= github.com/sagernet/cors v1.2.1/go.mod h1:O64VyOjjhrkLmQIjF4KGRrJO/5dVXFdpEmCW/eISRAI= github.com/sagernet/fswatch v0.1.1 h1:YqID+93B7VRfqIH3PArW/XpJv5H4OLEVWDfProGoRQs= github.com/sagernet/fswatch v0.1.1/go.mod h1:nz85laH0mkQqJfaOrqPpkwtU1znMFNVTpT/5oRsVz/o= -github.com/sagernet/gomobile v0.1.7 h1:I9jCJZTH0weP5MsuydvYHX5QfN/r6Fe8ptAIj1+SJVg= -github.com/sagernet/gomobile v0.1.7/go.mod h1:Pqq2+ZVvs10U7xK+UwJgwYWUykewi8H6vlslAO73n9E= +github.com/sagernet/gomobile v0.1.8 h1:vXgoN0pjsMONAaYCTdsKBX2T1kxuS7sbT/mZ7PElGoo= +github.com/sagernet/gomobile v0.1.8/go.mod h1:A8l3FlHi2D/+mfcd4HHvk5DGFPW/ShFb9jHP5VmSiDY= github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb h1:pprQtDqNgqXkRsXn+0E8ikKOemzmum8bODjSfDene38= github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb/go.mod h1:QkkPEJLw59/tfxgapHta14UL5qMUah5NXhO0Kw2Kan4= github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis= @@ -167,12 +167,12 @@ github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/l github.com/sagernet/quic-go v0.52.0-beta.1 h1:hWkojLg64zjV+MJOvJU/kOeWndm3tiEfBLx5foisszs= github.com/sagernet/quic-go v0.52.0-beta.1/go.mod h1:OV+V5kEBb8kJS7k29MzDu6oj9GyMc7HA07sE1tedxz4= github.com/sagernet/sing v0.6.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= -github.com/sagernet/sing v0.7.5 h1:gNMwZCLPqR+4e0g6dwi0sSsrvOmoMjpZgqxKsuJZatc= -github.com/sagernet/sing v0.7.5/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= -github.com/sagernet/sing-mux v0.3.2 h1:meZVFiiStvHThb/trcpAkCrmtJOuItG5Dzl1RRP5/NE= -github.com/sagernet/sing-mux v0.3.2/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA= -github.com/sagernet/sing-quic v0.5.0-beta.3 h1:X/acRNsqQNfDlmwE7SorHfaZiny5e67hqIzM/592ric= -github.com/sagernet/sing-quic v0.5.0-beta.3/go.mod h1:SAv/qdeDN+75msGG5U5ZIwG+3Ua50jVIKNrRSY8pkx0= +github.com/sagernet/sing v0.7.6-0.20250814022454-614cd851ca6d h1:fTGA/vTcqceUqSiXeFAO99Iiai/nWBPV+yP8tPpHPf8= +github.com/sagernet/sing v0.7.6-0.20250814022454-614cd851ca6d/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= +github.com/sagernet/sing-mux v0.3.3 h1:YFgt9plMWzH994BMZLmyKL37PdIVaIilwP0Jg+EcLfw= +github.com/sagernet/sing-mux v0.3.3/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA= +github.com/sagernet/sing-quic v0.5.0 h1:jNLIyVk24lFPvu8A4x+ZNEnZdI+Tg1rp7eCJ6v0Csak= +github.com/sagernet/sing-quic v0.5.0/go.mod h1:SAv/qdeDN+75msGG5U5ZIwG+3Ua50jVIKNrRSY8pkx0= github.com/sagernet/sing-shadowsocks v0.2.8 h1:PURj5PRoAkqeHh2ZW205RWzN9E9RtKCVCzByXruQWfE= github.com/sagernet/sing-shadowsocks v0.2.8/go.mod h1:lo7TWEMDcN5/h5B8S0ew+r78ZODn6SwVaFhvB6H+PTI= github.com/sagernet/sing-shadowsocks2 v0.2.1 h1:dWV9OXCeFPuYGHb6IRqlSptVnSzOelnqqs2gQ2/Qioo= @@ -262,18 +262,18 @@ go4.org/mem v0.0.0-20240501181205-ae6ca9944745/go.mod h1:reUoABIJ9ikfM5sgtSF3Wus go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= -golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= +golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= +golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI= golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68= golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY= -golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg= -golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ= +golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= +golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= -golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= +golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= @@ -285,20 +285,20 @@ golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= -golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg= -golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0= +golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= +golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= -golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= -golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= +golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= +golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/sing-box/option/dns.go b/sing-box/option/dns.go index 0886dd64b7..422d7b3ba9 100644 --- a/sing-box/option/dns.go +++ b/sing-box/option/dns.go @@ -100,7 +100,6 @@ func rewriteRcodeAction(rcodeMap map[string]int, ruleAction *DNSRuleAction) { } ruleAction.Action = C.RuleActionTypePredefined ruleAction.PredefinedOptions.Rcode = common.Ptr(DNSRCode(rcode)) - return } type DNSClientOptions struct { diff --git a/sing-box/option/rule.go b/sing-box/option/rule.go index 41bcc12604..d12b679d5a 100644 --- a/sing-box/option/rule.go +++ b/sing-box/option/rule.go @@ -67,42 +67,45 @@ func (r Rule) IsValid() bool { } type RawDefaultRule struct { - Inbound badoption.Listable[string] `json:"inbound,omitempty"` - IPVersion int `json:"ip_version,omitempty"` - Network badoption.Listable[string] `json:"network,omitempty"` - AuthUser badoption.Listable[string] `json:"auth_user,omitempty"` - Protocol badoption.Listable[string] `json:"protocol,omitempty"` - Client badoption.Listable[string] `json:"client,omitempty"` - Domain badoption.Listable[string] `json:"domain,omitempty"` - DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"` - DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"` - DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"` - Geosite badoption.Listable[string] `json:"geosite,omitempty"` - SourceGeoIP badoption.Listable[string] `json:"source_geoip,omitempty"` - GeoIP badoption.Listable[string] `json:"geoip,omitempty"` - SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"` - SourceIPIsPrivate bool `json:"source_ip_is_private,omitempty"` - IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"` - IPIsPrivate bool `json:"ip_is_private,omitempty"` - SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"` - SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"` - Port badoption.Listable[uint16] `json:"port,omitempty"` - PortRange badoption.Listable[string] `json:"port_range,omitempty"` - ProcessName badoption.Listable[string] `json:"process_name,omitempty"` - ProcessPath badoption.Listable[string] `json:"process_path,omitempty"` - ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"` - PackageName badoption.Listable[string] `json:"package_name,omitempty"` - User badoption.Listable[string] `json:"user,omitempty"` - UserID badoption.Listable[int32] `json:"user_id,omitempty"` - ClashMode string `json:"clash_mode,omitempty"` - NetworkType badoption.Listable[InterfaceType] `json:"network_type,omitempty"` - NetworkIsExpensive bool `json:"network_is_expensive,omitempty"` - NetworkIsConstrained bool `json:"network_is_constrained,omitempty"` - WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"` - WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"` - RuleSet badoption.Listable[string] `json:"rule_set,omitempty"` - RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"` - Invert bool `json:"invert,omitempty"` + Inbound badoption.Listable[string] `json:"inbound,omitempty"` + IPVersion int `json:"ip_version,omitempty"` + Network badoption.Listable[string] `json:"network,omitempty"` + AuthUser badoption.Listable[string] `json:"auth_user,omitempty"` + Protocol badoption.Listable[string] `json:"protocol,omitempty"` + Client badoption.Listable[string] `json:"client,omitempty"` + Domain badoption.Listable[string] `json:"domain,omitempty"` + DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"` + DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"` + DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"` + Geosite badoption.Listable[string] `json:"geosite,omitempty"` + SourceGeoIP badoption.Listable[string] `json:"source_geoip,omitempty"` + GeoIP badoption.Listable[string] `json:"geoip,omitempty"` + SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"` + SourceIPIsPrivate bool `json:"source_ip_is_private,omitempty"` + IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"` + IPIsPrivate bool `json:"ip_is_private,omitempty"` + SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"` + SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"` + Port badoption.Listable[uint16] `json:"port,omitempty"` + PortRange badoption.Listable[string] `json:"port_range,omitempty"` + ProcessName badoption.Listable[string] `json:"process_name,omitempty"` + ProcessPath badoption.Listable[string] `json:"process_path,omitempty"` + ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"` + PackageName badoption.Listable[string] `json:"package_name,omitempty"` + User badoption.Listable[string] `json:"user,omitempty"` + UserID badoption.Listable[int32] `json:"user_id,omitempty"` + ClashMode string `json:"clash_mode,omitempty"` + NetworkType badoption.Listable[InterfaceType] `json:"network_type,omitempty"` + NetworkIsExpensive bool `json:"network_is_expensive,omitempty"` + NetworkIsConstrained bool `json:"network_is_constrained,omitempty"` + WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"` + WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"` + InterfaceAddress *badjson.TypedMap[string, badoption.Listable[badoption.Prefixable]] `json:"interface_address,omitempty"` + NetworkInterfaceAddress *badjson.TypedMap[InterfaceType, badoption.Listable[badoption.Prefixable]] `json:"network_interface_address,omitempty"` + DefaultInterfaceAddress badoption.Listable[badoption.Prefixable] `json:"default_interface_address,omitempty"` + RuleSet badoption.Listable[string] `json:"rule_set,omitempty"` + RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"` + Invert bool `json:"invert,omitempty"` // Deprecated: renamed to rule_set_ip_cidr_match_source Deprecated_RulesetIPCIDRMatchSource bool `json:"rule_set_ipcidr_match_source,omitempty"` diff --git a/sing-box/option/rule_dns.go b/sing-box/option/rule_dns.go index 87b1501745..bbab993c6b 100644 --- a/sing-box/option/rule_dns.go +++ b/sing-box/option/rule_dns.go @@ -68,45 +68,48 @@ func (r DNSRule) IsValid() bool { } type RawDefaultDNSRule struct { - Inbound badoption.Listable[string] `json:"inbound,omitempty"` - IPVersion int `json:"ip_version,omitempty"` - QueryType badoption.Listable[DNSQueryType] `json:"query_type,omitempty"` - Network badoption.Listable[string] `json:"network,omitempty"` - AuthUser badoption.Listable[string] `json:"auth_user,omitempty"` - Protocol badoption.Listable[string] `json:"protocol,omitempty"` - Domain badoption.Listable[string] `json:"domain,omitempty"` - DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"` - DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"` - DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"` - Geosite badoption.Listable[string] `json:"geosite,omitempty"` - SourceGeoIP badoption.Listable[string] `json:"source_geoip,omitempty"` - GeoIP badoption.Listable[string] `json:"geoip,omitempty"` - IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"` - IPIsPrivate bool `json:"ip_is_private,omitempty"` - IPAcceptAny bool `json:"ip_accept_any,omitempty"` - SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"` - SourceIPIsPrivate bool `json:"source_ip_is_private,omitempty"` - SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"` - SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"` - Port badoption.Listable[uint16] `json:"port,omitempty"` - PortRange badoption.Listable[string] `json:"port_range,omitempty"` - ProcessName badoption.Listable[string] `json:"process_name,omitempty"` - ProcessPath badoption.Listable[string] `json:"process_path,omitempty"` - ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"` - PackageName badoption.Listable[string] `json:"package_name,omitempty"` - User badoption.Listable[string] `json:"user,omitempty"` - UserID badoption.Listable[int32] `json:"user_id,omitempty"` - Outbound badoption.Listable[string] `json:"outbound,omitempty"` - ClashMode string `json:"clash_mode,omitempty"` - NetworkType badoption.Listable[InterfaceType] `json:"network_type,omitempty"` - NetworkIsExpensive bool `json:"network_is_expensive,omitempty"` - NetworkIsConstrained bool `json:"network_is_constrained,omitempty"` - WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"` - WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"` - RuleSet badoption.Listable[string] `json:"rule_set,omitempty"` - RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"` - RuleSetIPCIDRAcceptEmpty bool `json:"rule_set_ip_cidr_accept_empty,omitempty"` - Invert bool `json:"invert,omitempty"` + Inbound badoption.Listable[string] `json:"inbound,omitempty"` + IPVersion int `json:"ip_version,omitempty"` + QueryType badoption.Listable[DNSQueryType] `json:"query_type,omitempty"` + Network badoption.Listable[string] `json:"network,omitempty"` + AuthUser badoption.Listable[string] `json:"auth_user,omitempty"` + Protocol badoption.Listable[string] `json:"protocol,omitempty"` + Domain badoption.Listable[string] `json:"domain,omitempty"` + DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"` + DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"` + DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"` + Geosite badoption.Listable[string] `json:"geosite,omitempty"` + SourceGeoIP badoption.Listable[string] `json:"source_geoip,omitempty"` + GeoIP badoption.Listable[string] `json:"geoip,omitempty"` + IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"` + IPIsPrivate bool `json:"ip_is_private,omitempty"` + IPAcceptAny bool `json:"ip_accept_any,omitempty"` + SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"` + SourceIPIsPrivate bool `json:"source_ip_is_private,omitempty"` + SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"` + SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"` + Port badoption.Listable[uint16] `json:"port,omitempty"` + PortRange badoption.Listable[string] `json:"port_range,omitempty"` + ProcessName badoption.Listable[string] `json:"process_name,omitempty"` + ProcessPath badoption.Listable[string] `json:"process_path,omitempty"` + ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"` + PackageName badoption.Listable[string] `json:"package_name,omitempty"` + User badoption.Listable[string] `json:"user,omitempty"` + UserID badoption.Listable[int32] `json:"user_id,omitempty"` + Outbound badoption.Listable[string] `json:"outbound,omitempty"` + ClashMode string `json:"clash_mode,omitempty"` + NetworkType badoption.Listable[InterfaceType] `json:"network_type,omitempty"` + NetworkIsExpensive bool `json:"network_is_expensive,omitempty"` + NetworkIsConstrained bool `json:"network_is_constrained,omitempty"` + WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"` + WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"` + InterfaceAddress *badjson.TypedMap[string, badoption.Listable[badoption.Prefixable]] `json:"interface_address,omitempty"` + NetworkInterfaceAddress *badjson.TypedMap[InterfaceType, badoption.Listable[badoption.Prefixable]] `json:"network_interface_address,omitempty"` + DefaultInterfaceAddress badoption.Listable[badoption.Prefixable] `json:"default_interface_address,omitempty"` + RuleSet badoption.Listable[string] `json:"rule_set,omitempty"` + RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"` + RuleSetIPCIDRAcceptEmpty bool `json:"rule_set_ip_cidr_accept_empty,omitempty"` + Invert bool `json:"invert,omitempty"` // Deprecated: renamed to rule_set_ip_cidr_match_source Deprecated_RulesetIPCIDRMatchSource bool `json:"rule_set_ipcidr_match_source,omitempty"` diff --git a/sing-box/option/rule_set.go b/sing-box/option/rule_set.go index 610d7ba2a2..2775d743fa 100644 --- a/sing-box/option/rule_set.go +++ b/sing-box/option/rule_set.go @@ -182,28 +182,31 @@ func (r HeadlessRule) IsValid() bool { } type DefaultHeadlessRule struct { - QueryType badoption.Listable[DNSQueryType] `json:"query_type,omitempty"` - Network badoption.Listable[string] `json:"network,omitempty"` - Domain badoption.Listable[string] `json:"domain,omitempty"` - DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"` - DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"` - DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"` - SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"` - IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"` - SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"` - SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"` - Port badoption.Listable[uint16] `json:"port,omitempty"` - PortRange badoption.Listable[string] `json:"port_range,omitempty"` - ProcessName badoption.Listable[string] `json:"process_name,omitempty"` - ProcessPath badoption.Listable[string] `json:"process_path,omitempty"` - ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"` - PackageName badoption.Listable[string] `json:"package_name,omitempty"` - NetworkType badoption.Listable[InterfaceType] `json:"network_type,omitempty"` - NetworkIsExpensive bool `json:"network_is_expensive,omitempty"` - NetworkIsConstrained bool `json:"network_is_constrained,omitempty"` - WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"` - WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"` - Invert bool `json:"invert,omitempty"` + QueryType badoption.Listable[DNSQueryType] `json:"query_type,omitempty"` + Network badoption.Listable[string] `json:"network,omitempty"` + Domain badoption.Listable[string] `json:"domain,omitempty"` + DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"` + DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"` + DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"` + SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"` + IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"` + SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"` + SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"` + Port badoption.Listable[uint16] `json:"port,omitempty"` + PortRange badoption.Listable[string] `json:"port_range,omitempty"` + ProcessName badoption.Listable[string] `json:"process_name,omitempty"` + ProcessPath badoption.Listable[string] `json:"process_path,omitempty"` + ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"` + PackageName badoption.Listable[string] `json:"package_name,omitempty"` + NetworkType badoption.Listable[InterfaceType] `json:"network_type,omitempty"` + NetworkIsExpensive bool `json:"network_is_expensive,omitempty"` + NetworkIsConstrained bool `json:"network_is_constrained,omitempty"` + WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"` + WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"` + NetworkInterfaceAddress *badjson.TypedMap[InterfaceType, badoption.Listable[badoption.Prefixable]] `json:"network_interface_address,omitempty"` + DefaultInterfaceAddress badoption.Listable[badoption.Prefixable] `json:"default_interface_address,omitempty"` + + Invert bool `json:"invert,omitempty"` DomainMatcher *domain.Matcher `json:"-"` SourceIPSet *netipx.IPSet `json:"-"` @@ -240,7 +243,7 @@ type PlainRuleSetCompat _PlainRuleSetCompat func (r PlainRuleSetCompat) MarshalJSON() ([]byte, error) { var v any switch r.Version { - case C.RuleSetVersion1, C.RuleSetVersion2, C.RuleSetVersion3: + case C.RuleSetVersion1, C.RuleSetVersion2, C.RuleSetVersion3, C.RuleSetVersion4: v = r.Options default: return nil, E.New("unknown rule-set version: ", r.Version) diff --git a/sing-box/protocol/anytls/outbound.go b/sing-box/protocol/anytls/outbound.go index b026ed1803..ce71ed6161 100644 --- a/sing-box/protocol/anytls/outbound.go +++ b/sing-box/protocol/anytls/outbound.go @@ -26,7 +26,7 @@ func RegisterOutbound(registry *outbound.Registry) { type Outbound struct { outbound.Adapter - dialer N.Dialer + dialer tls.Dialer server M.Socksaddr tlsConfig tls.Config client *anytls.Client @@ -58,7 +58,8 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL if err != nil { return nil, err } - outbound.dialer = outboundDialer + + outbound.dialer = tls.NewDialer(outboundDialer, tlsConfig) client, err := anytls.NewClient(ctx, anytls.ClientConfig{ Password: options.Password, @@ -91,16 +92,7 @@ func (d anytlsDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) } func (h *Outbound) dialOut(ctx context.Context) (net.Conn, error) { - conn, err := h.dialer.DialContext(ctx, N.NetworkTCP, h.server) - if err != nil { - return nil, err - } - tlsConn, err := tls.ClientHandshake(ctx, conn, h.tlsConfig) - if err != nil { - common.Close(tlsConn, conn) - return nil, err - } - return tlsConn, nil + return h.dialer.DialTLSContext(ctx, h.server) } func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { diff --git a/sing-box/protocol/group/urltest.go b/sing-box/protocol/group/urltest.go index f44698e140..719f4a2c6d 100644 --- a/sing-box/protocol/group/urltest.go +++ b/sing-box/protocol/group/urltest.go @@ -313,7 +313,7 @@ func (g *URLTestGroup) Select(network string) (adapter.Outbound, bool) { } func (g *URLTestGroup) loopCheck() { - if time.Now().Sub(g.lastActive.Load()) > g.interval { + if time.Since(g.lastActive.Load()) > g.interval { g.lastActive.Store(time.Now()) g.CheckOutbounds(false) } @@ -323,7 +323,7 @@ func (g *URLTestGroup) loopCheck() { return case <-g.ticker.C: } - if time.Now().Sub(g.lastActive.Load()) > g.idleTimeout { + if time.Since(g.lastActive.Load()) > g.idleTimeout { g.access.Lock() g.ticker.Stop() g.ticker = nil @@ -360,7 +360,7 @@ func (g *URLTestGroup) urlTest(ctx context.Context, force bool) (map[string]uint continue } history := g.history.LoadURLTestHistory(realTag) - if !force && history != nil && time.Now().Sub(history.Time) < g.interval { + if !force && history != nil && time.Since(history.Time) < g.interval { continue } checked[realTag] = true diff --git a/sing-box/protocol/http/outbound.go b/sing-box/protocol/http/outbound.go index 0570dde5a9..3b631b39b5 100644 --- a/sing-box/protocol/http/outbound.go +++ b/sing-box/protocol/http/outbound.go @@ -34,7 +34,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL if err != nil { return nil, err } - detour, err := tls.NewDialerFromOptions(ctx, router, outboundDialer, options.Server, common.PtrValueOrDefault(options.TLS)) + detour, err := tls.NewDialerFromOptions(ctx, outboundDialer, options.Server, common.PtrValueOrDefault(options.TLS)) if err != nil { return nil, err } diff --git a/sing-box/protocol/shadowsocks/outbound.go b/sing-box/protocol/shadowsocks/outbound.go index 875c9e69a3..9b9d9252fe 100644 --- a/sing-box/protocol/shadowsocks/outbound.go +++ b/sing-box/protocol/shadowsocks/outbound.go @@ -128,7 +128,6 @@ func (h *Outbound) InterfaceUpdated() { if h.multiplexDialer != nil { h.multiplexDialer.Reset() } - return } func (h *Outbound) Close() error { diff --git a/sing-box/protocol/ssh/outbound.go b/sing-box/protocol/ssh/outbound.go index aeb43d3427..b2f9680747 100644 --- a/sing-box/protocol/ssh/outbound.go +++ b/sing-box/protocol/ssh/outbound.go @@ -180,7 +180,6 @@ func (s *Outbound) connect() (*ssh.Client, error) { func (s *Outbound) InterfaceUpdated() { common.Close(s.clientConn) - return } func (s *Outbound) Close() error { diff --git a/sing-box/protocol/trojan/outbound.go b/sing-box/protocol/trojan/outbound.go index 37a6933c8e..dc2e0fe44f 100644 --- a/sing-box/protocol/trojan/outbound.go +++ b/sing-box/protocol/trojan/outbound.go @@ -34,6 +34,7 @@ type Outbound struct { key [56]byte multiplexDialer *mux.Client tlsConfig tls.Config + tlsDialer tls.Dialer transport adapter.V2RayClientTransport } @@ -54,6 +55,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL if err != nil { return nil, err } + outbound.tlsDialer = tls.NewDialer(outboundDialer, outbound.tlsConfig) } if options.Transport != nil { outbound.transport, err = v2ray.NewClientTransport(ctx, outbound.dialer, outbound.serverAddr, common.PtrValueOrDefault(options.Transport), outbound.tlsConfig) @@ -105,7 +107,6 @@ func (h *Outbound) InterfaceUpdated() { if h.multiplexDialer != nil { h.multiplexDialer.Reset() } - return } func (h *Outbound) Close() error { @@ -122,11 +123,10 @@ func (h *trojanDialer) DialContext(ctx context.Context, network string, destinat var err error if h.transport != nil { conn, err = h.transport.DialContext(ctx) + } else if h.tlsDialer != nil { + conn, err = h.tlsDialer.DialTLSContext(ctx, h.serverAddr) } else { conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr) - if err == nil && h.tlsConfig != nil { - conn, err = tls.ClientHandshake(ctx, conn, h.tlsConfig) - } } if err != nil { common.Close(conn) diff --git a/sing-box/protocol/vless/outbound.go b/sing-box/protocol/vless/outbound.go index e0208be9af..a96d12a0a3 100644 --- a/sing-box/protocol/vless/outbound.go +++ b/sing-box/protocol/vless/outbound.go @@ -35,6 +35,7 @@ type Outbound struct { serverAddr M.Socksaddr multiplexDialer *mux.Client tlsConfig tls.Config + tlsDialer tls.Dialer transport adapter.V2RayClientTransport packetAddr bool xudp bool @@ -56,6 +57,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL if err != nil { return nil, err } + outbound.tlsDialer = tls.NewDialer(outboundDialer, outbound.tlsConfig) } if options.Transport != nil { outbound.transport, err = v2ray.NewClientTransport(ctx, outbound.dialer, outbound.serverAddr, common.PtrValueOrDefault(options.Transport), outbound.tlsConfig) @@ -124,7 +126,6 @@ func (h *Outbound) InterfaceUpdated() { if h.multiplexDialer != nil { h.multiplexDialer.Reset() } - return } func (h *Outbound) Close() error { @@ -141,11 +142,10 @@ func (h *vlessDialer) DialContext(ctx context.Context, network string, destinati var err error if h.transport != nil { conn, err = h.transport.DialContext(ctx) + } else if h.tlsDialer != nil { + conn, err = h.tlsDialer.DialTLSContext(ctx, h.serverAddr) } else { conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr) - if err == nil && h.tlsConfig != nil { - conn, err = tls.ClientHandshake(ctx, conn, h.tlsConfig) - } } if err != nil { return nil, err @@ -184,11 +184,10 @@ func (h *vlessDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) var err error if h.transport != nil { conn, err = h.transport.DialContext(ctx) + } else if h.tlsDialer != nil { + conn, err = h.tlsDialer.DialTLSContext(ctx, h.serverAddr) } else { conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr) - if err == nil && h.tlsConfig != nil { - conn, err = tls.ClientHandshake(ctx, conn, h.tlsConfig) - } } if err != nil { common.Close(conn) diff --git a/sing-box/protocol/vmess/outbound.go b/sing-box/protocol/vmess/outbound.go index be05990ee1..716570f3c3 100644 --- a/sing-box/protocol/vmess/outbound.go +++ b/sing-box/protocol/vmess/outbound.go @@ -35,6 +35,7 @@ type Outbound struct { serverAddr M.Socksaddr multiplexDialer *mux.Client tlsConfig tls.Config + tlsDialer tls.Dialer transport adapter.V2RayClientTransport packetAddr bool xudp bool @@ -56,6 +57,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL if err != nil { return nil, err } + outbound.tlsDialer = tls.NewDialer(outboundDialer, outbound.tlsConfig) } if options.Transport != nil { outbound.transport, err = v2ray.NewClientTransport(ctx, outbound.dialer, outbound.serverAddr, common.PtrValueOrDefault(options.Transport), outbound.tlsConfig) @@ -108,7 +110,6 @@ func (h *Outbound) InterfaceUpdated() { if h.multiplexDialer != nil { h.multiplexDialer.Reset() } - return } func (h *Outbound) Close() error { @@ -155,11 +156,10 @@ func (h *vmessDialer) DialContext(ctx context.Context, network string, destinati var err error if h.transport != nil { conn, err = h.transport.DialContext(ctx) + } else if h.tlsDialer != nil { + conn, err = h.tlsDialer.DialTLSContext(ctx, h.serverAddr) } else { conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr) - if err == nil && h.tlsConfig != nil { - conn, err = tls.ClientHandshake(ctx, conn, h.tlsConfig) - } } if err != nil { common.Close(conn) @@ -183,11 +183,10 @@ func (h *vmessDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) var err error if h.transport != nil { conn, err = h.transport.DialContext(ctx) + } else if h.tlsDialer != nil { + conn, err = h.tlsDialer.DialTLSContext(ctx, h.serverAddr) } else { conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr) - if err == nil && h.tlsConfig != nil { - conn, err = tls.ClientHandshake(ctx, conn, h.tlsConfig) - } } if err != nil { return nil, err diff --git a/sing-box/route/rule/rule_default.go b/sing-box/route/rule/rule_default.go index 08e21e5f3a..e0677b9769 100644 --- a/sing-box/route/rule/rule_default.go +++ b/sing-box/route/rule/rule_default.go @@ -246,6 +246,21 @@ func NewDefaultRule(ctx context.Context, logger log.ContextLogger, options optio rule.items = append(rule.items, item) rule.allItems = append(rule.allItems, item) } + if options.InterfaceAddress != nil && options.InterfaceAddress.Size() > 0 { + item := NewInterfaceAddressItem(networkManager, options.InterfaceAddress) + rule.items = append(rule.items, item) + rule.allItems = append(rule.allItems, item) + } + if options.NetworkInterfaceAddress != nil && options.NetworkInterfaceAddress.Size() > 0 { + item := NewNetworkInterfaceAddressItem(networkManager, options.NetworkInterfaceAddress) + rule.items = append(rule.items, item) + rule.allItems = append(rule.allItems, item) + } + if len(options.DefaultInterfaceAddress) > 0 { + item := NewDefaultInterfaceAddressItem(networkManager, options.DefaultInterfaceAddress) + rule.items = append(rule.items, item) + rule.allItems = append(rule.allItems, item) + } if len(options.RuleSet) > 0 { var matchSource bool if options.RuleSetIPCIDRMatchSource { diff --git a/sing-box/route/rule/rule_default_interface_address.go b/sing-box/route/rule/rule_default_interface_address.go new file mode 100644 index 0000000000..940d3a064f --- /dev/null +++ b/sing-box/route/rule/rule_default_interface_address.go @@ -0,0 +1,56 @@ +package rule + +import ( + "net/netip" + "strings" + + "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-tun" + "github.com/sagernet/sing/common" + "github.com/sagernet/sing/common/json/badoption" +) + +var _ RuleItem = (*DefaultInterfaceAddressItem)(nil) + +type DefaultInterfaceAddressItem struct { + interfaceMonitor tun.DefaultInterfaceMonitor + interfaceAddresses []netip.Prefix +} + +func NewDefaultInterfaceAddressItem(networkManager adapter.NetworkManager, interfaceAddresses badoption.Listable[badoption.Prefixable]) *DefaultInterfaceAddressItem { + item := &DefaultInterfaceAddressItem{ + interfaceMonitor: networkManager.InterfaceMonitor(), + interfaceAddresses: make([]netip.Prefix, 0, len(interfaceAddresses)), + } + for _, prefixable := range interfaceAddresses { + item.interfaceAddresses = append(item.interfaceAddresses, prefixable.Build(netip.Prefix{})) + } + return item +} + +func (r *DefaultInterfaceAddressItem) Match(metadata *adapter.InboundContext) bool { + defaultInterface := r.interfaceMonitor.DefaultInterface() + if defaultInterface == nil { + return false + } + for _, address := range r.interfaceAddresses { + if common.All(defaultInterface.Addresses, func(it netip.Prefix) bool { + return !address.Overlaps(it) + }) { + return false + } + } + return true +} + +func (r *DefaultInterfaceAddressItem) String() string { + addressLen := len(r.interfaceAddresses) + switch { + case addressLen == 1: + return "default_interface_address=" + r.interfaceAddresses[0].String() + case addressLen > 3: + return "default_interface_address=[" + strings.Join(common.Map(r.interfaceAddresses[:3], netip.Prefix.String), " ") + "...]" + default: + return "default_interface_address=[" + strings.Join(common.Map(r.interfaceAddresses, netip.Prefix.String), " ") + "]" + } +} diff --git a/sing-box/route/rule/rule_dns.go b/sing-box/route/rule/rule_dns.go index 30442abf9f..d9570cae9f 100644 --- a/sing-box/route/rule/rule_dns.go +++ b/sing-box/route/rule/rule_dns.go @@ -247,6 +247,21 @@ func NewDefaultDNSRule(ctx context.Context, logger log.ContextLogger, options op rule.items = append(rule.items, item) rule.allItems = append(rule.allItems, item) } + if options.InterfaceAddress != nil && options.InterfaceAddress.Size() > 0 { + item := NewInterfaceAddressItem(networkManager, options.InterfaceAddress) + rule.items = append(rule.items, item) + rule.allItems = append(rule.allItems, item) + } + if options.NetworkInterfaceAddress != nil && options.NetworkInterfaceAddress.Size() > 0 { + item := NewNetworkInterfaceAddressItem(networkManager, options.NetworkInterfaceAddress) + rule.items = append(rule.items, item) + rule.allItems = append(rule.allItems, item) + } + if len(options.DefaultInterfaceAddress) > 0 { + item := NewDefaultInterfaceAddressItem(networkManager, options.DefaultInterfaceAddress) + rule.items = append(rule.items, item) + rule.allItems = append(rule.allItems, item) + } if len(options.RuleSet) > 0 { var matchSource bool if options.RuleSetIPCIDRMatchSource { diff --git a/sing-box/route/rule/rule_headless.go b/sing-box/route/rule/rule_headless.go index ba17ca374c..689e6e3ecc 100644 --- a/sing-box/route/rule/rule_headless.go +++ b/sing-box/route/rule/rule_headless.go @@ -164,13 +164,21 @@ func NewDefaultHeadlessRule(ctx context.Context, options option.DefaultHeadlessR item := NewWIFISSIDItem(networkManager, options.WIFISSID) rule.items = append(rule.items, item) rule.allItems = append(rule.allItems, item) - } if len(options.WIFIBSSID) > 0 { item := NewWIFIBSSIDItem(networkManager, options.WIFIBSSID) rule.items = append(rule.items, item) rule.allItems = append(rule.allItems, item) - + } + if options.NetworkInterfaceAddress != nil && options.NetworkInterfaceAddress.Size() > 0 { + item := NewNetworkInterfaceAddressItem(networkManager, options.NetworkInterfaceAddress) + rule.items = append(rule.items, item) + rule.allItems = append(rule.allItems, item) + } + if len(options.DefaultInterfaceAddress) > 0 { + item := NewDefaultInterfaceAddressItem(networkManager, options.DefaultInterfaceAddress) + rule.items = append(rule.items, item) + rule.allItems = append(rule.allItems, item) } } if len(options.AdGuardDomain) > 0 { diff --git a/sing-box/route/rule/rule_interface_address.go b/sing-box/route/rule/rule_interface_address.go new file mode 100644 index 0000000000..53ece6834c --- /dev/null +++ b/sing-box/route/rule/rule_interface_address.go @@ -0,0 +1,62 @@ +package rule + +import ( + "net/netip" + "strings" + + "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing/common" + "github.com/sagernet/sing/common/control" + "github.com/sagernet/sing/common/json/badjson" + "github.com/sagernet/sing/common/json/badoption" +) + +var _ RuleItem = (*InterfaceAddressItem)(nil) + +type InterfaceAddressItem struct { + networkManager adapter.NetworkManager + interfaceAddresses map[string][]netip.Prefix + description string +} + +func NewInterfaceAddressItem(networkManager adapter.NetworkManager, interfaceAddresses *badjson.TypedMap[string, badoption.Listable[badoption.Prefixable]]) *InterfaceAddressItem { + item := &InterfaceAddressItem{ + networkManager: networkManager, + interfaceAddresses: make(map[string][]netip.Prefix, interfaceAddresses.Size()), + } + var entryDescriptions []string + for _, entry := range interfaceAddresses.Entries() { + prefixes := make([]netip.Prefix, 0, len(entry.Value)) + for _, prefixable := range entry.Value { + prefixes = append(prefixes, prefixable.Build(netip.Prefix{})) + } + item.interfaceAddresses[entry.Key] = prefixes + entryDescriptions = append(entryDescriptions, entry.Key+"="+strings.Join(common.Map(prefixes, netip.Prefix.String), ",")) + } + item.description = "interface_address=[" + strings.Join(entryDescriptions, " ") + "]" + return item +} + +func (r *InterfaceAddressItem) Match(metadata *adapter.InboundContext) bool { + interfaces := r.networkManager.InterfaceFinder().Interfaces() + for ifName, addresses := range r.interfaceAddresses { + iface := common.Find(interfaces, func(it control.Interface) bool { + return it.Name == ifName + }) + if iface.Name == "" { + return false + } + if common.All(addresses, func(address netip.Prefix) bool { + return common.All(iface.Addresses, func(it netip.Prefix) bool { + return !address.Overlaps(it) + }) + }) { + return false + } + } + return true +} + +func (r *InterfaceAddressItem) String() string { + return r.description +} diff --git a/sing-box/route/rule/rule_network_interface_address.go b/sing-box/route/rule/rule_network_interface_address.go new file mode 100644 index 0000000000..c365be3bed --- /dev/null +++ b/sing-box/route/rule/rule_network_interface_address.go @@ -0,0 +1,64 @@ +package rule + +import ( + "net/netip" + "strings" + + "github.com/sagernet/sing-box/adapter" + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/option" + "github.com/sagernet/sing/common" + "github.com/sagernet/sing/common/json/badjson" + "github.com/sagernet/sing/common/json/badoption" +) + +var _ RuleItem = (*NetworkInterfaceAddressItem)(nil) + +type NetworkInterfaceAddressItem struct { + networkManager adapter.NetworkManager + interfaceAddresses map[C.InterfaceType][]netip.Prefix + description string +} + +func NewNetworkInterfaceAddressItem(networkManager adapter.NetworkManager, interfaceAddresses *badjson.TypedMap[option.InterfaceType, badoption.Listable[badoption.Prefixable]]) *NetworkInterfaceAddressItem { + item := &NetworkInterfaceAddressItem{ + networkManager: networkManager, + interfaceAddresses: make(map[C.InterfaceType][]netip.Prefix, interfaceAddresses.Size()), + } + var entryDescriptions []string + for _, entry := range interfaceAddresses.Entries() { + prefixes := make([]netip.Prefix, 0, len(entry.Value)) + for _, prefixable := range entry.Value { + prefixes = append(prefixes, prefixable.Build(netip.Prefix{})) + } + item.interfaceAddresses[entry.Key.Build()] = prefixes + entryDescriptions = append(entryDescriptions, entry.Key.Build().String()+"="+strings.Join(common.Map(prefixes, netip.Prefix.String), ",")) + } + item.description = "network_interface_address=[" + strings.Join(entryDescriptions, " ") + "]" + return item +} + +func (r *NetworkInterfaceAddressItem) Match(metadata *adapter.InboundContext) bool { + interfaces := r.networkManager.NetworkInterfaces() +match: + for ifType, addresses := range r.interfaceAddresses { + for _, networkInterface := range interfaces { + if networkInterface.Type != ifType { + continue + } + if common.Any(networkInterface.Addresses, func(it netip.Prefix) bool { + return common.Any(addresses, func(prefix netip.Prefix) bool { + return prefix.Overlaps(it) + }) + }) { + continue match + } + } + return false + } + return true +} + +func (r *NetworkInterfaceAddressItem) String() string { + return r.description +} diff --git a/sing-box/route/rule/rule_set.go b/sing-box/route/rule/rule_set.go index 5e639a47f9..39068dbf35 100644 --- a/sing-box/route/rule/rule_set.go +++ b/sing-box/route/rule/rule_set.go @@ -42,7 +42,7 @@ func extractIPSetFromRule(rawRule adapter.HeadlessRule) []*netipx.IPSet { } } -func hasHeadlessRule(rules []option.HeadlessRule, cond func(rule option.DefaultHeadlessRule) bool) bool { +func HasHeadlessRule(rules []option.HeadlessRule, cond func(rule option.DefaultHeadlessRule) bool) bool { for _, rule := range rules { switch rule.Type { case C.RuleTypeDefault: @@ -50,7 +50,7 @@ func hasHeadlessRule(rules []option.HeadlessRule, cond func(rule option.DefaultH return true } case C.RuleTypeLogical: - if hasHeadlessRule(rule.LogicalOptions.Rules, cond) { + if HasHeadlessRule(rule.LogicalOptions.Rules, cond) { return true } } diff --git a/sing-box/route/rule/rule_set_local.go b/sing-box/route/rule/rule_set_local.go index 4f2fabcc9f..442302e2a8 100644 --- a/sing-box/route/rule/rule_set_local.go +++ b/sing-box/route/rule/rule_set_local.go @@ -138,9 +138,9 @@ func (s *LocalRuleSet) reloadRules(headlessRules []option.HeadlessRule) error { } } var metadata adapter.RuleSetMetadata - metadata.ContainsProcessRule = hasHeadlessRule(headlessRules, isProcessHeadlessRule) - metadata.ContainsWIFIRule = hasHeadlessRule(headlessRules, isWIFIHeadlessRule) - metadata.ContainsIPCIDRRule = hasHeadlessRule(headlessRules, isIPCIDRHeadlessRule) + metadata.ContainsProcessRule = HasHeadlessRule(headlessRules, isProcessHeadlessRule) + metadata.ContainsWIFIRule = HasHeadlessRule(headlessRules, isWIFIHeadlessRule) + metadata.ContainsIPCIDRRule = HasHeadlessRule(headlessRules, isIPCIDRHeadlessRule) s.rules = rules s.metadata = metadata s.callbackAccess.Lock() diff --git a/sing-box/route/rule/rule_set_remote.go b/sing-box/route/rule/rule_set_remote.go index 16a95bb645..f31ec1430d 100644 --- a/sing-box/route/rule/rule_set_remote.go +++ b/sing-box/route/rule/rule_set_remote.go @@ -185,9 +185,9 @@ func (s *RemoteRuleSet) loadBytes(content []byte) error { return E.Cause(err, "parse rule_set.rules.[", i, "]") } } - s.metadata.ContainsProcessRule = hasHeadlessRule(plainRuleSet.Rules, isProcessHeadlessRule) - s.metadata.ContainsWIFIRule = hasHeadlessRule(plainRuleSet.Rules, isWIFIHeadlessRule) - s.metadata.ContainsIPCIDRRule = hasHeadlessRule(plainRuleSet.Rules, isIPCIDRHeadlessRule) + s.metadata.ContainsProcessRule = HasHeadlessRule(plainRuleSet.Rules, isProcessHeadlessRule) + s.metadata.ContainsWIFIRule = HasHeadlessRule(plainRuleSet.Rules, isWIFIHeadlessRule) + s.metadata.ContainsIPCIDRRule = HasHeadlessRule(plainRuleSet.Rules, isIPCIDRHeadlessRule) s.rules = rules s.callbackAccess.Lock() callbacks := s.callbacks.Array() diff --git a/sing-box/service/ssmapi/api.go b/sing-box/service/ssmapi/api.go index 14c3ac32e1..6a067c50cf 100644 --- a/sing-box/service/ssmapi/api.go +++ b/sing-box/service/ssmapi/api.go @@ -2,7 +2,6 @@ package ssmapi import ( "net/http" - "strconv" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing/common/logger" @@ -157,18 +156,14 @@ func (s *APIServer) deleteUser(writer http.ResponseWriter, request *http.Request } func (s *APIServer) getStats(writer http.ResponseWriter, request *http.Request) { - requireClear, _ := strconv.ParseBool(chi.URLParam(request, "clear")) + requireClear := request.URL.Query().Get("clear") == "true" users := s.user.List() - s.traffic.ReadUsers(users) + s.traffic.ReadUsers(users, requireClear) for i := range users { users[i].Password = "" } - uplinkBytes, downlinkBytes, uplinkPackets, downlinkPackets, tcpSessions, udpSessions := s.traffic.ReadGlobal() - - if requireClear { - s.traffic.Clear() - } + uplinkBytes, downlinkBytes, uplinkPackets, downlinkPackets, tcpSessions, udpSessions := s.traffic.ReadGlobal(requireClear) render.JSON(writer, request, render.M{ "uplinkBytes": uplinkBytes, diff --git a/sing-box/service/ssmapi/traffic.go b/sing-box/service/ssmapi/traffic.go index 23a034c157..f39d56d8f1 100644 --- a/sing-box/service/ssmapi/traffic.go +++ b/sing-box/service/ssmapi/traffic.go @@ -142,86 +142,82 @@ func (s *TrafficManager) TrackPacketConnection(conn N.PacketConn, metadata adapt readPacketCounter = append(readPacketCounter, upPacketsCounter) writePacketCounter = append(writePacketCounter, downPacketsCounter) udpSessionCounter.Add(1) - return bufio.NewInt64CounterPacketConn(conn, append(readCounter, readPacketCounter...), append(writeCounter, writePacketCounter...)) + return bufio.NewInt64CounterPacketConn(conn, readCounter, readPacketCounter, writeCounter, writePacketCounter) } func (s *TrafficManager) ReadUser(user *UserObject) { s.userAccess.Lock() defer s.userAccess.Unlock() - s.readUser(user) + s.readUser(user, false) } -func (s *TrafficManager) readUser(user *UserObject) { +func (s *TrafficManager) readUser(user *UserObject, swap bool) { if counter, loaded := s.userUplink[user.UserName]; loaded { - user.UplinkBytes = counter.Load() + if swap { + user.UplinkBytes = counter.Swap(0) + } else { + user.UplinkBytes = counter.Load() + } } if counter, loaded := s.userDownlink[user.UserName]; loaded { - user.DownlinkBytes = counter.Load() + if swap { + user.DownlinkBytes = counter.Swap(0) + } else { + user.DownlinkBytes = counter.Load() + } } if counter, loaded := s.userUplinkPackets[user.UserName]; loaded { - user.UplinkPackets = counter.Load() + if swap { + user.UplinkPackets = counter.Swap(0) + } else { + user.UplinkPackets = counter.Load() + } } if counter, loaded := s.userDownlinkPackets[user.UserName]; loaded { - user.DownlinkPackets = counter.Load() + if swap { + user.DownlinkPackets = counter.Swap(0) + } else { + user.DownlinkPackets = counter.Load() + } } if counter, loaded := s.userTCPSessions[user.UserName]; loaded { - user.TCPSessions = counter.Load() + if swap { + user.TCPSessions = counter.Swap(0) + } else { + user.TCPSessions = counter.Load() + } } if counter, loaded := s.userUDPSessions[user.UserName]; loaded { - user.UDPSessions = counter.Load() + if swap { + user.UDPSessions = counter.Swap(0) + } else { + user.UDPSessions = counter.Load() + } } } -func (s *TrafficManager) ReadUsers(users []*UserObject) { +func (s *TrafficManager) ReadUsers(users []*UserObject, swap bool) { s.userAccess.Lock() defer s.userAccess.Unlock() for _, user := range users { - s.readUser(user) + s.readUser(user, swap) } - return } -func (s *TrafficManager) ReadGlobal() ( - uplinkBytes int64, - downlinkBytes int64, - uplinkPackets int64, - downlinkPackets int64, - tcpSessions int64, - udpSessions int64, -) { - return s.globalUplink.Load(), - s.globalDownlink.Load(), - s.globalUplinkPackets.Load(), - s.globalDownlinkPackets.Load(), - s.globalTCPSessions.Load(), - s.globalUDPSessions.Load() -} - -func (s *TrafficManager) Clear() { - s.globalUplink.Store(0) - s.globalDownlink.Store(0) - s.globalUplinkPackets.Store(0) - s.globalDownlinkPackets.Store(0) - s.globalTCPSessions.Store(0) - s.globalUDPSessions.Store(0) - s.userAccess.Lock() - defer s.userAccess.Unlock() - for _, counter := range s.userUplink { - counter.Store(0) - } - for _, counter := range s.userDownlink { - counter.Store(0) - } - for _, counter := range s.userUplinkPackets { - counter.Store(0) - } - for _, counter := range s.userDownlinkPackets { - counter.Store(0) - } - for _, counter := range s.userTCPSessions { - counter.Store(0) - } - for _, counter := range s.userUDPSessions { - counter.Store(0) +func (s *TrafficManager) ReadGlobal(swap bool) (uplinkBytes int64, downlinkBytes int64, uplinkPackets int64, downlinkPackets int64, tcpSessions int64, udpSessions int64) { + if swap { + return s.globalUplink.Swap(0), + s.globalDownlink.Swap(0), + s.globalUplinkPackets.Swap(0), + s.globalDownlinkPackets.Swap(0), + s.globalTCPSessions.Swap(0), + s.globalUDPSessions.Swap(0) + } else { + return s.globalUplink.Load(), + s.globalDownlink.Load(), + s.globalUplinkPackets.Load(), + s.globalDownlinkPackets.Load(), + s.globalTCPSessions.Load(), + s.globalUDPSessions.Load() } } diff --git a/sing-box/transport/v2raygrpc/client.go b/sing-box/transport/v2raygrpc/client.go index e649aa8f26..2bbaa627b4 100644 --- a/sing-box/transport/v2raygrpc/client.go +++ b/sing-box/transport/v2raygrpc/client.go @@ -4,6 +4,7 @@ import ( "context" "net" "sync" + "sync/atomic" "time" "github.com/sagernet/sing-box/adapter" @@ -29,7 +30,7 @@ type Client struct { serverAddr string serviceName string dialOptions []grpc.DialOption - conn *grpc.ClientConn + conn atomic.Pointer[grpc.ClientConn] connAccess sync.Mutex } @@ -74,13 +75,13 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt } func (c *Client) connect() (*grpc.ClientConn, error) { - conn := c.conn + conn := c.conn.Load() if conn != nil && conn.GetState() != connectivity.Shutdown { return conn, nil } c.connAccess.Lock() defer c.connAccess.Unlock() - conn = c.conn + conn = c.conn.Load() if conn != nil && conn.GetState() != connectivity.Shutdown { return conn, nil } @@ -89,7 +90,7 @@ func (c *Client) connect() (*grpc.ClientConn, error) { if err != nil { return nil, err } - c.conn = conn + c.conn.Store(conn) return conn, nil } @@ -109,11 +110,9 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) { } func (c *Client) Close() error { - c.connAccess.Lock() - defer c.connAccess.Unlock() - if c.conn != nil { - c.conn.Close() - c.conn = nil + conn := c.conn.Swap(nil) + if conn != nil { + conn.Close() } return nil } diff --git a/sing-box/transport/v2raygrpclite/client.go b/sing-box/transport/v2raygrpclite/client.go index de8915a17c..b2aab911f2 100644 --- a/sing-box/transport/v2raygrpclite/client.go +++ b/sing-box/transport/v2raygrpclite/client.go @@ -29,7 +29,6 @@ var defaultClientHeader = http.Header{ type Client struct { ctx context.Context - dialer N.Dialer serverAddr M.Socksaddr transport *http2.Transport options option.V2RayGRPCOptions @@ -46,7 +45,6 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt } client := &Client{ ctx: ctx, - dialer: dialer, serverAddr: serverAddr, options: options, transport: &http2.Transport{ @@ -62,7 +60,6 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt }, host: host, } - if tlsConfig == nil { client.transport.DialTLSContext = func(ctx context.Context, network, addr string, cfg *tls.STDConfig) (net.Conn, error) { return dialer.DialContext(ctx, network, M.ParseSocksaddr(addr)) @@ -71,12 +68,9 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt if len(tlsConfig.NextProtos()) == 0 { tlsConfig.SetNextProtos([]string{http2.NextProtoTLS}) } + tlsDialer := tls.NewDialer(dialer, tlsConfig) client.transport.DialTLSContext = func(ctx context.Context, network, addr string, cfg *tls.STDConfig) (net.Conn, error) { - conn, err := dialer.DialContext(ctx, network, M.ParseSocksaddr(addr)) - if err != nil { - return nil, err - } - return tls.ClientHandshake(ctx, conn, tlsConfig) + return tlsDialer.DialTLSContext(ctx, M.ParseSocksaddr(addr)) } } diff --git a/sing-box/transport/v2rayhttp/client.go b/sing-box/transport/v2rayhttp/client.go index a105a4f35f..6c327cd65f 100644 --- a/sing-box/transport/v2rayhttp/client.go +++ b/sing-box/transport/v2rayhttp/client.go @@ -47,15 +47,12 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt if len(tlsConfig.NextProtos()) == 0 { tlsConfig.SetNextProtos([]string{http2.NextProtoTLS}) } + tlsDialer := tls.NewDialer(dialer, tlsConfig) transport = &http2.Transport{ ReadIdleTimeout: time.Duration(options.IdleTimeout), PingTimeout: time.Duration(options.PingTimeout), DialTLSContext: func(ctx context.Context, network, addr string, cfg *tls.STDConfig) (net.Conn, error) { - conn, err := dialer.DialContext(ctx, network, M.ParseSocksaddr(addr)) - if err != nil { - return nil, err - } - return tls.ClientHandshake(ctx, conn, tlsConfig) + return tlsDialer.DialTLSContext(ctx, M.ParseSocksaddr(addr)) }, } } diff --git a/sing-box/transport/v2rayhttpupgrade/client.go b/sing-box/transport/v2rayhttpupgrade/client.go index e2b86b1f6b..f282d3f697 100644 --- a/sing-box/transport/v2rayhttpupgrade/client.go +++ b/sing-box/transport/v2rayhttpupgrade/client.go @@ -23,7 +23,6 @@ var _ adapter.V2RayClientTransport = (*Client)(nil) type Client struct { dialer N.Dialer - tlsConfig tls.Config serverAddr M.Socksaddr requestURL url.URL headers http.Header @@ -35,6 +34,7 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt if len(tlsConfig.NextProtos()) == 0 { tlsConfig.SetNextProtos([]string{"http/1.1"}) } + dialer = tls.NewDialer(dialer, tlsConfig) } var host string if options.Host != "" { @@ -65,7 +65,6 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt } return &Client{ dialer: dialer, - tlsConfig: tlsConfig, serverAddr: serverAddr, requestURL: requestURL, headers: headers, @@ -78,12 +77,6 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) { if err != nil { return nil, err } - if c.tlsConfig != nil { - conn, err = tls.ClientHandshake(ctx, conn, c.tlsConfig) - if err != nil { - return nil, err - } - } request := &http.Request{ Method: http.MethodGet, URL: &c.requestURL, diff --git a/sing-box/transport/v2rayquic/client.go b/sing-box/transport/v2rayquic/client.go index a1c3e3a681..3d1d916e39 100644 --- a/sing-box/transport/v2rayquic/client.go +++ b/sing-box/transport/v2rayquic/client.go @@ -15,6 +15,7 @@ import ( "github.com/sagernet/sing-box/option" "github.com/sagernet/sing-quic" "github.com/sagernet/sing/common" + "github.com/sagernet/sing/common/atomic" "github.com/sagernet/sing/common/bufio" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" @@ -29,7 +30,7 @@ type Client struct { tlsConfig tls.Config quicConfig *quic.Config connAccess sync.Mutex - conn quic.Connection + conn atomic.TypedValue[quic.Connection] rawConn net.Conn } @@ -50,13 +51,13 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt } func (c *Client) offer() (quic.Connection, error) { - conn := c.conn + conn := c.conn.Load() if conn != nil && !common.Done(conn.Context()) { return conn, nil } c.connAccess.Lock() defer c.connAccess.Unlock() - conn = c.conn + conn = c.conn.Load() if conn != nil && !common.Done(conn.Context()) { return conn, nil } @@ -72,14 +73,13 @@ func (c *Client) offerNew() (quic.Connection, error) { if err != nil { return nil, err } - var packetConn net.PacketConn - packetConn = bufio.NewUnbindPacketConn(udpConn) + packetConn := bufio.NewUnbindPacketConn(udpConn) quicConn, err := qtls.Dial(c.ctx, packetConn, udpConn.RemoteAddr(), c.tlsConfig, c.quicConfig) if err != nil { packetConn.Close() return nil, err } - c.conn = quicConn + c.conn.Store(quicConn) c.rawConn = udpConn return quicConn, nil } @@ -99,13 +99,13 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) { func (c *Client) Close() error { c.connAccess.Lock() defer c.connAccess.Unlock() - if c.conn != nil { - c.conn.CloseWithError(0, "") + conn := c.conn.Swap(nil) + if conn != nil { + conn.CloseWithError(0, "") } if c.rawConn != nil { c.rawConn.Close() } - c.conn = nil c.rawConn = nil return nil } diff --git a/sing-box/transport/v2raywebsocket/client.go b/sing-box/transport/v2raywebsocket/client.go index 748bae4cee..e56301093e 100644 --- a/sing-box/transport/v2raywebsocket/client.go +++ b/sing-box/transport/v2raywebsocket/client.go @@ -26,7 +26,6 @@ var _ adapter.V2RayClientTransport = (*Client)(nil) type Client struct { dialer N.Dialer - tlsConfig tls.Config serverAddr M.Socksaddr requestURL url.URL headers http.Header @@ -39,6 +38,7 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt if len(tlsConfig.NextProtos()) == 0 { tlsConfig.SetNextProtos([]string{"http/1.1"}) } + dialer = tls.NewDialer(dialer, tlsConfig) } var requestURL url.URL if tlsConfig == nil { @@ -65,7 +65,6 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt } return &Client{ dialer, - tlsConfig, serverAddr, requestURL, headers, @@ -79,12 +78,6 @@ func (c *Client) dialContext(ctx context.Context, requestURL *url.URL, headers h if err != nil { return nil, err } - if c.tlsConfig != nil { - conn, err = tls.ClientHandshake(ctx, conn, c.tlsConfig) - if err != nil { - return nil, err - } - } var deadlineConn net.Conn if deadline.NeedAdditionalReadDeadline(conn) { deadlineConn = deadline.NewConn(conn) diff --git a/sing-box/transport/v2raywebsocket/conn.go b/sing-box/transport/v2raywebsocket/conn.go index 7f347dc987..009cadd87b 100644 --- a/sing-box/transport/v2raywebsocket/conn.go +++ b/sing-box/transport/v2raywebsocket/conn.go @@ -8,6 +8,7 @@ import ( "net" "os" "sync" + "sync/atomic" "time" C "github.com/sagernet/sing-box/constant" @@ -135,20 +136,22 @@ func (c *WebsocketConn) Upstream() any { type EarlyWebsocketConn struct { *Client ctx context.Context - conn *WebsocketConn + conn atomic.Pointer[WebsocketConn] access sync.Mutex create chan struct{} err error } func (c *EarlyWebsocketConn) Read(b []byte) (n int, err error) { - if c.conn == nil { + conn := c.conn.Load() + if conn == nil { <-c.create if c.err != nil { return 0, c.err } + conn = c.conn.Load() } - return wrapWsError0(c.conn.Read(b)) + return wrapWsError0(conn.Read(b)) } func (c *EarlyWebsocketConn) writeRequest(content []byte) error { @@ -187,21 +190,23 @@ func (c *EarlyWebsocketConn) writeRequest(content []byte) error { return err } } - c.conn = conn + c.conn.Store(conn) return nil } func (c *EarlyWebsocketConn) Write(b []byte) (n int, err error) { - if c.conn != nil { - return wrapWsError0(c.conn.Write(b)) + conn := c.conn.Load() + if conn != nil { + return wrapWsError0(conn.Write(b)) } c.access.Lock() defer c.access.Unlock() + conn = c.conn.Load() if c.err != nil { return 0, c.err } - if c.conn != nil { - return wrapWsError0(c.conn.Write(b)) + if conn != nil { + return wrapWsError0(conn.Write(b)) } err = c.writeRequest(b) c.err = err @@ -213,17 +218,19 @@ func (c *EarlyWebsocketConn) Write(b []byte) (n int, err error) { } func (c *EarlyWebsocketConn) WriteBuffer(buffer *buf.Buffer) error { - if c.conn != nil { - return wrapWsError(c.conn.WriteBuffer(buffer)) + conn := c.conn.Load() + if conn != nil { + return wrapWsError(conn.WriteBuffer(buffer)) } c.access.Lock() defer c.access.Unlock() - if c.conn != nil { - return wrapWsError(c.conn.WriteBuffer(buffer)) - } if c.err != nil { return c.err } + conn = c.conn.Load() + if conn != nil { + return wrapWsError(conn.WriteBuffer(buffer)) + } err := c.writeRequest(buffer.Bytes()) c.err = err close(c.create) @@ -231,24 +238,27 @@ func (c *EarlyWebsocketConn) WriteBuffer(buffer *buf.Buffer) error { } func (c *EarlyWebsocketConn) Close() error { - if c.conn == nil { + conn := c.conn.Load() + if conn == nil { return nil } - return c.conn.Close() + return conn.Close() } func (c *EarlyWebsocketConn) LocalAddr() net.Addr { - if c.conn == nil { + conn := c.conn.Load() + if conn == nil { return M.Socksaddr{} } - return c.conn.LocalAddr() + return conn.LocalAddr() } func (c *EarlyWebsocketConn) RemoteAddr() net.Addr { - if c.conn == nil { + conn := c.conn.Load() + if conn == nil { return M.Socksaddr{} } - return c.conn.RemoteAddr() + return conn.RemoteAddr() } func (c *EarlyWebsocketConn) SetDeadline(t time.Time) error { @@ -268,11 +278,11 @@ func (c *EarlyWebsocketConn) NeedAdditionalReadDeadline() bool { } func (c *EarlyWebsocketConn) Upstream() any { - return common.PtrOrNil(c.conn) + return common.PtrOrNil(c.conn.Load()) } func (c *EarlyWebsocketConn) LazyHeadroom() bool { - return c.conn == nil + return c.conn.Load() == nil } func wrapWsError(err error) error { diff --git a/small/v2ray-geodata/Makefile b/small/v2ray-geodata/Makefile index f7538b5d1e..269cf96812 100644 --- a/small/v2ray-geodata/Makefile +++ b/small/v2ray-geodata/Makefile @@ -12,7 +12,7 @@ PKG_MAINTAINER:=Tianling Shen include $(INCLUDE_DIR)/package.mk -GEOIP_VER:=202508091008 +GEOIP_VER:=202508140022 GEOIP_FILE:=geoip.dat.$(GEOIP_VER) define Download/geoip URL:=https://github.com/v2fly/geoip/releases/download/$(GEOIP_VER)/ @@ -21,7 +21,7 @@ define Download/geoip HASH:=54761d8691a5756fdb08d2cd4d0a9c889dbaab786e1cf758592e09fb00377f53 endef -GEOSITE_VER:=20250627153051 +GEOSITE_VER:=20250814002625 GEOSITE_FILE:=dlc.dat.$(GEOSITE_VER) define Download/geosite URL:=https://github.com/v2fly/domain-list-community/releases/download/$(GEOSITE_VER)/ diff --git a/v2ray-rules-dat/.github/workflows/run.yml b/v2ray-rules-dat/.github/workflows/run.yml index 3c5eec0631..78dcff78a6 100644 --- a/v2ray-rules-dat/.github/workflows/run.yml +++ b/v2ray-rules-dat/.github/workflows/run.yml @@ -31,24 +31,24 @@ jobs: shell: bash - name: Checkout the "hidden" branch of this repo - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: ref: hidden - name: Checkout Loyalsoldier/domain-list-custom - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: repository: Loyalsoldier/domain-list-custom path: custom - name: Checkout v2fly/domain-list-community - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: repository: v2fly/domain-list-community path: community - name: Checkout cokebar/gfwlist2dnsmasq - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: repository: cokebar/gfwlist2dnsmasq path: gfwlist2dnsmasq diff --git a/v2rayn/v2rayN/ServiceLib/Global.cs b/v2rayn/v2rayN/ServiceLib/Global.cs index 3e7b8858da..c0c997504d 100644 --- a/v2rayn/v2rayN/ServiceLib/Global.cs +++ b/v2rayn/v2rayN/ServiceLib/Global.cs @@ -48,6 +48,7 @@ public class Global public const string ProxyTag = "proxy"; public const string DirectTag = "direct"; public const string BlockTag = "block"; + public const string DnsTag = "dns-module"; public const string StreamSecurity = "tls"; public const string StreamSecurityReality = "reality"; public const string Loopback = "127.0.0.1"; @@ -56,6 +57,9 @@ public class Global public const string HttpsProtocol = "https://"; public const string SocksProtocol = "socks://"; public const string Socks5Protocol = "socks5://"; + public const string AsIs = "AsIs"; + public const string IPIfNonMatch = "IPIfNonMatch"; + public const string IPOnDemand = "IPOnDemand"; public const string UserEMail = "t@t.tt"; public const string AutoRunRegPath = @"Software\Microsoft\Windows\CurrentVersion\Run"; @@ -287,9 +291,9 @@ public class Global public static readonly List DomainStrategies = [ - "AsIs", - "IPIfNonMatch", - "IPOnDemand" + AsIs, + IPIfNonMatch, + IPOnDemand ]; public static readonly List DomainStrategies4Singbox = diff --git a/v2rayn/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayn/v2rayN/ServiceLib/Handler/ConfigHandler.cs index c05424a2dc..981fd2f537 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/ConfigHandler.cs +++ b/v2rayn/v2rayN/ServiceLib/Handler/ConfigHandler.cs @@ -2262,6 +2262,7 @@ public class ConfigHandler return 0; } + public static async Task SaveFullConfigTemplate(Config config, FullConfigTemplateItem item) { if (item == null) diff --git a/v2rayn/v2rayN/ServiceLib/Handler/SubscriptionHandler.cs b/v2rayn/v2rayN/ServiceLib/Handler/SubscriptionHandler.cs new file mode 100644 index 0000000000..b06700f8e2 --- /dev/null +++ b/v2rayn/v2rayN/ServiceLib/Handler/SubscriptionHandler.cs @@ -0,0 +1,133 @@ +namespace ServiceLib.Handler; + +public class SubscriptionHandler +{ + public static async Task UpdateProcess(Config config, string subId, bool blProxy, Action updateFunc) + { + updateFunc?.Invoke(false, ResUI.MsgUpdateSubscriptionStart); + var subItem = await AppHandler.Instance.SubItems(); + + if (subItem is not { Count: > 0 }) + { + updateFunc?.Invoke(false, ResUI.MsgNoValidSubscription); + return; + } + + foreach (var item in subItem) + { + var id = item.Id.TrimEx(); + var url = item.Url.TrimEx(); + var userAgent = item.UserAgent.TrimEx(); + var hashCode = $"{item.Remarks}->"; + if (id.IsNullOrEmpty() || url.IsNullOrEmpty() || (subId.IsNotEmpty() && item.Id != subId)) + { + //_updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgNoValidSubscription}"); + continue; + } + if (!url.StartsWith(Global.HttpsProtocol) && !url.StartsWith(Global.HttpProtocol)) + { + continue; + } + if (item.Enabled == false) + { + updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgSkipSubscriptionUpdate}"); + continue; + } + + var downloadHandle = new DownloadService(); + downloadHandle.Error += (sender2, args) => + { + updateFunc?.Invoke(false, $"{hashCode}{args.GetException().Message}"); + }; + + updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgStartGettingSubscriptions}"); + + //one url + url = Utils.GetPunycode(url); + //convert + if (item.ConvertTarget.IsNotEmpty()) + { + var subConvertUrl = config.ConstItem.SubConvertUrl.IsNullOrEmpty() ? Global.SubConvertUrls.FirstOrDefault() : config.ConstItem.SubConvertUrl; + url = string.Format(subConvertUrl!, Utils.UrlEncode(url)); + if (!url.Contains("target=")) + { + url += string.Format("&target={0}", item.ConvertTarget); + } + if (!url.Contains("config=")) + { + url += string.Format("&config={0}", Global.SubConvertConfig.FirstOrDefault()); + } + } + var result = await downloadHandle.TryDownloadString(url, blProxy, userAgent); + if (blProxy && result.IsNullOrEmpty()) + { + result = await downloadHandle.TryDownloadString(url, false, userAgent); + } + + //more url + if (item.ConvertTarget.IsNullOrEmpty() && item.MoreUrl.TrimEx().IsNotEmpty()) + { + if (result.IsNotEmpty() && Utils.IsBase64String(result)) + { + result = Utils.Base64Decode(result); + } + + var lstUrl = item.MoreUrl.TrimEx().Split(",") ?? []; + foreach (var it in lstUrl) + { + var url2 = Utils.GetPunycode(it); + if (url2.IsNullOrEmpty()) + { + continue; + } + + var result2 = await downloadHandle.TryDownloadString(url2, blProxy, userAgent); + if (blProxy && result2.IsNullOrEmpty()) + { + result2 = await downloadHandle.TryDownloadString(url2, false, userAgent); + } + if (result2.IsNotEmpty()) + { + if (Utils.IsBase64String(result2)) + { + result += Environment.NewLine + Utils.Base64Decode(result2); + } + else + { + result += Environment.NewLine + result2; + } + } + } + } + + if (result.IsNullOrEmpty()) + { + updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgSubscriptionDecodingFailed}"); + } + else + { + updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgGetSubscriptionSuccessfully}"); + if (result?.Length < 99) + { + updateFunc?.Invoke(false, $"{hashCode}{result}"); + } + + var ret = await ConfigHandler.AddBatchServers(config, result, id, true); + if (ret <= 0) + { + Logging.SaveLog("FailedImportSubscription"); + Logging.SaveLog(result); + } + updateFunc?.Invoke(false, + ret > 0 + ? $"{hashCode}{ResUI.MsgUpdateSubscriptionEnd}" + : $"{hashCode}{ResUI.MsgFailedImportSubscription}"); + } + updateFunc?.Invoke(false, "-------------------------------------------------------"); + + //await ConfigHandler.DedupServerList(config, id); + } + + updateFunc?.Invoke(true, $"{ResUI.MsgUpdateSubscriptionEnd}"); + } +} diff --git a/v2rayn/v2rayN/ServiceLib/Handler/TaskHandler.cs b/v2rayn/v2rayN/ServiceLib/Handler/TaskHandler.cs index ef1d4e382c..b49570be73 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/TaskHandler.cs +++ b/v2rayn/v2rayN/ServiceLib/Handler/TaskHandler.cs @@ -63,11 +63,10 @@ public class TaskHandler } Logging.SaveLog("Execute update subscription"); - var updateHandle = new UpdateService(); foreach (var item in lstSubs) { - await updateHandle.UpdateSubscriptionProcess(config, item.Id, true, (bool success, string msg) => + await SubscriptionHandler.UpdateProcess(config, item.Id, true, (bool success, string msg) => { updateFunc?.Invoke(success, msg); if (success) diff --git a/v2rayn/v2rayN/ServiceLib/Models/V2rayConfig.cs b/v2rayn/v2rayN/ServiceLib/Models/V2rayConfig.cs index 354e33dc88..702bbae144 100644 --- a/v2rayn/v2rayN/ServiceLib/Models/V2rayConfig.cs +++ b/v2rayn/v2rayN/ServiceLib/Models/V2rayConfig.cs @@ -203,8 +203,15 @@ public class Response4Ray public class Dns4Ray { - public Dictionary>? hosts { get; set; } + public Dictionary? hosts { get; set; } public List servers { get; set; } + public string? clientIp { get; set; } + public string? queryStrategy { get; set; } + public bool? disableCache { get; set; } + public bool? disableFallback { get; set; } + public bool? disableFallbackIfMatch { get; set; } + public bool? useSystemHosts { get; set; } + public string? tag { get; set; } } public class DnsServer4Ray @@ -214,6 +221,12 @@ public class DnsServer4Ray public bool? skipFallback { get; set; } public List? expectedIPs { get; set; } public List? unexpectedIPs { get; set; } + public string? clientIp { get; set; } + public string? queryStrategy { get; set; } + public int? timeoutMs { get; set; } + public bool? disableCache { get; set; } + public bool? finalQuery { get; set; } + public string? tag { get; set; } } public class Routing4Ray diff --git a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs index fa9957865e..fa2b14d80e 100644 --- a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs +++ b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs @@ -1329,7 +1329,7 @@ public class CoreConfigSingboxService action = "resolve", strategy = domainStrategy }; - if (_config.RoutingBasicItem.DomainStrategy == "IPOnDemand") + if (_config.RoutingBasicItem.DomainStrategy == Global.IPOnDemand) { singboxConfig.route.rules.Add(resolveRule); } @@ -1351,7 +1351,7 @@ public class CoreConfigSingboxService } } } - if (_config.RoutingBasicItem.DomainStrategy == "IPIfNonMatch") + if (_config.RoutingBasicItem.DomainStrategy == Global.IPIfNonMatch) { singboxConfig.route.rules.Add(resolveRule); foreach (var item2 in ipRules) @@ -1865,8 +1865,7 @@ public class CoreConfigSingboxService else if (item.OutboundTag == Global.BlockTag) { rule.action = "predefined"; - rule.rcode = "NOERROR"; - rule.answer = new List { "A" }; + rule.rcode = "NXDOMAIN"; } else { diff --git a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs index 43911046b8..62c2e1822b 100644 --- a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs +++ b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs @@ -177,7 +177,7 @@ public class CoreConfigV2rayService rule.balancerTag = balancer.tag; } } - if (v2rayConfig.routing.domainStrategy == "IPIfNonMatch") + if (v2rayConfig.routing.domainStrategy == Global.IPIfNonMatch) { v2rayConfig.routing.rules.Add(new() { @@ -1143,7 +1143,21 @@ public class CoreConfigV2rayService var item = await AppHandler.Instance.GetDNSItem(ECoreType.Xray); if (item != null && item.Enabled == true) { - return await GenDnsCompatible(node, v2rayConfig); + var result = await GenDnsCompatible(node, v2rayConfig); + + if (v2rayConfig.routing.domainStrategy == Global.IPIfNonMatch) + { + // DNS routing + v2rayConfig.dns.tag = Global.DnsTag; + v2rayConfig.routing.rules.Add(new RulesItem4Ray + { + type = "field", + inboundTag = new List { Global.DnsTag }, + outboundTag = Global.ProxyTag, + }); + } + + return result; } var simpleDNSItem = _config.SimpleDNSItem; var domainStrategy4Freedom = simpleDNSItem?.RayStrategy4Freedom; @@ -1164,6 +1178,18 @@ public class CoreConfigV2rayService await GenDnsServers(node, v2rayConfig, simpleDNSItem); await GenDnsHosts(v2rayConfig, simpleDNSItem); + + if (v2rayConfig.routing.domainStrategy == Global.IPIfNonMatch) + { + // DNS routing + v2rayConfig.dns.tag = Global.DnsTag; + v2rayConfig.routing.rules.Add(new RulesItem4Ray + { + type = "field", + inboundTag = new List { Global.DnsTag }, + outboundTag = Global.ProxyTag, + }); + } } catch (Exception ex) { @@ -1263,7 +1289,7 @@ public class CoreConfigV2rayService directDomainList.Add(normalizedDomain); } } - else if (item.OutboundTag == Global.ProxyTag) + else if (item.OutboundTag != Global.BlockTag) { if (normalizedDomain.StartsWith("geosite:")) { @@ -1340,10 +1366,13 @@ public class CoreConfigV2rayService return await Task.FromResult(0); } v2rayConfig.dns ??= new Dns4Ray(); - v2rayConfig.dns.hosts ??= new Dictionary>(); + v2rayConfig.dns.hosts ??= new Dictionary(); if (simpleDNSItem.AddCommonHosts == true) { - v2rayConfig.dns.hosts = Global.PredefinedHosts; + v2rayConfig.dns.hosts = Global.PredefinedHosts.ToDictionary( + kvp => kvp.Key, + kvp => (object)kvp.Value + ); } if (simpleDNSItem.UseSystemHosts == true) diff --git a/v2rayn/v2rayN/ServiceLib/Services/UpdateService.cs b/v2rayn/v2rayN/ServiceLib/Services/UpdateService.cs index 831ef2c373..b60ddc6cc3 100644 --- a/v2rayn/v2rayN/ServiceLib/Services/UpdateService.cs +++ b/v2rayn/v2rayN/ServiceLib/Services/UpdateService.cs @@ -6,7 +6,7 @@ namespace ServiceLib.Services; public class UpdateService { private Action? _updateFunc; - private int _timeout = 30; + private readonly int _timeout = 30; private static readonly string _tag = "UpdateService"; public async Task CheckUpdateGuiN(Config config, Action updateFunc, bool preRelease) @@ -104,137 +104,6 @@ public class UpdateService } } - public async Task UpdateSubscriptionProcess(Config config, string subId, bool blProxy, Action updateFunc) - { - _updateFunc = updateFunc; - - _updateFunc?.Invoke(false, ResUI.MsgUpdateSubscriptionStart); - var subItem = await AppHandler.Instance.SubItems(); - - if (subItem is not { Count: > 0 }) - { - _updateFunc?.Invoke(false, ResUI.MsgNoValidSubscription); - return; - } - - foreach (var item in subItem) - { - var id = item.Id.TrimEx(); - var url = item.Url.TrimEx(); - var userAgent = item.UserAgent.TrimEx(); - var hashCode = $"{item.Remarks}->"; - if (id.IsNullOrEmpty() || url.IsNullOrEmpty() || (subId.IsNotEmpty() && item.Id != subId)) - { - //_updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgNoValidSubscription}"); - continue; - } - if (!url.StartsWith(Global.HttpsProtocol) && !url.StartsWith(Global.HttpProtocol)) - { - continue; - } - if (item.Enabled == false) - { - _updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgSkipSubscriptionUpdate}"); - continue; - } - - var downloadHandle = new DownloadService(); - downloadHandle.Error += (sender2, args) => - { - _updateFunc?.Invoke(false, $"{hashCode}{args.GetException().Message}"); - }; - - _updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgStartGettingSubscriptions}"); - - //one url - url = Utils.GetPunycode(url); - //convert - if (item.ConvertTarget.IsNotEmpty()) - { - var subConvertUrl = config.ConstItem.SubConvertUrl.IsNullOrEmpty() ? Global.SubConvertUrls.FirstOrDefault() : config.ConstItem.SubConvertUrl; - url = string.Format(subConvertUrl!, Utils.UrlEncode(url)); - if (!url.Contains("target=")) - { - url += string.Format("&target={0}", item.ConvertTarget); - } - if (!url.Contains("config=")) - { - url += string.Format("&config={0}", Global.SubConvertConfig.FirstOrDefault()); - } - } - var result = await downloadHandle.TryDownloadString(url, blProxy, userAgent); - if (blProxy && result.IsNullOrEmpty()) - { - result = await downloadHandle.TryDownloadString(url, false, userAgent); - } - - //more url - if (item.ConvertTarget.IsNullOrEmpty() && item.MoreUrl.TrimEx().IsNotEmpty()) - { - if (result.IsNotEmpty() && Utils.IsBase64String(result)) - { - result = Utils.Base64Decode(result); - } - - var lstUrl = item.MoreUrl.TrimEx().Split(",") ?? []; - foreach (var it in lstUrl) - { - var url2 = Utils.GetPunycode(it); - if (url2.IsNullOrEmpty()) - { - continue; - } - - var result2 = await downloadHandle.TryDownloadString(url2, blProxy, userAgent); - if (blProxy && result2.IsNullOrEmpty()) - { - result2 = await downloadHandle.TryDownloadString(url2, false, userAgent); - } - if (result2.IsNotEmpty()) - { - if (Utils.IsBase64String(result2)) - { - result += Environment.NewLine + Utils.Base64Decode(result2); - } - else - { - result += Environment.NewLine + result2; - } - } - } - } - - if (result.IsNullOrEmpty()) - { - _updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgSubscriptionDecodingFailed}"); - } - else - { - _updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgGetSubscriptionSuccessfully}"); - if (result?.Length < 99) - { - _updateFunc?.Invoke(false, $"{hashCode}{result}"); - } - - var ret = await ConfigHandler.AddBatchServers(config, result, id, true); - if (ret <= 0) - { - Logging.SaveLog("FailedImportSubscription"); - Logging.SaveLog(result); - } - _updateFunc?.Invoke(false, - ret > 0 - ? $"{hashCode}{ResUI.MsgUpdateSubscriptionEnd}" - : $"{hashCode}{ResUI.MsgFailedImportSubscription}"); - } - _updateFunc?.Invoke(false, "-------------------------------------------------------"); - - //await ConfigHandler.DedupServerList(config, id); - } - - _updateFunc?.Invoke(true, $"{ResUI.MsgUpdateSubscriptionEnd}"); - } - public async Task UpdateGeoFileAll(Config config, Action updateFunc) { await UpdateGeoFiles(config, updateFunc); diff --git a/v2rayn/v2rayN/ServiceLib/ViewModels/FullConfigTemplateViewModel.cs b/v2rayn/v2rayN/ServiceLib/ViewModels/FullConfigTemplateViewModel.cs index 3aa618bf25..f36a7fb603 100644 --- a/v2rayn/v2rayN/ServiceLib/ViewModels/FullConfigTemplateViewModel.cs +++ b/v2rayn/v2rayN/ServiceLib/ViewModels/FullConfigTemplateViewModel.cs @@ -1,12 +1,13 @@ using System.Reactive; -using DynamicData.Binding; using ReactiveUI; using ReactiveUI.Fody.Helpers; namespace ServiceLib.ViewModels; + public class FullConfigTemplateViewModel : MyReactiveObject { #region Reactive + [Reactive] public bool EnableFullConfigTemplate4Ray { get; set; } @@ -35,6 +36,7 @@ public class FullConfigTemplateViewModel : MyReactiveObject public string ProxyDetour4Singbox { get; set; } public ReactiveCommand SaveCmd { get; } + #endregion Reactive public FullConfigTemplateViewModel(Func>? updateView) @@ -48,6 +50,7 @@ public class FullConfigTemplateViewModel : MyReactiveObject _ = Init(); } + private async Task Init() { var item = await AppHandler.Instance.GetFullConfigTemplateItem(ECoreType.Xray); diff --git a/v2rayn/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs b/v2rayn/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs index 8355996dca..8578036d7a 100644 --- a/v2rayn/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs +++ b/v2rayn/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs @@ -477,7 +477,7 @@ public class MainWindowViewModel : MyReactiveObject public async Task UpdateSubscriptionProcess(string subId, bool blProxy) { - await (new UpdateService()).UpdateSubscriptionProcess(_config, subId, blProxy, UpdateTaskHandler); + await SubscriptionHandler.UpdateProcess(_config, subId, blProxy, UpdateTaskHandler); } #endregion Subscription diff --git a/v2rayn/v2rayN/v2rayN/Views/OptionSettingWindow.xaml b/v2rayn/v2rayN/v2rayN/Views/OptionSettingWindow.xaml index bdd47f1c85..e3e38992b0 100644 --- a/v2rayn/v2rayN/v2rayN/Views/OptionSettingWindow.xaml +++ b/v2rayn/v2rayN/v2rayN/Views/OptionSettingWindow.xaml @@ -1246,4 +1246,3 @@ - diff --git a/v2rayng/V2rayNG/app/src/main/res/values-ru/strings.xml b/v2rayng/V2rayNG/app/src/main/res/values-ru/strings.xml index e700406913..fb45db8729 100644 --- a/v2rayng/V2rayNG/app/src/main/res/values-ru/strings.xml +++ b/v2rayng/V2rayNG/app/src/main/res/values-ru/strings.xml @@ -391,6 +391,6 @@ Наименьшая задержка Наименьшая нагрузка - Pre-resolving domain… + Предварительное определение домена… diff --git a/xray-core/.github/workflows/docker.yml b/xray-core/.github/workflows/docker.yml index 35b8e7be24..1fcee94979 100644 --- a/xray-core/.github/workflows/docker.yml +++ b/xray-core/.github/workflows/docker.yml @@ -65,7 +65,7 @@ jobs: echo "LATEST=$LATEST" >>${GITHUB_ENV} - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set up QEMU uses: docker/setup-qemu-action@v3 diff --git a/xray-core/.github/workflows/release-win7.yml b/xray-core/.github/workflows/release-win7.yml index be9173e348..fa33a345a7 100644 --- a/xray-core/.github/workflows/release-win7.yml +++ b/xray-core/.github/workflows/release-win7.yml @@ -63,7 +63,7 @@ jobs: CGO_ENABLED: 0 steps: - name: Checkout codebase - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Show workflow information run: | diff --git a/xray-core/.github/workflows/release.yml b/xray-core/.github/workflows/release.yml index b18e94adcb..3777f6f64f 100644 --- a/xray-core/.github/workflows/release.yml +++ b/xray-core/.github/workflows/release.yml @@ -153,7 +153,7 @@ jobs: CGO_ENABLED: 0 steps: - name: Checkout codebase - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set up NDK if: matrix.goos == 'android' diff --git a/xray-core/.github/workflows/test.yml b/xray-core/.github/workflows/test.yml index c853a1435b..60ff8aaad9 100644 --- a/xray-core/.github/workflows/test.yml +++ b/xray-core/.github/workflows/test.yml @@ -45,7 +45,7 @@ jobs: os: [windows-latest, ubuntu-latest, macos-latest] steps: - name: Checkout codebase - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set up Go uses: actions/setup-go@v5 with: diff --git a/yt-dlp/.github/workflows/build.yml b/yt-dlp/.github/workflows/build.yml index 6679eb0221..ec5d4020ab 100644 --- a/yt-dlp/.github/workflows/build.yml +++ b/yt-dlp/.github/workflows/build.yml @@ -21,9 +21,6 @@ on: macos: default: true type: boolean - macos_legacy: - default: true - type: boolean windows: default: true type: boolean @@ -67,10 +64,6 @@ on: description: yt-dlp_macos, yt-dlp_macos.zip default: true type: boolean - macos_legacy: - description: yt-dlp_macos_legacy - default: true - type: boolean windows: description: yt-dlp.exe, yt-dlp_win.zip default: true @@ -344,58 +337,6 @@ jobs: ~/yt-dlp-build-venv key: cache-reqs-${{ github.job }}-${{ github.ref }} - macos_legacy: - needs: process - if: inputs.macos_legacy - runs-on: macos-13 - - steps: - - uses: actions/checkout@v4 - - name: Install Python - # We need the official Python, because the GA ones only support newer macOS versions - env: - PYTHON_VERSION: 3.10.5 - MACOSX_DEPLOYMENT_TARGET: 10.9 # Used up by the Python build tools - run: | - # Hack to get the latest patch version. Uncomment if needed - #brew install python@3.10 - #export PYTHON_VERSION=$( $(brew --prefix)/opt/python@3.10/bin/python3 --version | cut -d ' ' -f 2 ) - curl "https://www.python.org/ftp/python/${PYTHON_VERSION}/python-${PYTHON_VERSION}-macos11.pkg" -o "python.pkg" - sudo installer -pkg python.pkg -target / - python3 --version - - name: Install Requirements - run: | - brew install coreutils - python3 devscripts/install_deps.py --user -o --include build - python3 devscripts/install_deps.py --user --include pyinstaller - - - name: Prepare - run: | - python3 devscripts/update-version.py -c "${{ inputs.channel }}" -r "${{ needs.process.outputs.origin }}" "${{ inputs.version }}" - python3 devscripts/make_lazy_extractors.py - - name: Build - run: | - python3 -m bundle.pyinstaller - mv dist/yt-dlp_macos dist/yt-dlp_macos_legacy - - - name: Verify --update-to - if: vars.UPDATE_TO_VERIFICATION - run: | - chmod +x ./dist/yt-dlp_macos_legacy - cp ./dist/yt-dlp_macos_legacy ./dist/yt-dlp_macos_legacy_downgraded - version="$(./dist/yt-dlp_macos_legacy --version)" - ./dist/yt-dlp_macos_legacy_downgraded -v --update-to yt-dlp/yt-dlp@2023.03.04 - downgraded_version="$(./dist/yt-dlp_macos_legacy_downgraded --version)" - [[ "$version" != "$downgraded_version" ]] - - - name: Upload artifacts - uses: actions/upload-artifact@v4 - with: - name: build-bin-${{ github.job }} - path: | - dist/yt-dlp_macos_legacy - compression-level: 0 - windows: needs: process if: inputs.windows @@ -498,7 +439,6 @@ jobs: - linux_static - linux_arm - macos - - macos_legacy - windows - windows32 runs-on: ubuntu-latest @@ -530,27 +470,31 @@ jobs: lock 2023.11.16 win_x86_exe .+ Windows-(?:Vista|2008Server) lock 2024.10.22 py2exe .+ lock 2024.10.22 linux_(?:armv7l|aarch64)_exe .+-glibc2\.(?:[12]?\d|30)\b - lock 2024.10.22 (?!\w+_exe).+ Python 3\.8 + lock 2024.10.22 zip Python 3\.8 lock 2024.10.22 win(?:_x86)?_exe Python 3\.[78].+ Windows-(?:7-|2008ServerR2) + lock 2025.08.11 darwin_legacy_exe .+ lockV2 yt-dlp/yt-dlp 2022.08.18.36 .+ Python 3\.6 lockV2 yt-dlp/yt-dlp 2023.11.16 (?!win_x86_exe).+ Python 3\.7 lockV2 yt-dlp/yt-dlp 2023.11.16 win_x86_exe .+ Windows-(?:Vista|2008Server) lockV2 yt-dlp/yt-dlp 2024.10.22 py2exe .+ lockV2 yt-dlp/yt-dlp 2024.10.22 linux_(?:armv7l|aarch64)_exe .+-glibc2\.(?:[12]?\d|30)\b - lockV2 yt-dlp/yt-dlp 2024.10.22 (?!\w+_exe).+ Python 3\.8 + lockV2 yt-dlp/yt-dlp 2024.10.22 zip Python 3\.8 lockV2 yt-dlp/yt-dlp 2024.10.22 win(?:_x86)?_exe Python 3\.[78].+ Windows-(?:7-|2008ServerR2) + lockV2 yt-dlp/yt-dlp 2025.08.11 darwin_legacy_exe .+ lockV2 yt-dlp/yt-dlp-nightly-builds 2023.11.15.232826 (?!win_x86_exe).+ Python 3\.7 lockV2 yt-dlp/yt-dlp-nightly-builds 2023.11.15.232826 win_x86_exe .+ Windows-(?:Vista|2008Server) lockV2 yt-dlp/yt-dlp-nightly-builds 2024.10.22.051025 py2exe .+ lockV2 yt-dlp/yt-dlp-nightly-builds 2024.10.22.051025 linux_(?:armv7l|aarch64)_exe .+-glibc2\.(?:[12]?\d|30)\b - lockV2 yt-dlp/yt-dlp-nightly-builds 2024.10.22.051025 (?!\w+_exe).+ Python 3\.8 + lockV2 yt-dlp/yt-dlp-nightly-builds 2024.10.22.051025 zip Python 3\.8 lockV2 yt-dlp/yt-dlp-nightly-builds 2024.10.22.051025 win(?:_x86)?_exe Python 3\.[78].+ Windows-(?:7-|2008ServerR2) + lockV2 yt-dlp/yt-dlp-nightly-builds 2025.08.12.233030 darwin_legacy_exe .+ lockV2 yt-dlp/yt-dlp-master-builds 2023.11.15.232812 (?!win_x86_exe).+ Python 3\.7 lockV2 yt-dlp/yt-dlp-master-builds 2023.11.15.232812 win_x86_exe .+ Windows-(?:Vista|2008Server) lockV2 yt-dlp/yt-dlp-master-builds 2024.10.22.045052 py2exe .+ lockV2 yt-dlp/yt-dlp-master-builds 2024.10.22.060347 linux_(?:armv7l|aarch64)_exe .+-glibc2\.(?:[12]?\d|30)\b - lockV2 yt-dlp/yt-dlp-master-builds 2024.10.22.060347 (?!\w+_exe).+ Python 3\.8 + lockV2 yt-dlp/yt-dlp-master-builds 2024.10.22.060347 zip Python 3\.8 lockV2 yt-dlp/yt-dlp-master-builds 2024.10.22.060347 win(?:_x86)?_exe Python 3\.[78].+ Windows-(?:7-|2008ServerR2) + lockV2 yt-dlp/yt-dlp-master-builds 2025.08.12.232447 darwin_legacy_exe .+ EOF - name: Sign checksum files diff --git a/yt-dlp/README.md b/yt-dlp/README.md index 9b28147f2c..aa8b1d4f24 100644 --- a/yt-dlp/README.md +++ b/yt-dlp/README.md @@ -111,7 +111,6 @@ File|Description [yt-dlp_linux_aarch64](https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_linux_aarch64)|Linux standalone aarch64 (64-bit) binary [yt-dlp_win.zip](https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_win.zip)|Unpackaged Windows executable (no auto-update) [yt-dlp_macos.zip](https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_macos.zip)|Unpackaged MacOS (10.15+) executable (no auto-update) -[yt-dlp_macos_legacy](https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_macos_legacy)|MacOS (10.9+) standalone x64 executable #### Misc diff --git a/yt-dlp/test/test_update.py b/yt-dlp/test/test_update.py index 23c12d38c1..b4979bc92c 100644 --- a/yt-dlp/test/test_update.py +++ b/yt-dlp/test/test_update.py @@ -84,8 +84,9 @@ lock 2023.11.16 (?!win_x86_exe).+ Python 3\.7 lock 2023.11.16 win_x86_exe .+ Windows-(?:Vista|2008Server) lock 2024.10.22 py2exe .+ lock 2024.10.22 linux_(?:armv7l|aarch64)_exe .+-glibc2\.(?:[12]?\d|30)\b -lock 2024.10.22 (?!\w+_exe).+ Python 3\.8 +lock 2024.10.22 zip Python 3\.8 lock 2024.10.22 win(?:_x86)?_exe Python 3\.[78].+ Windows-(?:7-|2008ServerR2) +lock 2025.08.11 darwin_legacy_exe .+ ''' TEST_LOCKFILE_V2_TMPL = r'''%s @@ -94,20 +95,23 @@ lockV2 yt-dlp/yt-dlp 2023.11.16 (?!win_x86_exe).+ Python 3\.7 lockV2 yt-dlp/yt-dlp 2023.11.16 win_x86_exe .+ Windows-(?:Vista|2008Server) lockV2 yt-dlp/yt-dlp 2024.10.22 py2exe .+ lockV2 yt-dlp/yt-dlp 2024.10.22 linux_(?:armv7l|aarch64)_exe .+-glibc2\.(?:[12]?\d|30)\b -lockV2 yt-dlp/yt-dlp 2024.10.22 (?!\w+_exe).+ Python 3\.8 +lockV2 yt-dlp/yt-dlp 2024.10.22 zip Python 3\.8 lockV2 yt-dlp/yt-dlp 2024.10.22 win(?:_x86)?_exe Python 3\.[78].+ Windows-(?:7-|2008ServerR2) +lockV2 yt-dlp/yt-dlp 2025.08.11 darwin_legacy_exe .+ lockV2 yt-dlp/yt-dlp-nightly-builds 2023.11.15.232826 (?!win_x86_exe).+ Python 3\.7 lockV2 yt-dlp/yt-dlp-nightly-builds 2023.11.15.232826 win_x86_exe .+ Windows-(?:Vista|2008Server) lockV2 yt-dlp/yt-dlp-nightly-builds 2024.10.22.051025 py2exe .+ lockV2 yt-dlp/yt-dlp-nightly-builds 2024.10.22.051025 linux_(?:armv7l|aarch64)_exe .+-glibc2\.(?:[12]?\d|30)\b -lockV2 yt-dlp/yt-dlp-nightly-builds 2024.10.22.051025 (?!\w+_exe).+ Python 3\.8 +lockV2 yt-dlp/yt-dlp-nightly-builds 2024.10.22.051025 zip Python 3\.8 lockV2 yt-dlp/yt-dlp-nightly-builds 2024.10.22.051025 win(?:_x86)?_exe Python 3\.[78].+ Windows-(?:7-|2008ServerR2) +lockV2 yt-dlp/yt-dlp-nightly-builds 2025.08.12.233030 darwin_legacy_exe .+ lockV2 yt-dlp/yt-dlp-master-builds 2023.11.15.232812 (?!win_x86_exe).+ Python 3\.7 lockV2 yt-dlp/yt-dlp-master-builds 2023.11.15.232812 win_x86_exe .+ Windows-(?:Vista|2008Server) lockV2 yt-dlp/yt-dlp-master-builds 2024.10.22.045052 py2exe .+ lockV2 yt-dlp/yt-dlp-master-builds 2024.10.22.060347 linux_(?:armv7l|aarch64)_exe .+-glibc2\.(?:[12]?\d|30)\b -lockV2 yt-dlp/yt-dlp-master-builds 2024.10.22.060347 (?!\w+_exe).+ Python 3\.8 +lockV2 yt-dlp/yt-dlp-master-builds 2024.10.22.060347 zip Python 3\.8 lockV2 yt-dlp/yt-dlp-master-builds 2024.10.22.060347 win(?:_x86)?_exe Python 3\.[78].+ Windows-(?:7-|2008ServerR2) +lockV2 yt-dlp/yt-dlp-master-builds 2025.08.12.232447 darwin_legacy_exe .+ ''' TEST_LOCKFILE_V2 = TEST_LOCKFILE_V2_TMPL % TEST_LOCKFILE_COMMENT @@ -217,6 +221,10 @@ class TestUpdate(unittest.TestCase): test( # linux_aarch64_exe w/glibc2.3 should only update to glibc<2.31 lock lockfile, 'linux_aarch64_exe Python 3.8.0 (CPython aarch64 64bit) - Linux-6.5.0-1025-azure-aarch64-with-glibc2.3 (OpenSSL', '2025.01.01', '2024.10.22') + test(lockfile, 'darwin_legacy_exe Python 3.10.5', '2025.08.11', '2025.08.11') + test(lockfile, 'darwin_legacy_exe Python 3.10.5', '2025.08.11', '2025.08.11', exact=True) + test(lockfile, 'darwin_legacy_exe Python 3.10.5', '2025.08.12', '2025.08.11') + test(lockfile, 'darwin_legacy_exe Python 3.10.5', '2025.08.12', None, exact=True) # Forks can block updates to non-numeric tags rather than lock test(TEST_LOCKFILE_FORK, 'zip Python 3.6.3', 'pr0000', None, repo='fork/yt-dlp') diff --git a/yt-dlp/yt_dlp/update.py b/yt-dlp/yt_dlp/update.py index ca69fbbada..dd948cd521 100644 --- a/yt-dlp/yt_dlp/update.py +++ b/yt-dlp/yt_dlp/update.py @@ -58,26 +58,30 @@ def _get_variant_and_executable_path(): """@returns (variant, executable_path)""" if getattr(sys, 'frozen', False): path = sys.executable + # py2exe is unsupported but we should still correctly identify it for debugging purposes if not hasattr(sys, '_MEIPASS'): return 'py2exe', path - elif sys._MEIPASS == os.path.dirname(path): + if sys._MEIPASS == os.path.dirname(path): return f'{sys.platform}_dir', path - elif sys.platform == 'darwin': + if sys.platform == 'darwin': + # darwin_legacy_exe is no longer supported, but still identify it to block updates machine = '_legacy' if version_tuple(platform.mac_ver()[0]) < (10, 15) else '' - else: - machine = f'_{platform.machine().lower()}' - is_64bits = sys.maxsize > 2**32 - # Ref: https://en.wikipedia.org/wiki/Uname#Examples - if machine[1:] in ('x86', 'x86_64', 'amd64', 'i386', 'i686'): - machine = '_x86' if not is_64bits else '' - # platform.machine() on 32-bit raspbian OS may return 'aarch64', so check "64-bitness" - # See: https://github.com/yt-dlp/yt-dlp/issues/11813 - elif machine[1:] == 'aarch64' and not is_64bits: - machine = '_armv7l' - # sys.executable returns a /tmp/ path for staticx builds (linux_static) - # Ref: https://staticx.readthedocs.io/en/latest/usage.html#run-time-information - if static_exe_path := os.getenv('STATICX_PROG_PATH'): - path = static_exe_path + return f'darwin{machine}_exe', path + + machine = f'_{platform.machine().lower()}' + is_64bits = sys.maxsize > 2**32 + # Ref: https://en.wikipedia.org/wiki/Uname#Examples + if machine[1:] in ('x86', 'x86_64', 'amd64', 'i386', 'i686'): + machine = '_x86' if not is_64bits else '' + # platform.machine() on 32-bit raspbian OS may return 'aarch64', so check "64-bitness" + # See: https://github.com/yt-dlp/yt-dlp/issues/11813 + elif machine[1:] == 'aarch64' and not is_64bits: + machine = '_armv7l' + # sys.executable returns a /tmp/ path for staticx builds (linux_static) + # Ref: https://staticx.readthedocs.io/en/latest/usage.html#run-time-information + if static_exe_path := os.getenv('STATICX_PROG_PATH'): + path = static_exe_path + return f'{remove_end(sys.platform, "32")}{machine}_exe', path path = os.path.dirname(__file__) @@ -111,7 +115,6 @@ _FILE_SUFFIXES = { 'win_exe': '.exe', 'win_x86_exe': '_x86.exe', 'darwin_exe': '_macos', - 'darwin_legacy_exe': '_macos_legacy', 'linux_exe': '_linux', 'linux_aarch64_exe': '_linux_aarch64', 'linux_armv7l_exe': '_linux_armv7l', @@ -147,12 +150,6 @@ def _get_system_deprecation(): STOP_MSG = 'You may stop receiving updates on this version at any time!' variant = detect_variant() - # Temporary until macos_legacy executable builds are discontinued - if variant == 'darwin_legacy_exe': - return EXE_MSG_TMPL.format( - f'{variant} (the PyInstaller-bundled executable for macOS versions older than 10.15)', - 'issues/13856', STOP_MSG) - # Temporary until linux_armv7l executable builds are discontinued if variant == 'linux_armv7l_exe': return EXE_MSG_TMPL.format(