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

View File

@@ -5,58 +5,50 @@ import (
"sync/atomic"
)
func DefaultValue[T any]() T {
var defaultValue T
return defaultValue
}
type TypedValue[T any] struct {
_ noCopy
value atomic.Value
value atomic.Pointer[T]
}
// tValue is a struct with determined type to resolve atomic.Value usages with interface types
// https://github.com/golang/go/issues/22550
//
// The intention to have an atomic value store for errors. However, running this code panics:
// panic: sync/atomic: store of inconsistently typed value into Value
// This is because atomic.Value requires that the underlying concrete type be the same (which is a reasonable expectation for its implementation).
// When going through the atomic.Value.Store method call, the fact that both these are of the error interface is lost.
type tValue[T any] struct {
value T
func (t *TypedValue[T]) Load() (v T) {
v, _ = t.LoadOk()
return
}
func (t *TypedValue[T]) Load() T {
value, _ := t.LoadOk()
return value
}
func (t *TypedValue[T]) LoadOk() (_ T, ok bool) {
func (t *TypedValue[T]) LoadOk() (v T, ok bool) {
value := t.value.Load()
if value == nil {
return DefaultValue[T](), false
return
}
return value.(tValue[T]).value, true
return *value, true
}
func (t *TypedValue[T]) Store(value T) {
t.value.Store(tValue[T]{value})
t.value.Store(&value)
}
func (t *TypedValue[T]) Swap(new T) T {
old := t.value.Swap(tValue[T]{new})
func (t *TypedValue[T]) Swap(new T) (v T) {
old := t.value.Swap(&new)
if old == nil {
return DefaultValue[T]()
return
}
return old.(tValue[T]).value
return *old
}
func (t *TypedValue[T]) CompareAndSwap(old, new T) bool {
return t.value.CompareAndSwap(tValue[T]{old}, tValue[T]{new}) ||
// In the edge-case where [atomic.Value.Store] is uninitialized
// and trying to compare with the zero value of T,
// then compare-and-swap with the nil any value.
(any(old) == any(DefaultValue[T]()) && t.value.CompareAndSwap(any(nil), tValue[T]{new}))
for {
currentP := t.value.Load()
var currentValue T
if currentP != nil {
currentValue = *currentP
}
// Compare old and current via runtime equality check.
if any(currentValue) != any(old) {
return false
}
if t.value.CompareAndSwap(currentP, &new) {
return true
}
}
}
func (t *TypedValue[T]) MarshalJSON() ([]byte, error) {
@@ -89,9 +81,3 @@ func NewTypedValue[T any](t T) (v TypedValue[T]) {
v.Store(t)
return
}
type noCopy struct{}
// Lock is a no-op used by -copylocks checker from `go vet`.
func (*noCopy) Lock() {}
func (*noCopy) Unlock() {}

View File

@@ -7,20 +7,6 @@ import (
)
func TestTypedValue(t *testing.T) {
{
// Always wrapping should not allocate for simple values
// because tValue[T] has the same memory layout as T.
var v TypedValue[bool]
bools := []bool{true, false}
if n := int(testing.AllocsPerRun(1000, func() {
for _, b := range bools {
v.Store(b)
}
})); n != 0 {
t.Errorf("AllocsPerRun = %d, want 0", n)
}
}
{
var v TypedValue[int]
got, gotOk := v.LoadOk()
@@ -58,20 +44,126 @@ func TestTypedValue(t *testing.T) {
}
}
{
e1, e2, e3 := io.EOF, &os.PathError{}, &os.PathError{}
var v TypedValue[error]
if v.CompareAndSwap(e1, e2) != false {
t.Fatalf("CompareAndSwap = true, want false")
}
if value := v.Load(); value != nil {
t.Fatalf("Load = (%v), want (%v)", value, nil)
}
if v.CompareAndSwap(nil, e1) != true {
t.Fatalf("CompareAndSwap = false, want true")
}
if value := v.Load(); value != e1 {
t.Fatalf("Load = (%v), want (%v)", value, e1)
}
if v.CompareAndSwap(e2, e3) != false {
t.Fatalf("CompareAndSwap = true, want false")
}
if value := v.Load(); value != e1 {
t.Fatalf("Load = (%v), want (%v)", value, e1)
}
if v.CompareAndSwap(e1, e2) != true {
t.Fatalf("CompareAndSwap = false, want true")
}
if value := v.Load(); value != e2 {
t.Fatalf("Load = (%v), want (%v)", value, e2)
}
if v.CompareAndSwap(e3, e2) != false {
t.Fatalf("CompareAndSwap = true, want false")
}
if value := v.Load(); value != e2 {
t.Fatalf("Load = (%v), want (%v)", value, e2)
}
if v.CompareAndSwap(nil, e3) != false {
t.Fatalf("CompareAndSwap = true, want false")
}
if value := v.Load(); value != e2 {
t.Fatalf("Load = (%v), want (%v)", value, e2)
}
}
{
c1, c2, c3 := make(chan struct{}), make(chan struct{}), make(chan struct{})
var v TypedValue[chan struct{}]
if v.CompareAndSwap(c1, c2) != false {
t.Fatalf("CompareAndSwap = true, want false")
}
if value := v.Load(); value != nil {
t.Fatalf("Load = (%v), want (%v)", value, nil)
}
if v.CompareAndSwap(nil, c1) != true {
t.Fatalf("CompareAndSwap = false, want true")
}
if value := v.Load(); value != c1 {
t.Fatalf("Load = (%v), want (%v)", value, c1)
}
if v.CompareAndSwap(c2, c3) != false {
t.Fatalf("CompareAndSwap = true, want false")
}
if value := v.Load(); value != c1 {
t.Fatalf("Load = (%v), want (%v)", value, c1)
}
if v.CompareAndSwap(c1, c2) != true {
t.Fatalf("CompareAndSwap = false, want true")
}
if value := v.Load(); value != c2 {
t.Fatalf("Load = (%v), want (%v)", value, c2)
}
if v.CompareAndSwap(c3, c2) != false {
t.Fatalf("CompareAndSwap = true, want false")
}
if value := v.Load(); value != c2 {
t.Fatalf("Load = (%v), want (%v)", value, c2)
}
if v.CompareAndSwap(nil, c3) != false {
t.Fatalf("CompareAndSwap = true, want false")
}
if value := v.Load(); value != c2 {
t.Fatalf("Load = (%v), want (%v)", value, c2)
}
}
{
c1, c2, c3 := &io.LimitedReader{}, &io.SectionReader{}, &io.SectionReader{}
var v TypedValue[io.Reader]
if v.CompareAndSwap(c1, c2) != false {
t.Fatalf("CompareAndSwap = true, want false")
}
if value := v.Load(); value != nil {
t.Fatalf("Load = (%v), want (%v)", value, nil)
}
if v.CompareAndSwap(nil, c1) != true {
t.Fatalf("CompareAndSwap = false, want true")
}
if value := v.Load(); value != c1 {
t.Fatalf("Load = (%v), want (%v)", value, c1)
}
if v.CompareAndSwap(c2, c3) != false {
t.Fatalf("CompareAndSwap = true, want false")
}
if value := v.Load(); value != c1 {
t.Fatalf("Load = (%v), want (%v)", value, c1)
}
if v.CompareAndSwap(c1, c2) != true {
t.Fatalf("CompareAndSwap = false, want true")
}
if value := v.Load(); value != c2 {
t.Fatalf("Load = (%v), want (%v)", value, c2)
}
if v.CompareAndSwap(c3, c2) != false {
t.Fatalf("CompareAndSwap = true, want false")
}
if value := v.Load(); value != c2 {
t.Fatalf("Load = (%v), want (%v)", value, c2)
}
if v.CompareAndSwap(nil, c3) != false {
t.Fatalf("CompareAndSwap = true, want false")
}
if value := v.Load(); value != c2 {
t.Fatalf("Load = (%v), want (%v)", value, c2)
}
}
}

View File

@@ -10,27 +10,27 @@ import (
)
var (
keepAliveIdle = atomic.NewTypedValue[time.Duration](0 * time.Second)
keepAliveInterval = atomic.NewTypedValue[time.Duration](0 * time.Second)
keepAliveIdle = atomic.NewInt64(0)
keepAliveInterval = atomic.NewInt64(0)
disableKeepAlive = atomic.NewBool(false)
SetDisableKeepAliveCallback = utils.NewCallback[bool]()
)
func SetKeepAliveIdle(t time.Duration) {
keepAliveIdle.Store(t)
keepAliveIdle.Store(int64(t))
}
func SetKeepAliveInterval(t time.Duration) {
keepAliveInterval.Store(t)
keepAliveInterval.Store(int64(t))
}
func KeepAliveIdle() time.Duration {
return keepAliveIdle.Load()
return time.Duration(keepAliveIdle.Load())
}
func KeepAliveInterval() time.Duration {
return keepAliveInterval.Load()
return time.Duration(keepAliveInterval.Load())
}
func SetDisableKeepAlive(disable bool) {

View File

@@ -3,7 +3,6 @@ module github.com/metacubex/mihomo
go 1.20
require (
github.com/3andne/restls-client-go v0.1.6
github.com/bahlo/generic-list-go v0.2.0
github.com/coreos/go-iptables v0.8.0
github.com/dlclark/regexp2 v1.11.5
@@ -19,18 +18,20 @@ require (
github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab
github.com/metacubex/bart v0.20.5
github.com/metacubex/bbolt v0.0.0-20250725135710-010dbbbb7a5b
github.com/metacubex/blake3 v0.1.0
github.com/metacubex/chacha v0.1.5
github.com/metacubex/fswatch v0.1.1
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759
github.com/metacubex/quic-go v0.54.1-0.20250730114134-a1ae705fe295
github.com/metacubex/randv2 v0.2.0
github.com/metacubex/sing v0.5.4
github.com/metacubex/restls-client-go v0.1.7
github.com/metacubex/sing v0.5.5
github.com/metacubex/sing-mux v0.3.3-0.20250813083925-d7c9aeaeeaac
github.com/metacubex/sing-quic v0.0.0-20250718154553-1b193bec4cbb
github.com/metacubex/sing-shadowsocks v0.2.11
github.com/metacubex/sing-shadowsocks2 v0.2.5
github.com/metacubex/sing-shadowsocks v0.2.12
github.com/metacubex/sing-shadowsocks2 v0.2.6
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2
github.com/metacubex/sing-tun v0.4.7-0.20250801130030-308b828865ae
github.com/metacubex/sing-tun v0.4.7
github.com/metacubex/sing-vmess v0.2.4-0.20250731011226-ea28d589924d
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f
github.com/metacubex/smux v0.0.0-20250503055512-501391591dee
@@ -59,7 +60,6 @@ require (
golang.org/x/sys v0.30.0 // lastest version compatible with golang1.20
google.golang.org/protobuf v1.34.2 // lastest version compatible with golang1.20
gopkg.in/yaml.v3 v3.0.1
lukechampine.com/blake3 v1.3.0 // lastest version compatible with golang1.20
)
require (
@@ -85,11 +85,11 @@ require (
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
github.com/hashicorp/yamux v0.1.2 // indirect
github.com/josharian/native v1.1.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mdlayher/socket v0.4.1 // indirect
github.com/metacubex/ascon v0.1.0 // indirect
github.com/metacubex/gvisor v0.0.0-20250324165734-5857f47bd43b // indirect
github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793 // indirect
github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect

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/go.mod h1:9R9TU936laAIqAmjcHo/LsaXYOZlymudOAxjaBf62UM=
github.com/RyuaNerin/testingutil v0.1.0 h1:IYT6JL57RV3U2ml3dLHZsVtPOP6yNK7WUVdzzlpNrss=
@@ -81,8 +79,6 @@ github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtL
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
@@ -98,10 +94,14 @@ github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab h1:Chbw+/31UC14YFNr78pESt5Vowlc62zziw05JCUqoL4=
github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab/go.mod h1:xVKK8jC5Sd3hfh7WjmCq+HorehIbrBijaUWmcuKjPcI=
github.com/metacubex/ascon v0.1.0 h1:6ZWxmXYszT1XXtwkf6nxfFhc/OTtQ9R3Vyj1jN32lGM=
github.com/metacubex/ascon v0.1.0/go.mod h1:eV5oim4cVPPdEL8/EYaTZ0iIKARH9pnhAK/fcT5Kacc=
github.com/metacubex/bart v0.20.5 h1:XkgLZ17QxfxkqKdGsojoM2Zu01mmHyyQSFzt2/calTM=
github.com/metacubex/bart v0.20.5/go.mod h1:DCcyfP4MC+Zy7sLK7XeGuMw+P5K9mIRsYOBgiE8icsI=
github.com/metacubex/bbolt v0.0.0-20250725135710-010dbbbb7a5b h1:j7dadXD8I2KTmMt8jg1JcaP1ANL3JEObJPdANKcSYPY=
github.com/metacubex/bbolt v0.0.0-20250725135710-010dbbbb7a5b/go.mod h1:+WmP0VJZDkDszvpa83HzfUp6QzARl/IKkMorH4+nODw=
github.com/metacubex/blake3 v0.1.0 h1:KGnjh/56REO7U+cgZA8dnBhxdP7jByrG7hTP+bu6cqY=
github.com/metacubex/blake3 v0.1.0/go.mod h1:CCkLdzFrqf7xmxCdhQFvJsRRV2mwOLDoSPg6vUTB9Uk=
github.com/metacubex/chacha v0.1.5 h1:fKWMb/5c7ZrY8Uoqi79PPFxl+qwR7X/q0OrsAubyX2M=
github.com/metacubex/chacha v0.1.5/go.mod h1:Djn9bPZxLTXbJFSeyo0/qzEzQI+gUSSzttuzZM75GH8=
github.com/metacubex/fswatch v0.1.1 h1:jqU7C/v+g0qc2RUFgmAOPoVvfl2BXXUXEumn6oQuxhU=
@@ -116,21 +116,23 @@ github.com/metacubex/quic-go v0.54.1-0.20250730114134-a1ae705fe295 h1:8JVlYuE8uS
github.com/metacubex/quic-go v0.54.1-0.20250730114134-a1ae705fe295/go.mod h1:1lktQFtCD17FZliVypbrDHwbsFSsmz2xz2TRXydvB5c=
github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs=
github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY=
github.com/metacubex/restls-client-go v0.1.7 h1:eCwiXCTQb5WJu9IlgYvDBA1OgrINv58dEe7hcN5H15k=
github.com/metacubex/restls-client-go v0.1.7/go.mod h1:BN/U52vPw7j8VTSh2vleD/MnmVKCov84mS5VcjVHH4g=
github.com/metacubex/sing v0.5.2/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w=
github.com/metacubex/sing v0.5.4 h1:a4kAOZmF+OXosbzPEcrSc5QD35/ex+MNuZsrcuWskHk=
github.com/metacubex/sing v0.5.4/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w=
github.com/metacubex/sing v0.5.5 h1:m5U8iHvRAUxlme3FZlE/LPIGHjU8oMCUzXWGbQQAC1E=
github.com/metacubex/sing v0.5.5/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w=
github.com/metacubex/sing-mux v0.3.3-0.20250813083925-d7c9aeaeeaac h1:wDH/Jh/yqWbzPktqJP+Y1cUG8hchcrzKzUxJiSpnaQs=
github.com/metacubex/sing-mux v0.3.3-0.20250813083925-d7c9aeaeeaac/go.mod h1:3rt1soewn0O6j89GCLmwAQFsq257u0jf2zQSPhTL3Bw=
github.com/metacubex/sing-quic v0.0.0-20250718154553-1b193bec4cbb h1:U/m3h8lp/j7i8zFgfvScLdZa1/Y8dd74oO7iZaQq80s=
github.com/metacubex/sing-quic v0.0.0-20250718154553-1b193bec4cbb/go.mod h1:B60FxaPHjR1SeQB0IiLrgwgvKsaoASfOWdiqhLjmMGA=
github.com/metacubex/sing-shadowsocks v0.2.11 h1:p2NGNOdF95e6XvdDKipLj1FRRqR8dnbfC/7pw2CCTlw=
github.com/metacubex/sing-shadowsocks v0.2.11/go.mod h1:bT1PCTV316zFnlToRMk5zt9HmIQYRBveiT71mplYPfc=
github.com/metacubex/sing-shadowsocks2 v0.2.5 h1:MnPn0hbcDkSJt6TlpI15XImHKK6IqaOwBUGPKyMnJnE=
github.com/metacubex/sing-shadowsocks2 v0.2.5/go.mod h1:Zyh+rAQRyevYfG/COCvDs1c/YMhGqCuknn7QrGmoQIw=
github.com/metacubex/sing-shadowsocks v0.2.12 h1:Wqzo8bYXrK5aWqxu/TjlTnYZzAKtKsaFQBdr6IHFaBE=
github.com/metacubex/sing-shadowsocks v0.2.12/go.mod h1:2e5EIaw0rxKrm1YTRmiMnDulwbGxH9hAFlrwQLQMQkU=
github.com/metacubex/sing-shadowsocks2 v0.2.6 h1:ZR1kYT0f0Vi64iQSS09OdhFfppiNkh7kjgRdMm0SB98=
github.com/metacubex/sing-shadowsocks2 v0.2.6/go.mod h1:vOEbfKC60txi0ca+yUlqEwOGc3Obl6cnSgx9Gf45KjE=
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 h1:gXU+MYPm7Wme3/OAY2FFzVq9d9GxPHOqu5AQfg/ddhI=
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2/go.mod h1:mbfboaXauKJNIHJYxQRa+NJs4JU9NZfkA+I33dS2+9E=
github.com/metacubex/sing-tun v0.4.7-0.20250801130030-308b828865ae h1:hKYGuBaJBLqNmySrYrOGDXXsyxXNaR8omO96QDsRI2s=
github.com/metacubex/sing-tun v0.4.7-0.20250801130030-308b828865ae/go.mod h1:2YywXPWW8Z97kTH7RffOeykKzU+l0aiKlglWV1PAS64=
github.com/metacubex/sing-tun v0.4.7 h1:ZDY/W+1c7PeWWKeKRyUo18fySF/TWjB0i5ui81Ar778=
github.com/metacubex/sing-tun v0.4.7/go.mod h1:xHecZRwBnKWe6zG9amAK9cXf91lF6blgjBqm+VvOrmU=
github.com/metacubex/sing-vmess v0.2.4-0.20250731011226-ea28d589924d h1:koSVAQiYqU/qtJ527e+wbsEPvTaM39HW75LSvh04IT0=
github.com/metacubex/sing-vmess v0.2.4-0.20250731011226-ea28d589924d/go.mod h1:21R5R1u90uUvBQF0owoooEu96/SAYYD56nDrwm6nFaM=
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f h1:Sr/DYKYofKHKc4GF3qkRGNuj6XA6c0eqPgEDN+VAsYU=
@@ -279,5 +281,3 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE=
lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=

View File

@@ -4,7 +4,7 @@ import (
"context"
"net"
tls "github.com/3andne/restls-client-go"
tls "github.com/metacubex/restls-client-go"
)
const (

View File

@@ -8,8 +8,8 @@ import (
"net/netip"
"strconv"
"github.com/metacubex/blake3"
"github.com/metacubex/quic-go"
"lukechampine.com/blake3"
C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/transport/socks5"

View File

@@ -105,26 +105,20 @@ func (vc *Conn) sendRequest(p []byte) bool {
}
}
var buffer *buf.Buffer
if vc.IsXTLSVisionEnabled() {
buffer = buf.New()
defer buffer.Release()
} else {
requestLen := 1 // protocol version
requestLen += 16 // UUID
requestLen += 1 // addons length
requestLen += len(addonsBytes)
requestLen += 1 // command
if !vc.dst.Mux {
requestLen += 2 // port
requestLen += 1 // addr type
requestLen += len(vc.dst.Addr)
}
requestLen += len(p)
buffer = buf.NewSize(requestLen)
defer buffer.Release()
requestLen := 1 // protocol version
requestLen += 16 // UUID
requestLen += 1 // addons length
requestLen += len(addonsBytes)
requestLen += 1 // command
if !vc.dst.Mux {
requestLen += 2 // port
requestLen += 1 // addr type
requestLen += len(vc.dst.Addr)
}
requestLen += len(p)
buffer := buf.NewSize(requestLen)
defer buffer.Release()
buf.Must(
buffer.WriteByte(Version), // protocol version
@@ -182,10 +176,6 @@ func (vc *Conn) NeedHandshake() bool {
return vc.needHandshake
}
func (vc *Conn) IsXTLSVisionEnabled() bool {
return vc.addons != nil && vc.addons.Flow == XRV
}
// newConn return a Conn instance
func newConn(conn net.Conn, client *Client, dst *DstAddr) (net.Conn, error) {
c := &Conn{

View File

@@ -4,11 +4,13 @@ import (
"bytes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
"errors"
"fmt"
"io"
"net"
"runtime"
"strings"
"sync"
"time"
@@ -36,13 +38,12 @@ func init() {
type ClientInstance struct {
sync.RWMutex
nfsEKey *mlkem.EncapsulationKey768
nfsEKeyBytes []byte
xor uint32
minutes time.Duration
expire time.Time
baseKey []byte
ticket []byte
nfsEKey *mlkem.EncapsulationKey768
xorKey []byte
minutes time.Duration
expire time.Time
baseKey []byte
ticket []byte
}
type ClientConn struct {
@@ -59,10 +60,17 @@ type ClientConn struct {
}
func (i *ClientInstance) Init(nfsEKeyBytes []byte, xor uint32, minutes time.Duration) (err error) {
if i.nfsEKey != nil {
err = errors.New("already initialized")
return
}
i.nfsEKey, err = mlkem.NewEncapsulationKey768(nfsEKeyBytes)
if err != nil {
return
}
if xor > 0 {
i.nfsEKeyBytes = nfsEKeyBytes
i.xor = xor
xorKey := sha256.Sum256(nfsEKeyBytes)
i.xorKey = xorKey[:]
}
i.minutes = minutes
return
@@ -72,8 +80,8 @@ func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) {
if i.nfsEKey == nil {
return nil, errors.New("uninitialized")
}
if i.xor > 0 {
conn = NewXorConn(conn, i.nfsEKeyBytes)
if i.xorKey != nil {
conn = NewXorConn(conn, i.xorKey)
}
c := &ClientConn{Conn: conn}
@@ -107,16 +115,16 @@ func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) {
if _, err := c.Conn.Write(clientHello); err != nil {
return nil, err
}
// client can send more padding / NFS AEAD messages if needed
// client can send more paddings / NFS AEAD messages if needed
_, t, l, err := ReadAndDecodeHeader(c.Conn)
_, t, l, err := ReadAndDiscardPaddings(c.Conn) // allow paddings before server hello
if err != nil {
return nil, err
}
if t != 1 {
return nil, fmt.Errorf("unexpected type %v, expect random hello", t)
}
peerRandomHello := make([]byte, 1088+21)
if l != len(peerRandomHello) {
return nil, fmt.Errorf("unexpected length %v for random hello", l)
@@ -193,27 +201,9 @@ func (c *ClientConn) Read(b []byte) (int, error) {
return 0, nil
}
if c.peerAead == nil {
var t byte
var l int
var err error
if c.instance == nil { // from 1-RTT
for {
if _, t, l, err = ReadAndDecodeHeader(c.Conn); err != nil {
return 0, err
}
if t != 23 {
break
}
if _, err := io.ReadFull(c.Conn, make([]byte, l)); err != nil {
return 0, err
}
}
} else {
h := make([]byte, 5)
if _, err := io.ReadFull(c.Conn, h); err != nil {
return 0, err
}
if t, l, err = DecodeHeader(h); err != nil {
_, t, l, err := ReadAndDiscardPaddings(c.Conn) // allow paddings before random hello
if err != nil {
if c.instance != nil && strings.HasPrefix(err.Error(), "invalid header: ") { // 0-RTT's 0-RTT
c.instance.Lock()
if bytes.Equal(c.ticket, c.instance.ticket) {
c.instance.expire = time.Now() // expired
@@ -221,6 +211,7 @@ func (c *ClientConn) Read(b []byte) (int, error) {
c.instance.Unlock()
return 0, errors.New("new handshake needed")
}
return 0, err
}
if t != 0 {
return 0, fmt.Errorf("unexpected type %v, expect server random", t)

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 {
t = 1
} else {
h = nil
l = 0
}
if h == nil || l < 17 || l > 17000 { // TODO: TLSv1.3 max length
err = fmt.Errorf("invalid header: %v", h[:5])
if l < 17 || l > 17000 { // TODO: TLSv1.3 max length
err = fmt.Errorf("invalid header: %v", h[:5]) // DO NOT CHANGE: relied by client's Read()
}
return
}
@@ -62,6 +62,17 @@ func ReadAndDecodeHeader(conn net.Conn) (h []byte, t byte, l int, err error) {
return
}
func ReadAndDiscardPaddings(conn net.Conn) (h []byte, t byte, l int, err error) {
for {
if h, t, l, err = ReadAndDecodeHeader(conn); err != nil || t != 23 {
return
}
if _, err = io.ReadFull(conn, make([]byte, l)); err != nil {
return
}
}
}
func NewAead(c byte, secret, salt, info []byte) (aead cipher.AEAD) {
key := make([]byte, 32)
hkdf.New(sha256.New, secret, salt, info).Read(key)

View File

@@ -9,4 +9,6 @@
// https://github.com/XTLS/Xray-core/commit/1720be168fa069332c418503d30341fc6e01df7f
// https://github.com/XTLS/Xray-core/commit/0fd7691d6b28e05922d7a5a9313d97745a51ea63
// https://github.com/XTLS/Xray-core/commit/09cc92c61d9067e0d65c1cae9124664ecfc78f43
// https://github.com/XTLS/Xray-core/commit/2807ee432a1fbeb301815647189eacd650b12a8b
// https://github.com/XTLS/Xray-core/commit/bfe4820f2f086daf639b1957eb23dc13c843cad1
package encryption

View File

@@ -4,6 +4,7 @@ import (
"bytes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
"errors"
"fmt"
"io"
@@ -23,12 +24,11 @@ type ServerSession struct {
type ServerInstance struct {
sync.RWMutex
nfsDKey *mlkem.DecapsulationKey768
nfsEKeyBytes []byte
xor uint32
minutes time.Duration
sessions map[[21]byte]*ServerSession
closed bool
nfsDKey *mlkem.DecapsulationKey768
xorKey []byte
minutes time.Duration
sessions map[[21]byte]*ServerSession
closed bool
}
type ServerConn struct {
@@ -45,10 +45,17 @@ type ServerConn struct {
}
func (i *ServerInstance) Init(nfsDKeySeed []byte, xor uint32, minutes time.Duration) (err error) {
if i.nfsDKey != nil {
err = errors.New("already initialized")
return
}
i.nfsDKey, err = mlkem.NewDecapsulationKey768(nfsDKeySeed)
if err != nil {
return
}
if xor > 0 {
i.nfsEKeyBytes = i.nfsDKey.EncapsulationKey().Bytes()
i.xor = xor
xorKey := sha256.Sum256(i.nfsDKey.EncapsulationKey().Bytes())
i.xorKey = xorKey[:]
}
if minutes > 0 {
i.minutes = minutes
@@ -85,18 +92,15 @@ func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) {
if i.nfsDKey == nil {
return nil, errors.New("uninitialized")
}
if i.xor > 0 {
conn = NewXorConn(conn, i.nfsEKeyBytes)
if i.xorKey != nil {
conn = NewXorConn(conn, i.xorKey)
}
c := &ServerConn{Conn: conn}
_, t, l, err := ReadAndDecodeHeader(c.Conn)
_, t, l, err := ReadAndDiscardPaddings(c.Conn) // allow paddings before client/ticket hello
if err != nil {
return nil, err
}
if t == 23 {
return nil, errors.New("unexpected data")
}
if t == 0 {
if i.minutes == 0 {
@@ -113,9 +117,13 @@ func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) {
s := i.sessions[[21]byte(peerTicketHello)]
i.RUnlock()
if s == nil {
noise := make([]byte, randBetween(100, 1000))
rand.Read(noise)
c.Conn.Write(noise) // make client do new handshake
noises := make([]byte, randBetween(100, 1000))
var err error
for err == nil {
rand.Read(noises)
_, _, err = DecodeHeader(noises)
}
c.Conn.Write(noises) // make client do new handshake
return nil, errors.New("expired ticket")
}
if _, replay := s.randoms.LoadOrStore([32]byte(peerTicketHello[21:]), true); replay {
@@ -165,7 +173,7 @@ func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) {
if _, err := c.Conn.Write(serverHello); err != nil {
return nil, err
}
// server can send more padding / PFS AEAD messages if needed
// server can send more paddings / PFS AEAD messages if needed
if i.minutes > 0 {
i.Lock()
@@ -185,20 +193,10 @@ func (c *ServerConn) Read(b []byte) (int, error) {
return 0, nil
}
if c.peerAead == nil {
if c.peerRandom == nil { // from 1-RTT
var t byte
var l int
var err error
for {
if _, t, l, err = ReadAndDecodeHeader(c.Conn); err != nil {
return 0, err
}
if t != 23 {
break
}
if _, err := io.ReadFull(c.Conn, make([]byte, l)); err != nil {
return 0, err
}
if c.peerRandom == nil { // 1-RTT's 0-RTT
_, t, l, err := ReadAndDiscardPaddings(c.Conn) // allow paddings before ticket hello
if err != nil {
return 0, err
}
if t != 0 {
return 0, fmt.Errorf("unexpected type %v, expect ticket hello", t)

View File

@@ -18,11 +18,11 @@ type XorConn struct {
}
func NewXorConn(conn net.Conn, key []byte) *XorConn {
return &XorConn{Conn: conn, key: key[:16]}
return &XorConn{Conn: conn, key: key}
//chacha20.NewUnauthenticatedCipher()
}
func (c *XorConn) Write(b []byte) (int, error) { // two records at most
func (c *XorConn) Write(b []byte) (int, error) { // whole one/two records
if len(b) == 0 {
return 0, nil
}
@@ -34,10 +34,10 @@ func (c *XorConn) Write(b []byte) (int, error) { // two records at most
c.ctr = cipher.NewCTR(block, iv)
}
t, l, _ := DecodeHeader(b)
if t != 23 {
l += 10 // 5+l+5
} else {
if t == 23 { // single 23
l = 5
} else { // 1/0 + 23, or noises only
l += 10
}
c.ctr.XORKeyStream(b[:l], b[:l]) // caller MUST discard b
if iv != nil {
@@ -73,8 +73,8 @@ func (c *XorConn) Read(b []byte) (int, error) { // 5-bytes, data, 5-bytes...
return len(b), nil
}
c.peerCtr.XORKeyStream(b, b)
if c.isHeader {
if t, _, _ := DecodeHeader(b); t == 23 { // always 5-bytes
if c.isHeader { // always 5-bytes
if t, _, _ := DecodeHeader(b); t == 23 {
c.skipNext = true
} else {
c.isHeader = false

View File

@@ -3,7 +3,6 @@ package vision
import (
"bytes"
"crypto/subtle"
gotls "crypto/tls"
"encoding/binary"
"errors"
"fmt"
@@ -12,7 +11,6 @@ import (
"github.com/metacubex/mihomo/common/buf"
N "github.com/metacubex/mihomo/common/net"
tlsC "github.com/metacubex/mihomo/component/tls"
"github.com/metacubex/mihomo/log"
"github.com/gofrs/uuid/v5"
@@ -181,17 +179,10 @@ func (vc *Conn) WriteBuffer(buffer *buf.Buffer) (err error) {
buffer.Release()
return err
}
switch underlying := vc.tlsConn.(type) {
case *gotls.Conn:
if underlying.ConnectionState().Version != gotls.VersionTLS13 {
buffer.Release()
return ErrNotTLS13
}
case *tlsC.UConn:
if underlying.ConnectionState().Version != tlsC.VersionTLS13 {
buffer.Release()
return ErrNotTLS13
}
err = vc.checkTLSVersion()
if err != nil {
buffer.Release()
return err
}
vc.tlsConn = nil
return nil

View File

@@ -67,3 +67,21 @@ func NewConn(conn connWithUpstream, userUUID *uuid.UUID) (*Conn, error) {
c.rawInput = (*bytes.Buffer)(unsafe.Add(p, r.Offset))
return c, nil
}
func (vc *Conn) checkTLSVersion() error {
switch underlying := vc.tlsConn.(type) {
case *gotls.Conn:
if underlying.ConnectionState().Version != gotls.VersionTLS13 {
return ErrNotTLS13
}
case *tlsC.Conn:
if underlying.ConnectionState().Version != tlsC.VersionTLS13 {
return ErrNotTLS13
}
case *tlsC.UConn:
if underlying.ConnectionState().Version != tlsC.VersionTLS13 {
return ErrNotTLS13
}
}
return nil
}