Update On Thu Aug 14 20:41:00 CEST 2025

This commit is contained in:
github-action[bot]
2025-08-14 20:41:01 +02:00
parent 443688d1bb
commit e7629184d4
128 changed files with 2166 additions and 1213 deletions
+1
View File
@@ -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 Mon Aug 11 20:43:53 CEST 2025
Update On Tue Aug 12 20:40:42 CEST 2025 Update On Tue Aug 12 20:40:42 CEST 2025
Update On Wed Aug 13 20:43:46 CEST 2025 Update On Wed Aug 13 20:43:46 CEST 2025
Update On Thu Aug 14 20:40:52 CEST 2025
+26 -40
View File
@@ -5,58 +5,50 @@ import (
"sync/atomic" "sync/atomic"
) )
func DefaultValue[T any]() T {
var defaultValue T
return defaultValue
}
type TypedValue[T any] struct { type TypedValue[T any] struct {
_ noCopy value atomic.Pointer[T]
value atomic.Value
} }
// tValue is a struct with determined type to resolve atomic.Value usages with interface types func (t *TypedValue[T]) Load() (v T) {
// https://github.com/golang/go/issues/22550 v, _ = t.LoadOk()
// return
// 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() T { func (t *TypedValue[T]) LoadOk() (v T, ok bool) {
value, _ := t.LoadOk()
return value
}
func (t *TypedValue[T]) LoadOk() (_ T, ok bool) {
value := t.value.Load() value := t.value.Load()
if value == nil { if value == nil {
return DefaultValue[T](), false return
} }
return value.(tValue[T]).value, true return *value, true
} }
func (t *TypedValue[T]) Store(value T) { 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 { func (t *TypedValue[T]) Swap(new T) (v T) {
old := t.value.Swap(tValue[T]{new}) old := t.value.Swap(&new)
if old == nil { if old == nil {
return DefaultValue[T]() return
} }
return old.(tValue[T]).value return *old
} }
func (t *TypedValue[T]) CompareAndSwap(old, new T) bool { func (t *TypedValue[T]) CompareAndSwap(old, new T) bool {
return t.value.CompareAndSwap(tValue[T]{old}, tValue[T]{new}) || for {
// In the edge-case where [atomic.Value.Store] is uninitialized currentP := t.value.Load()
// and trying to compare with the zero value of T, var currentValue T
// then compare-and-swap with the nil any value. if currentP != nil {
(any(old) == any(DefaultValue[T]()) && t.value.CompareAndSwap(any(nil), tValue[T]{new})) 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) { func (t *TypedValue[T]) MarshalJSON() ([]byte, error) {
@@ -89,9 +81,3 @@ func NewTypedValue[T any](t T) (v TypedValue[T]) {
v.Store(t) v.Store(t)
return return
} }
type noCopy struct{}
// Lock is a no-op used by -copylocks checker from `go vet`.
func (*noCopy) Lock() {}
func (*noCopy) Unlock() {}
+106 -14
View File
@@ -7,20 +7,6 @@ import (
) )
func TestTypedValue(t *testing.T) { 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] var v TypedValue[int]
got, gotOk := v.LoadOk() 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{}) c1, c2, c3 := make(chan struct{}), make(chan struct{}), make(chan struct{})
var v TypedValue[chan struct{}] var v TypedValue[chan struct{}]
if v.CompareAndSwap(c1, c2) != false { if v.CompareAndSwap(c1, c2) != false {
t.Fatalf("CompareAndSwap = true, want 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 { if v.CompareAndSwap(nil, c1) != true {
t.Fatalf("CompareAndSwap = false, want 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 { if v.CompareAndSwap(c2, c3) != false {
t.Fatalf("CompareAndSwap = true, want 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 { if v.CompareAndSwap(c1, c2) != true {
t.Fatalf("CompareAndSwap = false, want 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)
}
} }
} }
@@ -10,27 +10,27 @@ import (
) )
var ( var (
keepAliveIdle = atomic.NewTypedValue[time.Duration](0 * time.Second) keepAliveIdle = atomic.NewInt64(0)
keepAliveInterval = atomic.NewTypedValue[time.Duration](0 * time.Second) keepAliveInterval = atomic.NewInt64(0)
disableKeepAlive = atomic.NewBool(false) disableKeepAlive = atomic.NewBool(false)
SetDisableKeepAliveCallback = utils.NewCallback[bool]() SetDisableKeepAliveCallback = utils.NewCallback[bool]()
) )
func SetKeepAliveIdle(t time.Duration) { func SetKeepAliveIdle(t time.Duration) {
keepAliveIdle.Store(t) keepAliveIdle.Store(int64(t))
} }
func SetKeepAliveInterval(t time.Duration) { func SetKeepAliveInterval(t time.Duration) {
keepAliveInterval.Store(t) keepAliveInterval.Store(int64(t))
} }
func KeepAliveIdle() time.Duration { func KeepAliveIdle() time.Duration {
return keepAliveIdle.Load() return time.Duration(keepAliveIdle.Load())
} }
func KeepAliveInterval() time.Duration { func KeepAliveInterval() time.Duration {
return keepAliveInterval.Load() return time.Duration(keepAliveInterval.Load())
} }
func SetDisableKeepAlive(disable bool) { func SetDisableKeepAlive(disable bool) {
+7 -7
View File
@@ -3,7 +3,6 @@ module github.com/metacubex/mihomo
go 1.20 go 1.20
require ( require (
github.com/3andne/restls-client-go v0.1.6
github.com/bahlo/generic-list-go v0.2.0 github.com/bahlo/generic-list-go v0.2.0
github.com/coreos/go-iptables v0.8.0 github.com/coreos/go-iptables v0.8.0
github.com/dlclark/regexp2 v1.11.5 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/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab
github.com/metacubex/bart v0.20.5 github.com/metacubex/bart v0.20.5
github.com/metacubex/bbolt v0.0.0-20250725135710-010dbbbb7a5b 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/chacha v0.1.5
github.com/metacubex/fswatch v0.1.1 github.com/metacubex/fswatch v0.1.1
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759
github.com/metacubex/quic-go v0.54.1-0.20250730114134-a1ae705fe295 github.com/metacubex/quic-go v0.54.1-0.20250730114134-a1ae705fe295
github.com/metacubex/randv2 v0.2.0 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-mux v0.3.3-0.20250813083925-d7c9aeaeeaac
github.com/metacubex/sing-quic v0.0.0-20250718154553-1b193bec4cbb github.com/metacubex/sing-quic v0.0.0-20250718154553-1b193bec4cbb
github.com/metacubex/sing-shadowsocks v0.2.11 github.com/metacubex/sing-shadowsocks v0.2.12
github.com/metacubex/sing-shadowsocks2 v0.2.5 github.com/metacubex/sing-shadowsocks2 v0.2.6
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 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-vmess v0.2.4-0.20250731011226-ea28d589924d
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f
github.com/metacubex/smux v0.0.0-20250503055512-501391591dee 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 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 google.golang.org/protobuf v1.34.2 // lastest version compatible with golang1.20
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
lukechampine.com/blake3 v1.3.0 // lastest version compatible with golang1.20
) )
require ( require (
@@ -85,11 +85,11 @@ require (
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
github.com/hashicorp/yamux v0.1.2 // indirect github.com/hashicorp/yamux v0.1.2 // indirect
github.com/josharian/native v1.1.0 // 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/kr/text v0.2.0 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect
github.com/mdlayher/socket v0.4.1 // 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/gvisor v0.0.0-20250324165734-5857f47bd43b // indirect
github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793 // indirect github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793 // indirect
github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect
+14 -14
View File
@@ -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 h1:smavTzSMAx8iuVlGb4pEwl9MD2qicqMzuXR2QWp2/Pg=
github.com/RyuaNerin/go-krypto v1.3.0/go.mod h1:9R9TU936laAIqAmjcHo/LsaXYOZlymudOAxjaBf62UM= github.com/RyuaNerin/go-krypto v1.3.0/go.mod h1:9R9TU936laAIqAmjcHo/LsaXYOZlymudOAxjaBf62UM=
github.com/RyuaNerin/testingutil v0.1.0 h1:IYT6JL57RV3U2ml3dLHZsVtPOP6yNK7WUVdzzlpNrss= 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/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 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= 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/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 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 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/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 h1:Chbw+/31UC14YFNr78pESt5Vowlc62zziw05JCUqoL4=
github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab/go.mod h1:xVKK8jC5Sd3hfh7WjmCq+HorehIbrBijaUWmcuKjPcI= 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 h1:XkgLZ17QxfxkqKdGsojoM2Zu01mmHyyQSFzt2/calTM=
github.com/metacubex/bart v0.20.5/go.mod h1:DCcyfP4MC+Zy7sLK7XeGuMw+P5K9mIRsYOBgiE8icsI= 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 h1:j7dadXD8I2KTmMt8jg1JcaP1ANL3JEObJPdANKcSYPY=
github.com/metacubex/bbolt v0.0.0-20250725135710-010dbbbb7a5b/go.mod h1:+WmP0VJZDkDszvpa83HzfUp6QzARl/IKkMorH4+nODw= 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 h1:fKWMb/5c7ZrY8Uoqi79PPFxl+qwR7X/q0OrsAubyX2M=
github.com/metacubex/chacha v0.1.5/go.mod h1:Djn9bPZxLTXbJFSeyo0/qzEzQI+gUSSzttuzZM75GH8= github.com/metacubex/chacha v0.1.5/go.mod h1:Djn9bPZxLTXbJFSeyo0/qzEzQI+gUSSzttuzZM75GH8=
github.com/metacubex/fswatch v0.1.1 h1:jqU7C/v+g0qc2RUFgmAOPoVvfl2BXXUXEumn6oQuxhU= 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/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 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs=
github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= 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.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.5 h1:m5U8iHvRAUxlme3FZlE/LPIGHjU8oMCUzXWGbQQAC1E=
github.com/metacubex/sing v0.5.4/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w= 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 h1:wDH/Jh/yqWbzPktqJP+Y1cUG8hchcrzKzUxJiSpnaQs=
github.com/metacubex/sing-mux v0.3.3-0.20250813083925-d7c9aeaeeaac/go.mod h1:3rt1soewn0O6j89GCLmwAQFsq257u0jf2zQSPhTL3Bw= 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 h1:U/m3h8lp/j7i8zFgfvScLdZa1/Y8dd74oO7iZaQq80s=
github.com/metacubex/sing-quic v0.0.0-20250718154553-1b193bec4cbb/go.mod h1:B60FxaPHjR1SeQB0IiLrgwgvKsaoASfOWdiqhLjmMGA= 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.12 h1:Wqzo8bYXrK5aWqxu/TjlTnYZzAKtKsaFQBdr6IHFaBE=
github.com/metacubex/sing-shadowsocks v0.2.11/go.mod h1:bT1PCTV316zFnlToRMk5zt9HmIQYRBveiT71mplYPfc= github.com/metacubex/sing-shadowsocks v0.2.12/go.mod h1:2e5EIaw0rxKrm1YTRmiMnDulwbGxH9hAFlrwQLQMQkU=
github.com/metacubex/sing-shadowsocks2 v0.2.5 h1:MnPn0hbcDkSJt6TlpI15XImHKK6IqaOwBUGPKyMnJnE= github.com/metacubex/sing-shadowsocks2 v0.2.6 h1:ZR1kYT0f0Vi64iQSS09OdhFfppiNkh7kjgRdMm0SB98=
github.com/metacubex/sing-shadowsocks2 v0.2.5/go.mod h1:Zyh+rAQRyevYfG/COCvDs1c/YMhGqCuknn7QrGmoQIw= 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 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-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 h1:ZDY/W+1c7PeWWKeKRyUo18fySF/TWjB0i5ui81Ar778=
github.com/metacubex/sing-tun v0.4.7-0.20250801130030-308b828865ae/go.mod h1:2YywXPWW8Z97kTH7RffOeykKzU+l0aiKlglWV1PAS64= 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 h1:koSVAQiYqU/qtJ527e+wbsEPvTaM39HW75LSvh04IT0=
github.com/metacubex/sing-vmess v0.2.4-0.20250731011226-ea28d589924d/go.mod h1:21R5R1u90uUvBQF0owoooEu96/SAYYD56nDrwm6nFaM= 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= 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.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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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=
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"context" "context"
"net" "net"
tls "github.com/3andne/restls-client-go" tls "github.com/metacubex/restls-client-go"
) )
const ( const (
+1 -1
View File
@@ -8,8 +8,8 @@ import (
"net/netip" "net/netip"
"strconv" "strconv"
"github.com/metacubex/blake3"
"github.com/metacubex/quic-go" "github.com/metacubex/quic-go"
"lukechampine.com/blake3"
C "github.com/metacubex/mihomo/constant" C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/transport/socks5" "github.com/metacubex/mihomo/transport/socks5"
+13 -23
View File
@@ -105,26 +105,20 @@ func (vc *Conn) sendRequest(p []byte) bool {
} }
} }
var buffer *buf.Buffer requestLen := 1 // protocol version
if vc.IsXTLSVisionEnabled() { requestLen += 16 // UUID
buffer = buf.New() requestLen += 1 // addons length
defer buffer.Release() requestLen += len(addonsBytes)
} else { requestLen += 1 // command
requestLen := 1 // protocol version if !vc.dst.Mux {
requestLen += 16 // UUID requestLen += 2 // port
requestLen += 1 // addons length requestLen += 1 // addr type
requestLen += len(addonsBytes) requestLen += len(vc.dst.Addr)
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 += len(p)
buffer := buf.NewSize(requestLen)
defer buffer.Release()
buf.Must( buf.Must(
buffer.WriteByte(Version), // protocol version buffer.WriteByte(Version), // protocol version
@@ -182,10 +176,6 @@ func (vc *Conn) NeedHandshake() bool {
return vc.needHandshake return vc.needHandshake
} }
func (vc *Conn) IsXTLSVisionEnabled() bool {
return vc.addons != nil && vc.addons.Flow == XRV
}
// newConn return a Conn instance // newConn return a Conn instance
func newConn(conn net.Conn, client *Client, dst *DstAddr) (net.Conn, error) { func newConn(conn net.Conn, client *Client, dst *DstAddr) (net.Conn, error) {
c := &Conn{ c := &Conn{
+26 -35
View File
@@ -4,11 +4,13 @@ import (
"bytes" "bytes"
"crypto/cipher" "crypto/cipher"
"crypto/rand" "crypto/rand"
"crypto/sha256"
"errors" "errors"
"fmt" "fmt"
"io" "io"
"net" "net"
"runtime" "runtime"
"strings"
"sync" "sync"
"time" "time"
@@ -36,13 +38,12 @@ func init() {
type ClientInstance struct { type ClientInstance struct {
sync.RWMutex sync.RWMutex
nfsEKey *mlkem.EncapsulationKey768 nfsEKey *mlkem.EncapsulationKey768
nfsEKeyBytes []byte xorKey []byte
xor uint32 minutes time.Duration
minutes time.Duration expire time.Time
expire time.Time baseKey []byte
baseKey []byte ticket []byte
ticket []byte
} }
type ClientConn struct { type ClientConn struct {
@@ -59,10 +60,17 @@ type ClientConn struct {
} }
func (i *ClientInstance) Init(nfsEKeyBytes []byte, xor uint32, minutes time.Duration) (err error) { 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) i.nfsEKey, err = mlkem.NewEncapsulationKey768(nfsEKeyBytes)
if err != nil {
return
}
if xor > 0 { if xor > 0 {
i.nfsEKeyBytes = nfsEKeyBytes xorKey := sha256.Sum256(nfsEKeyBytes)
i.xor = xor i.xorKey = xorKey[:]
} }
i.minutes = minutes i.minutes = minutes
return return
@@ -72,8 +80,8 @@ func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) {
if i.nfsEKey == nil { if i.nfsEKey == nil {
return nil, errors.New("uninitialized") return nil, errors.New("uninitialized")
} }
if i.xor > 0 { if i.xorKey != nil {
conn = NewXorConn(conn, i.nfsEKeyBytes) conn = NewXorConn(conn, i.xorKey)
} }
c := &ClientConn{Conn: conn} 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 { if _, err := c.Conn.Write(clientHello); err != nil {
return nil, err 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 { if err != nil {
return nil, err return nil, err
} }
if t != 1 { if t != 1 {
return nil, fmt.Errorf("unexpected type %v, expect random hello", t) return nil, fmt.Errorf("unexpected type %v, expect random hello", t)
} }
peerRandomHello := make([]byte, 1088+21) peerRandomHello := make([]byte, 1088+21)
if l != len(peerRandomHello) { if l != len(peerRandomHello) {
return nil, fmt.Errorf("unexpected length %v for random hello", l) 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 return 0, nil
} }
if c.peerAead == nil { if c.peerAead == nil {
var t byte _, t, l, err := ReadAndDiscardPaddings(c.Conn) // allow paddings before random hello
var l int if err != nil {
var err error if c.instance != nil && strings.HasPrefix(err.Error(), "invalid header: ") { // 0-RTT's 0-RTT
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 {
c.instance.Lock() c.instance.Lock()
if bytes.Equal(c.ticket, c.instance.ticket) { if bytes.Equal(c.ticket, c.instance.ticket) {
c.instance.expire = time.Now() // expired c.instance.expire = time.Now() // expired
@@ -221,6 +211,7 @@ func (c *ClientConn) Read(b []byte) (int, error) {
c.instance.Unlock() c.instance.Unlock()
return 0, errors.New("new handshake needed") return 0, errors.New("new handshake needed")
} }
return 0, err
} }
if t != 0 { if t != 0 {
return 0, fmt.Errorf("unexpected type %v, expect server random", t) return 0, fmt.Errorf("unexpected type %v, expect server random", t)
@@ -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 { } else if h[0] == 1 && h[1] == 1 && h[2] == 1 {
t = 1 t = 1
} else { } else {
h = nil l = 0
} }
if h == nil || l < 17 || l > 17000 { // TODO: TLSv1.3 max length if l < 17 || l > 17000 { // TODO: TLSv1.3 max length
err = fmt.Errorf("invalid header: %v", h[:5]) err = fmt.Errorf("invalid header: %v", h[:5]) // DO NOT CHANGE: relied by client's Read()
} }
return return
} }
@@ -62,6 +62,17 @@ func ReadAndDecodeHeader(conn net.Conn) (h []byte, t byte, l int, err error) {
return 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) { func NewAead(c byte, secret, salt, info []byte) (aead cipher.AEAD) {
key := make([]byte, 32) key := make([]byte, 32)
hkdf.New(sha256.New, secret, salt, info).Read(key) hkdf.New(sha256.New, secret, salt, info).Read(key)
@@ -9,4 +9,6 @@
// https://github.com/XTLS/Xray-core/commit/1720be168fa069332c418503d30341fc6e01df7f // https://github.com/XTLS/Xray-core/commit/1720be168fa069332c418503d30341fc6e01df7f
// https://github.com/XTLS/Xray-core/commit/0fd7691d6b28e05922d7a5a9313d97745a51ea63 // https://github.com/XTLS/Xray-core/commit/0fd7691d6b28e05922d7a5a9313d97745a51ea63
// https://github.com/XTLS/Xray-core/commit/09cc92c61d9067e0d65c1cae9124664ecfc78f43 // 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 package encryption
+30 -32
View File
@@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"crypto/cipher" "crypto/cipher"
"crypto/rand" "crypto/rand"
"crypto/sha256"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@@ -23,12 +24,11 @@ type ServerSession struct {
type ServerInstance struct { type ServerInstance struct {
sync.RWMutex sync.RWMutex
nfsDKey *mlkem.DecapsulationKey768 nfsDKey *mlkem.DecapsulationKey768
nfsEKeyBytes []byte xorKey []byte
xor uint32 minutes time.Duration
minutes time.Duration sessions map[[21]byte]*ServerSession
sessions map[[21]byte]*ServerSession closed bool
closed bool
} }
type ServerConn struct { type ServerConn struct {
@@ -45,10 +45,17 @@ type ServerConn struct {
} }
func (i *ServerInstance) Init(nfsDKeySeed []byte, xor uint32, minutes time.Duration) (err error) { 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) i.nfsDKey, err = mlkem.NewDecapsulationKey768(nfsDKeySeed)
if err != nil {
return
}
if xor > 0 { if xor > 0 {
i.nfsEKeyBytes = i.nfsDKey.EncapsulationKey().Bytes() xorKey := sha256.Sum256(i.nfsDKey.EncapsulationKey().Bytes())
i.xor = xor i.xorKey = xorKey[:]
} }
if minutes > 0 { if minutes > 0 {
i.minutes = minutes i.minutes = minutes
@@ -85,18 +92,15 @@ func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) {
if i.nfsDKey == nil { if i.nfsDKey == nil {
return nil, errors.New("uninitialized") return nil, errors.New("uninitialized")
} }
if i.xor > 0 { if i.xorKey != nil {
conn = NewXorConn(conn, i.nfsEKeyBytes) conn = NewXorConn(conn, i.xorKey)
} }
c := &ServerConn{Conn: conn} 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 { if err != nil {
return nil, err return nil, err
} }
if t == 23 {
return nil, errors.New("unexpected data")
}
if t == 0 { if t == 0 {
if i.minutes == 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)] s := i.sessions[[21]byte(peerTicketHello)]
i.RUnlock() i.RUnlock()
if s == nil { if s == nil {
noise := make([]byte, randBetween(100, 1000)) noises := make([]byte, randBetween(100, 1000))
rand.Read(noise) var err error
c.Conn.Write(noise) // make client do new handshake for err == nil {
rand.Read(noises)
_, _, err = DecodeHeader(noises)
}
c.Conn.Write(noises) // make client do new handshake
return nil, errors.New("expired ticket") return nil, errors.New("expired ticket")
} }
if _, replay := s.randoms.LoadOrStore([32]byte(peerTicketHello[21:]), true); replay { 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 { if _, err := c.Conn.Write(serverHello); err != nil {
return nil, err 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 { if i.minutes > 0 {
i.Lock() i.Lock()
@@ -185,20 +193,10 @@ func (c *ServerConn) Read(b []byte) (int, error) {
return 0, nil return 0, nil
} }
if c.peerAead == nil { if c.peerAead == nil {
if c.peerRandom == nil { // from 1-RTT if c.peerRandom == nil { // 1-RTT's 0-RTT
var t byte _, t, l, err := ReadAndDiscardPaddings(c.Conn) // allow paddings before ticket hello
var l int if err != nil {
var err error return 0, err
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 t != 0 { if t != 0 {
return 0, fmt.Errorf("unexpected type %v, expect ticket hello", t) return 0, fmt.Errorf("unexpected type %v, expect ticket hello", t)
+7 -7
View File
@@ -18,11 +18,11 @@ type XorConn struct {
} }
func NewXorConn(conn net.Conn, key []byte) *XorConn { func NewXorConn(conn net.Conn, key []byte) *XorConn {
return &XorConn{Conn: conn, key: key[:16]} return &XorConn{Conn: conn, key: key}
//chacha20.NewUnauthenticatedCipher() //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 { if len(b) == 0 {
return 0, nil 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) c.ctr = cipher.NewCTR(block, iv)
} }
t, l, _ := DecodeHeader(b) t, l, _ := DecodeHeader(b)
if t != 23 { if t == 23 { // single 23
l += 10 // 5+l+5
} else {
l = 5 l = 5
} else { // 1/0 + 23, or noises only
l += 10
} }
c.ctr.XORKeyStream(b[:l], b[:l]) // caller MUST discard b c.ctr.XORKeyStream(b[:l], b[:l]) // caller MUST discard b
if iv != nil { if iv != nil {
@@ -73,8 +73,8 @@ func (c *XorConn) Read(b []byte) (int, error) { // 5-bytes, data, 5-bytes...
return len(b), nil return len(b), nil
} }
c.peerCtr.XORKeyStream(b, b) c.peerCtr.XORKeyStream(b, b)
if c.isHeader { if c.isHeader { // always 5-bytes
if t, _, _ := DecodeHeader(b); t == 23 { // always 5-bytes if t, _, _ := DecodeHeader(b); t == 23 {
c.skipNext = true c.skipNext = true
} else { } else {
c.isHeader = false c.isHeader = false
+4 -13
View File
@@ -3,7 +3,6 @@ package vision
import ( import (
"bytes" "bytes"
"crypto/subtle" "crypto/subtle"
gotls "crypto/tls"
"encoding/binary" "encoding/binary"
"errors" "errors"
"fmt" "fmt"
@@ -12,7 +11,6 @@ import (
"github.com/metacubex/mihomo/common/buf" "github.com/metacubex/mihomo/common/buf"
N "github.com/metacubex/mihomo/common/net" N "github.com/metacubex/mihomo/common/net"
tlsC "github.com/metacubex/mihomo/component/tls"
"github.com/metacubex/mihomo/log" "github.com/metacubex/mihomo/log"
"github.com/gofrs/uuid/v5" "github.com/gofrs/uuid/v5"
@@ -181,17 +179,10 @@ func (vc *Conn) WriteBuffer(buffer *buf.Buffer) (err error) {
buffer.Release() buffer.Release()
return err return err
} }
switch underlying := vc.tlsConn.(type) { err = vc.checkTLSVersion()
case *gotls.Conn: if err != nil {
if underlying.ConnectionState().Version != gotls.VersionTLS13 { buffer.Release()
buffer.Release() return err
return ErrNotTLS13
}
case *tlsC.UConn:
if underlying.ConnectionState().Version != tlsC.VersionTLS13 {
buffer.Release()
return ErrNotTLS13
}
} }
vc.tlsConn = nil vc.tlsConn = nil
return nil return nil
@@ -67,3 +67,21 @@ func NewConn(conn connWithUpstream, userUUID *uuid.UUID) (*Conn, error) {
c.rawInput = (*bytes.Buffer)(unsafe.Add(p, r.Offset)) c.rawInput = (*bytes.Buffer)(unsafe.Add(p, r.Offset))
return c, nil 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
}
@@ -56,12 +56,12 @@
"@csstools/normalize.css": "12.1.1", "@csstools/normalize.css": "12.1.1",
"@emotion/babel-plugin": "11.13.5", "@emotion/babel-plugin": "11.13.5",
"@emotion/react": "11.14.0", "@emotion/react": "11.14.0",
"@iconify/json": "2.2.371", "@iconify/json": "2.2.373",
"@monaco-editor/react": "4.7.0", "@monaco-editor/react": "4.7.0",
"@tanstack/react-query": "5.84.2", "@tanstack/react-query": "5.84.2",
"@tanstack/react-router": "1.131.5", "@tanstack/react-router": "1.131.10",
"@tanstack/react-router-devtools": "1.131.5", "@tanstack/react-router-devtools": "1.131.10",
"@tanstack/router-plugin": "1.131.5", "@tanstack/router-plugin": "1.131.11",
"@tauri-apps/plugin-clipboard-manager": "2.3.0", "@tauri-apps/plugin-clipboard-manager": "2.3.0",
"@tauri-apps/plugin-dialog": "2.3.0", "@tauri-apps/plugin-dialog": "2.3.0",
"@tauri-apps/plugin-fs": "2.4.0", "@tauri-apps/plugin-fs": "2.4.0",
+2 -2
View File
@@ -2,7 +2,7 @@
"manifest_version": 1, "manifest_version": 1,
"latest": { "latest": {
"mihomo": "v1.19.12", "mihomo": "v1.19.12",
"mihomo_alpha": "alpha-0e9102d", "mihomo_alpha": "alpha-0408da2",
"clash_rs": "v0.8.2", "clash_rs": "v0.8.2",
"clash_premium": "2023-09-05-gdcc8d87", "clash_premium": "2023-09-05-gdcc8d87",
"clash_rs_alpha": "0.8.2-alpha+sha.df9f591" "clash_rs_alpha": "0.8.2-alpha+sha.df9f591"
@@ -69,5 +69,5 @@
"linux-armv7hf": "clash-armv7-unknown-linux-gnueabihf" "linux-armv7hf": "clash-armv7-unknown-linux-gnueabihf"
} }
}, },
"updated_at": "2025-08-12T22:21:09.244Z" "updated_at": "2025-08-13T22:21:26.184Z"
} }
+1 -1
View File
@@ -105,7 +105,7 @@
"stylelint-order": "7.0.0", "stylelint-order": "7.0.0",
"stylelint-scss": "6.12.1", "stylelint-scss": "6.12.1",
"tailwindcss": "4.1.11", "tailwindcss": "4.1.11",
"tsx": "4.20.3", "tsx": "4.20.4",
"typescript": "5.9.2", "typescript": "5.9.2",
"typescript-eslint": "8.39.1" "typescript-eslint": "8.39.1"
}, },
+81 -88
View File
@@ -164,8 +164,8 @@ importers:
specifier: 4.1.11 specifier: 4.1.11
version: 4.1.11 version: 4.1.11
tsx: tsx:
specifier: 4.20.3 specifier: 4.20.4
version: 4.20.3 version: 4.20.4
typescript: typescript:
specifier: 5.9.2 specifier: 5.9.2
version: 5.9.2 version: 5.9.2
@@ -250,7 +250,7 @@ importers:
version: 4.1.11 version: 4.1.11
'@tanstack/router-zod-adapter': '@tanstack/router-zod-adapter':
specifier: 1.81.5 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': '@tauri-apps/api':
specifier: 2.6.0 specifier: 2.6.0
version: 2.6.0 version: 2.6.0
@@ -343,8 +343,8 @@ importers:
specifier: 11.14.0 specifier: 11.14.0
version: 11.14.0(@types/react@19.1.10)(react@19.1.1) version: 11.14.0(@types/react@19.1.10)(react@19.1.1)
'@iconify/json': '@iconify/json':
specifier: 2.2.371 specifier: 2.2.373
version: 2.2.371 version: 2.2.373
'@monaco-editor/react': '@monaco-editor/react':
specifier: 4.7.0 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) 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 specifier: 5.84.2
version: 5.84.2(react@19.1.1) version: 5.84.2(react@19.1.1)
'@tanstack/react-router': '@tanstack/react-router':
specifier: 1.131.5 specifier: 1.131.10
version: 1.131.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) version: 1.131.10(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
'@tanstack/react-router-devtools': '@tanstack/react-router-devtools':
specifier: 1.131.5 specifier: 1.131.10
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) 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': '@tanstack/router-plugin':
specifier: 1.131.5 specifier: 1.131.11
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)) 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': '@tauri-apps/plugin-clipboard-manager':
specifier: 2.3.0 specifier: 2.3.0
version: 2.3.0 version: 2.3.0
@@ -395,13 +395,13 @@ importers:
version: 13.15.2 version: 13.15.2
'@vitejs/plugin-legacy': '@vitejs/plugin-legacy':
specifier: 7.2.1 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': '@vitejs/plugin-react':
specifier: 4.7.0 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': '@vitejs/plugin-react-swc':
specifier: 3.11.0 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: change-case:
specifier: 5.4.4 specifier: 5.4.4
version: 5.4.4 version: 5.4.4
@@ -440,19 +440,19 @@ importers:
version: 13.15.15 version: 13.15.15
vite: vite:
specifier: 7.1.1 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: vite-plugin-html:
specifier: 3.2.2 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: vite-plugin-sass-dts:
specifier: 1.3.31 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: vite-plugin-svgr:
specifier: 4.3.0 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: vite-tsconfig-paths:
specifier: 5.1.4 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: zod:
specifier: 4.0.17 specifier: 4.0.17
version: 4.0.17 version: 4.0.17
@@ -488,7 +488,7 @@ importers:
version: 19.1.10 version: 19.1.10
'@vitejs/plugin-react': '@vitejs/plugin-react':
specifier: 4.7.0 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: ahooks:
specifier: 3.9.0 specifier: 3.9.0
version: 3.9.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) 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 version: 4.1.11
vite: vite:
specifier: 7.1.1 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: vite-tsconfig-paths:
specifier: 5.1.4 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: devDependencies:
'@emotion/react': '@emotion/react':
specifier: 11.14.0 specifier: 11.14.0
@@ -546,7 +546,7 @@ importers:
version: 5.2.0(typescript@5.9.2) version: 5.2.0(typescript@5.9.2)
vite-plugin-dts: vite-plugin-dts:
specifier: 4.5.4 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: scripts:
dependencies: dependencies:
@@ -1759,8 +1759,8 @@ packages:
prettier-plugin-ember-template-tag: prettier-plugin-ember-template-tag:
optional: true optional: true
'@iconify/json@2.2.371': '@iconify/json@2.2.373':
resolution: {integrity: sha512-dnamUrgw8aDWz4STJOkQqqCz6GB2s7SHGPIY84EVQmlVXzM7T1V7L/csdL7r9bkA3AHF80+OXhx7o4FXn5L8pQ==} resolution: {integrity: sha512-ZgDwDLgbzf/nEyDoSdC75A1XA0L7Q4rfz0ZKGiq/ZWTztrlM7ASBvjKb6/dhs+gtvepnOc5CLUrBcDUYKZblLg==}
'@iconify/types@2.0.0': '@iconify/types@2.0.0':
resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
@@ -2945,16 +2945,16 @@ packages:
peerDependencies: peerDependencies:
react: ^18 || ^19 react: ^18 || ^19
'@tanstack/react-router-devtools@1.131.5': '@tanstack/react-router-devtools@1.131.10':
resolution: {integrity: sha512-3LaEbWDYGnzw4J8DM7KX32qRslGrSt67Cg7uoAYReBfDlLpWVRnjpyG2fef7nHDqn5HaAuV0IbY1n6Duwp5IyQ==} resolution: {integrity: sha512-D9DlyPTQt0bkM1YcTykB9yYAiw6GhsvhvzOuprO0xKz3lMXbyuaQ5QO1xBfzhL+2Wv5nlnCLu68Ra3skE8TQTA==}
engines: {node: '>=12'} engines: {node: '>=12'}
peerDependencies: peerDependencies:
'@tanstack/react-router': ^1.131.5 '@tanstack/react-router': ^1.131.10
react: '>=18.0.0 || >=19.0.0' react: '>=18.0.0 || >=19.0.0'
react-dom: '>=18.0.0 || >=19.0.0' react-dom: '>=18.0.0 || >=19.0.0'
'@tanstack/react-router@1.131.5': '@tanstack/react-router@1.131.10':
resolution: {integrity: sha512-71suJGuCmrHN9PLLRUDB3CGnW5RNcEEfgfX616TOpKamHs977H8P4/75BgWPRWcLHCga/1kkA6c7bddCwZ35Fw==} resolution: {integrity: sha512-RjfCgHEHChmW3icKjLQSRKWKCJyzVd8qQXW2jF+cil5XXFeKvyuqm3R92uRIF/ONUUJ8b/V9/ET4/+dH/U8FPA==}
engines: {node: '>=12'} engines: {node: '>=12'}
peerDependencies: peerDependencies:
react: '>=18.0.0 || >=19.0.0' 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: ^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 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
'@tanstack/router-core@1.131.5': '@tanstack/router-core@1.131.7':
resolution: {integrity: sha512-XVfZdnKNQbWfkQ6G7I9ml2wHp98Wy7wgTboP5SfrJHfOE+kPeHeZRJqF/pp5oqLZ2feBJqsDDKNWo9323L7sWQ==} resolution: {integrity: sha512-NpFfAG1muv4abrCij6sEtRrVzlU+xYpY30NAgquHNhMMMNIiN7djzsaGV+vCJdR4u5mi13+f0c3f+f9MdekY5A==}
engines: {node: '>=12'} engines: {node: '>=12'}
'@tanstack/router-devtools-core@1.131.5': '@tanstack/router-devtools-core@1.131.7':
resolution: {integrity: sha512-kH3cZz7UfnVQW9vMZJ/CAx15pu+iGkn10N4rRKBVWCEJZFPX3GZvYEwkeALHASsV0Io7yUvKDcWfPsc+UowyzQ==} resolution: {integrity: sha512-1GHWILJr69Ej/c8UUMhT7Srx392FbsDqRrPhCWWtrjmYOv6Fdx3HdKDJt/YdJGBc8z6x+V7EE41j+LZggD+70Q==}
engines: {node: '>=12'} engines: {node: '>=12'}
peerDependencies: peerDependencies:
'@tanstack/router-core': ^1.131.5 '@tanstack/router-core': ^1.131.7
csstype: ^3.0.10 csstype: ^3.0.10
solid-js: '>=1.9.5' solid-js: '>=1.9.5'
tiny-invariant: ^1.3.3 tiny-invariant: ^1.3.3
@@ -2995,16 +2995,16 @@ packages:
csstype: csstype:
optional: true optional: true
'@tanstack/router-generator@1.131.5': '@tanstack/router-generator@1.131.7':
resolution: {integrity: sha512-5+/zyp/R9WN8tHNVIEYQZpRMzcsOrNH06HoPnPMiLiB9T4WsOLFJCcHdyso9ofGQq+hoxB4M9SUBXVBbJVWbSw==} resolution: {integrity: sha512-djwY5O1LdJo300EOZiYog5RsjB1DYzFtgX6a3uOkAmii7LHX9k9mhFXx2KrI4dLHLQsnlKexV9QvU6cSTFmsag==}
engines: {node: '>=12'} engines: {node: '>=12'}
'@tanstack/router-plugin@1.131.5': '@tanstack/router-plugin@1.131.11':
resolution: {integrity: sha512-Px+GSijNv1cbSm74+U+kEbuSFsspL+/BakzytAJFdh2O4342G32tha1cMFMlzXbDd9SW1FaLWgu3VqNHjGIpOg==} resolution: {integrity: sha512-cPDWynVPHpST2UcdEqUAmpqXh50IuSenTPZj7aDhUkw1q8Oeerv6ixYy6GTfH2JDdgQ2IgzJpPBIWkvJ+mOYHA==}
engines: {node: '>=12'} engines: {node: '>=12'}
peerDependencies: peerDependencies:
'@rsbuild/core': '>=1.0.2' '@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: '>=5.0.0 || >=6.0.0'
vite-plugin-solid: ^2.11.2 vite-plugin-solid: ^2.11.2
webpack: '>=5.92.0' webpack: '>=5.92.0'
@@ -5282,9 +5282,6 @@ packages:
get-tsconfig@4.10.1: get-tsconfig@4.10.1:
resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} 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: git-raw-commits@4.0.0:
resolution: {integrity: sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==} resolution: {integrity: sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==}
engines: {node: '>=16'} engines: {node: '>=16'}
@@ -8037,8 +8034,8 @@ packages:
tslib@2.8.1: tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
tsx@4.20.3: tsx@4.20.4:
resolution: {integrity: sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==} resolution: {integrity: sha512-yyxBKfORQ7LuRt/BQKBXrpcq59ZvSW0XxwfjAt3w2/8PmdxaFzijtMhTawprSHhpzeM5BgU2hXHG3lklIERZXg==}
engines: {node: '>=18.0.0'} engines: {node: '>=18.0.0'}
hasBin: true hasBin: true
@@ -9970,7 +9967,7 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@iconify/json@2.2.371': '@iconify/json@2.2.373':
dependencies: dependencies:
'@iconify/types': 2.0.0 '@iconify/types': 2.0.0
pathe: 1.1.2 pathe: 1.1.2
@@ -11057,10 +11054,10 @@ snapshots:
'@tanstack/query-core': 5.83.1 '@tanstack/query-core': 5.83.1
react: 19.1.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: 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)
'@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)
react: 19.1.1 react: 19.1.1
react-dom: 19.1.1(react@19.1.1) react-dom: 19.1.1(react@19.1.1)
transitivePeerDependencies: transitivePeerDependencies:
@@ -11069,11 +11066,11 @@ snapshots:
- solid-js - solid-js
- tiny-invariant - 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: dependencies:
'@tanstack/history': 1.131.2 '@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/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 isbot: 5.1.28
react: 19.1.1 react: 19.1.1
react-dom: 19.1.1(react@19.1.1) react-dom: 19.1.1(react@19.1.1)
@@ -11099,7 +11096,7 @@ snapshots:
react: 19.1.1 react: 19.1.1
react-dom: 19.1.1(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: dependencies:
'@tanstack/history': 1.131.2 '@tanstack/history': 1.131.2
'@tanstack/store': 0.7.0 '@tanstack/store': 0.7.0
@@ -11109,9 +11106,9 @@ snapshots:
tiny-invariant: 1.3.3 tiny-invariant: 1.3.3
tiny-warning: 1.0.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: dependencies:
'@tanstack/router-core': 1.131.5 '@tanstack/router-core': 1.131.7
clsx: 2.1.1 clsx: 2.1.1
goober: 2.1.16(csstype@3.1.3) goober: 2.1.16(csstype@3.1.3)
solid-js: 1.9.5 solid-js: 1.9.5
@@ -11119,20 +11116,20 @@ snapshots:
optionalDependencies: optionalDependencies:
csstype: 3.1.3 csstype: 3.1.3
'@tanstack/router-generator@1.131.5': '@tanstack/router-generator@1.131.7':
dependencies: dependencies:
'@tanstack/router-core': 1.131.5 '@tanstack/router-core': 1.131.7
'@tanstack/router-utils': 1.131.2 '@tanstack/router-utils': 1.131.2
'@tanstack/virtual-file-routes': 1.131.2 '@tanstack/virtual-file-routes': 1.131.2
prettier: 3.6.2 prettier: 3.6.2
recast: 0.23.11 recast: 0.23.11
source-map: 0.7.4 source-map: 0.7.4
tsx: 4.20.3 tsx: 4.20.4
zod: 3.25.76 zod: 3.25.76
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - 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: dependencies:
'@babel/core': 7.28.0 '@babel/core': 7.28.0
'@babel/plugin-syntax-jsx': 7.27.1(@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/template': 7.27.2
'@babel/traverse': 7.28.0 '@babel/traverse': 7.28.0
'@babel/types': 7.28.1 '@babel/types': 7.28.1
'@tanstack/router-core': 1.131.5 '@tanstack/router-core': 1.131.7
'@tanstack/router-generator': 1.131.5 '@tanstack/router-generator': 1.131.7
'@tanstack/router-utils': 1.131.2 '@tanstack/router-utils': 1.131.2
'@tanstack/virtual-file-routes': 1.131.2 '@tanstack/virtual-file-routes': 1.131.2
babel-dead-code-elimination: 1.0.10 babel-dead-code-elimination: 1.0.10
@@ -11149,8 +11146,8 @@ snapshots:
unplugin: 2.3.5 unplugin: 2.3.5
zod: 3.25.76 zod: 3.25.76
optionalDependencies: optionalDependencies:
'@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)
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: transitivePeerDependencies:
- supports-color - supports-color
@@ -11165,9 +11162,9 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - 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: 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 zod: 4.0.17
'@tanstack/store@0.7.0': {} '@tanstack/store@0.7.0': {}
@@ -11762,7 +11759,7 @@ snapshots:
'@unrs/resolver-binding-win32-x64-msvc@1.10.1': '@unrs/resolver-binding-win32-x64-msvc@1.10.1':
optional: true 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: dependencies:
'@babel/core': 7.28.0 '@babel/core': 7.28.0
'@babel/plugin-transform-dynamic-import': 7.27.1(@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 regenerator-runtime: 0.14.1
systemjs: 6.15.1 systemjs: 6.15.1
terser: 5.36.0 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: transitivePeerDependencies:
- supports-color - 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: dependencies:
'@rolldown/pluginutils': 1.0.0-beta.27 '@rolldown/pluginutils': 1.0.0-beta.27
'@swc/core': 1.13.0 '@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: transitivePeerDependencies:
- '@swc/helpers' - '@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: dependencies:
'@babel/core': 7.28.0 '@babel/core': 7.28.0
'@babel/plugin-transform-react-jsx-self': 7.27.1(@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 '@rolldown/pluginutils': 1.0.0-beta.27
'@types/babel__core': 7.20.5 '@types/babel__core': 7.20.5
react-refresh: 0.17.0 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: transitivePeerDependencies:
- supports-color - supports-color
@@ -13834,10 +13831,6 @@ snapshots:
dependencies: dependencies:
resolve-pkg-maps: 1.0.0 resolve-pkg-maps: 1.0.0
get-tsconfig@4.8.1:
dependencies:
resolve-pkg-maps: 1.0.0
git-raw-commits@4.0.0: git-raw-commits@4.0.0:
dependencies: dependencies:
dargs: 8.1.0 dargs: 8.1.0
@@ -16798,10 +16791,10 @@ snapshots:
tslib@2.8.1: {} tslib@2.8.1: {}
tsx@4.20.3: tsx@4.20.4:
dependencies: dependencies:
esbuild: 0.25.0 esbuild: 0.25.0
get-tsconfig: 4.8.1 get-tsconfig: 4.10.1
optionalDependencies: optionalDependencies:
fsevents: 2.3.3 fsevents: 2.3.3
@@ -17168,7 +17161,7 @@ snapshots:
- rollup - rollup
- supports-color - 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: dependencies:
'@microsoft/api-extractor': 7.51.0(@types/node@22.17.1) '@microsoft/api-extractor': 7.51.0(@types/node@22.17.1)
'@rollup/pluginutils': 5.1.4(rollup@4.46.2) '@rollup/pluginutils': 5.1.4(rollup@4.46.2)
@@ -17181,13 +17174,13 @@ snapshots:
magic-string: 0.30.17 magic-string: 0.30.17
typescript: 5.9.2 typescript: 5.9.2
optionalDependencies: 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: transitivePeerDependencies:
- '@types/node' - '@types/node'
- rollup - rollup
- supports-color - 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: dependencies:
'@rollup/pluginutils': 4.2.1 '@rollup/pluginutils': 4.2.1
colorette: 2.0.20 colorette: 2.0.20
@@ -17201,39 +17194,39 @@ snapshots:
html-minifier-terser: 6.1.0 html-minifier-terser: 6.1.0
node-html-parser: 5.4.2 node-html-parser: 5.4.2
pathe: 0.2.0 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: dependencies:
postcss: 8.5.6 postcss: 8.5.6
postcss-js: 4.0.1(postcss@8.5.6) postcss-js: 4.0.1(postcss@8.5.6)
prettier: 3.6.2 prettier: 3.6.2
sass-embedded: 1.90.0 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: dependencies:
'@rollup/pluginutils': 5.1.3(rollup@4.46.2) '@rollup/pluginutils': 5.1.3(rollup@4.46.2)
'@svgr/core': 8.1.0(typescript@5.9.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)) '@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: transitivePeerDependencies:
- rollup - rollup
- supports-color - supports-color
- typescript - 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: dependencies:
debug: 4.3.7 debug: 4.3.7
globrex: 0.1.2 globrex: 0.1.2
tsconfck: 3.0.3(typescript@5.9.2) tsconfck: 3.0.3(typescript@5.9.2)
optionalDependencies: 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: transitivePeerDependencies:
- supports-color - supports-color
- typescript - 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: dependencies:
esbuild: 0.25.0 esbuild: 0.25.0
fdir: 6.4.6(picomatch@4.0.3) fdir: 6.4.6(picomatch@4.0.3)
@@ -17251,7 +17244,7 @@ snapshots:
sass-embedded: 1.90.0 sass-embedded: 1.90.0
stylus: 0.62.0 stylus: 0.62.0
terser: 5.36.0 terser: 5.36.0
tsx: 4.20.3 tsx: 4.20.4
yaml: 2.8.1 yaml: 2.8.1
void-elements@3.1.0: {} void-elements@3.1.0: {}
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout codebase - name: Checkout codebase
uses: actions/checkout@v4 uses: actions/checkout@v5
with: with:
fetch-depth: 0 fetch-depth: 0
@@ -0,0 +1,125 @@
From 9989fcd49c52500a2bf1f6d49411690dec45d2dc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= <marko.makela@iki.fi>
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 <shi05275@163.com>.
Link: https://lore.kernel.org/all/20231220221724.3822-4-ansuelsmth@gmail.com/
Signed-off-by: Marko Mäkelä <marko.makela@iki.fi>
Tested-by: Marko Mäkelä <marko.makela@iki.fi>
---
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,
},
};
@@ -28,7 +28,7 @@ Signed-off-by: Robert Marko <robimarko@gmail.com>
{ .hw = &gpll0.clkr.hw }, { .hw = &gpll0.clkr.hw },
{ .hw = &gpll4.clkr.hw }, { .hw = &gpll4.clkr.hw },
{ .hw = &nss_crypto_pll.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 static const struct clk_parent_data
gcc_xo_uniphy0_rx_tx_uniphy1_rx_tx_ubi32_bias[] = { gcc_xo_uniphy0_rx_tx_uniphy1_rx_tx_ubi32_bias[] = {
{ .fw_name = "xo" }, { .fw_name = "xo" },
@@ -46,7 +46,7 @@ Signed-off-by: Robert Marko <robimarko@gmail.com>
}; };
static const struct parent_map 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 static const struct clk_parent_data
gcc_xo_uniphy0_tx_rx_uniphy1_tx_rx_ubi32_bias[] = { gcc_xo_uniphy0_tx_rx_uniphy1_tx_rx_ubi32_bias[] = {
{ .fw_name = "xo" }, { .fw_name = "xo" },
@@ -64,7 +64,7 @@ Signed-off-by: Robert Marko <robimarko@gmail.com>
}; };
static const struct parent_map 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[] = { static const struct clk_parent_data gcc_xo_uniphy0_rx_tx_ubi32_bias[] = {
{ .fw_name = "xo" }, { .fw_name = "xo" },
@@ -78,7 +78,7 @@ Signed-off-by: Robert Marko <robimarko@gmail.com>
}; };
static const struct parent_map gcc_xo_uniphy0_rx_tx_ubi32_bias_map[] = { 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[] = { static const struct clk_parent_data gcc_xo_uniphy0_tx_rx_ubi32_bias[] = {
{ .fw_name = "xo" }, { .fw_name = "xo" },
@@ -92,7 +92,7 @@ Signed-off-by: Robert Marko <robimarko@gmail.com>
}; };
static const struct parent_map gcc_xo_uniphy0_tx_rx_ubi32_bias_map[] = { 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_
{ } { }
}; };
+2 -2
View File
@@ -47,7 +47,7 @@ An example of client configuration is as follows.
"multiplexing": { "multiplexing": {
"level": "MULTIPLEXING_HIGH" "level": "MULTIPLEXING_HIGH"
}, },
"handshakeMode": "HANDSHAKE_NO_WAIT" "handshakeMode": "HANDSHAKE_STANDARD"
} }
], ],
"activeProfile": "default", "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. 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. 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`. 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. 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`. 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`. 11. [Optional] If the client needs to provide proxy services to other devices on the LAN, set the `socks5ListenLAN` property to `true`.
+2 -2
View File
@@ -47,7 +47,7 @@ mieru apply config <FILE>
"multiplexing": { "multiplexing": {
"level": "MULTIPLEXING_HIGH" "level": "MULTIPLEXING_HIGH"
}, },
"handshakeMode": "HANDSHAKE_NO_WAIT" "handshakeMode": "HANDSHAKE_STANDARD"
} }
], ],
"activeProfile": "default", "activeProfile": "default",
@@ -69,7 +69,7 @@ mieru apply config <FILE>
5.`profiles` -> `servers` -> `portBindings` -> `port` 中填写 mita 监听的 TCP 或 UDP 端口号。这个端口号必须与代理服务器中的设置相同。如果想要监听连续的端口号,也可以改为使用 `portRange` 属性。 5.`profiles` -> `servers` -> `portBindings` -> `port` 中填写 mita 监听的 TCP 或 UDP 端口号。这个端口号必须与代理服务器中的设置相同。如果想要监听连续的端口号,也可以改为使用 `portRange` 属性。
6. 【可选】请为 `profiles` -> `mtu` 属性中指定一个从 1280 到 1400 之间的值。默认值为 1400。这个值必须与代理服务器相同。 6. 【可选】请为 `profiles` -> `mtu` 属性中指定一个从 1280 到 1400 之间的值。默认值为 1400。这个值必须与代理服务器相同。
7. 【可选】如果想要调整多路复用的频率,是更多地创建新连接,还是更多地重用旧连接,可以为 `profiles` -> `multiplexing` -> `level` 属性设定一个值。这里可以使用的值包括 `MULTIPLEXING_OFF`, `MULTIPLEXING_LOW`, `MULTIPLEXING_MIDDLE`, `MULTIPLEXING_HIGH`。其中 `MULTIPLEXING_OFF` 会关闭多路复用功能。默认值为 `MULTIPLEXING_LOW` 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 之间的数值。 9. 请为 `rpcPort` 属性指定一个从 1025 到 65535 之间的数值。
10. 请为 `socks5Port` 属性指定一个从 1025 到 65535 之间的数值。该端口不能与 `rpcPort` 相同。 10. 请为 `socks5Port` 属性指定一个从 1025 到 65535 之间的数值。该端口不能与 `rpcPort` 相同。
11. 【可选】如果客户端需要为局域网中的其他设备提供代理服务,请将 `socks5ListenLAN` 属性设置为 `true` 11. 【可选】如果客户端需要为局域网中的其他设备提供代理服务,请将 `socks5ListenLAN` 属性设置为 `true`
+1 -1
View File
@@ -462,7 +462,7 @@ class ClientConfig:
'ipAddress': '', 'ipAddress': '',
'portBindings': [{}], 'portBindings': [{}],
}], }],
'handshakeMode': 'HANDSHAKE_NO_WAIT' 'handshakeMode': 'HANDSHAKE_STANDARD'
}], }],
'activeProfile': 'default', 'activeProfile': 'default',
'rpcPort': 0, 'rpcPort': 0,
+26 -40
View File
@@ -5,58 +5,50 @@ import (
"sync/atomic" "sync/atomic"
) )
func DefaultValue[T any]() T {
var defaultValue T
return defaultValue
}
type TypedValue[T any] struct { type TypedValue[T any] struct {
_ noCopy value atomic.Pointer[T]
value atomic.Value
} }
// tValue is a struct with determined type to resolve atomic.Value usages with interface types func (t *TypedValue[T]) Load() (v T) {
// https://github.com/golang/go/issues/22550 v, _ = t.LoadOk()
// return
// 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() T { func (t *TypedValue[T]) LoadOk() (v T, ok bool) {
value, _ := t.LoadOk()
return value
}
func (t *TypedValue[T]) LoadOk() (_ T, ok bool) {
value := t.value.Load() value := t.value.Load()
if value == nil { if value == nil {
return DefaultValue[T](), false return
} }
return value.(tValue[T]).value, true return *value, true
} }
func (t *TypedValue[T]) Store(value T) { 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 { func (t *TypedValue[T]) Swap(new T) (v T) {
old := t.value.Swap(tValue[T]{new}) old := t.value.Swap(&new)
if old == nil { if old == nil {
return DefaultValue[T]() return
} }
return old.(tValue[T]).value return *old
} }
func (t *TypedValue[T]) CompareAndSwap(old, new T) bool { func (t *TypedValue[T]) CompareAndSwap(old, new T) bool {
return t.value.CompareAndSwap(tValue[T]{old}, tValue[T]{new}) || for {
// In the edge-case where [atomic.Value.Store] is uninitialized currentP := t.value.Load()
// and trying to compare with the zero value of T, var currentValue T
// then compare-and-swap with the nil any value. if currentP != nil {
(any(old) == any(DefaultValue[T]()) && t.value.CompareAndSwap(any(nil), tValue[T]{new})) 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) { func (t *TypedValue[T]) MarshalJSON() ([]byte, error) {
@@ -89,9 +81,3 @@ func NewTypedValue[T any](t T) (v TypedValue[T]) {
v.Store(t) v.Store(t)
return return
} }
type noCopy struct{}
// Lock is a no-op used by -copylocks checker from `go vet`.
func (*noCopy) Lock() {}
func (*noCopy) Unlock() {}
+106 -14
View File
@@ -7,20 +7,6 @@ import (
) )
func TestTypedValue(t *testing.T) { 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] var v TypedValue[int]
got, gotOk := v.LoadOk() 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{}) c1, c2, c3 := make(chan struct{}), make(chan struct{}), make(chan struct{})
var v TypedValue[chan struct{}] var v TypedValue[chan struct{}]
if v.CompareAndSwap(c1, c2) != false { if v.CompareAndSwap(c1, c2) != false {
t.Fatalf("CompareAndSwap = true, want 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 { if v.CompareAndSwap(nil, c1) != true {
t.Fatalf("CompareAndSwap = false, want 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 { if v.CompareAndSwap(c2, c3) != false {
t.Fatalf("CompareAndSwap = true, want 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 { if v.CompareAndSwap(c1, c2) != true {
t.Fatalf("CompareAndSwap = false, want 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)
}
} }
} }
+6 -6
View File
@@ -10,27 +10,27 @@ import (
) )
var ( var (
keepAliveIdle = atomic.NewTypedValue[time.Duration](0 * time.Second) keepAliveIdle = atomic.NewInt64(0)
keepAliveInterval = atomic.NewTypedValue[time.Duration](0 * time.Second) keepAliveInterval = atomic.NewInt64(0)
disableKeepAlive = atomic.NewBool(false) disableKeepAlive = atomic.NewBool(false)
SetDisableKeepAliveCallback = utils.NewCallback[bool]() SetDisableKeepAliveCallback = utils.NewCallback[bool]()
) )
func SetKeepAliveIdle(t time.Duration) { func SetKeepAliveIdle(t time.Duration) {
keepAliveIdle.Store(t) keepAliveIdle.Store(int64(t))
} }
func SetKeepAliveInterval(t time.Duration) { func SetKeepAliveInterval(t time.Duration) {
keepAliveInterval.Store(t) keepAliveInterval.Store(int64(t))
} }
func KeepAliveIdle() time.Duration { func KeepAliveIdle() time.Duration {
return keepAliveIdle.Load() return time.Duration(keepAliveIdle.Load())
} }
func KeepAliveInterval() time.Duration { func KeepAliveInterval() time.Duration {
return keepAliveInterval.Load() return time.Duration(keepAliveInterval.Load())
} }
func SetDisableKeepAlive(disable bool) { func SetDisableKeepAlive(disable bool) {
+7 -7
View File
@@ -3,7 +3,6 @@ module github.com/metacubex/mihomo
go 1.20 go 1.20
require ( require (
github.com/3andne/restls-client-go v0.1.6
github.com/bahlo/generic-list-go v0.2.0 github.com/bahlo/generic-list-go v0.2.0
github.com/coreos/go-iptables v0.8.0 github.com/coreos/go-iptables v0.8.0
github.com/dlclark/regexp2 v1.11.5 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/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab
github.com/metacubex/bart v0.20.5 github.com/metacubex/bart v0.20.5
github.com/metacubex/bbolt v0.0.0-20250725135710-010dbbbb7a5b 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/chacha v0.1.5
github.com/metacubex/fswatch v0.1.1 github.com/metacubex/fswatch v0.1.1
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759
github.com/metacubex/quic-go v0.54.1-0.20250730114134-a1ae705fe295 github.com/metacubex/quic-go v0.54.1-0.20250730114134-a1ae705fe295
github.com/metacubex/randv2 v0.2.0 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-mux v0.3.3-0.20250813083925-d7c9aeaeeaac
github.com/metacubex/sing-quic v0.0.0-20250718154553-1b193bec4cbb github.com/metacubex/sing-quic v0.0.0-20250718154553-1b193bec4cbb
github.com/metacubex/sing-shadowsocks v0.2.11 github.com/metacubex/sing-shadowsocks v0.2.12
github.com/metacubex/sing-shadowsocks2 v0.2.5 github.com/metacubex/sing-shadowsocks2 v0.2.6
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 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-vmess v0.2.4-0.20250731011226-ea28d589924d
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f
github.com/metacubex/smux v0.0.0-20250503055512-501391591dee 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 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 google.golang.org/protobuf v1.34.2 // lastest version compatible with golang1.20
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
lukechampine.com/blake3 v1.3.0 // lastest version compatible with golang1.20
) )
require ( require (
@@ -85,11 +85,11 @@ require (
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
github.com/hashicorp/yamux v0.1.2 // indirect github.com/hashicorp/yamux v0.1.2 // indirect
github.com/josharian/native v1.1.0 // 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/kr/text v0.2.0 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect
github.com/mdlayher/socket v0.4.1 // 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/gvisor v0.0.0-20250324165734-5857f47bd43b // indirect
github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793 // indirect github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793 // indirect
github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect
+14 -14
View File
@@ -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 h1:smavTzSMAx8iuVlGb4pEwl9MD2qicqMzuXR2QWp2/Pg=
github.com/RyuaNerin/go-krypto v1.3.0/go.mod h1:9R9TU936laAIqAmjcHo/LsaXYOZlymudOAxjaBf62UM= github.com/RyuaNerin/go-krypto v1.3.0/go.mod h1:9R9TU936laAIqAmjcHo/LsaXYOZlymudOAxjaBf62UM=
github.com/RyuaNerin/testingutil v0.1.0 h1:IYT6JL57RV3U2ml3dLHZsVtPOP6yNK7WUVdzzlpNrss= 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/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 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= 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/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 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 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/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 h1:Chbw+/31UC14YFNr78pESt5Vowlc62zziw05JCUqoL4=
github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab/go.mod h1:xVKK8jC5Sd3hfh7WjmCq+HorehIbrBijaUWmcuKjPcI= 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 h1:XkgLZ17QxfxkqKdGsojoM2Zu01mmHyyQSFzt2/calTM=
github.com/metacubex/bart v0.20.5/go.mod h1:DCcyfP4MC+Zy7sLK7XeGuMw+P5K9mIRsYOBgiE8icsI= 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 h1:j7dadXD8I2KTmMt8jg1JcaP1ANL3JEObJPdANKcSYPY=
github.com/metacubex/bbolt v0.0.0-20250725135710-010dbbbb7a5b/go.mod h1:+WmP0VJZDkDszvpa83HzfUp6QzARl/IKkMorH4+nODw= 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 h1:fKWMb/5c7ZrY8Uoqi79PPFxl+qwR7X/q0OrsAubyX2M=
github.com/metacubex/chacha v0.1.5/go.mod h1:Djn9bPZxLTXbJFSeyo0/qzEzQI+gUSSzttuzZM75GH8= github.com/metacubex/chacha v0.1.5/go.mod h1:Djn9bPZxLTXbJFSeyo0/qzEzQI+gUSSzttuzZM75GH8=
github.com/metacubex/fswatch v0.1.1 h1:jqU7C/v+g0qc2RUFgmAOPoVvfl2BXXUXEumn6oQuxhU= 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/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 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs=
github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= 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.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.5 h1:m5U8iHvRAUxlme3FZlE/LPIGHjU8oMCUzXWGbQQAC1E=
github.com/metacubex/sing v0.5.4/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w= 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 h1:wDH/Jh/yqWbzPktqJP+Y1cUG8hchcrzKzUxJiSpnaQs=
github.com/metacubex/sing-mux v0.3.3-0.20250813083925-d7c9aeaeeaac/go.mod h1:3rt1soewn0O6j89GCLmwAQFsq257u0jf2zQSPhTL3Bw= 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 h1:U/m3h8lp/j7i8zFgfvScLdZa1/Y8dd74oO7iZaQq80s=
github.com/metacubex/sing-quic v0.0.0-20250718154553-1b193bec4cbb/go.mod h1:B60FxaPHjR1SeQB0IiLrgwgvKsaoASfOWdiqhLjmMGA= 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.12 h1:Wqzo8bYXrK5aWqxu/TjlTnYZzAKtKsaFQBdr6IHFaBE=
github.com/metacubex/sing-shadowsocks v0.2.11/go.mod h1:bT1PCTV316zFnlToRMk5zt9HmIQYRBveiT71mplYPfc= github.com/metacubex/sing-shadowsocks v0.2.12/go.mod h1:2e5EIaw0rxKrm1YTRmiMnDulwbGxH9hAFlrwQLQMQkU=
github.com/metacubex/sing-shadowsocks2 v0.2.5 h1:MnPn0hbcDkSJt6TlpI15XImHKK6IqaOwBUGPKyMnJnE= github.com/metacubex/sing-shadowsocks2 v0.2.6 h1:ZR1kYT0f0Vi64iQSS09OdhFfppiNkh7kjgRdMm0SB98=
github.com/metacubex/sing-shadowsocks2 v0.2.5/go.mod h1:Zyh+rAQRyevYfG/COCvDs1c/YMhGqCuknn7QrGmoQIw= 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 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-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 h1:ZDY/W+1c7PeWWKeKRyUo18fySF/TWjB0i5ui81Ar778=
github.com/metacubex/sing-tun v0.4.7-0.20250801130030-308b828865ae/go.mod h1:2YywXPWW8Z97kTH7RffOeykKzU+l0aiKlglWV1PAS64= 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 h1:koSVAQiYqU/qtJ527e+wbsEPvTaM39HW75LSvh04IT0=
github.com/metacubex/sing-vmess v0.2.4-0.20250731011226-ea28d589924d/go.mod h1:21R5R1u90uUvBQF0owoooEu96/SAYYD56nDrwm6nFaM= 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= 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.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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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=
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"context" "context"
"net" "net"
tls "github.com/3andne/restls-client-go" tls "github.com/metacubex/restls-client-go"
) )
const ( const (
+1 -1
View File
@@ -8,8 +8,8 @@ import (
"net/netip" "net/netip"
"strconv" "strconv"
"github.com/metacubex/blake3"
"github.com/metacubex/quic-go" "github.com/metacubex/quic-go"
"lukechampine.com/blake3"
C "github.com/metacubex/mihomo/constant" C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/transport/socks5" "github.com/metacubex/mihomo/transport/socks5"
+13 -23
View File
@@ -105,26 +105,20 @@ func (vc *Conn) sendRequest(p []byte) bool {
} }
} }
var buffer *buf.Buffer requestLen := 1 // protocol version
if vc.IsXTLSVisionEnabled() { requestLen += 16 // UUID
buffer = buf.New() requestLen += 1 // addons length
defer buffer.Release() requestLen += len(addonsBytes)
} else { requestLen += 1 // command
requestLen := 1 // protocol version if !vc.dst.Mux {
requestLen += 16 // UUID requestLen += 2 // port
requestLen += 1 // addons length requestLen += 1 // addr type
requestLen += len(addonsBytes) requestLen += len(vc.dst.Addr)
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 += len(p)
buffer := buf.NewSize(requestLen)
defer buffer.Release()
buf.Must( buf.Must(
buffer.WriteByte(Version), // protocol version buffer.WriteByte(Version), // protocol version
@@ -182,10 +176,6 @@ func (vc *Conn) NeedHandshake() bool {
return vc.needHandshake return vc.needHandshake
} }
func (vc *Conn) IsXTLSVisionEnabled() bool {
return vc.addons != nil && vc.addons.Flow == XRV
}
// newConn return a Conn instance // newConn return a Conn instance
func newConn(conn net.Conn, client *Client, dst *DstAddr) (net.Conn, error) { func newConn(conn net.Conn, client *Client, dst *DstAddr) (net.Conn, error) {
c := &Conn{ c := &Conn{
+26 -35
View File
@@ -4,11 +4,13 @@ import (
"bytes" "bytes"
"crypto/cipher" "crypto/cipher"
"crypto/rand" "crypto/rand"
"crypto/sha256"
"errors" "errors"
"fmt" "fmt"
"io" "io"
"net" "net"
"runtime" "runtime"
"strings"
"sync" "sync"
"time" "time"
@@ -36,13 +38,12 @@ func init() {
type ClientInstance struct { type ClientInstance struct {
sync.RWMutex sync.RWMutex
nfsEKey *mlkem.EncapsulationKey768 nfsEKey *mlkem.EncapsulationKey768
nfsEKeyBytes []byte xorKey []byte
xor uint32 minutes time.Duration
minutes time.Duration expire time.Time
expire time.Time baseKey []byte
baseKey []byte ticket []byte
ticket []byte
} }
type ClientConn struct { type ClientConn struct {
@@ -59,10 +60,17 @@ type ClientConn struct {
} }
func (i *ClientInstance) Init(nfsEKeyBytes []byte, xor uint32, minutes time.Duration) (err error) { 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) i.nfsEKey, err = mlkem.NewEncapsulationKey768(nfsEKeyBytes)
if err != nil {
return
}
if xor > 0 { if xor > 0 {
i.nfsEKeyBytes = nfsEKeyBytes xorKey := sha256.Sum256(nfsEKeyBytes)
i.xor = xor i.xorKey = xorKey[:]
} }
i.minutes = minutes i.minutes = minutes
return return
@@ -72,8 +80,8 @@ func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) {
if i.nfsEKey == nil { if i.nfsEKey == nil {
return nil, errors.New("uninitialized") return nil, errors.New("uninitialized")
} }
if i.xor > 0 { if i.xorKey != nil {
conn = NewXorConn(conn, i.nfsEKeyBytes) conn = NewXorConn(conn, i.xorKey)
} }
c := &ClientConn{Conn: conn} 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 { if _, err := c.Conn.Write(clientHello); err != nil {
return nil, err 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 { if err != nil {
return nil, err return nil, err
} }
if t != 1 { if t != 1 {
return nil, fmt.Errorf("unexpected type %v, expect random hello", t) return nil, fmt.Errorf("unexpected type %v, expect random hello", t)
} }
peerRandomHello := make([]byte, 1088+21) peerRandomHello := make([]byte, 1088+21)
if l != len(peerRandomHello) { if l != len(peerRandomHello) {
return nil, fmt.Errorf("unexpected length %v for random hello", l) 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 return 0, nil
} }
if c.peerAead == nil { if c.peerAead == nil {
var t byte _, t, l, err := ReadAndDiscardPaddings(c.Conn) // allow paddings before random hello
var l int if err != nil {
var err error if c.instance != nil && strings.HasPrefix(err.Error(), "invalid header: ") { // 0-RTT's 0-RTT
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 {
c.instance.Lock() c.instance.Lock()
if bytes.Equal(c.ticket, c.instance.ticket) { if bytes.Equal(c.ticket, c.instance.ticket) {
c.instance.expire = time.Now() // expired c.instance.expire = time.Now() // expired
@@ -221,6 +211,7 @@ func (c *ClientConn) Read(b []byte) (int, error) {
c.instance.Unlock() c.instance.Unlock()
return 0, errors.New("new handshake needed") return 0, errors.New("new handshake needed")
} }
return 0, err
} }
if t != 0 { if t != 0 {
return 0, fmt.Errorf("unexpected type %v, expect server random", t) return 0, fmt.Errorf("unexpected type %v, expect server random", t)
+14 -3
View File
@@ -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 { } else if h[0] == 1 && h[1] == 1 && h[2] == 1 {
t = 1 t = 1
} else { } else {
h = nil l = 0
} }
if h == nil || l < 17 || l > 17000 { // TODO: TLSv1.3 max length if l < 17 || l > 17000 { // TODO: TLSv1.3 max length
err = fmt.Errorf("invalid header: %v", h[:5]) err = fmt.Errorf("invalid header: %v", h[:5]) // DO NOT CHANGE: relied by client's Read()
} }
return return
} }
@@ -62,6 +62,17 @@ func ReadAndDecodeHeader(conn net.Conn) (h []byte, t byte, l int, err error) {
return 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) { func NewAead(c byte, secret, salt, info []byte) (aead cipher.AEAD) {
key := make([]byte, 32) key := make([]byte, 32)
hkdf.New(sha256.New, secret, salt, info).Read(key) hkdf.New(sha256.New, secret, salt, info).Read(key)
+2
View File
@@ -9,4 +9,6 @@
// https://github.com/XTLS/Xray-core/commit/1720be168fa069332c418503d30341fc6e01df7f // https://github.com/XTLS/Xray-core/commit/1720be168fa069332c418503d30341fc6e01df7f
// https://github.com/XTLS/Xray-core/commit/0fd7691d6b28e05922d7a5a9313d97745a51ea63 // https://github.com/XTLS/Xray-core/commit/0fd7691d6b28e05922d7a5a9313d97745a51ea63
// https://github.com/XTLS/Xray-core/commit/09cc92c61d9067e0d65c1cae9124664ecfc78f43 // 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 package encryption
+30 -32
View File
@@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"crypto/cipher" "crypto/cipher"
"crypto/rand" "crypto/rand"
"crypto/sha256"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@@ -23,12 +24,11 @@ type ServerSession struct {
type ServerInstance struct { type ServerInstance struct {
sync.RWMutex sync.RWMutex
nfsDKey *mlkem.DecapsulationKey768 nfsDKey *mlkem.DecapsulationKey768
nfsEKeyBytes []byte xorKey []byte
xor uint32 minutes time.Duration
minutes time.Duration sessions map[[21]byte]*ServerSession
sessions map[[21]byte]*ServerSession closed bool
closed bool
} }
type ServerConn struct { type ServerConn struct {
@@ -45,10 +45,17 @@ type ServerConn struct {
} }
func (i *ServerInstance) Init(nfsDKeySeed []byte, xor uint32, minutes time.Duration) (err error) { 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) i.nfsDKey, err = mlkem.NewDecapsulationKey768(nfsDKeySeed)
if err != nil {
return
}
if xor > 0 { if xor > 0 {
i.nfsEKeyBytes = i.nfsDKey.EncapsulationKey().Bytes() xorKey := sha256.Sum256(i.nfsDKey.EncapsulationKey().Bytes())
i.xor = xor i.xorKey = xorKey[:]
} }
if minutes > 0 { if minutes > 0 {
i.minutes = minutes i.minutes = minutes
@@ -85,18 +92,15 @@ func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) {
if i.nfsDKey == nil { if i.nfsDKey == nil {
return nil, errors.New("uninitialized") return nil, errors.New("uninitialized")
} }
if i.xor > 0 { if i.xorKey != nil {
conn = NewXorConn(conn, i.nfsEKeyBytes) conn = NewXorConn(conn, i.xorKey)
} }
c := &ServerConn{Conn: conn} 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 { if err != nil {
return nil, err return nil, err
} }
if t == 23 {
return nil, errors.New("unexpected data")
}
if t == 0 { if t == 0 {
if i.minutes == 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)] s := i.sessions[[21]byte(peerTicketHello)]
i.RUnlock() i.RUnlock()
if s == nil { if s == nil {
noise := make([]byte, randBetween(100, 1000)) noises := make([]byte, randBetween(100, 1000))
rand.Read(noise) var err error
c.Conn.Write(noise) // make client do new handshake for err == nil {
rand.Read(noises)
_, _, err = DecodeHeader(noises)
}
c.Conn.Write(noises) // make client do new handshake
return nil, errors.New("expired ticket") return nil, errors.New("expired ticket")
} }
if _, replay := s.randoms.LoadOrStore([32]byte(peerTicketHello[21:]), true); replay { 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 { if _, err := c.Conn.Write(serverHello); err != nil {
return nil, err 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 { if i.minutes > 0 {
i.Lock() i.Lock()
@@ -185,20 +193,10 @@ func (c *ServerConn) Read(b []byte) (int, error) {
return 0, nil return 0, nil
} }
if c.peerAead == nil { if c.peerAead == nil {
if c.peerRandom == nil { // from 1-RTT if c.peerRandom == nil { // 1-RTT's 0-RTT
var t byte _, t, l, err := ReadAndDiscardPaddings(c.Conn) // allow paddings before ticket hello
var l int if err != nil {
var err error return 0, err
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 t != 0 { if t != 0 {
return 0, fmt.Errorf("unexpected type %v, expect ticket hello", t) return 0, fmt.Errorf("unexpected type %v, expect ticket hello", t)
+7 -7
View File
@@ -18,11 +18,11 @@ type XorConn struct {
} }
func NewXorConn(conn net.Conn, key []byte) *XorConn { func NewXorConn(conn net.Conn, key []byte) *XorConn {
return &XorConn{Conn: conn, key: key[:16]} return &XorConn{Conn: conn, key: key}
//chacha20.NewUnauthenticatedCipher() //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 { if len(b) == 0 {
return 0, nil 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) c.ctr = cipher.NewCTR(block, iv)
} }
t, l, _ := DecodeHeader(b) t, l, _ := DecodeHeader(b)
if t != 23 { if t == 23 { // single 23
l += 10 // 5+l+5
} else {
l = 5 l = 5
} else { // 1/0 + 23, or noises only
l += 10
} }
c.ctr.XORKeyStream(b[:l], b[:l]) // caller MUST discard b c.ctr.XORKeyStream(b[:l], b[:l]) // caller MUST discard b
if iv != nil { if iv != nil {
@@ -73,8 +73,8 @@ func (c *XorConn) Read(b []byte) (int, error) { // 5-bytes, data, 5-bytes...
return len(b), nil return len(b), nil
} }
c.peerCtr.XORKeyStream(b, b) c.peerCtr.XORKeyStream(b, b)
if c.isHeader { if c.isHeader { // always 5-bytes
if t, _, _ := DecodeHeader(b); t == 23 { // always 5-bytes if t, _, _ := DecodeHeader(b); t == 23 {
c.skipNext = true c.skipNext = true
} else { } else {
c.isHeader = false c.isHeader = false
+4 -13
View File
@@ -3,7 +3,6 @@ package vision
import ( import (
"bytes" "bytes"
"crypto/subtle" "crypto/subtle"
gotls "crypto/tls"
"encoding/binary" "encoding/binary"
"errors" "errors"
"fmt" "fmt"
@@ -12,7 +11,6 @@ import (
"github.com/metacubex/mihomo/common/buf" "github.com/metacubex/mihomo/common/buf"
N "github.com/metacubex/mihomo/common/net" N "github.com/metacubex/mihomo/common/net"
tlsC "github.com/metacubex/mihomo/component/tls"
"github.com/metacubex/mihomo/log" "github.com/metacubex/mihomo/log"
"github.com/gofrs/uuid/v5" "github.com/gofrs/uuid/v5"
@@ -181,17 +179,10 @@ func (vc *Conn) WriteBuffer(buffer *buf.Buffer) (err error) {
buffer.Release() buffer.Release()
return err return err
} }
switch underlying := vc.tlsConn.(type) { err = vc.checkTLSVersion()
case *gotls.Conn: if err != nil {
if underlying.ConnectionState().Version != gotls.VersionTLS13 { buffer.Release()
buffer.Release() return err
return ErrNotTLS13
}
case *tlsC.UConn:
if underlying.ConnectionState().Version != tlsC.VersionTLS13 {
buffer.Release()
return ErrNotTLS13
}
} }
vc.tlsConn = nil vc.tlsConn = nil
return nil return nil
+18
View File
@@ -67,3 +67,21 @@ func NewConn(conn connWithUpstream, userUUID *uuid.UUID) (*Conn, error) {
c.rawInput = (*bytes.Buffer)(unsafe.Add(p, r.Offset)) c.rawInput = (*bytes.Buffer)(unsafe.Add(p, r.Offset))
return c, nil 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
}
+1 -1
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
VERSION="1.23.6" VERSION="1.23.12"
mkdir -p $HOME/go mkdir -p $HOME/go
cd $HOME/go cd $HOME/go
+31 -25
View File
@@ -40,13 +40,13 @@ jobs:
version: ${{ steps.outputs.outputs.version }} version: ${{ steps.outputs.outputs.version }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: ^1.24.6 go-version: ^1.25.0
- name: Check input version - name: Check input version
if: github.event_name == 'workflow_dispatch' if: github.event_name == 'workflow_dispatch'
run: |- run: |-
@@ -88,13 +88,14 @@ jobs:
- { os: linux, arch: loong64, debian: loongarch64, rpm: loongarch64, openwrt: "loongarch64_generic" } - { os: linux, arch: loong64, debian: loongarch64, rpm: loongarch64, openwrt: "loongarch64_generic" }
- { os: windows, arch: amd64 } - { 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" }
- { os: windows, arch: "386", legacy_go: true } - { os: windows, arch: "386", legacy_go123: true, legacy_name: "windows-7" }
- { os: windows, arch: arm64 } - { os: windows, arch: arm64 }
- { os: darwin, arch: amd64 } - { os: darwin, arch: amd64 }
- { os: darwin, arch: arm64 } - { 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: arm64, ndk: "aarch64-linux-android21" }
- { os: android, arch: arm, ndk: "armv7a-linux-androideabi21" } - { os: android, arch: arm, ndk: "armv7a-linux-androideabi21" }
@@ -102,28 +103,33 @@ jobs:
- { os: android, arch: "386", ndk: "i686-linux-android21" } - { os: android, arch: "386", ndk: "i686-linux-android21" }
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Setup Go - name: Setup Go
if: ${{ ! matrix.legacy_go }} if: ${{ ! (matrix.legacy_go123 || matrix.legacy_go124) }}
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: ^1.24.6 go-version: ^1.25.0
- name: Cache Legacy Go - name: Setup Go 1.24
if: matrix.require_legacy_go 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 id: cache-legacy-go
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
path: | path: |
~/go/go_legacy ~/go/go_legacy
key: go_legacy_1236 key: go_legacy_12312
- name: Setup Legacy Go - name: Setup Go 1.23
if: matrix.legacy_go && steps.cache-legacy-go.outputs.cache-hit != 'true' if: matrix.legacy_go123 && steps.cache-legacy-go.outputs.cache-hit != 'true'
run: |- run: |-
.github/setup_legacy_go.sh .github/setup_legacy_go.sh
- name: Setup Legacy Go 2 - name: Setup Go 1.23
if: matrix.legacy_go if: matrix.legacy_go123
run: |- run: |-
echo "PATH=$HOME/go/go_legacy/bin:$PATH" >> $GITHUB_ENV echo "PATH=$HOME/go/go_legacy/bin:$PATH" >> $GITHUB_ENV
echo "GOROOT=$HOME/go/go_legacy" >> $GITHUB_ENV echo "GOROOT=$HOME/go/go_legacy" >> $GITHUB_ENV
@@ -184,8 +190,8 @@ jobs:
DIR_NAME="${DIR_NAME}-${{ matrix.go386 }}" DIR_NAME="${DIR_NAME}-${{ matrix.go386 }}"
elif [[ -n "${{ matrix.gomips }}" && "${{ matrix.gomips }}" != 'hardfloat' ]]; then elif [[ -n "${{ matrix.gomips }}" && "${{ matrix.gomips }}" != 'hardfloat' ]]; then
DIR_NAME="${DIR_NAME}-${{ matrix.gomips }}" DIR_NAME="${DIR_NAME}-${{ matrix.gomips }}"
elif [[ "${{ matrix.legacy_go }}" == 'true' ]]; then elif [[ -n "${{ matrix.legacy_name }}" ]]; then
DIR_NAME="${DIR_NAME}-legacy" DIR_NAME="${DIR_NAME}-legacy-${{ matrix.legacy_name }}"
fi fi
echo "DIR_NAME=${DIR_NAME}" >> "${GITHUB_ENV}" echo "DIR_NAME=${DIR_NAME}" >> "${GITHUB_ENV}"
PKG_VERSION="${{ needs.calculate_version.outputs.version }}" PKG_VERSION="${{ needs.calculate_version.outputs.version }}"
@@ -277,7 +283,7 @@ jobs:
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: 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" path: "dist"
build_android: build_android:
name: Build Android name: Build Android
@@ -287,14 +293,14 @@ jobs:
- calculate_version - calculate_version
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: 'recursive' submodules: 'recursive'
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: ^1.24.6 go-version: ^1.25.0
- name: Setup Android NDK - name: Setup Android NDK
id: setup-ndk id: setup-ndk
uses: nttld/setup-ndk@v1 uses: nttld/setup-ndk@v1
@@ -367,14 +373,14 @@ jobs:
- calculate_version - calculate_version
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: 'recursive' submodules: 'recursive'
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: ^1.24.6 go-version: ^1.25.0
- name: Setup Android NDK - name: Setup Android NDK
id: setup-ndk id: setup-ndk
uses: nttld/setup-ndk@v1 uses: nttld/setup-ndk@v1
@@ -464,7 +470,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
if: matrix.if if: matrix.if
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: 'recursive' submodules: 'recursive'
@@ -472,7 +478,7 @@ jobs:
if: matrix.if if: matrix.if
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: ^1.24.6 go-version: ^1.25.0
- name: Setup Xcode stable - name: Setup Xcode stable
if: matrix.if && github.ref == 'refs/heads/main-next' if: matrix.if && github.ref == 'refs/heads/main-next'
run: |- run: |-
@@ -624,7 +630,7 @@ jobs:
- build_apple - build_apple
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Cache ghr - name: Cache ghr
@@ -647,7 +653,7 @@ jobs:
git tag v${{ needs.calculate_version.outputs.version }} -f git tag v${{ needs.calculate_version.outputs.version }} -f
echo "VERSION=${{ needs.calculate_version.outputs.version }}" >> "$GITHUB_ENV" echo "VERSION=${{ needs.calculate_version.outputs.version }}" >> "$GITHUB_ENV"
- name: Download builds - name: Download builds
uses: actions/download-artifact@v4 uses: actions/download-artifact@v5
with: with:
path: dist path: dist
merge-multiple: true merge-multiple: true
+2 -2
View File
@@ -39,7 +39,7 @@ jobs:
echo "ref=$ref" echo "ref=$ref"
echo "ref=$ref" >> $GITHUB_OUTPUT echo "ref=$ref" >> $GITHUB_OUTPUT
- name: Checkout - name: Checkout
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
with: with:
ref: ${{ steps.ref.outputs.ref }} ref: ${{ steps.ref.outputs.ref }}
fetch-depth: 0 fetch-depth: 0
@@ -107,7 +107,7 @@ jobs:
echo "latest=$latest" echo "latest=$latest"
echo "latest=$latest" >> $GITHUB_OUTPUT echo "latest=$latest" >> $GITHUB_OUTPUT
- name: Download digests - name: Download digests
uses: actions/download-artifact@v4 uses: actions/download-artifact@v5
with: with:
path: /tmp/digests path: /tmp/digests
pattern: digests-* pattern: digests-*
+3 -3
View File
@@ -22,15 +22,15 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: ^1.24.6 go-version: ~1.24.6
- name: golangci-lint - name: golangci-lint
uses: golangci/golangci-lint-action@v6 uses: golangci/golangci-lint-action@v8
with: with:
version: latest version: latest
args: --timeout=30m args: --timeout=30m
+6 -6
View File
@@ -24,13 +24,13 @@ jobs:
version: ${{ steps.outputs.outputs.version }} version: ${{ steps.outputs.outputs.version }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: ^1.24.6 go-version: ^1.25.0
- name: Check input version - name: Check input version
if: github.event_name == 'workflow_dispatch' if: github.event_name == 'workflow_dispatch'
run: |- run: |-
@@ -65,13 +65,13 @@ jobs:
- { os: linux, arch: loong64, debian: loongarch64, rpm: loongarch64 } - { os: linux, arch: loong64, debian: loongarch64, rpm: loongarch64 }
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: ^1.24.6 go-version: ^1.25.0
- name: Setup Android NDK - name: Setup Android NDK
if: matrix.os == 'android' if: matrix.os == 'android'
uses: nttld/setup-ndk@v1 uses: nttld/setup-ndk@v1
@@ -171,7 +171,7 @@ jobs:
- build - build
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Set tag - name: Set tag
@@ -180,7 +180,7 @@ jobs:
git tag v${{ needs.calculate_version.outputs.version }} -f git tag v${{ needs.calculate_version.outputs.version }} -f
echo "VERSION=${{ needs.calculate_version.outputs.version }}" >> "$GITHUB_ENV" echo "VERSION=${{ needs.calculate_version.outputs.version }}" >> "$GITHUB_ENV"
- name: Download builds - name: Download builds
uses: actions/download-artifact@v4 uses: actions/download-artifact@v5
with: with:
path: dist path: dist
merge-multiple: true merge-multiple: true
+50 -27
View File
@@ -1,27 +1,6 @@
linters: version: "2"
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
run: run:
go: "1.23" go: "1.24"
build-tags: build-tags:
- with_gvisor - with_gvisor
- with_quic - with_quic
@@ -30,7 +9,51 @@ run:
- with_utls - with_utls
- with_acme - with_acme
- with_clash_api - with_clash_api
linters:
issues: default: none
exclude-dirs: enable:
- transport/simple-obfs - 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$
+1 -1
View File
@@ -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 <contact-git@sekai.icu>" LABEL maintainer="nekohasekai <contact-git@sekai.icu>"
COPY . /go/src/github.com/sagernet/sing-box COPY . /go/src/github.com/sagernet/sing-box
WORKDIR /go/src/github.com/sagernet/sing-box WORKDIR /go/src/github.com/sagernet/sing-box
+3 -3
View File
@@ -45,7 +45,7 @@ lint:
GOOS=freebsd golangci-lint run ./... GOOS=freebsd golangci-lint run ./...
lint_install: 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: proto:
@go run ./cmd/internal/protogen @go run ./cmd/internal/protogen
@@ -245,8 +245,8 @@ lib:
go run ./cmd/internal/build_libbox -target ios go run ./cmd/internal/build_libbox -target ios
lib_install: lib_install:
go install -v github.com/sagernet/gomobile/cmd/gomobile@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.7 go install -v github.com/sagernet/gomobile/cmd/gobind@v0.1.8
docs: docs:
venv/bin/mkdocs serve venv/bin/mkdocs serve
+1 -2
View File
@@ -135,8 +135,7 @@ func ExtendContext(ctx context.Context) (context.Context, *InboundContext) {
func OverrideContext(ctx context.Context) context.Context { func OverrideContext(ctx context.Context) context.Context {
if metadata := ContextFrom(ctx); metadata != nil { if metadata := ContextFrom(ctx); metadata != nil {
var newMetadata InboundContext newMetadata := *metadata
newMetadata = *metadata
return WithContext(ctx, &newMetadata) return WithContext(ctx, &newMetadata)
} }
return ctx return ctx
+1 -3
View File
@@ -151,9 +151,7 @@ func testOnce(boxPath string, stackName string, mtu int, multiThread bool, flags
var sudoArgs []string var sudoArgs []string
if len(flags) > 0 { if len(flags) > 0 {
sudoArgs = append(sudoArgs, "env") sudoArgs = append(sudoArgs, "env")
for _, flag := range flags { sudoArgs = append(sudoArgs, flags...)
sudoArgs = append(sudoArgs, flag)
}
} }
sudoArgs = append(sudoArgs, boxPath, "run", "-c", tempConfig.Name()) sudoArgs = append(sudoArgs, boxPath, "run", "-c", tempConfig.Name())
boxProcess := shell.Exec("sudo", sudoArgs...) boxProcess := shell.Exec("sudo", sudoArgs...)
+18 -1
View File
@@ -6,8 +6,10 @@ import (
"strings" "strings"
"github.com/sagernet/sing-box/common/srs" "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/log"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/route/rule"
"github.com/sagernet/sing/common/json" "github.com/sagernet/sing/common/json"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -69,7 +71,7 @@ func compileRuleSet(sourcePath string) error {
if err != nil { if err != nil {
return err return err
} }
err = srs.Write(outputFile, plainRuleSet.Options, plainRuleSet.Version) err = srs.Write(outputFile, plainRuleSet.Options, downgradeRuleSetVersion(plainRuleSet.Version, plainRuleSet.Options))
if err != nil { if err != nil {
outputFile.Close() outputFile.Close()
os.Remove(outputPath) os.Remove(outputPath)
@@ -78,3 +80,18 @@ func compileRuleSet(sourcePath string) error {
outputFile.Close() outputFile.Close()
return nil 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
}
+1 -1
View File
@@ -271,7 +271,7 @@ func (d *DefaultDialer) DialParallelInterface(ctx context.Context, network strin
} else { } else {
dialer = d.udpDialer4 dialer = d.udpDialer4
} }
fastFallback := time.Now().Sub(d.networkLastFallback.Load()) < C.TCPTimeout fastFallback := time.Since(d.networkLastFallback.Load()) < C.TCPTimeout
var ( var (
conn net.Conn conn net.Conn
isPrimary bool isPrimary bool
+46 -34
View File
@@ -10,6 +10,8 @@ import (
"sync" "sync"
"time" "time"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/atomic"
"github.com/sagernet/sing/common/bufio" "github.com/sagernet/sing/common/bufio"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
@@ -22,7 +24,7 @@ type slowOpenConn struct {
ctx context.Context ctx context.Context
network string network string
destination M.Socksaddr destination M.Socksaddr
conn net.Conn conn atomic.Pointer[net.TCPConn]
create chan struct{} create chan struct{}
done chan struct{} done chan struct{}
access sync.Mutex 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) { func (c *slowOpenConn) Read(b []byte) (n int, err error) {
if c.conn == nil { conn := c.conn.Load()
select { if conn != nil {
case <-c.create: return conn.Read(b)
if c.err != nil { }
return 0, c.err select {
} case <-c.create:
case <-c.done: if c.err != nil {
return 0, os.ErrClosed 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) { func (c *slowOpenConn) Write(b []byte) (n int, err error) {
if c.conn != nil { tcpConn := c.conn.Load()
return c.conn.Write(b) if tcpConn != nil {
return tcpConn.Write(b)
} }
c.access.Lock() c.access.Lock()
defer c.access.Unlock() defer c.access.Unlock()
@@ -74,7 +79,7 @@ func (c *slowOpenConn) Write(b []byte) (n int, err error) {
if c.err != nil { if c.err != nil {
return 0, c.err return 0, c.err
} }
return c.conn.Write(b) return c.conn.Load().Write(b)
case <-c.done: case <-c.done:
return 0, os.ErrClosed return 0, os.ErrClosed
default: default:
@@ -83,7 +88,7 @@ func (c *slowOpenConn) Write(b []byte) (n int, err error) {
if err != nil { if err != nil {
c.err = err c.err = err
} else { } else {
c.conn = conn c.conn.Store(conn.(*net.TCPConn))
} }
n = len(b) n = len(b)
close(c.create) close(c.create)
@@ -93,70 +98,77 @@ func (c *slowOpenConn) Write(b []byte) (n int, err error) {
func (c *slowOpenConn) Close() error { func (c *slowOpenConn) Close() error {
c.closeOnce.Do(func() { c.closeOnce.Do(func() {
close(c.done) close(c.done)
if c.conn != nil { conn := c.conn.Load()
c.conn.Close() if conn != nil {
conn.Close()
} }
}) })
return nil return nil
} }
func (c *slowOpenConn) LocalAddr() net.Addr { func (c *slowOpenConn) LocalAddr() net.Addr {
if c.conn == nil { conn := c.conn.Load()
if conn == nil {
return M.Socksaddr{} return M.Socksaddr{}
} }
return c.conn.LocalAddr() return conn.LocalAddr()
} }
func (c *slowOpenConn) RemoteAddr() net.Addr { func (c *slowOpenConn) RemoteAddr() net.Addr {
if c.conn == nil { conn := c.conn.Load()
if conn == nil {
return M.Socksaddr{} return M.Socksaddr{}
} }
return c.conn.RemoteAddr() return conn.RemoteAddr()
} }
func (c *slowOpenConn) SetDeadline(t time.Time) error { func (c *slowOpenConn) SetDeadline(t time.Time) error {
if c.conn == nil { conn := c.conn.Load()
if conn == nil {
return os.ErrInvalid return os.ErrInvalid
} }
return c.conn.SetDeadline(t) return conn.SetDeadline(t)
} }
func (c *slowOpenConn) SetReadDeadline(t time.Time) error { func (c *slowOpenConn) SetReadDeadline(t time.Time) error {
if c.conn == nil { conn := c.conn.Load()
if conn == nil {
return os.ErrInvalid return os.ErrInvalid
} }
return c.conn.SetReadDeadline(t) return conn.SetReadDeadline(t)
} }
func (c *slowOpenConn) SetWriteDeadline(t time.Time) error { func (c *slowOpenConn) SetWriteDeadline(t time.Time) error {
if c.conn == nil { conn := c.conn.Load()
if conn == nil {
return os.ErrInvalid return os.ErrInvalid
} }
return c.conn.SetWriteDeadline(t) return conn.SetWriteDeadline(t)
} }
func (c *slowOpenConn) Upstream() any { func (c *slowOpenConn) Upstream() any {
return c.conn return common.PtrOrNil(c.conn.Load())
} }
func (c *slowOpenConn) ReaderReplaceable() bool { func (c *slowOpenConn) ReaderReplaceable() bool {
return c.conn != nil return c.conn.Load() != nil
} }
func (c *slowOpenConn) WriterReplaceable() bool { func (c *slowOpenConn) WriterReplaceable() bool {
return c.conn != nil return c.conn.Load() != nil
} }
func (c *slowOpenConn) LazyHeadroom() bool { func (c *slowOpenConn) LazyHeadroom() bool {
return c.conn == nil return c.conn.Load() == nil
} }
func (c *slowOpenConn) NeedHandshake() bool { 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) { func (c *slowOpenConn) WriteTo(w io.Writer) (n int64, err error) {
if c.conn == nil { conn := c.conn.Load()
if conn == nil {
select { select {
case <-c.create: case <-c.create:
if c.err != nil { if c.err != nil {
@@ -166,5 +178,5 @@ func (c *slowOpenConn) WriteTo(w io.Writer) (n int64, err error) {
return 0, c.err return 0, c.err
} }
} }
return bufio.Copy(w, c.conn) return bufio.Copy(w, c.conn.Load())
} }
+1 -4
View File
@@ -17,8 +17,5 @@ var uQUICChrome115 = &ja3.ClientHello{
} }
func maybeUQUIC(fingerprint *ja3.ClientHello) bool { func maybeUQUIC(fingerprint *ja3.ClientHello) bool {
if uQUICChrome115.Equals(fingerprint, true) { return !uQUICChrome115.Equals(fingerprint, true)
return true
}
return false
} }
+100 -1
View File
@@ -12,6 +12,8 @@ import (
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/domain" "github.com/sagernet/sing/common/domain"
E "github.com/sagernet/sing/common/exceptions" 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" "github.com/sagernet/sing/common/varbin"
"go4.org/netipx" "go4.org/netipx"
@@ -41,6 +43,8 @@ const (
ruleItemNetworkType ruleItemNetworkType
ruleItemNetworkIsExpensive ruleItemNetworkIsExpensive
ruleItemNetworkIsConstrained ruleItemNetworkIsConstrained
ruleItemNetworkInterfaceAddress
ruleItemDefaultInterfaceAddress
ruleItemFinal uint8 = 0xFF ruleItemFinal uint8 = 0xFF
) )
@@ -230,6 +234,51 @@ func readDefaultRule(reader varbin.Reader, recover bool) (rule option.DefaultHea
rule.NetworkIsExpensive = true rule.NetworkIsExpensive = true
case ruleItemNetworkIsConstrained: case ruleItemNetworkIsConstrained:
rule.NetworkIsConstrained = true 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: case ruleItemFinal:
err = binary.Read(reader, binary.BigEndian, &rule.Invert) err = binary.Read(reader, binary.BigEndian, &rule.Invert)
return return
@@ -346,7 +395,7 @@ func writeDefaultRule(writer varbin.Writer, rule option.DefaultHeadlessRule, gen
} }
if len(rule.NetworkType) > 0 { if len(rule.NetworkType) > 0 {
if generateVersion < C.RuleSetVersion3 { 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) err = writeRuleItemUint8(writer, ruleItemNetworkType, rule.NetworkType)
if err != nil { if err != nil {
@@ -354,17 +403,67 @@ func writeDefaultRule(writer varbin.Writer, rule option.DefaultHeadlessRule, gen
} }
} }
if rule.NetworkIsExpensive { 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) err = binary.Write(writer, binary.BigEndian, ruleItemNetworkIsExpensive)
if err != nil { if err != nil {
return err return err
} }
} }
if rule.NetworkIsConstrained { 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) err = binary.Write(writer, binary.BigEndian, ruleItemNetworkIsConstrained)
if err != nil { if err != nil {
return err 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 { if len(rule.WIFISSID) > 0 {
err = writeRuleItemString(writer, ruleItemWIFISSID, rule.WIFISSID) err = writeRuleItemString(writer, ruleItemWIFISSID, rule.WIFISSID)
if err != nil { if err != nil {
+33
View File
@@ -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
}
+41 -17
View File
@@ -7,7 +7,6 @@ import (
"net" "net"
"os" "os"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/badtls" "github.com/sagernet/sing-box/common/badtls"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
@@ -16,7 +15,7 @@ import (
aTLS "github.com/sagernet/sing/common/tls" 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 { if !options.Enabled {
return dialer, nil 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) ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout)
defer cancel() defer cancel()
tlsConn, err := aTLS.ClientHandshake(ctx, conn, config) 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 { if err != nil {
return nil, err return nil, err
} }
@@ -61,26 +54,57 @@ func ClientHandshake(ctx context.Context, conn net.Conn, config Config) (Conn, e
return tlsConn, nil 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 dialer N.Dialer
config Config config Config
} }
func NewDialer(dialer N.Dialer, config Config) N.Dialer { func NewDialer(dialer N.Dialer, config Config) Dialer {
return &Dialer{dialer, config} return &defaultDialer{dialer, config}
} }
func (d *Dialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { func (d *defaultDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
if network != N.NetworkTCP { if N.NetworkName(network) != N.NetworkTCP {
return nil, os.ErrInvalid 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 { if err != nil {
return nil, err 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) { func (d *defaultDialer) Upstream() any {
return nil, os.ErrInvalid return d.dialer
} }
+1 -1
View File
@@ -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) { func (s *ECHClientConfig) fetchAndHandshake(ctx context.Context, conn net.Conn) (aTLS.Conn, error) {
s.access.Lock() s.access.Lock()
defer s.access.Unlock() 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{ message := &mDNS.Msg{
MsgHdr: mDNS.MsgHdr{ MsgHdr: mDNS.MsgHdr{
RecursionDesired: true, RecursionDesired: true,
+2 -1
View File
@@ -22,7 +22,8 @@ const (
RuleSetVersion1 = 1 + iota RuleSetVersion1 = 1 + iota
RuleSetVersion2 RuleSetVersion2
RuleSetVersion3 RuleSetVersion3
RuleSetVersionCurrent = RuleSetVersion3 RuleSetVersion4
RuleSetVersionCurrent = RuleSetVersion4
) )
const ( const (
+4 -4
View File
@@ -104,7 +104,7 @@ func (c *dnsConfig) serverOffset() uint32 {
return 0 return 0
} }
func (conf *dnsConfig) nameList(name string) []string { func (c *dnsConfig) nameList(name string) []string {
l := len(name) l := len(name)
rooted := l > 0 && name[l-1] == '.' rooted := l > 0 && name[l-1] == '.'
if l > 254 || l == 254 && !rooted { if l > 254 || l == 254 && !rooted {
@@ -118,15 +118,15 @@ func (conf *dnsConfig) nameList(name string) []string {
return []string{name} return []string{name}
} }
hasNdots := strings.Count(name, ".") >= conf.ndots hasNdots := strings.Count(name, ".") >= c.ndots
name += "." name += "."
// l++ // l++
names := make([]string, 0, 1+len(conf.search)) names := make([]string, 0, 1+len(c.search))
if hasNdots && !avoidDNS(name) { if hasNdots && !avoidDNS(name) {
names = append(names, name) names = append(names, name)
} }
for _, suffix := range conf.search { for _, suffix := range c.search {
fqdn := name + suffix fqdn := name + suffix
if !avoidDNS(fqdn) && len(fqdn) <= 254 { if !avoidDNS(fqdn) && len(fqdn) <= 254 {
names = append(names, fqdn) names = append(names, fqdn)
+3 -8
View File
@@ -30,7 +30,7 @@ func RegisterTLS(registry *dns.TransportRegistry) {
type TLSTransport struct { type TLSTransport struct {
dns.TransportAdapter dns.TransportAdapter
logger logger.ContextLogger logger logger.ContextLogger
dialer N.Dialer dialer tls.Dialer
serverAddr M.Socksaddr serverAddr M.Socksaddr
tlsConfig tls.Config tlsConfig tls.Config
access sync.Mutex access sync.Mutex
@@ -67,7 +67,7 @@ func NewTLSRaw(logger logger.ContextLogger, adapter dns.TransportAdapter, dialer
return &TLSTransport{ return &TLSTransport{
TransportAdapter: adapter, TransportAdapter: adapter,
logger: logger, logger: logger,
dialer: dialer, dialer: tls.NewDialer(dialer, tlsConfig),
serverAddr: serverAddr, serverAddr: serverAddr,
tlsConfig: tlsConfig, tlsConfig: tlsConfig,
} }
@@ -100,15 +100,10 @@ func (t *TLSTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.M
return response, nil return response, nil
} }
} }
tcpConn, err := t.dialer.DialContext(ctx, N.NetworkTCP, t.serverAddr) tlsConn, err := t.dialer.DialTLSContext(ctx, t.serverAddr)
if err != nil { if err != nil {
return nil, err 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}) return t.exchange(message, &tlsDNSConn{Conn: tlsConn})
} }
+14 -2
View File
@@ -2,11 +2,23 @@
icon: material/alert-decagram 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 * Fixes and improvements
#### 1.12.0 ### 1.12.0
* Refactor DNS servers **1** * Refactor DNS servers **1**
* Add domain resolver options**2** * Add domain resolver options**2**
+49
View File
@@ -2,6 +2,12 @@
icon: material/alert-decagram 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" !!! quote "Changes in sing-box 1.12.0"
:material-plus: [ip_accept_any](#ip_accept_any) :material-plus: [ip_accept_any](#ip_accept_any)
@@ -130,6 +136,19 @@ icon: material/alert-decagram
], ],
"network_is_expensive": false, "network_is_expensive": false,
"network_is_constrained": false, "network_is_constrained": false,
"interface_address": {
"en0": [
"2000::/3"
]
},
"network_interface_address": {
"wifi": [
"2000::/3"
]
},
"default_interface_address": [
"2000::/3"
],
"wifi_ssid": [ "wifi_ssid": [
"My WIFI" "My WIFI"
], ],
@@ -359,6 +378,36 @@ such as Cellular or a Personal Hotspot (on Apple platforms).
Match if network is in Low Data Mode. 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 #### wifi_ssid
!!! quote "" !!! quote ""
@@ -2,6 +2,12 @@
icon: material/alert-decagram 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 中的更改" !!! quote "sing-box 1.12.0 中的更改"
:material-plus: [ip_accept_any](#ip_accept_any) :material-plus: [ip_accept_any](#ip_accept_any)
@@ -130,6 +136,19 @@ icon: material/alert-decagram
], ],
"network_is_expensive": false, "network_is_expensive": false,
"network_is_constrained": false, "network_is_constrained": false,
"interface_address": {
"en0": [
"2000::/3"
]
},
"network_interface_address": {
"wifi": [
"2000::/3"
]
},
"default_interface_address": [
"2000::/3"
],
"wifi_ssid": [ "wifi_ssid": [
"My WIFI" "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 #### wifi_ssid
!!! quote "" !!! quote ""
+49
View File
@@ -2,6 +2,12 @@
icon: material/new-box 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" !!! quote "Changes in sing-box 1.11.0"
:material-plus: [action](#action) :material-plus: [action](#action)
@@ -128,6 +134,19 @@ icon: material/new-box
], ],
"network_is_expensive": false, "network_is_expensive": false,
"network_is_constrained": false, "network_is_constrained": false,
"interface_address": {
"en0": [
"2000::/3"
]
},
"network_interface_address": {
"wifi": [
"2000::/3"
]
},
"default_interface_address": [
"2000::/3"
],
"wifi_ssid": [ "wifi_ssid": [
"My WIFI" "My WIFI"
], ],
@@ -363,6 +382,36 @@ such as Cellular or a Personal Hotspot (on Apple platforms).
Match if network is in Low Data Mode. 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 #### wifi_ssid
!!! quote "" !!! quote ""
+50 -1
View File
@@ -2,6 +2,12 @@
icon: material/new-box 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 中的更改" !!! quote "sing-box 1.11.0 中的更改"
:material-plus: [action](#action) :material-plus: [action](#action)
@@ -125,6 +131,19 @@ icon: material/new-box
], ],
"network_is_expensive": false, "network_is_expensive": false,
"network_is_constrained": false, "network_is_constrained": false,
"interface_address": {
"en0": [
"2000::/3"
]
},
"network_interface_address": {
"wifi": [
"2000::/3"
]
},
"default_interface_address": [
"2000::/3"
],
"wifi_ssid": [ "wifi_ssid": [
"My WIFI" "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 #### 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 #### wifi_ssid
!!! quote "" !!! quote ""
@@ -2,6 +2,11 @@
icon: material/new-box 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" !!! quote "Changes in sing-box 1.11.0"
:material-plus: [network_type](#network_type) :material-plus: [network_type](#network_type)
@@ -78,6 +83,14 @@ icon: material/new-box
], ],
"network_is_expensive": false, "network_is_expensive": false,
"network_is_constrained": false, "network_is_constrained": false,
"network_interface_address": {
"wifi": [
"2000::/3"
]
},
"default_interface_address": [
"2000::/3"
],
"wifi_ssid": [ "wifi_ssid": [
"My WIFI" "My WIFI"
], ],
@@ -225,6 +238,26 @@ such as Cellular or a Personal Hotspot (on Apple platforms).
Match if network is in Low Data Mode. 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 #### wifi_ssid
!!! quote "" !!! quote ""
@@ -2,6 +2,11 @@
icon: material/new-box 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 中的更改" !!! quote "sing-box 1.11.0 中的更改"
:material-plus: [network_type](#network_type) :material-plus: [network_type](#network_type)
@@ -78,6 +83,14 @@ icon: material/new-box
], ],
"network_is_expensive": false, "network_is_expensive": false,
"network_is_constrained": false, "network_is_constrained": false,
"network_interface_address": {
"wifi": [
"2000::/3"
]
},
"default_interface_address": [
"2000::/3"
],
"wifi_ssid": [ "wifi_ssid": [
"My WIFI" "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 #### wifi_ssid
!!! quote "" !!! quote ""
@@ -2,6 +2,10 @@
icon: material/new-box icon: material/new-box
--- ---
!!! quote "Changes in sing-box 1.13.0"
:material-plus: version `4`
!!! quote "Changes in sing-box 1.11.0" !!! quote "Changes in sing-box 1.11.0"
:material-plus: version `3` :material-plus: version `3`
@@ -36,6 +40,7 @@ Version of rule-set.
* 1: sing-box 1.8.0: Initial rule-set version. * 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. * 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. * 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 #### rules
@@ -2,6 +2,10 @@
icon: material/new-box icon: material/new-box
--- ---
!!! quote "sing-box 1.13.0 中的更改"
:material-plus: version `4`
!!! quote "sing-box 1.11.0 中的更改" !!! quote "sing-box 1.11.0 中的更改"
:material-plus: version `3` :material-plus: version `3`
@@ -36,6 +40,7 @@ icon: material/new-box
* 1: sing-box 1.8.0: 初始规则集版本。 * 1: sing-box 1.8.0: 初始规则集版本。
* 2: sing-box 1.10.0: 优化了二进制规则集中 `domain_suffix` 规则的内存使用。 * 2: sing-box 1.10.0: 优化了二进制规则集中 `domain_suffix` 规则的内存使用。
* 3: sing-box 1.11.0: 添加了 `network_type``network_is_expensive``network_is_constrainted` 规则项。 * 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 #### rules
@@ -62,10 +62,7 @@ func (s *CommandServer) handleURLTest(conn net.Conn) error {
return false return false
} }
_, isGroup := it.(adapter.OutboundGroup) _, isGroup := it.(adapter.OutboundGroup)
if isGroup { return !isGroup
return false
}
return true
}) })
b, _ := batch.New(serviceNow.ctx, batch.WithConcurrencyNum[any](10)) b, _ := batch.New(serviceNow.ctx, batch.WithConcurrencyNum[any](10))
for _, detour := range outbounds { for _, detour := range outbounds {
+1
View File
@@ -18,6 +18,7 @@ func newIterator[T any](values []T) *iterator[T] {
return &iterator[T]{values} return &iterator[T]{values}
} }
//go:noinline
func newPtrIterator[T any](values []T) *iterator[*T] { func newPtrIterator[T any](values []T) *iterator[*T] {
return &iterator[*T]{common.Map(values, func(value T) *T { return &value })} return &iterator[*T]{common.Map(values, func(value T) *T { return &value })}
} }
+2 -2
View File
@@ -115,7 +115,7 @@ func (s *StatsService) RoutedPacketConnection(ctx context.Context, conn N.Packet
writeCounter = append(writeCounter, s.loadOrCreateCounter("user>>>"+user+">>>traffic>>>downlink")) writeCounter = append(writeCounter, s.loadOrCreateCounter("user>>>"+user+">>>traffic>>>downlink"))
} }
s.access.Unlock() 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) { 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 var rtm runtime.MemStats
runtime.ReadMemStats(&rtm) runtime.ReadMemStats(&rtm)
response := &SysStatsResponse{ response := &SysStatsResponse{
Uptime: uint32(time.Now().Sub(s.createdAt).Seconds()), Uptime: uint32(time.Since(s.createdAt).Seconds()),
NumGoroutine: uint32(runtime.NumGoroutine()), NumGoroutine: uint32(runtime.NumGoroutine()),
Alloc: rtm.Alloc, Alloc: rtm.Alloc,
TotalAlloc: rtm.TotalAlloc, TotalAlloc: rtm.TotalAlloc,
+11 -11
View File
@@ -24,12 +24,12 @@ require (
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a
github.com/sagernet/cors v1.2.1 github.com/sagernet/cors v1.2.1
github.com/sagernet/fswatch v0.1.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/gvisor v0.0.0-20250325023245-7a9c0f5725fb
github.com/sagernet/quic-go v0.52.0-beta.1 github.com/sagernet/quic-go v0.52.0-beta.1
github.com/sagernet/sing v0.7.5 github.com/sagernet/sing v0.7.6-0.20250814022454-614cd851ca6d
github.com/sagernet/sing-mux v0.3.2 github.com/sagernet/sing-mux v0.3.3
github.com/sagernet/sing-quic v0.5.0-beta.3 github.com/sagernet/sing-quic v0.5.0
github.com/sagernet/sing-shadowsocks v0.2.8 github.com/sagernet/sing-shadowsocks v0.2.8
github.com/sagernet/sing-shadowsocks2 v0.2.1 github.com/sagernet/sing-shadowsocks2 v0.2.1
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11
@@ -44,11 +44,11 @@ require (
github.com/vishvananda/netns v0.0.5 github.com/vishvananda/netns v0.0.5
go.uber.org/zap v1.27.0 go.uber.org/zap v1.27.0
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba 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/exp v0.0.0-20250506013437-ce4c2cf36ca6
golang.org/x/mod v0.26.0 golang.org/x/mod v0.27.0
golang.org/x/net v0.42.0 golang.org/x/net v0.43.0
golang.org/x/sys v0.34.0 golang.org/x/sys v0.35.0
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10
google.golang.org/grpc v1.73.0 google.golang.org/grpc v1.73.0
google.golang.org/protobuf v1.36.6 google.golang.org/protobuf v1.36.6
@@ -123,10 +123,10 @@ require (
go.uber.org/zap/exp v0.3.0 // indirect go.uber.org/zap/exp v0.3.0 // indirect
go4.org/mem v0.0.0-20240501181205-ae6ca9944745 // indirect go4.org/mem v0.0.0-20240501181205-ae6ca9944745 // indirect
golang.org/x/sync v0.16.0 // indirect golang.org/x/sync v0.16.0 // indirect
golang.org/x/term v0.33.0 // indirect golang.org/x/term v0.34.0 // indirect
golang.org/x/text v0.27.0 // indirect golang.org/x/text v0.28.0 // indirect
golang.org/x/time v0.9.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/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
golang.zx2c4.com/wireguard/windows v0.5.3 // indirect golang.zx2c4.com/wireguard/windows v0.5.3 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect
+22 -22
View File
@@ -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/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 h1:YqID+93B7VRfqIH3PArW/XpJv5H4OLEVWDfProGoRQs=
github.com/sagernet/fswatch v0.1.1/go.mod h1:nz85laH0mkQqJfaOrqPpkwtU1znMFNVTpT/5oRsVz/o= 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.8 h1:vXgoN0pjsMONAaYCTdsKBX2T1kxuS7sbT/mZ7PElGoo=
github.com/sagernet/gomobile v0.1.7/go.mod h1:Pqq2+ZVvs10U7xK+UwJgwYWUykewi8H6vlslAO73n9E= 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 h1:pprQtDqNgqXkRsXn+0E8ikKOemzmum8bODjSfDene38=
github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb/go.mod h1:QkkPEJLw59/tfxgapHta14UL5qMUah5NXhO0Kw2Kan4= 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= 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 h1:hWkojLg64zjV+MJOvJU/kOeWndm3tiEfBLx5foisszs=
github.com/sagernet/quic-go v0.52.0-beta.1/go.mod h1:OV+V5kEBb8kJS7k29MzDu6oj9GyMc7HA07sE1tedxz4= 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.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.6-0.20250814022454-614cd851ca6d h1:fTGA/vTcqceUqSiXeFAO99Iiai/nWBPV+yP8tPpHPf8=
github.com/sagernet/sing v0.7.5/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/sagernet/sing v0.7.6-0.20250814022454-614cd851ca6d/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.3 h1:YFgt9plMWzH994BMZLmyKL37PdIVaIilwP0Jg+EcLfw=
github.com/sagernet/sing-mux v0.3.2/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA= github.com/sagernet/sing-mux v0.3.3/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 h1:jNLIyVk24lFPvu8A4x+ZNEnZdI+Tg1rp7eCJ6v0Csak=
github.com/sagernet/sing-quic v0.5.0-beta.3/go.mod h1:SAv/qdeDN+75msGG5U5ZIwG+3Ua50jVIKNrRSY8pkx0= 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 h1:PURj5PRoAkqeHh2ZW205RWzN9E9RtKCVCzByXruQWfE=
github.com/sagernet/sing-shadowsocks v0.2.8/go.mod h1:lo7TWEMDcN5/h5B8S0ew+r78ZODn6SwVaFhvB6H+PTI= 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= 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 h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= 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.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.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= 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 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI=
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= 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 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68=
golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY= 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.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ= 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-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 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.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= 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.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 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= 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.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.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.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 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.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg= golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0= 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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/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.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= 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 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 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.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= 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-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 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-1
View File
@@ -100,7 +100,6 @@ func rewriteRcodeAction(rcodeMap map[string]int, ruleAction *DNSRuleAction) {
} }
ruleAction.Action = C.RuleActionTypePredefined ruleAction.Action = C.RuleActionTypePredefined
ruleAction.PredefinedOptions.Rcode = common.Ptr(DNSRCode(rcode)) ruleAction.PredefinedOptions.Rcode = common.Ptr(DNSRCode(rcode))
return
} }
type DNSClientOptions struct { type DNSClientOptions struct {
+39 -36
View File
@@ -67,42 +67,45 @@ func (r Rule) IsValid() bool {
} }
type RawDefaultRule struct { type RawDefaultRule struct {
Inbound badoption.Listable[string] `json:"inbound,omitempty"` Inbound badoption.Listable[string] `json:"inbound,omitempty"`
IPVersion int `json:"ip_version,omitempty"` IPVersion int `json:"ip_version,omitempty"`
Network badoption.Listable[string] `json:"network,omitempty"` Network badoption.Listable[string] `json:"network,omitempty"`
AuthUser badoption.Listable[string] `json:"auth_user,omitempty"` AuthUser badoption.Listable[string] `json:"auth_user,omitempty"`
Protocol badoption.Listable[string] `json:"protocol,omitempty"` Protocol badoption.Listable[string] `json:"protocol,omitempty"`
Client badoption.Listable[string] `json:"client,omitempty"` Client badoption.Listable[string] `json:"client,omitempty"`
Domain badoption.Listable[string] `json:"domain,omitempty"` Domain badoption.Listable[string] `json:"domain,omitempty"`
DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"` DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"`
DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"` DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"`
DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"` DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"`
Geosite badoption.Listable[string] `json:"geosite,omitempty"` Geosite badoption.Listable[string] `json:"geosite,omitempty"`
SourceGeoIP badoption.Listable[string] `json:"source_geoip,omitempty"` SourceGeoIP badoption.Listable[string] `json:"source_geoip,omitempty"`
GeoIP badoption.Listable[string] `json:"geoip,omitempty"` GeoIP badoption.Listable[string] `json:"geoip,omitempty"`
SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"` SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"`
SourceIPIsPrivate bool `json:"source_ip_is_private,omitempty"` SourceIPIsPrivate bool `json:"source_ip_is_private,omitempty"`
IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"` IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"`
IPIsPrivate bool `json:"ip_is_private,omitempty"` IPIsPrivate bool `json:"ip_is_private,omitempty"`
SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"` SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"`
SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"` SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"`
Port badoption.Listable[uint16] `json:"port,omitempty"` Port badoption.Listable[uint16] `json:"port,omitempty"`
PortRange badoption.Listable[string] `json:"port_range,omitempty"` PortRange badoption.Listable[string] `json:"port_range,omitempty"`
ProcessName badoption.Listable[string] `json:"process_name,omitempty"` ProcessName badoption.Listable[string] `json:"process_name,omitempty"`
ProcessPath badoption.Listable[string] `json:"process_path,omitempty"` ProcessPath badoption.Listable[string] `json:"process_path,omitempty"`
ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"` ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"`
PackageName badoption.Listable[string] `json:"package_name,omitempty"` PackageName badoption.Listable[string] `json:"package_name,omitempty"`
User badoption.Listable[string] `json:"user,omitempty"` User badoption.Listable[string] `json:"user,omitempty"`
UserID badoption.Listable[int32] `json:"user_id,omitempty"` UserID badoption.Listable[int32] `json:"user_id,omitempty"`
ClashMode string `json:"clash_mode,omitempty"` ClashMode string `json:"clash_mode,omitempty"`
NetworkType badoption.Listable[InterfaceType] `json:"network_type,omitempty"` NetworkType badoption.Listable[InterfaceType] `json:"network_type,omitempty"`
NetworkIsExpensive bool `json:"network_is_expensive,omitempty"` NetworkIsExpensive bool `json:"network_is_expensive,omitempty"`
NetworkIsConstrained bool `json:"network_is_constrained,omitempty"` NetworkIsConstrained bool `json:"network_is_constrained,omitempty"`
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"` WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"` WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"`
RuleSet badoption.Listable[string] `json:"rule_set,omitempty"` InterfaceAddress *badjson.TypedMap[string, badoption.Listable[badoption.Prefixable]] `json:"interface_address,omitempty"`
RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"` NetworkInterfaceAddress *badjson.TypedMap[InterfaceType, badoption.Listable[badoption.Prefixable]] `json:"network_interface_address,omitempty"`
Invert bool `json:"invert,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: renamed to rule_set_ip_cidr_match_source
Deprecated_RulesetIPCIDRMatchSource bool `json:"rule_set_ipcidr_match_source,omitempty"` Deprecated_RulesetIPCIDRMatchSource bool `json:"rule_set_ipcidr_match_source,omitempty"`
+42 -39
View File
@@ -68,45 +68,48 @@ func (r DNSRule) IsValid() bool {
} }
type RawDefaultDNSRule struct { type RawDefaultDNSRule struct {
Inbound badoption.Listable[string] `json:"inbound,omitempty"` Inbound badoption.Listable[string] `json:"inbound,omitempty"`
IPVersion int `json:"ip_version,omitempty"` IPVersion int `json:"ip_version,omitempty"`
QueryType badoption.Listable[DNSQueryType] `json:"query_type,omitempty"` QueryType badoption.Listable[DNSQueryType] `json:"query_type,omitempty"`
Network badoption.Listable[string] `json:"network,omitempty"` Network badoption.Listable[string] `json:"network,omitempty"`
AuthUser badoption.Listable[string] `json:"auth_user,omitempty"` AuthUser badoption.Listable[string] `json:"auth_user,omitempty"`
Protocol badoption.Listable[string] `json:"protocol,omitempty"` Protocol badoption.Listable[string] `json:"protocol,omitempty"`
Domain badoption.Listable[string] `json:"domain,omitempty"` Domain badoption.Listable[string] `json:"domain,omitempty"`
DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"` DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"`
DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"` DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"`
DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"` DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"`
Geosite badoption.Listable[string] `json:"geosite,omitempty"` Geosite badoption.Listable[string] `json:"geosite,omitempty"`
SourceGeoIP badoption.Listable[string] `json:"source_geoip,omitempty"` SourceGeoIP badoption.Listable[string] `json:"source_geoip,omitempty"`
GeoIP badoption.Listable[string] `json:"geoip,omitempty"` GeoIP badoption.Listable[string] `json:"geoip,omitempty"`
IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"` IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"`
IPIsPrivate bool `json:"ip_is_private,omitempty"` IPIsPrivate bool `json:"ip_is_private,omitempty"`
IPAcceptAny bool `json:"ip_accept_any,omitempty"` IPAcceptAny bool `json:"ip_accept_any,omitempty"`
SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"` SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"`
SourceIPIsPrivate bool `json:"source_ip_is_private,omitempty"` SourceIPIsPrivate bool `json:"source_ip_is_private,omitempty"`
SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"` SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"`
SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"` SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"`
Port badoption.Listable[uint16] `json:"port,omitempty"` Port badoption.Listable[uint16] `json:"port,omitempty"`
PortRange badoption.Listable[string] `json:"port_range,omitempty"` PortRange badoption.Listable[string] `json:"port_range,omitempty"`
ProcessName badoption.Listable[string] `json:"process_name,omitempty"` ProcessName badoption.Listable[string] `json:"process_name,omitempty"`
ProcessPath badoption.Listable[string] `json:"process_path,omitempty"` ProcessPath badoption.Listable[string] `json:"process_path,omitempty"`
ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"` ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"`
PackageName badoption.Listable[string] `json:"package_name,omitempty"` PackageName badoption.Listable[string] `json:"package_name,omitempty"`
User badoption.Listable[string] `json:"user,omitempty"` User badoption.Listable[string] `json:"user,omitempty"`
UserID badoption.Listable[int32] `json:"user_id,omitempty"` UserID badoption.Listable[int32] `json:"user_id,omitempty"`
Outbound badoption.Listable[string] `json:"outbound,omitempty"` Outbound badoption.Listable[string] `json:"outbound,omitempty"`
ClashMode string `json:"clash_mode,omitempty"` ClashMode string `json:"clash_mode,omitempty"`
NetworkType badoption.Listable[InterfaceType] `json:"network_type,omitempty"` NetworkType badoption.Listable[InterfaceType] `json:"network_type,omitempty"`
NetworkIsExpensive bool `json:"network_is_expensive,omitempty"` NetworkIsExpensive bool `json:"network_is_expensive,omitempty"`
NetworkIsConstrained bool `json:"network_is_constrained,omitempty"` NetworkIsConstrained bool `json:"network_is_constrained,omitempty"`
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"` WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"` WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"`
RuleSet badoption.Listable[string] `json:"rule_set,omitempty"` InterfaceAddress *badjson.TypedMap[string, badoption.Listable[badoption.Prefixable]] `json:"interface_address,omitempty"`
RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"` NetworkInterfaceAddress *badjson.TypedMap[InterfaceType, badoption.Listable[badoption.Prefixable]] `json:"network_interface_address,omitempty"`
RuleSetIPCIDRAcceptEmpty bool `json:"rule_set_ip_cidr_accept_empty,omitempty"` DefaultInterfaceAddress badoption.Listable[badoption.Prefixable] `json:"default_interface_address,omitempty"`
Invert bool `json:"invert,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: renamed to rule_set_ip_cidr_match_source
Deprecated_RulesetIPCIDRMatchSource bool `json:"rule_set_ipcidr_match_source,omitempty"` Deprecated_RulesetIPCIDRMatchSource bool `json:"rule_set_ipcidr_match_source,omitempty"`
+26 -23
View File
@@ -182,28 +182,31 @@ func (r HeadlessRule) IsValid() bool {
} }
type DefaultHeadlessRule struct { type DefaultHeadlessRule struct {
QueryType badoption.Listable[DNSQueryType] `json:"query_type,omitempty"` QueryType badoption.Listable[DNSQueryType] `json:"query_type,omitempty"`
Network badoption.Listable[string] `json:"network,omitempty"` Network badoption.Listable[string] `json:"network,omitempty"`
Domain badoption.Listable[string] `json:"domain,omitempty"` Domain badoption.Listable[string] `json:"domain,omitempty"`
DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"` DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"`
DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"` DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"`
DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"` DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"`
SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"` SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"`
IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"` IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"`
SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"` SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"`
SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"` SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"`
Port badoption.Listable[uint16] `json:"port,omitempty"` Port badoption.Listable[uint16] `json:"port,omitempty"`
PortRange badoption.Listable[string] `json:"port_range,omitempty"` PortRange badoption.Listable[string] `json:"port_range,omitempty"`
ProcessName badoption.Listable[string] `json:"process_name,omitempty"` ProcessName badoption.Listable[string] `json:"process_name,omitempty"`
ProcessPath badoption.Listable[string] `json:"process_path,omitempty"` ProcessPath badoption.Listable[string] `json:"process_path,omitempty"`
ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"` ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"`
PackageName badoption.Listable[string] `json:"package_name,omitempty"` PackageName badoption.Listable[string] `json:"package_name,omitempty"`
NetworkType badoption.Listable[InterfaceType] `json:"network_type,omitempty"` NetworkType badoption.Listable[InterfaceType] `json:"network_type,omitempty"`
NetworkIsExpensive bool `json:"network_is_expensive,omitempty"` NetworkIsExpensive bool `json:"network_is_expensive,omitempty"`
NetworkIsConstrained bool `json:"network_is_constrained,omitempty"` NetworkIsConstrained bool `json:"network_is_constrained,omitempty"`
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"` WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"` WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"`
Invert bool `json:"invert,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:"-"` DomainMatcher *domain.Matcher `json:"-"`
SourceIPSet *netipx.IPSet `json:"-"` SourceIPSet *netipx.IPSet `json:"-"`
@@ -240,7 +243,7 @@ type PlainRuleSetCompat _PlainRuleSetCompat
func (r PlainRuleSetCompat) MarshalJSON() ([]byte, error) { func (r PlainRuleSetCompat) MarshalJSON() ([]byte, error) {
var v any var v any
switch r.Version { switch r.Version {
case C.RuleSetVersion1, C.RuleSetVersion2, C.RuleSetVersion3: case C.RuleSetVersion1, C.RuleSetVersion2, C.RuleSetVersion3, C.RuleSetVersion4:
v = r.Options v = r.Options
default: default:
return nil, E.New("unknown rule-set version: ", r.Version) return nil, E.New("unknown rule-set version: ", r.Version)
+4 -12
View File
@@ -26,7 +26,7 @@ func RegisterOutbound(registry *outbound.Registry) {
type Outbound struct { type Outbound struct {
outbound.Adapter outbound.Adapter
dialer N.Dialer dialer tls.Dialer
server M.Socksaddr server M.Socksaddr
tlsConfig tls.Config tlsConfig tls.Config
client *anytls.Client client *anytls.Client
@@ -58,7 +58,8 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
if err != nil { if err != nil {
return nil, err return nil, err
} }
outbound.dialer = outboundDialer
outbound.dialer = tls.NewDialer(outboundDialer, tlsConfig)
client, err := anytls.NewClient(ctx, anytls.ClientConfig{ client, err := anytls.NewClient(ctx, anytls.ClientConfig{
Password: options.Password, 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) { func (h *Outbound) dialOut(ctx context.Context) (net.Conn, error) {
conn, err := h.dialer.DialContext(ctx, N.NetworkTCP, h.server) return h.dialer.DialTLSContext(ctx, 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
} }
func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
+3 -3
View File
@@ -313,7 +313,7 @@ func (g *URLTestGroup) Select(network string) (adapter.Outbound, bool) {
} }
func (g *URLTestGroup) loopCheck() { 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.lastActive.Store(time.Now())
g.CheckOutbounds(false) g.CheckOutbounds(false)
} }
@@ -323,7 +323,7 @@ func (g *URLTestGroup) loopCheck() {
return return
case <-g.ticker.C: 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.access.Lock()
g.ticker.Stop() g.ticker.Stop()
g.ticker = nil g.ticker = nil
@@ -360,7 +360,7 @@ func (g *URLTestGroup) urlTest(ctx context.Context, force bool) (map[string]uint
continue continue
} }
history := g.history.LoadURLTestHistory(realTag) 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 continue
} }
checked[realTag] = true checked[realTag] = true
+1 -1
View File
@@ -34,7 +34,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
if err != nil { if err != nil {
return nil, err 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 { if err != nil {
return nil, err return nil, err
} }
@@ -128,7 +128,6 @@ func (h *Outbound) InterfaceUpdated() {
if h.multiplexDialer != nil { if h.multiplexDialer != nil {
h.multiplexDialer.Reset() h.multiplexDialer.Reset()
} }
return
} }
func (h *Outbound) Close() error { func (h *Outbound) Close() error {
-1
View File
@@ -180,7 +180,6 @@ func (s *Outbound) connect() (*ssh.Client, error) {
func (s *Outbound) InterfaceUpdated() { func (s *Outbound) InterfaceUpdated() {
common.Close(s.clientConn) common.Close(s.clientConn)
return
} }
func (s *Outbound) Close() error { func (s *Outbound) Close() error {
+4 -4
View File
@@ -34,6 +34,7 @@ type Outbound struct {
key [56]byte key [56]byte
multiplexDialer *mux.Client multiplexDialer *mux.Client
tlsConfig tls.Config tlsConfig tls.Config
tlsDialer tls.Dialer
transport adapter.V2RayClientTransport transport adapter.V2RayClientTransport
} }
@@ -54,6 +55,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
if err != nil { if err != nil {
return nil, err return nil, err
} }
outbound.tlsDialer = tls.NewDialer(outboundDialer, outbound.tlsConfig)
} }
if options.Transport != nil { if options.Transport != nil {
outbound.transport, err = v2ray.NewClientTransport(ctx, outbound.dialer, outbound.serverAddr, common.PtrValueOrDefault(options.Transport), outbound.tlsConfig) 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 { if h.multiplexDialer != nil {
h.multiplexDialer.Reset() h.multiplexDialer.Reset()
} }
return
} }
func (h *Outbound) Close() error { func (h *Outbound) Close() error {
@@ -122,11 +123,10 @@ func (h *trojanDialer) DialContext(ctx context.Context, network string, destinat
var err error var err error
if h.transport != nil { if h.transport != nil {
conn, err = h.transport.DialContext(ctx) conn, err = h.transport.DialContext(ctx)
} else if h.tlsDialer != nil {
conn, err = h.tlsDialer.DialTLSContext(ctx, h.serverAddr)
} else { } else {
conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr) 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 { if err != nil {
common.Close(conn) common.Close(conn)
+6 -7
View File
@@ -35,6 +35,7 @@ type Outbound struct {
serverAddr M.Socksaddr serverAddr M.Socksaddr
multiplexDialer *mux.Client multiplexDialer *mux.Client
tlsConfig tls.Config tlsConfig tls.Config
tlsDialer tls.Dialer
transport adapter.V2RayClientTransport transport adapter.V2RayClientTransport
packetAddr bool packetAddr bool
xudp bool xudp bool
@@ -56,6 +57,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
if err != nil { if err != nil {
return nil, err return nil, err
} }
outbound.tlsDialer = tls.NewDialer(outboundDialer, outbound.tlsConfig)
} }
if options.Transport != nil { if options.Transport != nil {
outbound.transport, err = v2ray.NewClientTransport(ctx, outbound.dialer, outbound.serverAddr, common.PtrValueOrDefault(options.Transport), outbound.tlsConfig) 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 { if h.multiplexDialer != nil {
h.multiplexDialer.Reset() h.multiplexDialer.Reset()
} }
return
} }
func (h *Outbound) Close() error { func (h *Outbound) Close() error {
@@ -141,11 +142,10 @@ func (h *vlessDialer) DialContext(ctx context.Context, network string, destinati
var err error var err error
if h.transport != nil { if h.transport != nil {
conn, err = h.transport.DialContext(ctx) conn, err = h.transport.DialContext(ctx)
} else if h.tlsDialer != nil {
conn, err = h.tlsDialer.DialTLSContext(ctx, h.serverAddr)
} else { } else {
conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr) 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 { if err != nil {
return nil, err return nil, err
@@ -184,11 +184,10 @@ func (h *vlessDialer) ListenPacket(ctx context.Context, destination M.Socksaddr)
var err error var err error
if h.transport != nil { if h.transport != nil {
conn, err = h.transport.DialContext(ctx) conn, err = h.transport.DialContext(ctx)
} else if h.tlsDialer != nil {
conn, err = h.tlsDialer.DialTLSContext(ctx, h.serverAddr)
} else { } else {
conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr) 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 { if err != nil {
common.Close(conn) common.Close(conn)
+6 -7
View File
@@ -35,6 +35,7 @@ type Outbound struct {
serverAddr M.Socksaddr serverAddr M.Socksaddr
multiplexDialer *mux.Client multiplexDialer *mux.Client
tlsConfig tls.Config tlsConfig tls.Config
tlsDialer tls.Dialer
transport adapter.V2RayClientTransport transport adapter.V2RayClientTransport
packetAddr bool packetAddr bool
xudp bool xudp bool
@@ -56,6 +57,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
if err != nil { if err != nil {
return nil, err return nil, err
} }
outbound.tlsDialer = tls.NewDialer(outboundDialer, outbound.tlsConfig)
} }
if options.Transport != nil { if options.Transport != nil {
outbound.transport, err = v2ray.NewClientTransport(ctx, outbound.dialer, outbound.serverAddr, common.PtrValueOrDefault(options.Transport), outbound.tlsConfig) 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 { if h.multiplexDialer != nil {
h.multiplexDialer.Reset() h.multiplexDialer.Reset()
} }
return
} }
func (h *Outbound) Close() error { func (h *Outbound) Close() error {
@@ -155,11 +156,10 @@ func (h *vmessDialer) DialContext(ctx context.Context, network string, destinati
var err error var err error
if h.transport != nil { if h.transport != nil {
conn, err = h.transport.DialContext(ctx) conn, err = h.transport.DialContext(ctx)
} else if h.tlsDialer != nil {
conn, err = h.tlsDialer.DialTLSContext(ctx, h.serverAddr)
} else { } else {
conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr) 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 { if err != nil {
common.Close(conn) common.Close(conn)
@@ -183,11 +183,10 @@ func (h *vmessDialer) ListenPacket(ctx context.Context, destination M.Socksaddr)
var err error var err error
if h.transport != nil { if h.transport != nil {
conn, err = h.transport.DialContext(ctx) conn, err = h.transport.DialContext(ctx)
} else if h.tlsDialer != nil {
conn, err = h.tlsDialer.DialTLSContext(ctx, h.serverAddr)
} else { } else {
conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr) 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 { if err != nil {
return nil, err return nil, err
+15
View File
@@ -246,6 +246,21 @@ func NewDefaultRule(ctx context.Context, logger log.ContextLogger, options optio
rule.items = append(rule.items, item) rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, 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 { if len(options.RuleSet) > 0 {
var matchSource bool var matchSource bool
if options.RuleSetIPCIDRMatchSource { if options.RuleSetIPCIDRMatchSource {
@@ -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), " ") + "]"
}
}
+15
View File
@@ -247,6 +247,21 @@ func NewDefaultDNSRule(ctx context.Context, logger log.ContextLogger, options op
rule.items = append(rule.items, item) rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, 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 { if len(options.RuleSet) > 0 {
var matchSource bool var matchSource bool
if options.RuleSetIPCIDRMatchSource { if options.RuleSetIPCIDRMatchSource {
+10 -2
View File
@@ -164,13 +164,21 @@ func NewDefaultHeadlessRule(ctx context.Context, options option.DefaultHeadlessR
item := NewWIFISSIDItem(networkManager, options.WIFISSID) item := NewWIFISSIDItem(networkManager, options.WIFISSID)
rule.items = append(rule.items, item) rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, item) rule.allItems = append(rule.allItems, item)
} }
if len(options.WIFIBSSID) > 0 { if len(options.WIFIBSSID) > 0 {
item := NewWIFIBSSIDItem(networkManager, options.WIFIBSSID) item := NewWIFIBSSIDItem(networkManager, options.WIFIBSSID)
rule.items = append(rule.items, item) rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, 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 { if len(options.AdGuardDomain) > 0 {
@@ -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
}
@@ -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
}
+2 -2
View File
@@ -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 { for _, rule := range rules {
switch rule.Type { switch rule.Type {
case C.RuleTypeDefault: case C.RuleTypeDefault:
@@ -50,7 +50,7 @@ func hasHeadlessRule(rules []option.HeadlessRule, cond func(rule option.DefaultH
return true return true
} }
case C.RuleTypeLogical: case C.RuleTypeLogical:
if hasHeadlessRule(rule.LogicalOptions.Rules, cond) { if HasHeadlessRule(rule.LogicalOptions.Rules, cond) {
return true return true
} }
} }
+3 -3
View File
@@ -138,9 +138,9 @@ func (s *LocalRuleSet) reloadRules(headlessRules []option.HeadlessRule) error {
} }
} }
var metadata adapter.RuleSetMetadata var metadata adapter.RuleSetMetadata
metadata.ContainsProcessRule = hasHeadlessRule(headlessRules, isProcessHeadlessRule) metadata.ContainsProcessRule = HasHeadlessRule(headlessRules, isProcessHeadlessRule)
metadata.ContainsWIFIRule = hasHeadlessRule(headlessRules, isWIFIHeadlessRule) metadata.ContainsWIFIRule = HasHeadlessRule(headlessRules, isWIFIHeadlessRule)
metadata.ContainsIPCIDRRule = hasHeadlessRule(headlessRules, isIPCIDRHeadlessRule) metadata.ContainsIPCIDRRule = HasHeadlessRule(headlessRules, isIPCIDRHeadlessRule)
s.rules = rules s.rules = rules
s.metadata = metadata s.metadata = metadata
s.callbackAccess.Lock() s.callbackAccess.Lock()
+3 -3
View File
@@ -185,9 +185,9 @@ func (s *RemoteRuleSet) loadBytes(content []byte) error {
return E.Cause(err, "parse rule_set.rules.[", i, "]") return E.Cause(err, "parse rule_set.rules.[", i, "]")
} }
} }
s.metadata.ContainsProcessRule = hasHeadlessRule(plainRuleSet.Rules, isProcessHeadlessRule) s.metadata.ContainsProcessRule = HasHeadlessRule(plainRuleSet.Rules, isProcessHeadlessRule)
s.metadata.ContainsWIFIRule = hasHeadlessRule(plainRuleSet.Rules, isWIFIHeadlessRule) s.metadata.ContainsWIFIRule = HasHeadlessRule(plainRuleSet.Rules, isWIFIHeadlessRule)
s.metadata.ContainsIPCIDRRule = hasHeadlessRule(plainRuleSet.Rules, isIPCIDRHeadlessRule) s.metadata.ContainsIPCIDRRule = HasHeadlessRule(plainRuleSet.Rules, isIPCIDRHeadlessRule)
s.rules = rules s.rules = rules
s.callbackAccess.Lock() s.callbackAccess.Lock()
callbacks := s.callbacks.Array() callbacks := s.callbacks.Array()
+3 -8
View File
@@ -2,7 +2,6 @@ package ssmapi
import ( import (
"net/http" "net/http"
"strconv"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing/common/logger" "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) { 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() users := s.user.List()
s.traffic.ReadUsers(users) s.traffic.ReadUsers(users, requireClear)
for i := range users { for i := range users {
users[i].Password = "" users[i].Password = ""
} }
uplinkBytes, downlinkBytes, uplinkPackets, downlinkPackets, tcpSessions, udpSessions := s.traffic.ReadGlobal() uplinkBytes, downlinkBytes, uplinkPackets, downlinkPackets, tcpSessions, udpSessions := s.traffic.ReadGlobal(requireClear)
if requireClear {
s.traffic.Clear()
}
render.JSON(writer, request, render.M{ render.JSON(writer, request, render.M{
"uplinkBytes": uplinkBytes, "uplinkBytes": uplinkBytes,
+50 -54
View File
@@ -142,86 +142,82 @@ func (s *TrafficManager) TrackPacketConnection(conn N.PacketConn, metadata adapt
readPacketCounter = append(readPacketCounter, upPacketsCounter) readPacketCounter = append(readPacketCounter, upPacketsCounter)
writePacketCounter = append(writePacketCounter, downPacketsCounter) writePacketCounter = append(writePacketCounter, downPacketsCounter)
udpSessionCounter.Add(1) 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) { func (s *TrafficManager) ReadUser(user *UserObject) {
s.userAccess.Lock() s.userAccess.Lock()
defer s.userAccess.Unlock() 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 { 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 { 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 { 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 { 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 { 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 { 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() s.userAccess.Lock()
defer s.userAccess.Unlock() defer s.userAccess.Unlock()
for _, user := range users { for _, user := range users {
s.readUser(user) s.readUser(user, swap)
} }
return
} }
func (s *TrafficManager) ReadGlobal() ( func (s *TrafficManager) ReadGlobal(swap bool) (uplinkBytes int64, downlinkBytes int64, uplinkPackets int64, downlinkPackets int64, tcpSessions int64, udpSessions int64) {
uplinkBytes int64, if swap {
downlinkBytes int64, return s.globalUplink.Swap(0),
uplinkPackets int64, s.globalDownlink.Swap(0),
downlinkPackets int64, s.globalUplinkPackets.Swap(0),
tcpSessions int64, s.globalDownlinkPackets.Swap(0),
udpSessions int64, s.globalTCPSessions.Swap(0),
) { s.globalUDPSessions.Swap(0)
return s.globalUplink.Load(), } else {
s.globalDownlink.Load(), return s.globalUplink.Load(),
s.globalUplinkPackets.Load(), s.globalDownlink.Load(),
s.globalDownlinkPackets.Load(), s.globalUplinkPackets.Load(),
s.globalTCPSessions.Load(), s.globalDownlinkPackets.Load(),
s.globalUDPSessions.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)
} }
} }
+8 -9
View File
@@ -4,6 +4,7 @@ import (
"context" "context"
"net" "net"
"sync" "sync"
"sync/atomic"
"time" "time"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
@@ -29,7 +30,7 @@ type Client struct {
serverAddr string serverAddr string
serviceName string serviceName string
dialOptions []grpc.DialOption dialOptions []grpc.DialOption
conn *grpc.ClientConn conn atomic.Pointer[grpc.ClientConn]
connAccess sync.Mutex 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) { func (c *Client) connect() (*grpc.ClientConn, error) {
conn := c.conn conn := c.conn.Load()
if conn != nil && conn.GetState() != connectivity.Shutdown { if conn != nil && conn.GetState() != connectivity.Shutdown {
return conn, nil return conn, nil
} }
c.connAccess.Lock() c.connAccess.Lock()
defer c.connAccess.Unlock() defer c.connAccess.Unlock()
conn = c.conn conn = c.conn.Load()
if conn != nil && conn.GetState() != connectivity.Shutdown { if conn != nil && conn.GetState() != connectivity.Shutdown {
return conn, nil return conn, nil
} }
@@ -89,7 +90,7 @@ func (c *Client) connect() (*grpc.ClientConn, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
c.conn = conn c.conn.Store(conn)
return conn, nil return conn, nil
} }
@@ -109,11 +110,9 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
} }
func (c *Client) Close() error { func (c *Client) Close() error {
c.connAccess.Lock() conn := c.conn.Swap(nil)
defer c.connAccess.Unlock() if conn != nil {
if c.conn != nil { conn.Close()
c.conn.Close()
c.conn = nil
} }
return nil return nil
} }

Some files were not shown because too many files have changed in this diff Show More