mirror of
https://github.com/bolucat/Archive.git
synced 2025-09-27 12:33:06 +08:00
Update On Sun Aug 31 20:34:09 CEST 2025
This commit is contained in:
1
.github/update.log
vendored
1
.github/update.log
vendored
@@ -1106,3 +1106,4 @@ Update On Wed Aug 27 20:38:26 CEST 2025
|
||||
Update On Thu Aug 28 20:40:03 CEST 2025
|
||||
Update On Fri Aug 29 20:33:46 CEST 2025
|
||||
Update On Sat Aug 30 20:36:28 CEST 2025
|
||||
Update On Sun Aug 31 20:34:00 CEST 2025
|
||||
|
@@ -610,7 +610,6 @@ proxies: # socks5
|
||||
uuid: uuid
|
||||
network: tcp
|
||||
servername: example.com # AKA SNI
|
||||
# flow: xtls-rprx-direct # xtls-rprx-origin # enable XTLS
|
||||
# skip-cert-verify: true
|
||||
# fingerprint: xxxx
|
||||
# client-fingerprint: random # Available: "chrome","firefox","safari","random","none"
|
||||
@@ -640,10 +639,16 @@ proxies: # socks5
|
||||
network: tcp
|
||||
# -------------------------
|
||||
# vless encryption客户端配置:
|
||||
# (native/xorpub 的 XTLS 可以 Splice。只使用 1-RTT 模式 / 若服务端发的 ticket 中秒数不为零则 0-RTT 复用)
|
||||
# (native/xorpub 的 XTLS Vision 可以 Splice。只使用 1-RTT 模式 / 若服务端发的 ticket 中秒数不为零则 0-RTT 复用)
|
||||
# / 是只能选一个,后面 base64 至少一个,无限串联,使用 mihomo generate vless-x25519 和 mihomo generate vless-mlkem768 生成,替换值时需去掉括号
|
||||
#
|
||||
# Padding 是可选的参数,仅作用于 1-RTT 以消除握手的长度特征,双端默认值均为 "100-111-1111.75-0-111.50-0-3333":
|
||||
# 在 1-RTT client/server hello 后以 100% 的概率粘上随机 111 到 1111 字节的 padding
|
||||
# 以 75% 的概率等待随机 0 到 111 毫秒("probability-from-to")
|
||||
# 再次以 50% 的概率发送随机 0 到 3333 字节的 padding(若为 0 则不 Write())
|
||||
# 服务端、客户端可以设置不同的 padding 参数,按 len、gap 的顺序无限串联,第一个 padding 需概率 100%、至少 35 字节
|
||||
# -------------------------
|
||||
encryption: "mlkem768x25519plus.native/xorpub/random.1rtt/0rtt.(X25519 Password).(ML-KEM-768 Client)..."
|
||||
encryption: "mlkem768x25519plus.native/xorpub/random.1rtt/0rtt.(padding len).(padding gap).(X25519 Password).(ML-KEM-768 Client)..."
|
||||
tls: false #可以不开启tls
|
||||
udp: true
|
||||
|
||||
@@ -1367,8 +1372,14 @@ listeners:
|
||||
# vless encryption服务端配置:
|
||||
# (原生外观 / 只 XOR 公钥 / 全随机数。只允许 1-RTT 模式 / 同时允许 1-RTT 模式与 600 秒复用的 0-RTT 模式)
|
||||
# / 是只能选一个,后面 base64 至少一个,无限串联,使用 mihomo generate vless-x25519 和 mihomo generate vless-mlkem768 生成,替换值时需去掉括号
|
||||
#
|
||||
# Padding 是可选的参数,仅作用于 1-RTT 以消除握手的长度特征,双端默认值均为 "100-111-1111.75-0-111.50-0-3333":
|
||||
# 在 1-RTT client/server hello 后以 100% 的概率粘上随机 111 到 1111 字节的 padding
|
||||
# 以 75% 的概率等待随机 0 到 111 毫秒("probability-from-to")
|
||||
# 再次以 50% 的概率发送随机 0 到 3333 字节的 padding(若为 0 则不 Write())
|
||||
# 服务端、客户端可以设置不同的 padding 参数,按 len、gap 的顺序无限串联,第一个 padding 需概率 100%、至少 35 字节
|
||||
# -------------------------
|
||||
# decryption: "mlkem768x25519plus.native/xorpub/random.1rtt/600s.(X25519 PrivateKey).(ML-KEM-768 Seed)..."
|
||||
# decryption: "mlkem768x25519plus.native/xorpub/random.1rtt/600s.(padding len).(padding gap).(X25519 PrivateKey).(ML-KEM-768 Seed)..."
|
||||
# 下面两项如果填写则开启 tls(需要同时填写)
|
||||
# certificate: ./server.crt
|
||||
# private-key: ./server.key
|
||||
|
@@ -99,6 +99,15 @@ func TestInboundVless_Encryption(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
paddings := []struct {
|
||||
name string
|
||||
data string
|
||||
}{
|
||||
{"unconfigured-padding", ""},
|
||||
{"default-padding", "100-111-1111.75-0-111.50-0-3333."},
|
||||
{"old-padding", "100-100-1000."}, // Xray-core v25.8.29
|
||||
{"custom-padding", "100-1234-7890.33-0-1111.66-0-6666.55-111-777."},
|
||||
}
|
||||
var modes = []string{
|
||||
"native",
|
||||
"xorpub",
|
||||
@@ -107,19 +116,26 @@ func TestInboundVless_Encryption(t *testing.T) {
|
||||
for i := range modes {
|
||||
mode := modes[i]
|
||||
t.Run(mode, func(t *testing.T) {
|
||||
inboundOptions := inbound.VlessOption{
|
||||
Decryption: "mlkem768x25519plus." + mode + ".600s." + privateKeyBase64 + "." + seedBase64,
|
||||
t.Parallel()
|
||||
for i := range paddings {
|
||||
padding := paddings[i].data
|
||||
t.Run(paddings[i].name, func(t *testing.T) {
|
||||
inboundOptions := inbound.VlessOption{
|
||||
Decryption: "mlkem768x25519plus." + mode + ".600s." + padding + privateKeyBase64 + "." + seedBase64,
|
||||
}
|
||||
outboundOptions := outbound.VlessOption{
|
||||
Encryption: "mlkem768x25519plus." + mode + ".0rtt." + padding + passwordBase64 + "." + clientBase64,
|
||||
}
|
||||
testInboundVless(t, inboundOptions, outboundOptions)
|
||||
t.Run("xtls-rprx-vision", func(t *testing.T) {
|
||||
outboundOptions := outboundOptions
|
||||
outboundOptions.Flow = "xtls-rprx-vision"
|
||||
testInboundVless(t, inboundOptions, outboundOptions)
|
||||
})
|
||||
})
|
||||
}
|
||||
outboundOptions := outbound.VlessOption{
|
||||
Encryption: "mlkem768x25519plus." + mode + ".0rtt." + passwordBase64 + "." + clientBase64,
|
||||
}
|
||||
testInboundVless(t, inboundOptions, outboundOptions)
|
||||
t.Run("xtls-rprx-vision", func(t *testing.T) {
|
||||
outboundOptions := outboundOptions
|
||||
outboundOptions.Flow = "xtls-rprx-vision"
|
||||
testInboundVless(t, inboundOptions, outboundOptions)
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -230,7 +230,7 @@ func (l *Listener) HandleConn(conn net.Conn, tunnel C.Tunnel, additions ...inbou
|
||||
ctx := sing.WithAdditions(context.TODO(), additions...)
|
||||
if l.decryption != nil {
|
||||
var err error
|
||||
conn, err = l.decryption.Handshake(conn)
|
||||
conn, err = l.decryption.Handshake(conn, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@@ -87,7 +87,11 @@ func (s *serverHandler) handle() {
|
||||
_ = s.handleMessage()
|
||||
}()
|
||||
|
||||
<-s.quicConn.HandshakeComplete()
|
||||
select {
|
||||
case <-s.quicConn.HandshakeComplete(): // this chan maybe not closed if handshake never complete
|
||||
case <-time.After(s.quicConn.Config().HandshakeIdleTimeout): // HandshakeIdleTimeout in real conn.Config() never be zero
|
||||
}
|
||||
|
||||
time.AfterFunc(s.AuthenticationTimeout, func() {
|
||||
if s.v4Handler != nil {
|
||||
if s.v4Handler.AuthOk() {
|
||||
|
@@ -7,11 +7,23 @@ import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/blake3"
|
||||
"github.com/metacubex/utls/mlkem"
|
||||
"golang.org/x/sys/cpu"
|
||||
)
|
||||
|
||||
var (
|
||||
// Keep in sync with crypto/tls/cipher_suites.go.
|
||||
hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ && cpu.X86.HasSSE41 && cpu.X86.HasSSSE3
|
||||
hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL
|
||||
hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCTR && cpu.S390X.HasGHASH
|
||||
hasGCMAsmPPC64 = runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le"
|
||||
|
||||
HasAESGCMHardwareSupport = hasGCMAsmAMD64 || hasGCMAsmARM64 || hasGCMAsmS390X || hasGCMAsmPPC64
|
||||
)
|
||||
|
||||
type ClientInstance struct {
|
||||
@@ -21,6 +33,8 @@ type ClientInstance struct {
|
||||
RelaysLength int
|
||||
XorMode uint32
|
||||
Seconds uint32
|
||||
PaddingLens [][3]int
|
||||
PaddingGaps [][3]int
|
||||
|
||||
RWLock sync.RWMutex
|
||||
Expire time.Time
|
||||
@@ -28,15 +42,13 @@ type ClientInstance struct {
|
||||
Ticket []byte
|
||||
}
|
||||
|
||||
func (i *ClientInstance) Init(nfsPKeysBytes [][]byte, xorMode, seconds uint32) (err error) {
|
||||
func (i *ClientInstance) Init(nfsPKeysBytes [][]byte, xorMode, seconds uint32, padding string) (err error) {
|
||||
if i.NfsPKeys != nil {
|
||||
err = errors.New("already initialized")
|
||||
return
|
||||
return errors.New("already initialized")
|
||||
}
|
||||
l := len(nfsPKeysBytes)
|
||||
if l == 0 {
|
||||
err = errors.New("empty nfsPKeysBytes")
|
||||
return
|
||||
return errors.New("empty nfsPKeysBytes")
|
||||
}
|
||||
i.NfsPKeys = make([]any, l)
|
||||
i.NfsPKeysBytes = nfsPKeysBytes
|
||||
@@ -58,18 +70,18 @@ func (i *ClientInstance) Init(nfsPKeysBytes [][]byte, xorMode, seconds uint32) (
|
||||
i.RelaysLength -= 32
|
||||
i.XorMode = xorMode
|
||||
i.Seconds = seconds
|
||||
return
|
||||
return ParsePadding(padding, &i.PaddingLens, &i.PaddingGaps)
|
||||
}
|
||||
|
||||
func (i *ClientInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||
if i.NfsPKeys == nil {
|
||||
return nil, errors.New("uninitialized")
|
||||
}
|
||||
c := NewCommonConn(conn)
|
||||
c := NewCommonConn(conn, HasAESGCMHardwareSupport)
|
||||
|
||||
ivAndRealysLength := 16 + i.RelaysLength
|
||||
pfsKeyExchangeLength := 18 + 1184 + 32 + 16
|
||||
paddingLength := int(randBetween(100, 1000))
|
||||
paddingLength, paddingLens, paddingGaps := CreatPadding(i.PaddingLens, i.PaddingGaps)
|
||||
clientHello := make([]byte, ivAndRealysLength+pfsKeyExchangeLength+paddingLength)
|
||||
|
||||
iv := clientHello[:16]
|
||||
@@ -107,18 +119,18 @@ func (i *ClientInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||
lastCTR.XORKeyStream(relays[index:], i.Hash32s[j+1][:])
|
||||
relays = relays[index+32:]
|
||||
}
|
||||
nfsGCM := NewGCM(iv, nfsKey)
|
||||
nfsAEAD := NewAEAD(iv, nfsKey, c.UseAES)
|
||||
|
||||
if i.Seconds > 0 {
|
||||
i.RWLock.RLock()
|
||||
if time.Now().Before(i.Expire) {
|
||||
c.Client = i
|
||||
c.UnitedKey = append(i.PfsKey, nfsKey...) // different unitedKey for each connection
|
||||
nfsGCM.Seal(clientHello[:ivAndRealysLength], nil, EncodeLength(32), nil)
|
||||
nfsGCM.Seal(clientHello[:ivAndRealysLength+18], nil, i.Ticket, nil)
|
||||
nfsAEAD.Seal(clientHello[:ivAndRealysLength], nil, EncodeLength(32), nil)
|
||||
nfsAEAD.Seal(clientHello[:ivAndRealysLength+18], nil, i.Ticket, nil)
|
||||
i.RWLock.RUnlock()
|
||||
c.PreWrite = clientHello[:ivAndRealysLength+18+32]
|
||||
c.GCM = NewGCM(clientHello[ivAndRealysLength+18:ivAndRealysLength+18+32], c.UnitedKey)
|
||||
c.AEAD = NewAEAD(clientHello[ivAndRealysLength+18:ivAndRealysLength+18+32], c.UnitedKey, c.UseAES)
|
||||
if i.XorMode == 2 {
|
||||
c.Conn = NewXorConn(conn, NewCTR(c.UnitedKey, iv), nil, len(c.PreWrite), 16)
|
||||
}
|
||||
@@ -128,26 +140,34 @@ func (i *ClientInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||
}
|
||||
|
||||
pfsKeyExchange := clientHello[ivAndRealysLength : ivAndRealysLength+pfsKeyExchangeLength]
|
||||
nfsGCM.Seal(pfsKeyExchange[:0], nil, EncodeLength(pfsKeyExchangeLength-18), nil)
|
||||
nfsAEAD.Seal(pfsKeyExchange[:0], nil, EncodeLength(pfsKeyExchangeLength-18), nil)
|
||||
mlkem768DKey, _ := mlkem.GenerateKey768()
|
||||
x25519SKey, _ := ecdh.X25519().GenerateKey(rand.Reader)
|
||||
pfsPublicKey := append(mlkem768DKey.EncapsulationKey().Bytes(), x25519SKey.PublicKey().Bytes()...)
|
||||
nfsGCM.Seal(pfsKeyExchange[:18], nil, pfsPublicKey, nil)
|
||||
nfsAEAD.Seal(pfsKeyExchange[:18], nil, pfsPublicKey, nil)
|
||||
|
||||
padding := clientHello[ivAndRealysLength+pfsKeyExchangeLength:]
|
||||
nfsGCM.Seal(padding[:0], nil, EncodeLength(paddingLength-18), nil)
|
||||
nfsGCM.Seal(padding[:18], nil, padding[18:paddingLength-16], nil)
|
||||
nfsAEAD.Seal(padding[:0], nil, EncodeLength(paddingLength-18), nil)
|
||||
nfsAEAD.Seal(padding[:18], nil, padding[18:paddingLength-16], nil)
|
||||
|
||||
if _, err := conn.Write(clientHello); err != nil {
|
||||
return nil, err
|
||||
paddingLens[0] = ivAndRealysLength + pfsKeyExchangeLength + paddingLens[0]
|
||||
for i, l := range paddingLens { // sends padding in a fragmented way, to create variable traffic pattern, before inner VLESS flow takes control
|
||||
if l > 0 {
|
||||
if _, err := conn.Write(clientHello[:l]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clientHello = clientHello[l:]
|
||||
}
|
||||
if len(paddingGaps) > i {
|
||||
time.Sleep(paddingGaps[i])
|
||||
}
|
||||
}
|
||||
// padding can be sent in a fragmented way, to create variable traffic pattern, before inner VLESS flow takes control
|
||||
|
||||
encryptedPfsPublicKey := make([]byte, 1088+32+16)
|
||||
if _, err := io.ReadFull(conn, encryptedPfsPublicKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nfsGCM.Open(encryptedPfsPublicKey[:0], MaxNonce, encryptedPfsPublicKey, nil)
|
||||
nfsAEAD.Open(encryptedPfsPublicKey[:0], MaxNonce, encryptedPfsPublicKey, nil)
|
||||
mlkem768Key, err := mlkem768DKey.Decapsulate(encryptedPfsPublicKey[:1088])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -164,14 +184,14 @@ func (i *ClientInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||
copy(pfsKey, mlkem768Key)
|
||||
copy(pfsKey[32:], x25519Key)
|
||||
c.UnitedKey = append(pfsKey, nfsKey...)
|
||||
c.GCM = NewGCM(pfsPublicKey, c.UnitedKey)
|
||||
c.PeerGCM = NewGCM(encryptedPfsPublicKey[:1088+32], c.UnitedKey)
|
||||
c.AEAD = NewAEAD(pfsPublicKey, c.UnitedKey, c.UseAES)
|
||||
c.PeerAEAD = NewAEAD(encryptedPfsPublicKey[:1088+32], c.UnitedKey, c.UseAES)
|
||||
|
||||
encryptedTicket := make([]byte, 32)
|
||||
if _, err := io.ReadFull(conn, encryptedTicket); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := c.PeerGCM.Open(encryptedTicket[:0], nil, encryptedTicket, nil); err != nil {
|
||||
if _, err := c.PeerAEAD.Open(encryptedTicket[:0], nil, encryptedTicket, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
seconds := DecodeLength(encryptedTicket)
|
||||
@@ -188,7 +208,7 @@ func (i *ClientInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||
if _, err := io.ReadFull(conn, encryptedLength); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := c.PeerGCM.Open(encryptedLength[:0], nil, encryptedLength, nil); err != nil {
|
||||
if _, err := c.PeerAEAD.Open(encryptedLength[:0], nil, encryptedLength, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
length := DecodeLength(encryptedLength[:2])
|
||||
|
@@ -8,29 +8,34 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/mihomo/common/pool"
|
||||
|
||||
"github.com/metacubex/blake3"
|
||||
"github.com/metacubex/randv2"
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
)
|
||||
|
||||
type CommonConn struct {
|
||||
net.Conn
|
||||
UseAES bool
|
||||
Client *ClientInstance
|
||||
UnitedKey []byte
|
||||
PreWrite []byte
|
||||
GCM *GCM
|
||||
AEAD *AEAD
|
||||
PeerAEAD *AEAD
|
||||
PeerPadding []byte
|
||||
rawInput bytes.Buffer // PeerInBytes
|
||||
PeerGCM *GCM
|
||||
input bytes.Reader // PeerCache
|
||||
}
|
||||
|
||||
func NewCommonConn(conn net.Conn) *CommonConn {
|
||||
func NewCommonConn(conn net.Conn, useAES bool) *CommonConn {
|
||||
return &CommonConn{
|
||||
Conn: conn,
|
||||
Conn: conn,
|
||||
UseAES: useAES,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,12 +54,12 @@ func (c *CommonConn) Write(b []byte) (int, error) {
|
||||
headerAndData := outBytes[:5+len(b)+16]
|
||||
EncodeHeader(headerAndData, len(b)+16)
|
||||
max := false
|
||||
if bytes.Equal(c.GCM.Nonce[:], MaxNonce) {
|
||||
if bytes.Equal(c.AEAD.Nonce[:], MaxNonce) {
|
||||
max = true
|
||||
}
|
||||
c.GCM.Seal(headerAndData[:5], nil, b, headerAndData[:5])
|
||||
c.AEAD.Seal(headerAndData[:5], nil, b, headerAndData[:5])
|
||||
if max {
|
||||
c.GCM = NewGCM(headerAndData, c.UnitedKey)
|
||||
c.AEAD = NewAEAD(headerAndData, c.UnitedKey, c.UseAES)
|
||||
}
|
||||
if c.PreWrite != nil {
|
||||
headerAndData = append(c.PreWrite, headerAndData...)
|
||||
@@ -71,12 +76,12 @@ func (c *CommonConn) Read(b []byte) (int, error) {
|
||||
if len(b) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
if c.PeerGCM == nil { // client's 0-RTT
|
||||
if c.PeerAEAD == nil { // client's 0-RTT
|
||||
serverRandom := make([]byte, 16)
|
||||
if _, err := io.ReadFull(c.Conn, serverRandom); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
c.PeerGCM = NewGCM(serverRandom, c.UnitedKey)
|
||||
c.PeerAEAD = NewAEAD(serverRandom, c.UnitedKey, c.UseAES)
|
||||
if xorConn, ok := c.Conn.(*XorConn); ok {
|
||||
xorConn.PeerCTR = NewCTR(c.UnitedKey, serverRandom)
|
||||
}
|
||||
@@ -85,7 +90,7 @@ func (c *CommonConn) Read(b []byte) (int, error) {
|
||||
if _, err := io.ReadFull(c.Conn, c.PeerPadding); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if _, err := c.PeerGCM.Open(c.PeerPadding[:0], nil, c.PeerPadding, nil); err != nil {
|
||||
if _, err := c.PeerAEAD.Open(c.PeerPadding[:0], nil, c.PeerPadding, nil); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
c.PeerPadding = nil
|
||||
@@ -93,11 +98,11 @@ func (c *CommonConn) Read(b []byte) (int, error) {
|
||||
if c.input.Len() > 0 {
|
||||
return c.input.Read(b)
|
||||
}
|
||||
peerHeader := make([]byte, 5)
|
||||
if _, err := io.ReadFull(c.Conn, peerHeader); err != nil {
|
||||
peerHeader := [5]byte{}
|
||||
if _, err := io.ReadFull(c.Conn, peerHeader[:]); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
l, err := DecodeHeader(peerHeader) // l: 17~17000
|
||||
l, err := DecodeHeader(peerHeader[:]) // l: 17~17000
|
||||
if err != nil {
|
||||
if c.Client != nil && errors.Is(err, ErrInvalidHeader) { // client's 0-RTT
|
||||
c.Client.RWLock.Lock()
|
||||
@@ -110,7 +115,9 @@ func (c *CommonConn) Read(b []byte) (int, error) {
|
||||
return 0, err
|
||||
}
|
||||
c.Client = nil
|
||||
c.rawInput.Grow(l)
|
||||
if c.rawInput.Cap() < l {
|
||||
c.rawInput.Grow(l) // no need to use sync.Pool, because we are always reading
|
||||
}
|
||||
peerData := c.rawInput.Bytes()[:l]
|
||||
if _, err := io.ReadFull(c.Conn, peerData); err != nil {
|
||||
return 0, err
|
||||
@@ -119,13 +126,13 @@ func (c *CommonConn) Read(b []byte) (int, error) {
|
||||
if len(dst) <= len(b) {
|
||||
dst = b[:len(dst)] // avoids another copy()
|
||||
}
|
||||
var newGCM *GCM
|
||||
if bytes.Equal(c.PeerGCM.Nonce[:], MaxNonce) {
|
||||
newGCM = NewGCM(append(peerHeader, peerData...), c.UnitedKey)
|
||||
var newAEAD *AEAD
|
||||
if bytes.Equal(c.PeerAEAD.Nonce[:], MaxNonce) {
|
||||
newAEAD = NewAEAD(append(peerHeader[:], peerData...), c.UnitedKey, c.UseAES)
|
||||
}
|
||||
_, err = c.PeerGCM.Open(dst[:0], nil, peerData, peerHeader)
|
||||
if newGCM != nil {
|
||||
c.PeerGCM = newGCM
|
||||
_, err = c.PeerAEAD.Open(dst[:0], nil, peerData, peerHeader[:])
|
||||
if newAEAD != nil {
|
||||
c.PeerAEAD = newAEAD
|
||||
}
|
||||
if err != nil {
|
||||
return 0, err
|
||||
@@ -137,28 +144,32 @@ func (c *CommonConn) Read(b []byte) (int, error) {
|
||||
return len(dst), nil
|
||||
}
|
||||
|
||||
type GCM struct {
|
||||
type AEAD struct {
|
||||
cipher.AEAD
|
||||
Nonce [12]byte
|
||||
}
|
||||
|
||||
func NewGCM(ctx, key []byte) *GCM {
|
||||
func NewAEAD(ctx, key []byte, useAES bool) *AEAD {
|
||||
k := make([]byte, 32)
|
||||
blake3.DeriveKey(k, string(ctx), key)
|
||||
block, _ := aes.NewCipher(k)
|
||||
aead, _ := cipher.NewGCM(block)
|
||||
return &GCM{AEAD: aead}
|
||||
//chacha20poly1305.New()
|
||||
var aead cipher.AEAD
|
||||
if useAES {
|
||||
block, _ := aes.NewCipher(k)
|
||||
aead, _ = cipher.NewGCM(block)
|
||||
} else {
|
||||
aead, _ = chacha20poly1305.New(k)
|
||||
}
|
||||
return &AEAD{AEAD: aead}
|
||||
}
|
||||
|
||||
func (a *GCM) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
|
||||
func (a *AEAD) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
|
||||
if nonce == nil {
|
||||
nonce = IncreaseNonce(a.Nonce[:])
|
||||
}
|
||||
return a.AEAD.Seal(dst, nonce, plaintext, additionalData)
|
||||
}
|
||||
|
||||
func (a *GCM) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
|
||||
func (a *AEAD) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
|
||||
if nonce == nil {
|
||||
nonce = IncreaseNonce(a.Nonce[:])
|
||||
}
|
||||
@@ -206,9 +217,80 @@ func DecodeHeader(h []byte) (l int, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func ParsePadding(padding string, paddingLens, paddingGaps *[][3]int) (err error) {
|
||||
if padding == "" {
|
||||
return
|
||||
}
|
||||
maxLen := 0
|
||||
for i, s := range strings.Split(padding, ".") {
|
||||
x := strings.Split(s, "-")
|
||||
if len(x) < 3 || x[0] == "" || x[1] == "" || x[2] == "" {
|
||||
return errors.New("invalid padding lenth/gap parameter: " + s)
|
||||
}
|
||||
y := [3]int{}
|
||||
if y[0], err = strconv.Atoi(x[0]); err != nil {
|
||||
return
|
||||
}
|
||||
if y[1], err = strconv.Atoi(x[1]); err != nil {
|
||||
return
|
||||
}
|
||||
if y[2], err = strconv.Atoi(x[2]); err != nil {
|
||||
return
|
||||
}
|
||||
if i == 0 && (y[0] < 100 || y[1] < 18+17 || y[2] < 18+17) {
|
||||
return errors.New("first padding length must not be smaller than 35")
|
||||
}
|
||||
if i%2 == 0 {
|
||||
*paddingLens = append(*paddingLens, y)
|
||||
maxLen += max(y[1], y[2])
|
||||
} else {
|
||||
*paddingGaps = append(*paddingGaps, y)
|
||||
}
|
||||
}
|
||||
if maxLen > 18+65535 {
|
||||
return errors.New("total padding length must not be larger than 65553")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func CreatPadding(paddingLens, paddingGaps [][3]int) (length int, lens []int, gaps []time.Duration) {
|
||||
if len(paddingLens) == 0 {
|
||||
paddingLens = [][3]int{{100, 111, 1111}, {50, 0, 3333}}
|
||||
paddingGaps = [][3]int{{75, 0, 111}}
|
||||
}
|
||||
for _, y := range paddingLens {
|
||||
l := 0
|
||||
if y[0] >= int(randBetween(0, 100)) {
|
||||
l = int(randBetween(int64(y[1]), int64(y[2])))
|
||||
}
|
||||
lens = append(lens, l)
|
||||
length += l
|
||||
}
|
||||
for _, y := range paddingGaps {
|
||||
g := 0
|
||||
if y[0] >= int(randBetween(0, 100)) {
|
||||
g = int(randBetween(int64(y[1]), int64(y[2])))
|
||||
}
|
||||
gaps = append(gaps, time.Duration(g)*time.Millisecond)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func max[T ~int | ~uint | ~int64 | ~uint64 | ~int32 | ~uint32 | ~int16 | ~uint16 | ~int8 | ~uint8](a, b T) T {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func randBetween(from int64, to int64) int64 {
|
||||
if from == to {
|
||||
return from
|
||||
}
|
||||
|
||||
if to < from {
|
||||
from, to = to, from
|
||||
}
|
||||
|
||||
return from + randv2.Int64N(to-from)
|
||||
}
|
||||
|
@@ -21,4 +21,7 @@
|
||||
// https://github.com/XTLS/Xray-core/commit/0199dea39988a1a1b846d0bf8598631bade40902
|
||||
// https://github.com/XTLS/Xray-core/commit/fce1195b60f48ca18a953dbd5c7d991869de9a5e
|
||||
// https://github.com/XTLS/Xray-core/commit/b0b220985c9c1bc832665458d5fd6e0c287b67ae
|
||||
// https://github.com/XTLS/Xray-core/commit/82ea7a3cc5ff23280b87e3052f0f83b04f0267fa
|
||||
// https://github.com/XTLS/Xray-core/commit/e8b02cd6649f14889841e8ab8ee6b2acca71dbe6
|
||||
// https://github.com/XTLS/Xray-core/commit/6768a22f676c9121cfc9dc4f51181a8a07837c8d
|
||||
package encryption
|
||||
|
@@ -34,7 +34,12 @@ func NewClient(encryption string) (*ClientInstance, error) {
|
||||
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
|
||||
}
|
||||
var nfsPKeysBytes [][]byte
|
||||
var paddings []string
|
||||
for _, r := range s[3:] {
|
||||
if len(r) < 20 {
|
||||
paddings = append(paddings, r)
|
||||
continue
|
||||
}
|
||||
b, err := base64.RawURLEncoding.DecodeString(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
|
||||
@@ -44,8 +49,9 @@ func NewClient(encryption string) (*ClientInstance, error) {
|
||||
}
|
||||
nfsPKeysBytes = append(nfsPKeysBytes, b)
|
||||
}
|
||||
padding := strings.Join(paddings, ".")
|
||||
client := &ClientInstance{}
|
||||
if err := client.Init(nfsPKeysBytes, xorMode, seconds); err != nil {
|
||||
if err := client.Init(nfsPKeysBytes, xorMode, seconds, padding); err != nil {
|
||||
return nil, fmt.Errorf("failed to use encryption: %w", err)
|
||||
}
|
||||
return client, nil
|
||||
@@ -84,7 +90,12 @@ func NewServer(decryption string) (*ServerInstance, error) {
|
||||
seconds = uint32(i)
|
||||
}
|
||||
var nfsSKeysBytes [][]byte
|
||||
var paddings []string
|
||||
for _, r := range s[3:] {
|
||||
if len(r) < 20 {
|
||||
paddings = append(paddings, r)
|
||||
continue
|
||||
}
|
||||
b, err := base64.RawURLEncoding.DecodeString(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
|
||||
@@ -94,8 +105,9 @@ func NewServer(decryption string) (*ServerInstance, error) {
|
||||
}
|
||||
nfsSKeysBytes = append(nfsSKeysBytes, b)
|
||||
}
|
||||
padding := strings.Join(paddings, ".")
|
||||
server := &ServerInstance{}
|
||||
if err := server.Init(nfsSKeysBytes, xorMode, seconds); err != nil {
|
||||
if err := server.Init(nfsSKeysBytes, xorMode, seconds, padding); err != nil {
|
||||
return nil, fmt.Errorf("failed to use decryption: %w", err)
|
||||
}
|
||||
return server, nil
|
||||
|
@@ -29,21 +29,21 @@ type ServerInstance struct {
|
||||
RelaysLength int
|
||||
XorMode uint32
|
||||
Seconds uint32
|
||||
PaddingLens [][3]int
|
||||
PaddingGaps [][3]int
|
||||
|
||||
RWLock sync.RWMutex
|
||||
Sessions map[[16]byte]*ServerSession
|
||||
Closed bool
|
||||
}
|
||||
|
||||
func (i *ServerInstance) Init(nfsSKeysBytes [][]byte, xorMode, seconds uint32) (err error) {
|
||||
func (i *ServerInstance) Init(nfsSKeysBytes [][]byte, xorMode, seconds uint32, padding string) (err error) {
|
||||
if i.NfsSKeys != nil {
|
||||
err = errors.New("already initialized")
|
||||
return
|
||||
return errors.New("already initialized")
|
||||
}
|
||||
l := len(nfsSKeysBytes)
|
||||
if l == 0 {
|
||||
err = errors.New("empty nfsSKeysBytes")
|
||||
return
|
||||
return errors.New("empty nfsSKeysBytes")
|
||||
}
|
||||
i.NfsSKeys = make([]any, l)
|
||||
i.NfsPKeysBytes = make([][]byte, l)
|
||||
@@ -87,7 +87,7 @@ func (i *ServerInstance) Init(nfsSKeysBytes [][]byte, xorMode, seconds uint32) (
|
||||
}
|
||||
}()
|
||||
}
|
||||
return
|
||||
return ParsePadding(padding, &i.PaddingLens, &i.PaddingGaps)
|
||||
}
|
||||
|
||||
func (i *ServerInstance) Close() (err error) {
|
||||
@@ -97,16 +97,19 @@ func (i *ServerInstance) Close() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||
func (i *ServerInstance) Handshake(conn net.Conn, fallback *[]byte) (*CommonConn, error) {
|
||||
if i.NfsSKeys == nil {
|
||||
return nil, errors.New("uninitialized")
|
||||
}
|
||||
c := NewCommonConn(conn)
|
||||
c := NewCommonConn(conn, true)
|
||||
|
||||
ivAndRelays := make([]byte, 16+i.RelaysLength)
|
||||
if _, err := io.ReadFull(conn, ivAndRelays); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if fallback != nil {
|
||||
*fallback = append(*fallback, ivAndRelays...)
|
||||
}
|
||||
iv := ivAndRelays[:16]
|
||||
relays := ivAndRelays[16:]
|
||||
var nfsKey []byte
|
||||
@@ -150,16 +153,27 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||
}
|
||||
relays = relays[32:]
|
||||
}
|
||||
nfsGCM := NewGCM(iv, nfsKey)
|
||||
nfsAEAD := NewAEAD(iv, nfsKey, c.UseAES)
|
||||
|
||||
encryptedLength := make([]byte, 18)
|
||||
if _, err := io.ReadFull(conn, encryptedLength); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := nfsGCM.Open(encryptedLength[:0], nil, encryptedLength, nil); err != nil {
|
||||
return nil, err
|
||||
if fallback != nil {
|
||||
*fallback = append(*fallback, encryptedLength...)
|
||||
}
|
||||
length := DecodeLength(encryptedLength[:2])
|
||||
decryptedLength := make([]byte, 2)
|
||||
if _, err := nfsAEAD.Open(decryptedLength[:0], nil, encryptedLength, nil); err != nil {
|
||||
c.UseAES = !c.UseAES
|
||||
nfsAEAD = NewAEAD(iv, nfsKey, c.UseAES)
|
||||
if _, err := nfsAEAD.Open(decryptedLength[:0], nil, encryptedLength, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if fallback != nil {
|
||||
*fallback = nil
|
||||
}
|
||||
length := DecodeLength(decryptedLength)
|
||||
|
||||
if length == 32 {
|
||||
if i.Seconds == 0 {
|
||||
@@ -169,7 +183,7 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||
if _, err := io.ReadFull(conn, encryptedTicket); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ticket, err := nfsGCM.Open(nil, nil, encryptedTicket, nil)
|
||||
ticket, err := nfsAEAD.Open(nil, nil, encryptedTicket, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -177,7 +191,7 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||
s := i.Sessions[[16]byte(ticket)]
|
||||
i.RWLock.RUnlock()
|
||||
if s == nil {
|
||||
noises := make([]byte, randBetween(1268, 2268)) // matches 1-RTT's server hello length for "random", though it is not important, just for example
|
||||
noises := make([]byte, randBetween(1279, 2279)) // matches 1-RTT's server hello length for "random", though it is not important, just for example
|
||||
var err error
|
||||
for err == nil {
|
||||
rand.Read(noises)
|
||||
@@ -192,8 +206,8 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||
c.UnitedKey = append(s.PfsKey, nfsKey...) // the same nfsKey links the upload & download (prevents server -> client's another request)
|
||||
c.PreWrite = make([]byte, 16)
|
||||
rand.Read(c.PreWrite) // always trust yourself, not the client (also prevents being parsed as TLS thus causing false interruption for "native" and "xorpub")
|
||||
c.GCM = NewGCM(c.PreWrite, c.UnitedKey)
|
||||
c.PeerGCM = NewGCM(encryptedTicket, c.UnitedKey) // unchangeable ctx (prevents server -> server), and different ctx length for upload / download (prevents client -> client)
|
||||
c.AEAD = NewAEAD(c.PreWrite, c.UnitedKey, c.UseAES)
|
||||
c.PeerAEAD = NewAEAD(encryptedTicket, c.UnitedKey, c.UseAES) // unchangeable ctx (prevents server -> server), and different ctx length for upload / download (prevents client -> client)
|
||||
if i.XorMode == 2 {
|
||||
c.Conn = NewXorConn(conn, NewCTR(c.UnitedKey, c.PreWrite), NewCTR(c.UnitedKey, iv), 16, 0) // it doesn't matter if the attacker sends client's iv back to the client
|
||||
}
|
||||
@@ -207,7 +221,7 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||
if _, err := io.ReadFull(conn, encryptedPfsPublicKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := nfsGCM.Open(encryptedPfsPublicKey[:0], nil, encryptedPfsPublicKey, nil); err != nil {
|
||||
if _, err := nfsAEAD.Open(encryptedPfsPublicKey[:0], nil, encryptedPfsPublicKey, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mlkem768EKey, err := mlkem.NewEncapsulationKey768(encryptedPfsPublicKey[:1184])
|
||||
@@ -229,27 +243,12 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||
copy(pfsKey[32:], x25519Key)
|
||||
pfsPublicKey := append(encapsulatedPfsKey, x25519SKey.PublicKey().Bytes()...)
|
||||
c.UnitedKey = append(pfsKey, nfsKey...)
|
||||
c.GCM = NewGCM(pfsPublicKey, c.UnitedKey)
|
||||
c.PeerGCM = NewGCM(encryptedPfsPublicKey[:1184+32], c.UnitedKey)
|
||||
c.AEAD = NewAEAD(pfsPublicKey, c.UnitedKey, c.UseAES)
|
||||
c.PeerAEAD = NewAEAD(encryptedPfsPublicKey[:1184+32], c.UnitedKey, c.UseAES)
|
||||
|
||||
ticket := make([]byte, 16)
|
||||
rand.Read(ticket)
|
||||
copy(ticket, EncodeLength(int(i.Seconds*4/5)))
|
||||
|
||||
pfsKeyExchangeLength := 1088 + 32 + 16
|
||||
encryptedTicketLength := 32
|
||||
paddingLength := int(randBetween(100, 1000))
|
||||
serverHello := make([]byte, pfsKeyExchangeLength+encryptedTicketLength+paddingLength)
|
||||
nfsGCM.Seal(serverHello[:0], MaxNonce, pfsPublicKey, nil)
|
||||
c.GCM.Seal(serverHello[:pfsKeyExchangeLength], nil, ticket, nil)
|
||||
padding := serverHello[pfsKeyExchangeLength+encryptedTicketLength:]
|
||||
c.GCM.Seal(padding[:0], nil, EncodeLength(paddingLength-18), nil)
|
||||
c.GCM.Seal(padding[:18], nil, padding[18:paddingLength-16], nil)
|
||||
|
||||
if _, err := conn.Write(serverHello); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// padding can be sent in a fragmented way, to create variable traffic pattern, before inner VLESS flow takes control
|
||||
|
||||
if i.Seconds > 0 {
|
||||
i.RWLock.Lock()
|
||||
i.Sessions[[16]byte(ticket)] = &ServerSession{
|
||||
@@ -259,18 +258,41 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||
i.RWLock.Unlock()
|
||||
}
|
||||
|
||||
pfsKeyExchangeLength := 1088 + 32 + 16
|
||||
encryptedTicketLength := 32
|
||||
paddingLength, paddingLens, paddingGaps := CreatPadding(i.PaddingLens, i.PaddingGaps)
|
||||
serverHello := make([]byte, pfsKeyExchangeLength+encryptedTicketLength+paddingLength)
|
||||
nfsAEAD.Seal(serverHello[:0], MaxNonce, pfsPublicKey, nil)
|
||||
c.AEAD.Seal(serverHello[:pfsKeyExchangeLength], nil, ticket, nil)
|
||||
padding := serverHello[pfsKeyExchangeLength+encryptedTicketLength:]
|
||||
c.AEAD.Seal(padding[:0], nil, EncodeLength(paddingLength-18), nil)
|
||||
c.AEAD.Seal(padding[:18], nil, padding[18:paddingLength-16], nil)
|
||||
|
||||
paddingLens[0] = pfsKeyExchangeLength + encryptedTicketLength + paddingLens[0]
|
||||
for i, l := range paddingLens { // sends padding in a fragmented way, to create variable traffic pattern, before inner VLESS flow takes control
|
||||
if l > 0 {
|
||||
if _, err := conn.Write(serverHello[:l]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
serverHello = serverHello[l:]
|
||||
}
|
||||
if len(paddingGaps) > i {
|
||||
time.Sleep(paddingGaps[i])
|
||||
}
|
||||
}
|
||||
|
||||
// important: allows client sends padding slowly, eliminating 1-RTT's traffic pattern
|
||||
if _, err := io.ReadFull(conn, encryptedLength); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := nfsGCM.Open(encryptedLength[:0], nil, encryptedLength, nil); err != nil {
|
||||
if _, err := nfsAEAD.Open(encryptedLength[:0], nil, encryptedLength, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
encryptedPadding := make([]byte, DecodeLength(encryptedLength[:2]))
|
||||
if _, err := io.ReadFull(conn, encryptedPadding); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := nfsGCM.Open(encryptedPadding[:0], nil, encryptedPadding, nil); err != nil {
|
||||
if _, err := nfsAEAD.Open(encryptedPadding[:0], nil, encryptedPadding, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@@ -49,7 +49,7 @@ jobs:
|
||||
|
||||
- uses: maxim-lobanov/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: 15
|
||||
xcode-version: 16
|
||||
|
||||
- name: install Rust stable
|
||||
run: |
|
||||
|
@@ -26,7 +26,7 @@ jobs:
|
||||
shared-key: 'release'
|
||||
- uses: maxim-lobanov/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: '15.0'
|
||||
xcode-version: 16
|
||||
- name: install the missing rust target
|
||||
run: |
|
||||
rustup target add aarch64-apple-darwin
|
||||
|
121
clash-nyanpasu/backend/Cargo.lock
generated
121
clash-nyanpasu/backend/Cargo.lock
generated
@@ -1569,7 +1569,6 @@ dependencies = [
|
||||
"once_cell",
|
||||
"oneshot",
|
||||
"open",
|
||||
"openssl",
|
||||
"os_pipe",
|
||||
"oxc_allocator",
|
||||
"oxc_ast",
|
||||
@@ -1821,7 +1820,7 @@ dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"core-foundation 0.9.4",
|
||||
"core-graphics-types 0.1.3",
|
||||
"foreign-types 0.5.0",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
]
|
||||
|
||||
@@ -1834,7 +1833,7 @@ dependencies = [
|
||||
"bitflags 2.9.2",
|
||||
"core-foundation 0.10.1",
|
||||
"core-graphics-types 0.2.0",
|
||||
"foreign-types 0.5.0",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
]
|
||||
|
||||
@@ -3055,15 +3054,6 @@ dependencies = [
|
||||
"ttf-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
||||
dependencies = [
|
||||
"foreign-types-shared 0.1.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
version = "0.5.0"
|
||||
@@ -3071,7 +3061,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
|
||||
dependencies = [
|
||||
"foreign-types-macros",
|
||||
"foreign-types-shared 0.3.1",
|
||||
"foreign-types-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3085,12 +3075,6 @@ dependencies = [
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types-shared"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types-shared"
|
||||
version = "0.3.1"
|
||||
@@ -4088,22 +4072,6 @@ dependencies = [
|
||||
"webpki-roots 1.0.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-tls"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"native-tls",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.16"
|
||||
@@ -5326,7 +5294,7 @@ dependencies = [
|
||||
"bitflags 2.9.2",
|
||||
"block",
|
||||
"core-graphics-types 0.1.3",
|
||||
"foreign-types 0.5.0",
|
||||
"foreign-types",
|
||||
"log",
|
||||
"objc",
|
||||
"paste",
|
||||
@@ -5471,23 +5439,6 @@ dependencies = [
|
||||
"rand 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"openssl",
|
||||
"openssl-probe",
|
||||
"openssl-sys",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
"security-framework-sys",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ndk"
|
||||
version = "0.9.0"
|
||||
@@ -6361,47 +6312,12 @@ dependencies = [
|
||||
"pathdiff",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8"
|
||||
dependencies = [
|
||||
"bitflags 2.9.2",
|
||||
"cfg-if",
|
||||
"foreign-types 0.3.2",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"openssl-macros",
|
||||
"openssl-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-src"
|
||||
version = "300.5.2+3.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d270b79e2926f5150189d475bc7e9d2c69f9c4697b185fa917d5a32b792d21b4"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.109"
|
||||
@@ -6410,7 +6326,6 @@ checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"openssl-src",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
@@ -7798,12 +7713,10 @@ dependencies = [
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper-rustls",
|
||||
"hyper-tls",
|
||||
"hyper-util",
|
||||
"js-sys",
|
||||
"log",
|
||||
"mime",
|
||||
"native-tls",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"quinn",
|
||||
@@ -7814,7 +7727,6 @@ dependencies = [
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tokio-rustls",
|
||||
"tokio-util",
|
||||
"tower",
|
||||
@@ -8195,19 +8107,6 @@ version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
|
||||
dependencies = [
|
||||
"bitflags 2.9.2",
|
||||
"core-foundation 0.9.4",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"security-framework-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework-sys"
|
||||
version = "2.14.0"
|
||||
@@ -8785,7 +8684,7 @@ dependencies = [
|
||||
"bytemuck",
|
||||
"cfg_aliases",
|
||||
"core-graphics 0.24.0",
|
||||
"foreign-types 0.5.0",
|
||||
"foreign-types",
|
||||
"js-sys",
|
||||
"log",
|
||||
"objc2 0.5.2",
|
||||
@@ -9999,16 +9898,6 @@ dependencies = [
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-native-tls"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
|
||||
dependencies = [
|
||||
"native-tls",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.26.2"
|
||||
|
@@ -89,7 +89,14 @@ rust-i18n = "3"
|
||||
axum = "0.8"
|
||||
url = "2"
|
||||
mime = "0.3"
|
||||
reqwest = { version = "0.12", features = ["json", "stream"] }
|
||||
reqwest = { version = "0.12", default-features = false, features = [
|
||||
"charset",
|
||||
"http2",
|
||||
"system-proxy",
|
||||
"json",
|
||||
"stream",
|
||||
"rustls-tls",
|
||||
] }
|
||||
tokio-tungstenite = "0.27"
|
||||
urlencoding = "2.1"
|
||||
port_scanner = "0.1.5"
|
||||
@@ -193,7 +200,13 @@ boa_utils = { path = "../boa_utils" } # should be removed wh
|
||||
boa_engine = { workspace = true, features = ["annex-b"] }
|
||||
|
||||
# Tauri Dependencies
|
||||
tauri = { version = "2.4", features = ["tray-icon", "image-png", "image-ico"] }
|
||||
tauri = { version = "2.4", features = [
|
||||
"tray-icon",
|
||||
"image-png",
|
||||
"image-ico",
|
||||
"rustls-tls",
|
||||
"specta",
|
||||
] }
|
||||
tauri-plugin-deep-link = { path = "../tauri-plugin-deep-link", version = "0.1.2" } # This should be migrated to official tauri plugin
|
||||
tauri-plugin-os = "2.2"
|
||||
tauri-plugin-clipboard-manager = "2.2"
|
||||
@@ -221,9 +234,6 @@ specta = { version = "=2.0.0-rc.22", features = [
|
||||
[target."cfg(not(any(target_os = \"android\", target_os = \"ios\")))".dependencies]
|
||||
tauri-plugin-global-shortcut = "2.2.0"
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
openssl = { version = "0.10", features = ["vendored"] }
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
objc2 = "0.6.1"
|
||||
objc2-app-kit = { version = "0.3.1", features = [
|
||||
@@ -260,4 +270,3 @@ verge-dev = []
|
||||
default-meta = []
|
||||
devtools = ["tauri/devtools"]
|
||||
deadlock-detection = ["parking_lot/deadlock_detection"]
|
||||
openssl_vendored = ["openssl/vendored"]
|
||||
|
@@ -56,7 +56,7 @@
|
||||
"@csstools/normalize.css": "12.1.1",
|
||||
"@emotion/babel-plugin": "11.13.5",
|
||||
"@emotion/react": "11.14.0",
|
||||
"@iconify/json": "2.2.379",
|
||||
"@iconify/json": "2.2.380",
|
||||
"@monaco-editor/react": "4.7.0",
|
||||
"@tanstack/react-query": "5.85.5",
|
||||
"@tanstack/react-router": "1.131.28",
|
||||
@@ -71,7 +71,7 @@
|
||||
"@tauri-apps/plugin-shell": "2.3.0",
|
||||
"@tauri-apps/plugin-updater": "2.9.0",
|
||||
"@types/react": "19.1.12",
|
||||
"@types/react-dom": "19.1.8",
|
||||
"@types/react-dom": "19.1.9",
|
||||
"@types/validator": "13.15.2",
|
||||
"@vitejs/plugin-legacy": "7.2.1",
|
||||
"@vitejs/plugin-react": "5.0.2",
|
||||
@@ -80,7 +80,7 @@
|
||||
"clsx": "2.1.1",
|
||||
"core-js": "3.45.1",
|
||||
"filesize": "11.0.2",
|
||||
"meta-json-schema": "1.19.12",
|
||||
"meta-json-schema": "1.19.13",
|
||||
"monaco-yaml": "5.4.0",
|
||||
"nanoid": "5.1.5",
|
||||
"sass-embedded": "1.90.0",
|
||||
|
@@ -5,7 +5,7 @@
|
||||
"mihomo_alpha": "alpha-472cefb",
|
||||
"clash_rs": "v0.9.0",
|
||||
"clash_premium": "2023-09-05-gdcc8d87",
|
||||
"clash_rs_alpha": "0.9.0-alpha+sha.14e9e4f"
|
||||
"clash_rs_alpha": "0.9.0-alpha+sha.117a718"
|
||||
},
|
||||
"arch_template": {
|
||||
"mihomo": {
|
||||
@@ -69,5 +69,5 @@
|
||||
"linux-armv7hf": "clash-armv7-unknown-linux-gnueabihf"
|
||||
}
|
||||
},
|
||||
"updated_at": "2025-08-29T22:20:50.863Z"
|
||||
"updated_at": "2025-08-30T22:20:39.907Z"
|
||||
}
|
||||
|
56
clash-nyanpasu/pnpm-lock.yaml
generated
56
clash-nyanpasu/pnpm-lock.yaml
generated
@@ -343,8 +343,8 @@ importers:
|
||||
specifier: 11.14.0
|
||||
version: 11.14.0(@types/react@19.1.12)(react@19.1.1)
|
||||
'@iconify/json':
|
||||
specifier: 2.2.379
|
||||
version: 2.2.379
|
||||
specifier: 2.2.380
|
||||
version: 2.2.380
|
||||
'@monaco-editor/react':
|
||||
specifier: 4.7.0
|
||||
version: 4.7.0(monaco-editor@0.52.2)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
@@ -388,8 +388,8 @@ importers:
|
||||
specifier: 19.1.12
|
||||
version: 19.1.12
|
||||
'@types/react-dom':
|
||||
specifier: 19.1.8
|
||||
version: 19.1.8(@types/react@19.1.12)
|
||||
specifier: 19.1.9
|
||||
version: 19.1.9(@types/react@19.1.12)
|
||||
'@types/validator':
|
||||
specifier: 13.15.2
|
||||
version: 13.15.2
|
||||
@@ -415,8 +415,8 @@ importers:
|
||||
specifier: 11.0.2
|
||||
version: 11.0.2
|
||||
meta-json-schema:
|
||||
specifier: 1.19.12
|
||||
version: 1.19.12
|
||||
specifier: 1.19.13
|
||||
version: 1.19.13
|
||||
monaco-yaml:
|
||||
specifier: 5.4.0
|
||||
version: 5.4.0(monaco-editor@0.52.2)
|
||||
@@ -473,10 +473,10 @@ importers:
|
||||
version: 7.3.1(@emotion/react@11.14.0(@types/react@19.1.12)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.12)(react@19.1.1))(@types/react@19.1.12)(react@19.1.1))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
'@radix-ui/react-portal':
|
||||
specifier: 1.1.9
|
||||
version: 1.1.9(@types/react-dom@19.1.8(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
version: 1.1.9(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
'@radix-ui/react-scroll-area':
|
||||
specifier: 1.2.10
|
||||
version: 1.2.10(@types/react-dom@19.1.8(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
version: 1.2.10(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
'@tauri-apps/api':
|
||||
specifier: 2.6.0
|
||||
version: 2.6.0
|
||||
@@ -1799,8 +1799,8 @@ packages:
|
||||
prettier-plugin-ember-template-tag:
|
||||
optional: true
|
||||
|
||||
'@iconify/json@2.2.379':
|
||||
resolution: {integrity: sha512-PInpWLQi2C+fDIbBdVNcKOj9QKl7TT6sXqFqYMa4e34sMx206PiUlg0puWgo1Q1C/TDNQiy/raGWUbssOb1eyg==}
|
||||
'@iconify/json@2.2.380':
|
||||
resolution: {integrity: sha512-+Al/Q+mMB/nLz/tawmJEOkCs6+RKKVUS/Yg9I80h2yRpu0kIzxVLQRfF0NifXz/fH92vDVXbS399wio4lMVF4Q==}
|
||||
|
||||
'@iconify/types@2.0.0':
|
||||
resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
|
||||
@@ -3438,8 +3438,8 @@ packages:
|
||||
'@types/prop-types@15.7.15':
|
||||
resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==}
|
||||
|
||||
'@types/react-dom@19.1.8':
|
||||
resolution: {integrity: sha512-xG7xaBMJCpcK0RpN8jDbAACQo54ycO6h4dSSmgv8+fu6ZIAdANkx/WsawASUjVXYfy+J9AbUpRMNNEsXCDfDBQ==}
|
||||
'@types/react-dom@19.1.9':
|
||||
resolution: {integrity: sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==}
|
||||
peerDependencies:
|
||||
'@types/react': ^19.0.0
|
||||
|
||||
@@ -6401,8 +6401,8 @@ packages:
|
||||
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
meta-json-schema@1.19.12:
|
||||
resolution: {integrity: sha512-aiI0eILS8y474h5Wmmxp7GgxaFimqEc3z3L4EfZMjqec7N7q7wbXVrZHIR5mUUDHpHagZYFoT1Qf1QNrHMukcw==}
|
||||
meta-json-schema@1.19.13:
|
||||
resolution: {integrity: sha512-7oYti4CQ7JPGSWmSddD8bCdNvOhCyza1U+Eb8q/KfbEMT3g3M7cKSXYGS6tN3r9B2pTR5nLzevkhEy/C0DPaxw==}
|
||||
engines: {node: '>=18', pnpm: '>=9'}
|
||||
|
||||
micromark-core-commonmark@2.0.1:
|
||||
@@ -10190,7 +10190,7 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@iconify/json@2.2.379':
|
||||
'@iconify/json@2.2.380':
|
||||
dependencies:
|
||||
'@iconify/types': 2.0.0
|
||||
pathe: 1.1.2
|
||||
@@ -10865,17 +10865,17 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@types/react': 19.1.12
|
||||
|
||||
'@radix-ui/react-portal@1.1.9(@types/react-dom@19.1.8(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
|
||||
'@radix-ui/react-portal@1.1.9(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
|
||||
dependencies:
|
||||
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.8(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.12)(react@19.1.1)
|
||||
react: 19.1.1
|
||||
react-dom: 19.1.1(react@19.1.1)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.1.12
|
||||
'@types/react-dom': 19.1.8(@types/react@19.1.12)
|
||||
'@types/react-dom': 19.1.9(@types/react@19.1.12)
|
||||
|
||||
'@radix-ui/react-presence@1.1.5(@types/react-dom@19.1.8(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
|
||||
'@radix-ui/react-presence@1.1.5(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
|
||||
dependencies:
|
||||
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.1)
|
||||
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.12)(react@19.1.1)
|
||||
@@ -10883,33 +10883,33 @@ snapshots:
|
||||
react-dom: 19.1.1(react@19.1.1)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.1.12
|
||||
'@types/react-dom': 19.1.8(@types/react@19.1.12)
|
||||
'@types/react-dom': 19.1.9(@types/react@19.1.12)
|
||||
|
||||
'@radix-ui/react-primitive@2.1.3(@types/react-dom@19.1.8(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
|
||||
'@radix-ui/react-primitive@2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
|
||||
dependencies:
|
||||
'@radix-ui/react-slot': 1.2.3(@types/react@19.1.12)(react@19.1.1)
|
||||
react: 19.1.1
|
||||
react-dom: 19.1.1(react@19.1.1)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.1.12
|
||||
'@types/react-dom': 19.1.8(@types/react@19.1.12)
|
||||
'@types/react-dom': 19.1.9(@types/react@19.1.12)
|
||||
|
||||
'@radix-ui/react-scroll-area@1.2.10(@types/react-dom@19.1.8(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
|
||||
'@radix-ui/react-scroll-area@1.2.10(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
|
||||
dependencies:
|
||||
'@radix-ui/number': 1.1.1
|
||||
'@radix-ui/primitive': 1.1.3
|
||||
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.1)
|
||||
'@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.1)
|
||||
'@radix-ui/react-direction': 1.1.1(@types/react@19.1.12)(react@19.1.1)
|
||||
'@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.8(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.8(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
'@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
'@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.12)(react@19.1.1)
|
||||
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.12)(react@19.1.1)
|
||||
react: 19.1.1
|
||||
react-dom: 19.1.1(react@19.1.1)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.1.12
|
||||
'@types/react-dom': 19.1.8(@types/react@19.1.12)
|
||||
'@types/react-dom': 19.1.9(@types/react@19.1.12)
|
||||
|
||||
'@radix-ui/react-slot@1.2.3(@types/react@19.1.12)(react@19.1.1)':
|
||||
dependencies:
|
||||
@@ -11770,7 +11770,7 @@ snapshots:
|
||||
|
||||
'@types/prop-types@15.7.15': {}
|
||||
|
||||
'@types/react-dom@19.1.8(@types/react@19.1.12)':
|
||||
'@types/react-dom@19.1.9(@types/react@19.1.12)':
|
||||
dependencies:
|
||||
'@types/react': 19.1.12
|
||||
|
||||
@@ -15170,7 +15170,7 @@ snapshots:
|
||||
|
||||
merge2@1.4.1: {}
|
||||
|
||||
meta-json-schema@1.19.12: {}
|
||||
meta-json-schema@1.19.13: {}
|
||||
|
||||
micromark-core-commonmark@2.0.1:
|
||||
dependencies:
|
||||
|
@@ -1,2 +1,2 @@
|
||||
LINUX_VERSION-5.10 = .240
|
||||
LINUX_KERNEL_HASH-5.10.240 = 8d88c3977226d666554b75f480d1e6c5f4e4d2acdf2a3462840c6bac88634d13
|
||||
LINUX_VERSION-5.10 = .241
|
||||
LINUX_KERNEL_HASH-5.10.241 = 08c1e982064c81f8445a8fff2293d430c716c11fd3185606b8275718d696d8d6
|
||||
|
@@ -1,2 +1,2 @@
|
||||
LINUX_VERSION-5.15 = .189
|
||||
LINUX_KERNEL_HASH-5.15.189 = e3d0025b87278e14733cb326700f17c7cceb54d920622b0d5fcd58a88c6850c3
|
||||
LINUX_VERSION-5.15 = .190
|
||||
LINUX_KERNEL_HASH-5.15.190 = 62b814f1a48e1d67764a28611f23ca1f1ce1084ad7f1d319acc05720a9a68604
|
||||
|
@@ -1,2 +1,2 @@
|
||||
LINUX_VERSION-5.4 = .296
|
||||
LINUX_KERNEL_HASH-5.4.296 = 3d63614e58bf1befaba3f5713145200d09f26e564832c8948094fdf5b11fa73a
|
||||
LINUX_VERSION-5.4 = .297
|
||||
LINUX_KERNEL_HASH-5.4.297 = f10cfcea7acf7588087d7cc17ebeeb7d3ff783c040536698c544eed7c5e8a841
|
||||
|
@@ -1,2 +1,2 @@
|
||||
LINUX_VERSION-6.1 = .148
|
||||
LINUX_KERNEL_HASH-6.1.148 = d9a03d3a2771c60fc726c58d3bba61123e5fd22d45ed27f6cf7a308c171180a1
|
||||
LINUX_VERSION-6.1 = .149
|
||||
LINUX_KERNEL_HASH-6.1.149 = c4e906b8d39a5866d25e06371d4de4454fbfaaeb67979a04e4137e807877be39
|
||||
|
@@ -1,2 +1,2 @@
|
||||
LINUX_VERSION-6.12 = .43
|
||||
LINUX_KERNEL_HASH-6.12.43 = 0fcbbbbcd456e87bbbfc8bf37af541fda62ccfcce76903503424fd101ef7bdee
|
||||
LINUX_VERSION-6.12 = .44
|
||||
LINUX_KERNEL_HASH-6.12.44 = b650210ed3027b224969d148aa377452a9aad3ae7f2851abedd31adfef16bdae
|
||||
|
@@ -1,2 +1,2 @@
|
||||
LINUX_VERSION-6.6 = .102
|
||||
LINUX_KERNEL_HASH-6.6.102 = 80d2feb7334c30bacbe1e7dafa9ea415efb2c0ea4f4740ecbd1467cf5d94de5c
|
||||
LINUX_VERSION-6.6 = .103
|
||||
LINUX_KERNEL_HASH-6.6.103 = d288dd38c3e62ba576ba6b3ad2a84cfba65cd43b702f6c50d1f701aee942b18e
|
||||
|
@@ -1,81 +0,0 @@
|
||||
From 26f732791f2bcab18f59c61915bbe35225f30136 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Sat, 12 Jul 2025 16:39:21 +0100
|
||||
Subject: [PATCH] Revert "leds: trigger: netdev: Configure LED blink interval
|
||||
for HW offload"
|
||||
|
||||
This reverts commit c629c972b310af41e9e072febb6dae9a299edde6.
|
||||
|
||||
While .led_blink_set() would previously put an LED into an unconditional
|
||||
permanently blinking state, the offending commit now uses same operation
|
||||
to (also?) set the blink timing of the netdev trigger when offloading.
|
||||
|
||||
This breaks many if not all of the existing PHY drivers which offer
|
||||
offloading LED operations, as those drivers would just put the LED into
|
||||
blinking state after .led_blink_set() has been called.
|
||||
|
||||
Unfortunately the change even made it into stable kernels for unknown
|
||||
reasons, so it should be reverted there as well.
|
||||
|
||||
Fixes: c629c972b310a ("leds: trigger: netdev: Configure LED blink interval for HW offload")
|
||||
Link: https://lore.kernel.org/linux-leds/c6134e26-2e45-4121-aa15-58aaef327201@lunn.ch/T/#m9d6fe81bbcb273e59f12bbedbd633edd32118387
|
||||
Suggested-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Cc: stable@vger.kernel.org
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Link: https://lore.kernel.org/r/6dcc77ee1c9676891d6250d8994850f521426a0f.1752334655.git.daniel@makrotopia.org
|
||||
Signed-off-by: Lee Jones <lee@kernel.org>
|
||||
---
|
||||
drivers/leds/trigger/ledtrig-netdev.c | 16 +++-------------
|
||||
1 file changed, 3 insertions(+), 13 deletions(-)
|
||||
|
||||
--- a/drivers/leds/trigger/ledtrig-netdev.c
|
||||
+++ b/drivers/leds/trigger/ledtrig-netdev.c
|
||||
@@ -54,7 +54,6 @@ struct led_netdev_data {
|
||||
unsigned int last_activity;
|
||||
|
||||
unsigned long mode;
|
||||
- unsigned long blink_delay;
|
||||
int link_speed;
|
||||
u8 duplex;
|
||||
|
||||
@@ -70,10 +69,6 @@ static void set_baseline_state(struct le
|
||||
/* Already validated, hw control is possible with the requested mode */
|
||||
if (trigger_data->hw_control) {
|
||||
led_cdev->hw_control_set(led_cdev, trigger_data->mode);
|
||||
- if (led_cdev->blink_set) {
|
||||
- led_cdev->blink_set(led_cdev, &trigger_data->blink_delay,
|
||||
- &trigger_data->blink_delay);
|
||||
- }
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -415,11 +410,10 @@ static ssize_t interval_store(struct dev
|
||||
size_t size)
|
||||
{
|
||||
struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
|
||||
- struct led_classdev *led_cdev = trigger_data->led_cdev;
|
||||
unsigned long value;
|
||||
int ret;
|
||||
|
||||
- if (trigger_data->hw_control && !led_cdev->blink_set)
|
||||
+ if (trigger_data->hw_control)
|
||||
return -EINVAL;
|
||||
|
||||
ret = kstrtoul(buf, 0, &value);
|
||||
@@ -428,13 +422,9 @@ static ssize_t interval_store(struct dev
|
||||
|
||||
/* impose some basic bounds on the timer interval */
|
||||
if (value >= 5 && value <= 10000) {
|
||||
- if (trigger_data->hw_control) {
|
||||
- trigger_data->blink_delay = value;
|
||||
- } else {
|
||||
- cancel_delayed_work_sync(&trigger_data->work);
|
||||
+ cancel_delayed_work_sync(&trigger_data->work);
|
||||
|
||||
- atomic_set(&trigger_data->interval, msecs_to_jiffies(value));
|
||||
- }
|
||||
+ atomic_set(&trigger_data->interval, msecs_to_jiffies(value));
|
||||
set_baseline_state(trigger_data); /* resets timer */
|
||||
}
|
||||
|
@@ -22,19 +22,11 @@ Link: https://bugzilla.kernel.org/show_bug.cgi?id=109581
|
||||
|
||||
--- a/net/sched/sch_codel.c
|
||||
+++ b/net/sched/sch_codel.c
|
||||
@@ -95,11 +95,17 @@ static struct sk_buff *codel_qdisc_deque
|
||||
&q->stats, qdisc_pkt_len, codel_get_enqueue_time,
|
||||
@@ -96,7 +96,12 @@ static struct sk_buff *codel_qdisc_deque
|
||||
drop_func, dequeue_func);
|
||||
|
||||
- /* We cant call qdisc_tree_reduce_backlog() if our qlen is 0,
|
||||
- * or HTB crashes. Defer it for next round.
|
||||
+ /* If our qlen is 0 qdisc_tree_reduce_backlog() will deactivate
|
||||
+ * parent class, dequeue in parent qdisc will do the same if we
|
||||
+ * return skb. Temporary increment qlen if we have skb.
|
||||
*/
|
||||
- if (q->stats.drop_count && sch->q.qlen) {
|
||||
if (q->stats.drop_count) {
|
||||
- qdisc_tree_reduce_backlog(sch, q->stats.drop_count, q->stats.drop_len);
|
||||
+ if (q->stats.drop_count) {
|
||||
+ if (skb)
|
||||
+ sch->q.qlen++;
|
||||
+ qdisc_tree_reduce_backlog(sch, q->stats.drop_count,
|
||||
@@ -47,9 +39,10 @@ Link: https://bugzilla.kernel.org/show_bug.cgi?id=109581
|
||||
--- a/net/sched/sch_fq_codel.c
|
||||
+++ b/net/sched/sch_fq_codel.c
|
||||
@@ -304,6 +304,21 @@ begin:
|
||||
skb = codel_dequeue(sch, &sch->qstats.backlog, &q->cparams,
|
||||
&flow->cvars, &q->cstats, qdisc_pkt_len,
|
||||
codel_get_enqueue_time, drop_func, dequeue_func);
|
||||
|
||||
+
|
||||
+ /* If our qlen is 0 qdisc_tree_reduce_backlog() will deactivate
|
||||
+ * parent class, dequeue in parent qdisc will do the same if we
|
||||
+ * return skb. Temporary increment qlen if we have skb.
|
||||
@@ -64,18 +57,15 @@ Link: https://bugzilla.kernel.org/show_bug.cgi?id=109581
|
||||
+ q->cstats.drop_count = 0;
|
||||
+ q->cstats.drop_len = 0;
|
||||
+ }
|
||||
+
|
||||
|
||||
if (!skb) {
|
||||
/* force a pass through old_flows to prevent starvation */
|
||||
if ((head == &q->new_flows) && !list_empty(&q->old_flows))
|
||||
@@ -314,15 +329,6 @@ begin:
|
||||
@@ -315,13 +330,6 @@ begin:
|
||||
}
|
||||
qdisc_bstats_update(sch, skb);
|
||||
flow->deficit -= qdisc_pkt_len(skb);
|
||||
- /* We cant call qdisc_tree_reduce_backlog() if our qlen is 0,
|
||||
- * or HTB crashes. Defer it for next round.
|
||||
- */
|
||||
- if (q->cstats.drop_count && sch->q.qlen) {
|
||||
-
|
||||
- if (q->cstats.drop_count) {
|
||||
- qdisc_tree_reduce_backlog(sch, q->cstats.drop_count,
|
||||
- q->cstats.drop_len);
|
||||
- q->cstats.drop_count = 0;
|
||||
|
@@ -22,19 +22,11 @@ Link: https://bugzilla.kernel.org/show_bug.cgi?id=109581
|
||||
|
||||
--- a/net/sched/sch_codel.c
|
||||
+++ b/net/sched/sch_codel.c
|
||||
@@ -95,11 +95,17 @@ static struct sk_buff *codel_qdisc_deque
|
||||
&q->stats, qdisc_pkt_len, codel_get_enqueue_time,
|
||||
@@ -96,7 +96,12 @@ static struct sk_buff *codel_qdisc_deque
|
||||
drop_func, dequeue_func);
|
||||
|
||||
- /* We cant call qdisc_tree_reduce_backlog() if our qlen is 0,
|
||||
- * or HTB crashes. Defer it for next round.
|
||||
+ /* If our qlen is 0 qdisc_tree_reduce_backlog() will deactivate
|
||||
+ * parent class, dequeue in parent qdisc will do the same if we
|
||||
+ * return skb. Temporary increment qlen if we have skb.
|
||||
*/
|
||||
- if (q->stats.drop_count && sch->q.qlen) {
|
||||
if (q->stats.drop_count) {
|
||||
- qdisc_tree_reduce_backlog(sch, q->stats.drop_count, q->stats.drop_len);
|
||||
+ if (q->stats.drop_count) {
|
||||
+ if (skb)
|
||||
+ sch->q.qlen++;
|
||||
+ qdisc_tree_reduce_backlog(sch, q->stats.drop_count,
|
||||
@@ -47,9 +39,10 @@ Link: https://bugzilla.kernel.org/show_bug.cgi?id=109581
|
||||
--- a/net/sched/sch_fq_codel.c
|
||||
+++ b/net/sched/sch_fq_codel.c
|
||||
@@ -304,6 +304,21 @@ begin:
|
||||
skb = codel_dequeue(sch, &sch->qstats.backlog, &q->cparams,
|
||||
&flow->cvars, &q->cstats, qdisc_pkt_len,
|
||||
codel_get_enqueue_time, drop_func, dequeue_func);
|
||||
|
||||
+
|
||||
+ /* If our qlen is 0 qdisc_tree_reduce_backlog() will deactivate
|
||||
+ * parent class, dequeue in parent qdisc will do the same if we
|
||||
+ * return skb. Temporary increment qlen if we have skb.
|
||||
@@ -64,18 +57,15 @@ Link: https://bugzilla.kernel.org/show_bug.cgi?id=109581
|
||||
+ q->cstats.drop_count = 0;
|
||||
+ q->cstats.drop_len = 0;
|
||||
+ }
|
||||
+
|
||||
|
||||
if (!skb) {
|
||||
/* force a pass through old_flows to prevent starvation */
|
||||
if ((head == &q->new_flows) && !list_empty(&q->old_flows))
|
||||
@@ -314,15 +329,6 @@ begin:
|
||||
@@ -315,13 +330,6 @@ begin:
|
||||
}
|
||||
qdisc_bstats_update(sch, skb);
|
||||
flow->deficit -= qdisc_pkt_len(skb);
|
||||
- /* We cant call qdisc_tree_reduce_backlog() if our qlen is 0,
|
||||
- * or HTB crashes. Defer it for next round.
|
||||
- */
|
||||
- if (q->cstats.drop_count && sch->q.qlen) {
|
||||
-
|
||||
- if (q->cstats.drop_count) {
|
||||
- qdisc_tree_reduce_backlog(sch, q->cstats.drop_count,
|
||||
- q->cstats.drop_len);
|
||||
- q->cstats.drop_count = 0;
|
||||
|
@@ -22,19 +22,11 @@ Link: https://bugzilla.kernel.org/show_bug.cgi?id=109581
|
||||
|
||||
--- a/net/sched/sch_codel.c
|
||||
+++ b/net/sched/sch_codel.c
|
||||
@@ -95,11 +95,17 @@ static struct sk_buff *codel_qdisc_deque
|
||||
&q->stats, qdisc_pkt_len, codel_get_enqueue_time,
|
||||
@@ -96,7 +96,12 @@ static struct sk_buff *codel_qdisc_deque
|
||||
drop_func, dequeue_func);
|
||||
|
||||
- /* We cant call qdisc_tree_reduce_backlog() if our qlen is 0,
|
||||
- * or HTB crashes. Defer it for next round.
|
||||
+ /* If our qlen is 0 qdisc_tree_reduce_backlog() will deactivate
|
||||
+ * parent class, dequeue in parent qdisc will do the same if we
|
||||
+ * return skb. Temporary increment qlen if we have skb.
|
||||
*/
|
||||
- if (q->stats.drop_count && sch->q.qlen) {
|
||||
if (q->stats.drop_count) {
|
||||
- qdisc_tree_reduce_backlog(sch, q->stats.drop_count, q->stats.drop_len);
|
||||
+ if (q->stats.drop_count) {
|
||||
+ if (skb)
|
||||
+ sch->q.qlen++;
|
||||
+ qdisc_tree_reduce_backlog(sch, q->stats.drop_count,
|
||||
@@ -46,10 +38,11 @@ Link: https://bugzilla.kernel.org/show_bug.cgi?id=109581
|
||||
}
|
||||
--- a/net/sched/sch_fq_codel.c
|
||||
+++ b/net/sched/sch_fq_codel.c
|
||||
@@ -305,6 +305,21 @@ begin:
|
||||
@@ -304,6 +304,21 @@ begin:
|
||||
skb = codel_dequeue(sch, &sch->qstats.backlog, &q->cparams,
|
||||
&flow->cvars, &q->cstats, qdisc_pkt_len,
|
||||
codel_get_enqueue_time, drop_func, dequeue_func);
|
||||
|
||||
+
|
||||
+ /* If our qlen is 0 qdisc_tree_reduce_backlog() will deactivate
|
||||
+ * parent class, dequeue in parent qdisc will do the same if we
|
||||
+ * return skb. Temporary increment qlen if we have skb.
|
||||
@@ -64,18 +57,15 @@ Link: https://bugzilla.kernel.org/show_bug.cgi?id=109581
|
||||
+ q->cstats.drop_count = 0;
|
||||
+ q->cstats.drop_len = 0;
|
||||
+ }
|
||||
+
|
||||
|
||||
if (!skb) {
|
||||
/* force a pass through old_flows to prevent starvation */
|
||||
if ((head == &q->new_flows) && !list_empty(&q->old_flows))
|
||||
@@ -315,15 +330,6 @@ begin:
|
||||
@@ -315,13 +330,6 @@ begin:
|
||||
}
|
||||
qdisc_bstats_update(sch, skb);
|
||||
flow->deficit -= qdisc_pkt_len(skb);
|
||||
- /* We cant call qdisc_tree_reduce_backlog() if our qlen is 0,
|
||||
- * or HTB crashes. Defer it for next round.
|
||||
- */
|
||||
- if (q->cstats.drop_count && sch->q.qlen) {
|
||||
-
|
||||
- if (q->cstats.drop_count) {
|
||||
- qdisc_tree_reduce_backlog(sch, q->cstats.drop_count,
|
||||
- q->cstats.drop_len);
|
||||
- q->cstats.drop_count = 0;
|
||||
|
@@ -610,7 +610,6 @@ proxies: # socks5
|
||||
uuid: uuid
|
||||
network: tcp
|
||||
servername: example.com # AKA SNI
|
||||
# flow: xtls-rprx-direct # xtls-rprx-origin # enable XTLS
|
||||
# skip-cert-verify: true
|
||||
# fingerprint: xxxx
|
||||
# client-fingerprint: random # Available: "chrome","firefox","safari","random","none"
|
||||
@@ -640,10 +639,16 @@ proxies: # socks5
|
||||
network: tcp
|
||||
# -------------------------
|
||||
# vless encryption客户端配置:
|
||||
# (native/xorpub 的 XTLS 可以 Splice。只使用 1-RTT 模式 / 若服务端发的 ticket 中秒数不为零则 0-RTT 复用)
|
||||
# (native/xorpub 的 XTLS Vision 可以 Splice。只使用 1-RTT 模式 / 若服务端发的 ticket 中秒数不为零则 0-RTT 复用)
|
||||
# / 是只能选一个,后面 base64 至少一个,无限串联,使用 mihomo generate vless-x25519 和 mihomo generate vless-mlkem768 生成,替换值时需去掉括号
|
||||
#
|
||||
# Padding 是可选的参数,仅作用于 1-RTT 以消除握手的长度特征,双端默认值均为 "100-111-1111.75-0-111.50-0-3333":
|
||||
# 在 1-RTT client/server hello 后以 100% 的概率粘上随机 111 到 1111 字节的 padding
|
||||
# 以 75% 的概率等待随机 0 到 111 毫秒("probability-from-to")
|
||||
# 再次以 50% 的概率发送随机 0 到 3333 字节的 padding(若为 0 则不 Write())
|
||||
# 服务端、客户端可以设置不同的 padding 参数,按 len、gap 的顺序无限串联,第一个 padding 需概率 100%、至少 35 字节
|
||||
# -------------------------
|
||||
encryption: "mlkem768x25519plus.native/xorpub/random.1rtt/0rtt.(X25519 Password).(ML-KEM-768 Client)..."
|
||||
encryption: "mlkem768x25519plus.native/xorpub/random.1rtt/0rtt.(padding len).(padding gap).(X25519 Password).(ML-KEM-768 Client)..."
|
||||
tls: false #可以不开启tls
|
||||
udp: true
|
||||
|
||||
@@ -1367,8 +1372,14 @@ listeners:
|
||||
# vless encryption服务端配置:
|
||||
# (原生外观 / 只 XOR 公钥 / 全随机数。只允许 1-RTT 模式 / 同时允许 1-RTT 模式与 600 秒复用的 0-RTT 模式)
|
||||
# / 是只能选一个,后面 base64 至少一个,无限串联,使用 mihomo generate vless-x25519 和 mihomo generate vless-mlkem768 生成,替换值时需去掉括号
|
||||
#
|
||||
# Padding 是可选的参数,仅作用于 1-RTT 以消除握手的长度特征,双端默认值均为 "100-111-1111.75-0-111.50-0-3333":
|
||||
# 在 1-RTT client/server hello 后以 100% 的概率粘上随机 111 到 1111 字节的 padding
|
||||
# 以 75% 的概率等待随机 0 到 111 毫秒("probability-from-to")
|
||||
# 再次以 50% 的概率发送随机 0 到 3333 字节的 padding(若为 0 则不 Write())
|
||||
# 服务端、客户端可以设置不同的 padding 参数,按 len、gap 的顺序无限串联,第一个 padding 需概率 100%、至少 35 字节
|
||||
# -------------------------
|
||||
# decryption: "mlkem768x25519plus.native/xorpub/random.1rtt/600s.(X25519 PrivateKey).(ML-KEM-768 Seed)..."
|
||||
# decryption: "mlkem768x25519plus.native/xorpub/random.1rtt/600s.(padding len).(padding gap).(X25519 PrivateKey).(ML-KEM-768 Seed)..."
|
||||
# 下面两项如果填写则开启 tls(需要同时填写)
|
||||
# certificate: ./server.crt
|
||||
# private-key: ./server.key
|
||||
|
@@ -99,6 +99,15 @@ func TestInboundVless_Encryption(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
paddings := []struct {
|
||||
name string
|
||||
data string
|
||||
}{
|
||||
{"unconfigured-padding", ""},
|
||||
{"default-padding", "100-111-1111.75-0-111.50-0-3333."},
|
||||
{"old-padding", "100-100-1000."}, // Xray-core v25.8.29
|
||||
{"custom-padding", "100-1234-7890.33-0-1111.66-0-6666.55-111-777."},
|
||||
}
|
||||
var modes = []string{
|
||||
"native",
|
||||
"xorpub",
|
||||
@@ -107,19 +116,26 @@ func TestInboundVless_Encryption(t *testing.T) {
|
||||
for i := range modes {
|
||||
mode := modes[i]
|
||||
t.Run(mode, func(t *testing.T) {
|
||||
inboundOptions := inbound.VlessOption{
|
||||
Decryption: "mlkem768x25519plus." + mode + ".600s." + privateKeyBase64 + "." + seedBase64,
|
||||
t.Parallel()
|
||||
for i := range paddings {
|
||||
padding := paddings[i].data
|
||||
t.Run(paddings[i].name, func(t *testing.T) {
|
||||
inboundOptions := inbound.VlessOption{
|
||||
Decryption: "mlkem768x25519plus." + mode + ".600s." + padding + privateKeyBase64 + "." + seedBase64,
|
||||
}
|
||||
outboundOptions := outbound.VlessOption{
|
||||
Encryption: "mlkem768x25519plus." + mode + ".0rtt." + padding + passwordBase64 + "." + clientBase64,
|
||||
}
|
||||
testInboundVless(t, inboundOptions, outboundOptions)
|
||||
t.Run("xtls-rprx-vision", func(t *testing.T) {
|
||||
outboundOptions := outboundOptions
|
||||
outboundOptions.Flow = "xtls-rprx-vision"
|
||||
testInboundVless(t, inboundOptions, outboundOptions)
|
||||
})
|
||||
})
|
||||
}
|
||||
outboundOptions := outbound.VlessOption{
|
||||
Encryption: "mlkem768x25519plus." + mode + ".0rtt." + passwordBase64 + "." + clientBase64,
|
||||
}
|
||||
testInboundVless(t, inboundOptions, outboundOptions)
|
||||
t.Run("xtls-rprx-vision", func(t *testing.T) {
|
||||
outboundOptions := outboundOptions
|
||||
outboundOptions.Flow = "xtls-rprx-vision"
|
||||
testInboundVless(t, inboundOptions, outboundOptions)
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -230,7 +230,7 @@ func (l *Listener) HandleConn(conn net.Conn, tunnel C.Tunnel, additions ...inbou
|
||||
ctx := sing.WithAdditions(context.TODO(), additions...)
|
||||
if l.decryption != nil {
|
||||
var err error
|
||||
conn, err = l.decryption.Handshake(conn)
|
||||
conn, err = l.decryption.Handshake(conn, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@@ -87,7 +87,11 @@ func (s *serverHandler) handle() {
|
||||
_ = s.handleMessage()
|
||||
}()
|
||||
|
||||
<-s.quicConn.HandshakeComplete()
|
||||
select {
|
||||
case <-s.quicConn.HandshakeComplete(): // this chan maybe not closed if handshake never complete
|
||||
case <-time.After(s.quicConn.Config().HandshakeIdleTimeout): // HandshakeIdleTimeout in real conn.Config() never be zero
|
||||
}
|
||||
|
||||
time.AfterFunc(s.AuthenticationTimeout, func() {
|
||||
if s.v4Handler != nil {
|
||||
if s.v4Handler.AuthOk() {
|
||||
|
@@ -7,11 +7,23 @@ import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/blake3"
|
||||
"github.com/metacubex/utls/mlkem"
|
||||
"golang.org/x/sys/cpu"
|
||||
)
|
||||
|
||||
var (
|
||||
// Keep in sync with crypto/tls/cipher_suites.go.
|
||||
hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ && cpu.X86.HasSSE41 && cpu.X86.HasSSSE3
|
||||
hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL
|
||||
hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCTR && cpu.S390X.HasGHASH
|
||||
hasGCMAsmPPC64 = runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le"
|
||||
|
||||
HasAESGCMHardwareSupport = hasGCMAsmAMD64 || hasGCMAsmARM64 || hasGCMAsmS390X || hasGCMAsmPPC64
|
||||
)
|
||||
|
||||
type ClientInstance struct {
|
||||
@@ -21,6 +33,8 @@ type ClientInstance struct {
|
||||
RelaysLength int
|
||||
XorMode uint32
|
||||
Seconds uint32
|
||||
PaddingLens [][3]int
|
||||
PaddingGaps [][3]int
|
||||
|
||||
RWLock sync.RWMutex
|
||||
Expire time.Time
|
||||
@@ -28,15 +42,13 @@ type ClientInstance struct {
|
||||
Ticket []byte
|
||||
}
|
||||
|
||||
func (i *ClientInstance) Init(nfsPKeysBytes [][]byte, xorMode, seconds uint32) (err error) {
|
||||
func (i *ClientInstance) Init(nfsPKeysBytes [][]byte, xorMode, seconds uint32, padding string) (err error) {
|
||||
if i.NfsPKeys != nil {
|
||||
err = errors.New("already initialized")
|
||||
return
|
||||
return errors.New("already initialized")
|
||||
}
|
||||
l := len(nfsPKeysBytes)
|
||||
if l == 0 {
|
||||
err = errors.New("empty nfsPKeysBytes")
|
||||
return
|
||||
return errors.New("empty nfsPKeysBytes")
|
||||
}
|
||||
i.NfsPKeys = make([]any, l)
|
||||
i.NfsPKeysBytes = nfsPKeysBytes
|
||||
@@ -58,18 +70,18 @@ func (i *ClientInstance) Init(nfsPKeysBytes [][]byte, xorMode, seconds uint32) (
|
||||
i.RelaysLength -= 32
|
||||
i.XorMode = xorMode
|
||||
i.Seconds = seconds
|
||||
return
|
||||
return ParsePadding(padding, &i.PaddingLens, &i.PaddingGaps)
|
||||
}
|
||||
|
||||
func (i *ClientInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||
if i.NfsPKeys == nil {
|
||||
return nil, errors.New("uninitialized")
|
||||
}
|
||||
c := NewCommonConn(conn)
|
||||
c := NewCommonConn(conn, HasAESGCMHardwareSupport)
|
||||
|
||||
ivAndRealysLength := 16 + i.RelaysLength
|
||||
pfsKeyExchangeLength := 18 + 1184 + 32 + 16
|
||||
paddingLength := int(randBetween(100, 1000))
|
||||
paddingLength, paddingLens, paddingGaps := CreatPadding(i.PaddingLens, i.PaddingGaps)
|
||||
clientHello := make([]byte, ivAndRealysLength+pfsKeyExchangeLength+paddingLength)
|
||||
|
||||
iv := clientHello[:16]
|
||||
@@ -107,18 +119,18 @@ func (i *ClientInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||
lastCTR.XORKeyStream(relays[index:], i.Hash32s[j+1][:])
|
||||
relays = relays[index+32:]
|
||||
}
|
||||
nfsGCM := NewGCM(iv, nfsKey)
|
||||
nfsAEAD := NewAEAD(iv, nfsKey, c.UseAES)
|
||||
|
||||
if i.Seconds > 0 {
|
||||
i.RWLock.RLock()
|
||||
if time.Now().Before(i.Expire) {
|
||||
c.Client = i
|
||||
c.UnitedKey = append(i.PfsKey, nfsKey...) // different unitedKey for each connection
|
||||
nfsGCM.Seal(clientHello[:ivAndRealysLength], nil, EncodeLength(32), nil)
|
||||
nfsGCM.Seal(clientHello[:ivAndRealysLength+18], nil, i.Ticket, nil)
|
||||
nfsAEAD.Seal(clientHello[:ivAndRealysLength], nil, EncodeLength(32), nil)
|
||||
nfsAEAD.Seal(clientHello[:ivAndRealysLength+18], nil, i.Ticket, nil)
|
||||
i.RWLock.RUnlock()
|
||||
c.PreWrite = clientHello[:ivAndRealysLength+18+32]
|
||||
c.GCM = NewGCM(clientHello[ivAndRealysLength+18:ivAndRealysLength+18+32], c.UnitedKey)
|
||||
c.AEAD = NewAEAD(clientHello[ivAndRealysLength+18:ivAndRealysLength+18+32], c.UnitedKey, c.UseAES)
|
||||
if i.XorMode == 2 {
|
||||
c.Conn = NewXorConn(conn, NewCTR(c.UnitedKey, iv), nil, len(c.PreWrite), 16)
|
||||
}
|
||||
@@ -128,26 +140,34 @@ func (i *ClientInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||
}
|
||||
|
||||
pfsKeyExchange := clientHello[ivAndRealysLength : ivAndRealysLength+pfsKeyExchangeLength]
|
||||
nfsGCM.Seal(pfsKeyExchange[:0], nil, EncodeLength(pfsKeyExchangeLength-18), nil)
|
||||
nfsAEAD.Seal(pfsKeyExchange[:0], nil, EncodeLength(pfsKeyExchangeLength-18), nil)
|
||||
mlkem768DKey, _ := mlkem.GenerateKey768()
|
||||
x25519SKey, _ := ecdh.X25519().GenerateKey(rand.Reader)
|
||||
pfsPublicKey := append(mlkem768DKey.EncapsulationKey().Bytes(), x25519SKey.PublicKey().Bytes()...)
|
||||
nfsGCM.Seal(pfsKeyExchange[:18], nil, pfsPublicKey, nil)
|
||||
nfsAEAD.Seal(pfsKeyExchange[:18], nil, pfsPublicKey, nil)
|
||||
|
||||
padding := clientHello[ivAndRealysLength+pfsKeyExchangeLength:]
|
||||
nfsGCM.Seal(padding[:0], nil, EncodeLength(paddingLength-18), nil)
|
||||
nfsGCM.Seal(padding[:18], nil, padding[18:paddingLength-16], nil)
|
||||
nfsAEAD.Seal(padding[:0], nil, EncodeLength(paddingLength-18), nil)
|
||||
nfsAEAD.Seal(padding[:18], nil, padding[18:paddingLength-16], nil)
|
||||
|
||||
if _, err := conn.Write(clientHello); err != nil {
|
||||
return nil, err
|
||||
paddingLens[0] = ivAndRealysLength + pfsKeyExchangeLength + paddingLens[0]
|
||||
for i, l := range paddingLens { // sends padding in a fragmented way, to create variable traffic pattern, before inner VLESS flow takes control
|
||||
if l > 0 {
|
||||
if _, err := conn.Write(clientHello[:l]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clientHello = clientHello[l:]
|
||||
}
|
||||
if len(paddingGaps) > i {
|
||||
time.Sleep(paddingGaps[i])
|
||||
}
|
||||
}
|
||||
// padding can be sent in a fragmented way, to create variable traffic pattern, before inner VLESS flow takes control
|
||||
|
||||
encryptedPfsPublicKey := make([]byte, 1088+32+16)
|
||||
if _, err := io.ReadFull(conn, encryptedPfsPublicKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nfsGCM.Open(encryptedPfsPublicKey[:0], MaxNonce, encryptedPfsPublicKey, nil)
|
||||
nfsAEAD.Open(encryptedPfsPublicKey[:0], MaxNonce, encryptedPfsPublicKey, nil)
|
||||
mlkem768Key, err := mlkem768DKey.Decapsulate(encryptedPfsPublicKey[:1088])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -164,14 +184,14 @@ func (i *ClientInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||
copy(pfsKey, mlkem768Key)
|
||||
copy(pfsKey[32:], x25519Key)
|
||||
c.UnitedKey = append(pfsKey, nfsKey...)
|
||||
c.GCM = NewGCM(pfsPublicKey, c.UnitedKey)
|
||||
c.PeerGCM = NewGCM(encryptedPfsPublicKey[:1088+32], c.UnitedKey)
|
||||
c.AEAD = NewAEAD(pfsPublicKey, c.UnitedKey, c.UseAES)
|
||||
c.PeerAEAD = NewAEAD(encryptedPfsPublicKey[:1088+32], c.UnitedKey, c.UseAES)
|
||||
|
||||
encryptedTicket := make([]byte, 32)
|
||||
if _, err := io.ReadFull(conn, encryptedTicket); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := c.PeerGCM.Open(encryptedTicket[:0], nil, encryptedTicket, nil); err != nil {
|
||||
if _, err := c.PeerAEAD.Open(encryptedTicket[:0], nil, encryptedTicket, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
seconds := DecodeLength(encryptedTicket)
|
||||
@@ -188,7 +208,7 @@ func (i *ClientInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||
if _, err := io.ReadFull(conn, encryptedLength); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := c.PeerGCM.Open(encryptedLength[:0], nil, encryptedLength, nil); err != nil {
|
||||
if _, err := c.PeerAEAD.Open(encryptedLength[:0], nil, encryptedLength, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
length := DecodeLength(encryptedLength[:2])
|
||||
|
@@ -8,29 +8,34 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/mihomo/common/pool"
|
||||
|
||||
"github.com/metacubex/blake3"
|
||||
"github.com/metacubex/randv2"
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
)
|
||||
|
||||
type CommonConn struct {
|
||||
net.Conn
|
||||
UseAES bool
|
||||
Client *ClientInstance
|
||||
UnitedKey []byte
|
||||
PreWrite []byte
|
||||
GCM *GCM
|
||||
AEAD *AEAD
|
||||
PeerAEAD *AEAD
|
||||
PeerPadding []byte
|
||||
rawInput bytes.Buffer // PeerInBytes
|
||||
PeerGCM *GCM
|
||||
input bytes.Reader // PeerCache
|
||||
}
|
||||
|
||||
func NewCommonConn(conn net.Conn) *CommonConn {
|
||||
func NewCommonConn(conn net.Conn, useAES bool) *CommonConn {
|
||||
return &CommonConn{
|
||||
Conn: conn,
|
||||
Conn: conn,
|
||||
UseAES: useAES,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,12 +54,12 @@ func (c *CommonConn) Write(b []byte) (int, error) {
|
||||
headerAndData := outBytes[:5+len(b)+16]
|
||||
EncodeHeader(headerAndData, len(b)+16)
|
||||
max := false
|
||||
if bytes.Equal(c.GCM.Nonce[:], MaxNonce) {
|
||||
if bytes.Equal(c.AEAD.Nonce[:], MaxNonce) {
|
||||
max = true
|
||||
}
|
||||
c.GCM.Seal(headerAndData[:5], nil, b, headerAndData[:5])
|
||||
c.AEAD.Seal(headerAndData[:5], nil, b, headerAndData[:5])
|
||||
if max {
|
||||
c.GCM = NewGCM(headerAndData, c.UnitedKey)
|
||||
c.AEAD = NewAEAD(headerAndData, c.UnitedKey, c.UseAES)
|
||||
}
|
||||
if c.PreWrite != nil {
|
||||
headerAndData = append(c.PreWrite, headerAndData...)
|
||||
@@ -71,12 +76,12 @@ func (c *CommonConn) Read(b []byte) (int, error) {
|
||||
if len(b) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
if c.PeerGCM == nil { // client's 0-RTT
|
||||
if c.PeerAEAD == nil { // client's 0-RTT
|
||||
serverRandom := make([]byte, 16)
|
||||
if _, err := io.ReadFull(c.Conn, serverRandom); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
c.PeerGCM = NewGCM(serverRandom, c.UnitedKey)
|
||||
c.PeerAEAD = NewAEAD(serverRandom, c.UnitedKey, c.UseAES)
|
||||
if xorConn, ok := c.Conn.(*XorConn); ok {
|
||||
xorConn.PeerCTR = NewCTR(c.UnitedKey, serverRandom)
|
||||
}
|
||||
@@ -85,7 +90,7 @@ func (c *CommonConn) Read(b []byte) (int, error) {
|
||||
if _, err := io.ReadFull(c.Conn, c.PeerPadding); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if _, err := c.PeerGCM.Open(c.PeerPadding[:0], nil, c.PeerPadding, nil); err != nil {
|
||||
if _, err := c.PeerAEAD.Open(c.PeerPadding[:0], nil, c.PeerPadding, nil); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
c.PeerPadding = nil
|
||||
@@ -93,11 +98,11 @@ func (c *CommonConn) Read(b []byte) (int, error) {
|
||||
if c.input.Len() > 0 {
|
||||
return c.input.Read(b)
|
||||
}
|
||||
peerHeader := make([]byte, 5)
|
||||
if _, err := io.ReadFull(c.Conn, peerHeader); err != nil {
|
||||
peerHeader := [5]byte{}
|
||||
if _, err := io.ReadFull(c.Conn, peerHeader[:]); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
l, err := DecodeHeader(peerHeader) // l: 17~17000
|
||||
l, err := DecodeHeader(peerHeader[:]) // l: 17~17000
|
||||
if err != nil {
|
||||
if c.Client != nil && errors.Is(err, ErrInvalidHeader) { // client's 0-RTT
|
||||
c.Client.RWLock.Lock()
|
||||
@@ -110,7 +115,9 @@ func (c *CommonConn) Read(b []byte) (int, error) {
|
||||
return 0, err
|
||||
}
|
||||
c.Client = nil
|
||||
c.rawInput.Grow(l)
|
||||
if c.rawInput.Cap() < l {
|
||||
c.rawInput.Grow(l) // no need to use sync.Pool, because we are always reading
|
||||
}
|
||||
peerData := c.rawInput.Bytes()[:l]
|
||||
if _, err := io.ReadFull(c.Conn, peerData); err != nil {
|
||||
return 0, err
|
||||
@@ -119,13 +126,13 @@ func (c *CommonConn) Read(b []byte) (int, error) {
|
||||
if len(dst) <= len(b) {
|
||||
dst = b[:len(dst)] // avoids another copy()
|
||||
}
|
||||
var newGCM *GCM
|
||||
if bytes.Equal(c.PeerGCM.Nonce[:], MaxNonce) {
|
||||
newGCM = NewGCM(append(peerHeader, peerData...), c.UnitedKey)
|
||||
var newAEAD *AEAD
|
||||
if bytes.Equal(c.PeerAEAD.Nonce[:], MaxNonce) {
|
||||
newAEAD = NewAEAD(append(peerHeader[:], peerData...), c.UnitedKey, c.UseAES)
|
||||
}
|
||||
_, err = c.PeerGCM.Open(dst[:0], nil, peerData, peerHeader)
|
||||
if newGCM != nil {
|
||||
c.PeerGCM = newGCM
|
||||
_, err = c.PeerAEAD.Open(dst[:0], nil, peerData, peerHeader[:])
|
||||
if newAEAD != nil {
|
||||
c.PeerAEAD = newAEAD
|
||||
}
|
||||
if err != nil {
|
||||
return 0, err
|
||||
@@ -137,28 +144,32 @@ func (c *CommonConn) Read(b []byte) (int, error) {
|
||||
return len(dst), nil
|
||||
}
|
||||
|
||||
type GCM struct {
|
||||
type AEAD struct {
|
||||
cipher.AEAD
|
||||
Nonce [12]byte
|
||||
}
|
||||
|
||||
func NewGCM(ctx, key []byte) *GCM {
|
||||
func NewAEAD(ctx, key []byte, useAES bool) *AEAD {
|
||||
k := make([]byte, 32)
|
||||
blake3.DeriveKey(k, string(ctx), key)
|
||||
block, _ := aes.NewCipher(k)
|
||||
aead, _ := cipher.NewGCM(block)
|
||||
return &GCM{AEAD: aead}
|
||||
//chacha20poly1305.New()
|
||||
var aead cipher.AEAD
|
||||
if useAES {
|
||||
block, _ := aes.NewCipher(k)
|
||||
aead, _ = cipher.NewGCM(block)
|
||||
} else {
|
||||
aead, _ = chacha20poly1305.New(k)
|
||||
}
|
||||
return &AEAD{AEAD: aead}
|
||||
}
|
||||
|
||||
func (a *GCM) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
|
||||
func (a *AEAD) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
|
||||
if nonce == nil {
|
||||
nonce = IncreaseNonce(a.Nonce[:])
|
||||
}
|
||||
return a.AEAD.Seal(dst, nonce, plaintext, additionalData)
|
||||
}
|
||||
|
||||
func (a *GCM) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
|
||||
func (a *AEAD) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
|
||||
if nonce == nil {
|
||||
nonce = IncreaseNonce(a.Nonce[:])
|
||||
}
|
||||
@@ -206,9 +217,80 @@ func DecodeHeader(h []byte) (l int, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func ParsePadding(padding string, paddingLens, paddingGaps *[][3]int) (err error) {
|
||||
if padding == "" {
|
||||
return
|
||||
}
|
||||
maxLen := 0
|
||||
for i, s := range strings.Split(padding, ".") {
|
||||
x := strings.Split(s, "-")
|
||||
if len(x) < 3 || x[0] == "" || x[1] == "" || x[2] == "" {
|
||||
return errors.New("invalid padding lenth/gap parameter: " + s)
|
||||
}
|
||||
y := [3]int{}
|
||||
if y[0], err = strconv.Atoi(x[0]); err != nil {
|
||||
return
|
||||
}
|
||||
if y[1], err = strconv.Atoi(x[1]); err != nil {
|
||||
return
|
||||
}
|
||||
if y[2], err = strconv.Atoi(x[2]); err != nil {
|
||||
return
|
||||
}
|
||||
if i == 0 && (y[0] < 100 || y[1] < 18+17 || y[2] < 18+17) {
|
||||
return errors.New("first padding length must not be smaller than 35")
|
||||
}
|
||||
if i%2 == 0 {
|
||||
*paddingLens = append(*paddingLens, y)
|
||||
maxLen += max(y[1], y[2])
|
||||
} else {
|
||||
*paddingGaps = append(*paddingGaps, y)
|
||||
}
|
||||
}
|
||||
if maxLen > 18+65535 {
|
||||
return errors.New("total padding length must not be larger than 65553")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func CreatPadding(paddingLens, paddingGaps [][3]int) (length int, lens []int, gaps []time.Duration) {
|
||||
if len(paddingLens) == 0 {
|
||||
paddingLens = [][3]int{{100, 111, 1111}, {50, 0, 3333}}
|
||||
paddingGaps = [][3]int{{75, 0, 111}}
|
||||
}
|
||||
for _, y := range paddingLens {
|
||||
l := 0
|
||||
if y[0] >= int(randBetween(0, 100)) {
|
||||
l = int(randBetween(int64(y[1]), int64(y[2])))
|
||||
}
|
||||
lens = append(lens, l)
|
||||
length += l
|
||||
}
|
||||
for _, y := range paddingGaps {
|
||||
g := 0
|
||||
if y[0] >= int(randBetween(0, 100)) {
|
||||
g = int(randBetween(int64(y[1]), int64(y[2])))
|
||||
}
|
||||
gaps = append(gaps, time.Duration(g)*time.Millisecond)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func max[T ~int | ~uint | ~int64 | ~uint64 | ~int32 | ~uint32 | ~int16 | ~uint16 | ~int8 | ~uint8](a, b T) T {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func randBetween(from int64, to int64) int64 {
|
||||
if from == to {
|
||||
return from
|
||||
}
|
||||
|
||||
if to < from {
|
||||
from, to = to, from
|
||||
}
|
||||
|
||||
return from + randv2.Int64N(to-from)
|
||||
}
|
||||
|
@@ -21,4 +21,7 @@
|
||||
// https://github.com/XTLS/Xray-core/commit/0199dea39988a1a1b846d0bf8598631bade40902
|
||||
// https://github.com/XTLS/Xray-core/commit/fce1195b60f48ca18a953dbd5c7d991869de9a5e
|
||||
// https://github.com/XTLS/Xray-core/commit/b0b220985c9c1bc832665458d5fd6e0c287b67ae
|
||||
// https://github.com/XTLS/Xray-core/commit/82ea7a3cc5ff23280b87e3052f0f83b04f0267fa
|
||||
// https://github.com/XTLS/Xray-core/commit/e8b02cd6649f14889841e8ab8ee6b2acca71dbe6
|
||||
// https://github.com/XTLS/Xray-core/commit/6768a22f676c9121cfc9dc4f51181a8a07837c8d
|
||||
package encryption
|
||||
|
@@ -34,7 +34,12 @@ func NewClient(encryption string) (*ClientInstance, error) {
|
||||
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
|
||||
}
|
||||
var nfsPKeysBytes [][]byte
|
||||
var paddings []string
|
||||
for _, r := range s[3:] {
|
||||
if len(r) < 20 {
|
||||
paddings = append(paddings, r)
|
||||
continue
|
||||
}
|
||||
b, err := base64.RawURLEncoding.DecodeString(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
|
||||
@@ -44,8 +49,9 @@ func NewClient(encryption string) (*ClientInstance, error) {
|
||||
}
|
||||
nfsPKeysBytes = append(nfsPKeysBytes, b)
|
||||
}
|
||||
padding := strings.Join(paddings, ".")
|
||||
client := &ClientInstance{}
|
||||
if err := client.Init(nfsPKeysBytes, xorMode, seconds); err != nil {
|
||||
if err := client.Init(nfsPKeysBytes, xorMode, seconds, padding); err != nil {
|
||||
return nil, fmt.Errorf("failed to use encryption: %w", err)
|
||||
}
|
||||
return client, nil
|
||||
@@ -84,7 +90,12 @@ func NewServer(decryption string) (*ServerInstance, error) {
|
||||
seconds = uint32(i)
|
||||
}
|
||||
var nfsSKeysBytes [][]byte
|
||||
var paddings []string
|
||||
for _, r := range s[3:] {
|
||||
if len(r) < 20 {
|
||||
paddings = append(paddings, r)
|
||||
continue
|
||||
}
|
||||
b, err := base64.RawURLEncoding.DecodeString(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
|
||||
@@ -94,8 +105,9 @@ func NewServer(decryption string) (*ServerInstance, error) {
|
||||
}
|
||||
nfsSKeysBytes = append(nfsSKeysBytes, b)
|
||||
}
|
||||
padding := strings.Join(paddings, ".")
|
||||
server := &ServerInstance{}
|
||||
if err := server.Init(nfsSKeysBytes, xorMode, seconds); err != nil {
|
||||
if err := server.Init(nfsSKeysBytes, xorMode, seconds, padding); err != nil {
|
||||
return nil, fmt.Errorf("failed to use decryption: %w", err)
|
||||
}
|
||||
return server, nil
|
||||
|
@@ -29,21 +29,21 @@ type ServerInstance struct {
|
||||
RelaysLength int
|
||||
XorMode uint32
|
||||
Seconds uint32
|
||||
PaddingLens [][3]int
|
||||
PaddingGaps [][3]int
|
||||
|
||||
RWLock sync.RWMutex
|
||||
Sessions map[[16]byte]*ServerSession
|
||||
Closed bool
|
||||
}
|
||||
|
||||
func (i *ServerInstance) Init(nfsSKeysBytes [][]byte, xorMode, seconds uint32) (err error) {
|
||||
func (i *ServerInstance) Init(nfsSKeysBytes [][]byte, xorMode, seconds uint32, padding string) (err error) {
|
||||
if i.NfsSKeys != nil {
|
||||
err = errors.New("already initialized")
|
||||
return
|
||||
return errors.New("already initialized")
|
||||
}
|
||||
l := len(nfsSKeysBytes)
|
||||
if l == 0 {
|
||||
err = errors.New("empty nfsSKeysBytes")
|
||||
return
|
||||
return errors.New("empty nfsSKeysBytes")
|
||||
}
|
||||
i.NfsSKeys = make([]any, l)
|
||||
i.NfsPKeysBytes = make([][]byte, l)
|
||||
@@ -87,7 +87,7 @@ func (i *ServerInstance) Init(nfsSKeysBytes [][]byte, xorMode, seconds uint32) (
|
||||
}
|
||||
}()
|
||||
}
|
||||
return
|
||||
return ParsePadding(padding, &i.PaddingLens, &i.PaddingGaps)
|
||||
}
|
||||
|
||||
func (i *ServerInstance) Close() (err error) {
|
||||
@@ -97,16 +97,19 @@ func (i *ServerInstance) Close() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||
func (i *ServerInstance) Handshake(conn net.Conn, fallback *[]byte) (*CommonConn, error) {
|
||||
if i.NfsSKeys == nil {
|
||||
return nil, errors.New("uninitialized")
|
||||
}
|
||||
c := NewCommonConn(conn)
|
||||
c := NewCommonConn(conn, true)
|
||||
|
||||
ivAndRelays := make([]byte, 16+i.RelaysLength)
|
||||
if _, err := io.ReadFull(conn, ivAndRelays); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if fallback != nil {
|
||||
*fallback = append(*fallback, ivAndRelays...)
|
||||
}
|
||||
iv := ivAndRelays[:16]
|
||||
relays := ivAndRelays[16:]
|
||||
var nfsKey []byte
|
||||
@@ -150,16 +153,27 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||
}
|
||||
relays = relays[32:]
|
||||
}
|
||||
nfsGCM := NewGCM(iv, nfsKey)
|
||||
nfsAEAD := NewAEAD(iv, nfsKey, c.UseAES)
|
||||
|
||||
encryptedLength := make([]byte, 18)
|
||||
if _, err := io.ReadFull(conn, encryptedLength); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := nfsGCM.Open(encryptedLength[:0], nil, encryptedLength, nil); err != nil {
|
||||
return nil, err
|
||||
if fallback != nil {
|
||||
*fallback = append(*fallback, encryptedLength...)
|
||||
}
|
||||
length := DecodeLength(encryptedLength[:2])
|
||||
decryptedLength := make([]byte, 2)
|
||||
if _, err := nfsAEAD.Open(decryptedLength[:0], nil, encryptedLength, nil); err != nil {
|
||||
c.UseAES = !c.UseAES
|
||||
nfsAEAD = NewAEAD(iv, nfsKey, c.UseAES)
|
||||
if _, err := nfsAEAD.Open(decryptedLength[:0], nil, encryptedLength, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if fallback != nil {
|
||||
*fallback = nil
|
||||
}
|
||||
length := DecodeLength(decryptedLength)
|
||||
|
||||
if length == 32 {
|
||||
if i.Seconds == 0 {
|
||||
@@ -169,7 +183,7 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||
if _, err := io.ReadFull(conn, encryptedTicket); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ticket, err := nfsGCM.Open(nil, nil, encryptedTicket, nil)
|
||||
ticket, err := nfsAEAD.Open(nil, nil, encryptedTicket, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -177,7 +191,7 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||
s := i.Sessions[[16]byte(ticket)]
|
||||
i.RWLock.RUnlock()
|
||||
if s == nil {
|
||||
noises := make([]byte, randBetween(1268, 2268)) // matches 1-RTT's server hello length for "random", though it is not important, just for example
|
||||
noises := make([]byte, randBetween(1279, 2279)) // matches 1-RTT's server hello length for "random", though it is not important, just for example
|
||||
var err error
|
||||
for err == nil {
|
||||
rand.Read(noises)
|
||||
@@ -192,8 +206,8 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||
c.UnitedKey = append(s.PfsKey, nfsKey...) // the same nfsKey links the upload & download (prevents server -> client's another request)
|
||||
c.PreWrite = make([]byte, 16)
|
||||
rand.Read(c.PreWrite) // always trust yourself, not the client (also prevents being parsed as TLS thus causing false interruption for "native" and "xorpub")
|
||||
c.GCM = NewGCM(c.PreWrite, c.UnitedKey)
|
||||
c.PeerGCM = NewGCM(encryptedTicket, c.UnitedKey) // unchangeable ctx (prevents server -> server), and different ctx length for upload / download (prevents client -> client)
|
||||
c.AEAD = NewAEAD(c.PreWrite, c.UnitedKey, c.UseAES)
|
||||
c.PeerAEAD = NewAEAD(encryptedTicket, c.UnitedKey, c.UseAES) // unchangeable ctx (prevents server -> server), and different ctx length for upload / download (prevents client -> client)
|
||||
if i.XorMode == 2 {
|
||||
c.Conn = NewXorConn(conn, NewCTR(c.UnitedKey, c.PreWrite), NewCTR(c.UnitedKey, iv), 16, 0) // it doesn't matter if the attacker sends client's iv back to the client
|
||||
}
|
||||
@@ -207,7 +221,7 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||
if _, err := io.ReadFull(conn, encryptedPfsPublicKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := nfsGCM.Open(encryptedPfsPublicKey[:0], nil, encryptedPfsPublicKey, nil); err != nil {
|
||||
if _, err := nfsAEAD.Open(encryptedPfsPublicKey[:0], nil, encryptedPfsPublicKey, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mlkem768EKey, err := mlkem.NewEncapsulationKey768(encryptedPfsPublicKey[:1184])
|
||||
@@ -229,27 +243,12 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||
copy(pfsKey[32:], x25519Key)
|
||||
pfsPublicKey := append(encapsulatedPfsKey, x25519SKey.PublicKey().Bytes()...)
|
||||
c.UnitedKey = append(pfsKey, nfsKey...)
|
||||
c.GCM = NewGCM(pfsPublicKey, c.UnitedKey)
|
||||
c.PeerGCM = NewGCM(encryptedPfsPublicKey[:1184+32], c.UnitedKey)
|
||||
c.AEAD = NewAEAD(pfsPublicKey, c.UnitedKey, c.UseAES)
|
||||
c.PeerAEAD = NewAEAD(encryptedPfsPublicKey[:1184+32], c.UnitedKey, c.UseAES)
|
||||
|
||||
ticket := make([]byte, 16)
|
||||
rand.Read(ticket)
|
||||
copy(ticket, EncodeLength(int(i.Seconds*4/5)))
|
||||
|
||||
pfsKeyExchangeLength := 1088 + 32 + 16
|
||||
encryptedTicketLength := 32
|
||||
paddingLength := int(randBetween(100, 1000))
|
||||
serverHello := make([]byte, pfsKeyExchangeLength+encryptedTicketLength+paddingLength)
|
||||
nfsGCM.Seal(serverHello[:0], MaxNonce, pfsPublicKey, nil)
|
||||
c.GCM.Seal(serverHello[:pfsKeyExchangeLength], nil, ticket, nil)
|
||||
padding := serverHello[pfsKeyExchangeLength+encryptedTicketLength:]
|
||||
c.GCM.Seal(padding[:0], nil, EncodeLength(paddingLength-18), nil)
|
||||
c.GCM.Seal(padding[:18], nil, padding[18:paddingLength-16], nil)
|
||||
|
||||
if _, err := conn.Write(serverHello); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// padding can be sent in a fragmented way, to create variable traffic pattern, before inner VLESS flow takes control
|
||||
|
||||
if i.Seconds > 0 {
|
||||
i.RWLock.Lock()
|
||||
i.Sessions[[16]byte(ticket)] = &ServerSession{
|
||||
@@ -259,18 +258,41 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||
i.RWLock.Unlock()
|
||||
}
|
||||
|
||||
pfsKeyExchangeLength := 1088 + 32 + 16
|
||||
encryptedTicketLength := 32
|
||||
paddingLength, paddingLens, paddingGaps := CreatPadding(i.PaddingLens, i.PaddingGaps)
|
||||
serverHello := make([]byte, pfsKeyExchangeLength+encryptedTicketLength+paddingLength)
|
||||
nfsAEAD.Seal(serverHello[:0], MaxNonce, pfsPublicKey, nil)
|
||||
c.AEAD.Seal(serverHello[:pfsKeyExchangeLength], nil, ticket, nil)
|
||||
padding := serverHello[pfsKeyExchangeLength+encryptedTicketLength:]
|
||||
c.AEAD.Seal(padding[:0], nil, EncodeLength(paddingLength-18), nil)
|
||||
c.AEAD.Seal(padding[:18], nil, padding[18:paddingLength-16], nil)
|
||||
|
||||
paddingLens[0] = pfsKeyExchangeLength + encryptedTicketLength + paddingLens[0]
|
||||
for i, l := range paddingLens { // sends padding in a fragmented way, to create variable traffic pattern, before inner VLESS flow takes control
|
||||
if l > 0 {
|
||||
if _, err := conn.Write(serverHello[:l]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
serverHello = serverHello[l:]
|
||||
}
|
||||
if len(paddingGaps) > i {
|
||||
time.Sleep(paddingGaps[i])
|
||||
}
|
||||
}
|
||||
|
||||
// important: allows client sends padding slowly, eliminating 1-RTT's traffic pattern
|
||||
if _, err := io.ReadFull(conn, encryptedLength); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := nfsGCM.Open(encryptedLength[:0], nil, encryptedLength, nil); err != nil {
|
||||
if _, err := nfsAEAD.Open(encryptedLength[:0], nil, encryptedLength, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
encryptedPadding := make([]byte, DecodeLength(encryptedLength[:2]))
|
||||
if _, err := io.ReadFull(conn, encryptedPadding); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := nfsGCM.Open(encryptedPadding[:0], nil, encryptedPadding, nil); err != nil {
|
||||
if _, err := nfsAEAD.Open(encryptedPadding[:0], nil, encryptedPadding, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@@ -185,6 +185,10 @@ if has_xray then
|
||||
o.default = "10-20"
|
||||
o:depends("fragment", true)
|
||||
|
||||
o = s_xray:option(Value, "fragment_maxSplit", translate("Max Split"), translate("Limit the maximum number of splits."))
|
||||
o.default = "100-200"
|
||||
o:depends("fragment", true)
|
||||
|
||||
o = s_xray:option(Flag, "noise", translate("Noise"), translate("UDP noise, Under some circumstances it can bypass some UDP based protocol restrictions."))
|
||||
o.default = 0
|
||||
|
||||
@@ -238,6 +242,11 @@ if has_xray then
|
||||
o = s_xray_noise:option(Value, "delay", translate("Delay (ms)"))
|
||||
o.datatype = "or(uinteger,portrange)"
|
||||
o.rmempty = false
|
||||
|
||||
o = s_xray_noise:option(ListValue, "applyTo", translate("IP Type"))
|
||||
o:value("ip", "ALL")
|
||||
o:value("ipv4", "IPv4")
|
||||
o:value("ipv6", "IPv6")
|
||||
end
|
||||
|
||||
if has_singbox then
|
||||
|
@@ -429,6 +429,18 @@ function is_ipv6(val)
|
||||
return false
|
||||
end
|
||||
|
||||
function is_local_ip(ip)
|
||||
ip = tostring(ip or ""):lower()
|
||||
ip = ip:gsub("^[%w%d]+://", "") -- 去掉协议头
|
||||
:gsub("/.*$", "") -- 去掉路径
|
||||
:gsub("^%[", ""):gsub("%]$", "") -- 去掉IPv6方括号
|
||||
:gsub(":%d+$", "") -- 去掉端口
|
||||
return ip:match("^127%.") or ip:match("^10%.") or
|
||||
ip:match("^172%.1[6-9]%.") or ip:match("^172%.2[0-9]%.") or
|
||||
ip:match("^172%.3[0-1]%.") or ip:match("^192%.168%.") or
|
||||
ip == "::1" or ip:match("^f[cd]") or ip:match("^fe[89ab]")
|
||||
end
|
||||
|
||||
function is_ipv6addrport(val)
|
||||
if is_ipv6(val) then
|
||||
local address, port = val:match('%[(.*)%]:([^:]+)$')
|
||||
|
@@ -1555,6 +1555,9 @@ function gen_config(var)
|
||||
end
|
||||
|
||||
if remote_server.address then
|
||||
if api.is_local_ip(remote_server.address) then --dns为本地ip,不走代理
|
||||
remote_server.detour = "direct"
|
||||
end
|
||||
table.insert(dns.servers, remote_server)
|
||||
end
|
||||
|
||||
@@ -1610,6 +1613,9 @@ function gen_config(var)
|
||||
end
|
||||
|
||||
if remote_server.server then
|
||||
if api.is_local_ip(remote_server.server) then --dns为本地ip,不走代理
|
||||
remote_server.detour = "direct"
|
||||
end
|
||||
table.insert(dns.servers, remote_server)
|
||||
end
|
||||
|
||||
@@ -1759,7 +1765,9 @@ function gen_config(var)
|
||||
if value.outboundTag ~= COMMON.default_outbound_tag and (remote_server.address or remote_server.server) then
|
||||
local remote_shunt_server = api.clone(remote_server)
|
||||
remote_shunt_server.tag = value.outboundTag
|
||||
remote_shunt_server.detour = value.outboundTag
|
||||
local is_local = (remote_server.address and api.is_local_ip(remote_server.address)) or
|
||||
(remote_server.server and api.is_local_ip(remote_server.server)) --dns为本地ip,不走代理
|
||||
remote_shunt_server.detour = is_local and "direct" or value.outboundTag
|
||||
table.insert(dns.servers, remote_shunt_server)
|
||||
dns_rule.server = remote_shunt_server.tag
|
||||
end
|
||||
|
@@ -23,7 +23,8 @@ local function get_noise_packets()
|
||||
local noise = (n.enabled == "1") and {
|
||||
type = n.type,
|
||||
packet = n.packet,
|
||||
delay = string.find(n.delay, "-") and n.delay or tonumber(n.delay)
|
||||
delay = string.find(n.delay, "-") and n.delay or tonumber(n.delay),
|
||||
applyTo = n.applyTo
|
||||
} or nil
|
||||
table.insert(noises, noise)
|
||||
end)
|
||||
@@ -1370,25 +1371,37 @@ function gen_config(var)
|
||||
end
|
||||
|
||||
if dns_server then
|
||||
local outboundTag, balancerTag
|
||||
if not api.is_local_ip(dns_server.address) or value.outboundTag == "blackhole" then --dns为本地ip,不走代理
|
||||
outboundTag = value.outboundTag
|
||||
balancerTag = value.balancerTag
|
||||
else
|
||||
outboundTag = "direct"
|
||||
balancerTag = nil
|
||||
end
|
||||
table.insert(dns.servers, dns_server)
|
||||
table.insert(routing.rules, {
|
||||
inboundTag = {
|
||||
dns_server.tag
|
||||
},
|
||||
outboundTag = value.outboundTag or nil,
|
||||
balancerTag = value.balancerTag or nil
|
||||
inboundTag = { dns_server.tag },
|
||||
outboundTag = outboundTag,
|
||||
balancerTag = balancerTag
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local _outboundTag, _balancerTag
|
||||
if not api.is_local_ip(_remote_dns.address) or dns_outbound_tag == "blackhole" then --dns为本地ip,不走代理
|
||||
_outboundTag = dns_outbound_tag
|
||||
_balancerTag = COMMON.default_balancer_tag
|
||||
else
|
||||
_outboundTag = "direct"
|
||||
_balancerTag = nil
|
||||
end
|
||||
table.insert(routing.rules, {
|
||||
inboundTag = {
|
||||
"dns-global"
|
||||
},
|
||||
balancerTag = COMMON.default_balancer_tag,
|
||||
outboundTag = dns_outbound_tag
|
||||
inboundTag = { "dns-global" },
|
||||
balancerTag = _balancerTag,
|
||||
outboundTag = _outboundTag
|
||||
})
|
||||
|
||||
local default_rule_index = nil
|
||||
@@ -1460,7 +1473,8 @@ function gen_config(var)
|
||||
fragment = (xray_settings.fragment == "1") and {
|
||||
packets = (xray_settings.fragment_packets and xray_settings.fragment_packets ~= "") and xray_settings.fragment_packets,
|
||||
length = (xray_settings.fragment_length and xray_settings.fragment_length ~= "") and xray_settings.fragment_length,
|
||||
interval = (xray_settings.fragment_interval and xray_settings.fragment_interval ~= "") and xray_settings.fragment_interval
|
||||
interval = (xray_settings.fragment_interval and xray_settings.fragment_interval ~= "") and xray_settings.fragment_interval,
|
||||
maxSplit = (xray_settings.fragment_maxSplit and xray_settings.fragment_maxSplit ~= "") and xray_settings.fragment_maxSplit
|
||||
} or nil,
|
||||
noises = (xray_settings.noise == "1") and get_noise_packets() or nil
|
||||
},
|
||||
|
@@ -1759,6 +1759,12 @@ msgstr "分片间隔"
|
||||
msgid "Fragmentation interval (ms)"
|
||||
msgstr "分片间隔(ms)"
|
||||
|
||||
msgid "Max Split"
|
||||
msgstr "最大分片数"
|
||||
|
||||
msgid "Limit the maximum number of splits."
|
||||
msgstr "限制分片的最大数量。"
|
||||
|
||||
msgid "Split handshake data into multiple TLS records for better censorship evasion. Low overhead. Recommended to enable first."
|
||||
msgstr "将握手数据拆分为多个 TLS 记录,提升抗封锁能力,几乎不增加延迟,建议优先启用。"
|
||||
|
||||
@@ -1783,6 +1789,9 @@ msgstr "数据包"
|
||||
msgid "Delay (ms)"
|
||||
msgstr "延迟(ms)"
|
||||
|
||||
msgid "IP Type"
|
||||
msgstr "IP 类型"
|
||||
|
||||
msgid "If is domain name, The requested domain name will be resolved to IP before connect."
|
||||
msgstr "如果是域名,域名将在请求发出之前解析为 IP。"
|
||||
|
||||
|
@@ -214,14 +214,7 @@ if DNS_MODE == "socks" then
|
||||
|
||||
end
|
||||
|
||||
-- 判断是否为本地地址
|
||||
local is_local = w:match("127%.0%.0%.")
|
||||
or w:match("192%.168%.")
|
||||
or w:match("10%.")
|
||||
or w:match("172%.1[6-9]%.")
|
||||
or w:match("172%.2[0-9]%.")
|
||||
or w:match("172%.3[0-1]%.")
|
||||
if not is_local then
|
||||
if not api.is_local_ip(w) then
|
||||
server_param = server_param .. " -proxy " .. proxy_server_name
|
||||
end
|
||||
|
||||
|
318
shadowsocks-rust/Cargo.lock
generated
318
shadowsocks-rust/Cargo.lock
generated
@@ -121,9 +121,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.19"
|
||||
version = "0.6.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933"
|
||||
checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
@@ -151,29 +151,29 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.3"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9"
|
||||
checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.9"
|
||||
version = "3.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882"
|
||||
checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.98"
|
||||
version = "1.0.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
||||
checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100"
|
||||
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
@@ -195,9 +195,9 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
||||
|
||||
[[package]]
|
||||
name = "async-channel"
|
||||
version = "2.4.0"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16c74e56284d2188cabb6ad99603d1ace887a5d7e7b695d01b728155ed9ed427"
|
||||
checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2"
|
||||
dependencies = [
|
||||
"concurrent-queue",
|
||||
"event-listener-strategy",
|
||||
@@ -213,13 +213,13 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.88"
|
||||
version = "0.1.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5"
|
||||
checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -273,7 +273,7 @@ version = "0.72.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f72209734318d0b619a5e0f5129918b848c416e122a3c4ce054e03cb87b726f"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"bitflags 2.9.3",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"itertools",
|
||||
@@ -282,7 +282,7 @@ dependencies = [
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -293,9 +293,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.9.1"
|
||||
version = "2.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
||||
checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d"
|
||||
|
||||
[[package]]
|
||||
name = "bitvec"
|
||||
@@ -333,9 +333,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "blocking"
|
||||
version = "1.6.1"
|
||||
version = "1.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea"
|
||||
checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21"
|
||||
dependencies = [
|
||||
"async-channel",
|
||||
"async-task",
|
||||
@@ -409,7 +409,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -478,9 +478,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.28"
|
||||
version = "1.2.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ad45f4f74e4e20eaa392913b7b33a7091c87e59628f4dd27888205ad888843c"
|
||||
checksum = "42bc4aea80032b7bf409b0bc7ccad88853858911b7713a8062fdc0623867bedc"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
@@ -679,9 +679,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.4.2"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
|
||||
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
@@ -792,7 +792,7 @@ dependencies = [
|
||||
"proc-macro-error2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -880,7 +880,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -900,7 +900,7 @@ checksum = "0b0713d5c1d52e774c5cd7bb8b043d7c0fc4f921abfb678556140bfbe6ab2364"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -966,7 +966,7 @@ dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1019,9 +1019,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
version = "5.4.0"
|
||||
version = "5.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae"
|
||||
checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab"
|
||||
dependencies = [
|
||||
"concurrent-queue",
|
||||
"parking",
|
||||
@@ -1159,9 +1159,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
|
||||
|
||||
[[package]]
|
||||
name = "futures-lite"
|
||||
version = "2.6.0"
|
||||
version = "2.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532"
|
||||
checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"pin-project-lite",
|
||||
@@ -1175,7 +1175,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1210,9 +1210,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "generator"
|
||||
version = "0.8.5"
|
||||
version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d18470a76cb7f8ff746cf1f7470914f900252ec36bbc40b569d74b1258446827"
|
||||
checksum = "605183a538e3e2a9c1038635cc5c2d194e2ee8fd0d1b66b8349fad7dbacce5a2"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
@@ -1256,7 +1256,7 @@ dependencies = [
|
||||
"js-sys",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"wasi 0.14.2+wasi-0.2.4",
|
||||
"wasi 0.14.3+wasi-0.2.4",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
@@ -1278,9 +1278,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.2"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
|
||||
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
|
||||
|
||||
[[package]]
|
||||
name = "group"
|
||||
@@ -1295,9 +1295,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.4.11"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17da50a276f1e01e0ba6c029e47b7100754904ee8a278f886546e98575380785"
|
||||
checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386"
|
||||
dependencies = [
|
||||
"atomic-waker",
|
||||
"bytes",
|
||||
@@ -1351,9 +1351,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.4"
|
||||
version = "0.15.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
|
||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||
|
||||
[[package]]
|
||||
name = "heapless"
|
||||
@@ -1568,9 +1568,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.14"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb"
|
||||
checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bytes",
|
||||
@@ -1584,7 +1584,7 @@ dependencies = [
|
||||
"libc",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"socket2 0.5.10",
|
||||
"socket2 0.6.0",
|
||||
"system-configuration",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
@@ -1725,9 +1725,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.10.0"
|
||||
version = "2.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661"
|
||||
checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
@@ -1739,7 +1739,7 @@ version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"bitflags 2.9.3",
|
||||
"inotify-sys",
|
||||
"libc",
|
||||
]
|
||||
@@ -1764,11 +1764,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "io-uring"
|
||||
version = "0.7.8"
|
||||
version = "0.7.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013"
|
||||
checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"bitflags 2.9.3",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
]
|
||||
@@ -1872,14 +1872,14 @@ checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.33"
|
||||
version = "0.1.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a"
|
||||
checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
|
||||
dependencies = [
|
||||
"getrandom 0.3.3",
|
||||
"libc",
|
||||
@@ -1945,7 +1945,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets 0.53.2",
|
||||
"windows-targets 0.53.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1960,11 +1960,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.4"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1580801010e535496706ba011c15f8532df6b42297d2e471fec38ceadd8c0638"
|
||||
checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"bitflags 2.9.3",
|
||||
"libc",
|
||||
]
|
||||
|
||||
@@ -2209,7 +2209,7 @@ version = "0.30.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"bitflags 2.9.3",
|
||||
"cfg-if",
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
@@ -2231,7 +2231,7 @@ version = "8.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"bitflags 2.9.3",
|
||||
"fsevent-sys",
|
||||
"inotify",
|
||||
"kqueue",
|
||||
@@ -2319,7 +2319,7 @@ version = "0.10.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"bitflags 2.9.3",
|
||||
"cfg-if",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
@@ -2336,7 +2336,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2347,9 +2347,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-src"
|
||||
version = "300.5.1+3.5.1"
|
||||
version = "300.5.2+3.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "735230c832b28c000e3bc117119e6466a663ec73506bc0a9907ea4187508e42a"
|
||||
checksum = "d270b79e2926f5150189d475bc7e9d2c69f9c4697b185fa917d5a32b792d21b4"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
@@ -2469,7 +2469,7 @@ dependencies = [
|
||||
"pest_meta",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2499,7 +2499,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2581,9 +2581,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "potential_utf"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585"
|
||||
checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a"
|
||||
dependencies = [
|
||||
"zerovec",
|
||||
]
|
||||
@@ -2631,14 +2631,14 @@ dependencies = [
|
||||
"proc-macro-error-attr2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.95"
|
||||
version = "1.0.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
||||
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -2651,9 +2651,9 @@ checksum = "d68782463e408eb1e668cf6152704bd856c78c5b6417adaee3203d8f4c1fc9ec"
|
||||
|
||||
[[package]]
|
||||
name = "quinn"
|
||||
version = "0.11.8"
|
||||
version = "0.11.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8"
|
||||
checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"cfg_aliases",
|
||||
@@ -2663,7 +2663,7 @@ dependencies = [
|
||||
"quinn-udp",
|
||||
"rustc-hash",
|
||||
"rustls",
|
||||
"socket2 0.5.10",
|
||||
"socket2 0.6.0",
|
||||
"thiserror 2.0.16",
|
||||
"tokio",
|
||||
"tracing",
|
||||
@@ -2672,9 +2672,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quinn-proto"
|
||||
version = "0.11.12"
|
||||
version = "0.11.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e"
|
||||
checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"getrandom 0.3.3",
|
||||
@@ -2693,16 +2693,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quinn-udp"
|
||||
version = "0.5.13"
|
||||
version = "0.5.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcebb1209ee276352ef14ff8732e24cc2b02bbac986cd74a4c81bcb2f9881970"
|
||||
checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd"
|
||||
dependencies = [
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"socket2 0.5.10",
|
||||
"socket2 0.6.0",
|
||||
"tracing",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2787,18 +2787,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.13"
|
||||
version = "0.5.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6"
|
||||
checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"bitflags 2.9.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.5.0"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b"
|
||||
checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
|
||||
dependencies = [
|
||||
"getrandom 0.2.16",
|
||||
"libredox",
|
||||
@@ -2819,9 +2819,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.9"
|
||||
version = "0.4.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||
checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -2830,9 +2830,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.5"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001"
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
@@ -2973,9 +2973,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.25"
|
||||
version = "0.1.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f"
|
||||
checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
@@ -2994,22 +2994,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.0.7"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
|
||||
checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"bitflags 2.9.3",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.28"
|
||||
version = "0.23.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7160e3e10bf4535308537f3c4e1641468cd0e485175d6163087c0393c7d46643"
|
||||
checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc"
|
||||
dependencies = [
|
||||
"log",
|
||||
"once_cell",
|
||||
@@ -3029,7 +3029,7 @@ dependencies = [
|
||||
"openssl-probe",
|
||||
"rustls-pki-types",
|
||||
"schannel",
|
||||
"security-framework 3.2.0",
|
||||
"security-framework 3.3.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3044,9 +3044,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.103.3"
|
||||
version = "0.103.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435"
|
||||
checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
@@ -3055,9 +3055,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.21"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
|
||||
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
@@ -3103,7 +3103,7 @@ checksum = "22f968c5ea23d555e670b449c1c5e7b2fc399fdaec1d304a17cd48e288abc107"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3125,7 +3125,7 @@ version = "2.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"bitflags 2.9.3",
|
||||
"core-foundation 0.9.4",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
@@ -3134,11 +3134,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "3.2.0"
|
||||
version = "3.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316"
|
||||
checksum = "80fb1d92c5028aa318b4b8bd7302a5bfcf48be96a37fc6fc790f806b0004ee0c"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"bitflags 2.9.3",
|
||||
"core-foundation 0.10.1",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
@@ -3207,7 +3207,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3445,9 +3445,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.5"
|
||||
version = "1.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410"
|
||||
checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
@@ -3597,9 +3597,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.104"
|
||||
version = "2.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
|
||||
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3623,7 +3623,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3638,7 +3638,7 @@ version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"bitflags 2.9.3",
|
||||
"core-foundation 0.9.4",
|
||||
"system-configuration-sys",
|
||||
]
|
||||
@@ -3682,25 +3682,25 @@ checksum = "3b7ad73e635dd232c2c2106d59269f59a61de421cc6b95252d2d932094ff1f40"
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.20.0"
|
||||
version = "3.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1"
|
||||
checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom 0.3.3",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "terminal_size"
|
||||
version = "0.4.2"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed"
|
||||
checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0"
|
||||
dependencies = [
|
||||
"rustix",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3729,7 +3729,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3740,7 +3740,7 @@ checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3807,9 +3807,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.9.0"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71"
|
||||
checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
@@ -3848,7 +3848,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3890,9 +3890,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.15"
|
||||
version = "0.7.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df"
|
||||
checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
@@ -3922,7 +3922,7 @@ version = "0.6.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"bitflags 2.9.3",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http",
|
||||
@@ -3978,7 +3978,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4029,7 +4029,7 @@ checksum = "70977707304198400eb4835a78f6a9f928bf41bba420deb8fdb175cd965d77a7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4143,9 +4143,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.17.0"
|
||||
version = "1.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d"
|
||||
checksum = "f33196643e165781c20a5ead5582283a7dacbb87855d867fbc2df3f81eddc1be"
|
||||
dependencies = [
|
||||
"getrandom 0.3.3",
|
||||
"js-sys",
|
||||
@@ -4198,11 +4198,11 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.14.2+wasi-0.2.4"
|
||||
version = "0.14.3+wasi-0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
|
||||
checksum = "6a51ae83037bdd272a9e28ce236db8c07016dd0d50c27038b3f407533c030c95"
|
||||
dependencies = [
|
||||
"wit-bindgen-rt",
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4227,7 +4227,7 @@ dependencies = [
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -4262,7 +4262,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@@ -4338,11 +4338,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.9"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4405,7 +4405,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4416,7 +4416,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4461,7 +4461,7 @@ version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "193cae8e647981c35bc947fdd57ba7928b1fa0d4a79305f6dd2dc55221ac35ac"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"bitflags 2.9.3",
|
||||
"widestring",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
@@ -4508,7 +4508,7 @@ version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||
dependencies = [
|
||||
"windows-targets 0.53.2",
|
||||
"windows-targets 0.53.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4544,10 +4544,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.2"
|
||||
version = "0.53.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef"
|
||||
checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows_aarch64_gnullvm 0.53.0",
|
||||
"windows_aarch64_msvc 0.53.0",
|
||||
"windows_i686_gnu 0.53.0",
|
||||
@@ -4742,13 +4743,10 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.39.0"
|
||||
name = "wit-bindgen"
|
||||
version = "0.45.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
]
|
||||
checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814"
|
||||
|
||||
[[package]]
|
||||
name = "writeable"
|
||||
@@ -4791,7 +4789,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
@@ -4812,7 +4810,7 @@ checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4832,7 +4830,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
@@ -4855,9 +4853,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerovec"
|
||||
version = "0.11.2"
|
||||
version = "0.11.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428"
|
||||
checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b"
|
||||
dependencies = [
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
@@ -4872,7 +4870,7 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@@ -185,6 +185,10 @@ if has_xray then
|
||||
o.default = "10-20"
|
||||
o:depends("fragment", true)
|
||||
|
||||
o = s_xray:option(Value, "fragment_maxSplit", translate("Max Split"), translate("Limit the maximum number of splits."))
|
||||
o.default = "100-200"
|
||||
o:depends("fragment", true)
|
||||
|
||||
o = s_xray:option(Flag, "noise", translate("Noise"), translate("UDP noise, Under some circumstances it can bypass some UDP based protocol restrictions."))
|
||||
o.default = 0
|
||||
|
||||
@@ -238,6 +242,11 @@ if has_xray then
|
||||
o = s_xray_noise:option(Value, "delay", translate("Delay (ms)"))
|
||||
o.datatype = "or(uinteger,portrange)"
|
||||
o.rmempty = false
|
||||
|
||||
o = s_xray_noise:option(ListValue, "applyTo", translate("IP Type"))
|
||||
o:value("ip", "ALL")
|
||||
o:value("ipv4", "IPv4")
|
||||
o:value("ipv6", "IPv6")
|
||||
end
|
||||
|
||||
if has_singbox then
|
||||
|
@@ -429,6 +429,18 @@ function is_ipv6(val)
|
||||
return false
|
||||
end
|
||||
|
||||
function is_local_ip(ip)
|
||||
ip = tostring(ip or ""):lower()
|
||||
ip = ip:gsub("^[%w%d]+://", "") -- 去掉协议头
|
||||
:gsub("/.*$", "") -- 去掉路径
|
||||
:gsub("^%[", ""):gsub("%]$", "") -- 去掉IPv6方括号
|
||||
:gsub(":%d+$", "") -- 去掉端口
|
||||
return ip:match("^127%.") or ip:match("^10%.") or
|
||||
ip:match("^172%.1[6-9]%.") or ip:match("^172%.2[0-9]%.") or
|
||||
ip:match("^172%.3[0-1]%.") or ip:match("^192%.168%.") or
|
||||
ip == "::1" or ip:match("^f[cd]") or ip:match("^fe[89ab]")
|
||||
end
|
||||
|
||||
function is_ipv6addrport(val)
|
||||
if is_ipv6(val) then
|
||||
local address, port = val:match('%[(.*)%]:([^:]+)$')
|
||||
|
@@ -1555,6 +1555,9 @@ function gen_config(var)
|
||||
end
|
||||
|
||||
if remote_server.address then
|
||||
if api.is_local_ip(remote_server.address) then --dns为本地ip,不走代理
|
||||
remote_server.detour = "direct"
|
||||
end
|
||||
table.insert(dns.servers, remote_server)
|
||||
end
|
||||
|
||||
@@ -1610,6 +1613,9 @@ function gen_config(var)
|
||||
end
|
||||
|
||||
if remote_server.server then
|
||||
if api.is_local_ip(remote_server.server) then --dns为本地ip,不走代理
|
||||
remote_server.detour = "direct"
|
||||
end
|
||||
table.insert(dns.servers, remote_server)
|
||||
end
|
||||
|
||||
@@ -1759,7 +1765,9 @@ function gen_config(var)
|
||||
if value.outboundTag ~= COMMON.default_outbound_tag and (remote_server.address or remote_server.server) then
|
||||
local remote_shunt_server = api.clone(remote_server)
|
||||
remote_shunt_server.tag = value.outboundTag
|
||||
remote_shunt_server.detour = value.outboundTag
|
||||
local is_local = (remote_server.address and api.is_local_ip(remote_server.address)) or
|
||||
(remote_server.server and api.is_local_ip(remote_server.server)) --dns为本地ip,不走代理
|
||||
remote_shunt_server.detour = is_local and "direct" or value.outboundTag
|
||||
table.insert(dns.servers, remote_shunt_server)
|
||||
dns_rule.server = remote_shunt_server.tag
|
||||
end
|
||||
|
@@ -23,7 +23,8 @@ local function get_noise_packets()
|
||||
local noise = (n.enabled == "1") and {
|
||||
type = n.type,
|
||||
packet = n.packet,
|
||||
delay = string.find(n.delay, "-") and n.delay or tonumber(n.delay)
|
||||
delay = string.find(n.delay, "-") and n.delay or tonumber(n.delay),
|
||||
applyTo = n.applyTo
|
||||
} or nil
|
||||
table.insert(noises, noise)
|
||||
end)
|
||||
@@ -1370,25 +1371,37 @@ function gen_config(var)
|
||||
end
|
||||
|
||||
if dns_server then
|
||||
local outboundTag, balancerTag
|
||||
if not api.is_local_ip(dns_server.address) or value.outboundTag == "blackhole" then --dns为本地ip,不走代理
|
||||
outboundTag = value.outboundTag
|
||||
balancerTag = value.balancerTag
|
||||
else
|
||||
outboundTag = "direct"
|
||||
balancerTag = nil
|
||||
end
|
||||
table.insert(dns.servers, dns_server)
|
||||
table.insert(routing.rules, {
|
||||
inboundTag = {
|
||||
dns_server.tag
|
||||
},
|
||||
outboundTag = value.outboundTag or nil,
|
||||
balancerTag = value.balancerTag or nil
|
||||
inboundTag = { dns_server.tag },
|
||||
outboundTag = outboundTag,
|
||||
balancerTag = balancerTag
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local _outboundTag, _balancerTag
|
||||
if not api.is_local_ip(_remote_dns.address) or dns_outbound_tag == "blackhole" then --dns为本地ip,不走代理
|
||||
_outboundTag = dns_outbound_tag
|
||||
_balancerTag = COMMON.default_balancer_tag
|
||||
else
|
||||
_outboundTag = "direct"
|
||||
_balancerTag = nil
|
||||
end
|
||||
table.insert(routing.rules, {
|
||||
inboundTag = {
|
||||
"dns-global"
|
||||
},
|
||||
balancerTag = COMMON.default_balancer_tag,
|
||||
outboundTag = dns_outbound_tag
|
||||
inboundTag = { "dns-global" },
|
||||
balancerTag = _balancerTag,
|
||||
outboundTag = _outboundTag
|
||||
})
|
||||
|
||||
local default_rule_index = nil
|
||||
@@ -1460,7 +1473,8 @@ function gen_config(var)
|
||||
fragment = (xray_settings.fragment == "1") and {
|
||||
packets = (xray_settings.fragment_packets and xray_settings.fragment_packets ~= "") and xray_settings.fragment_packets,
|
||||
length = (xray_settings.fragment_length and xray_settings.fragment_length ~= "") and xray_settings.fragment_length,
|
||||
interval = (xray_settings.fragment_interval and xray_settings.fragment_interval ~= "") and xray_settings.fragment_interval
|
||||
interval = (xray_settings.fragment_interval and xray_settings.fragment_interval ~= "") and xray_settings.fragment_interval,
|
||||
maxSplit = (xray_settings.fragment_maxSplit and xray_settings.fragment_maxSplit ~= "") and xray_settings.fragment_maxSplit
|
||||
} or nil,
|
||||
noises = (xray_settings.noise == "1") and get_noise_packets() or nil
|
||||
},
|
||||
|
@@ -1759,6 +1759,12 @@ msgstr "分片间隔"
|
||||
msgid "Fragmentation interval (ms)"
|
||||
msgstr "分片间隔(ms)"
|
||||
|
||||
msgid "Max Split"
|
||||
msgstr "最大分片数"
|
||||
|
||||
msgid "Limit the maximum number of splits."
|
||||
msgstr "限制分片的最大数量。"
|
||||
|
||||
msgid "Split handshake data into multiple TLS records for better censorship evasion. Low overhead. Recommended to enable first."
|
||||
msgstr "将握手数据拆分为多个 TLS 记录,提升抗封锁能力,几乎不增加延迟,建议优先启用。"
|
||||
|
||||
@@ -1783,6 +1789,9 @@ msgstr "数据包"
|
||||
msgid "Delay (ms)"
|
||||
msgstr "延迟(ms)"
|
||||
|
||||
msgid "IP Type"
|
||||
msgstr "IP 类型"
|
||||
|
||||
msgid "If is domain name, The requested domain name will be resolved to IP before connect."
|
||||
msgstr "如果是域名,域名将在请求发出之前解析为 IP。"
|
||||
|
||||
|
@@ -214,14 +214,7 @@ if DNS_MODE == "socks" then
|
||||
|
||||
end
|
||||
|
||||
-- 判断是否为本地地址
|
||||
local is_local = w:match("127%.0%.0%.")
|
||||
or w:match("192%.168%.")
|
||||
or w:match("10%.")
|
||||
or w:match("172%.1[6-9]%.")
|
||||
or w:match("172%.2[0-9]%.")
|
||||
or w:match("172%.3[0-1]%.")
|
||||
if not is_local then
|
||||
if not api.is_local_ip(w) then
|
||||
server_param = server_param .. " -proxy " .. proxy_server_name
|
||||
end
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<Version>7.14.4</Version>
|
||||
<Version>7.14.5</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
@@ -5,12 +5,12 @@
|
||||
<CentralPackageVersionOverrideEnabled>false</CentralPackageVersionOverrideEnabled>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="System.Reactive" Version="6.0.1" />
|
||||
<PackageVersion Include="coverlet.collector" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||
<PackageVersion Include="NUnit" Version="3.14.0" />
|
||||
<PackageVersion Include="NUnit.Analyzers" Version="3.9.0" />
|
||||
<PackageVersion Include="NUnit3TestAdapter" Version="4.5.0" />
|
||||
<PackageVersion Include="System.Reactive" Version="6.0.2" />
|
||||
<PackageVersion Include="coverlet.collector" Version="6.0.4" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||
<PackageVersion Include="NUnit" Version="4.4.0" />
|
||||
<PackageVersion Include="NUnit.Analyzers" Version="4.10.0" />
|
||||
<PackageVersion Include="NUnit3TestAdapter" Version="5.1.0" />
|
||||
<PackageVersion Include="Avalonia" Version="0.10.0" />
|
||||
<PackageVersion Include="Avalonia.Desktop" Version="0.10.0" />
|
||||
<PackageVersion Include="Avalonia.Diagnostics" Version="0.10.0" />
|
||||
|
@@ -4,7 +4,7 @@ using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace AvaloniaApp
|
||||
{
|
||||
public class MainWindow : Window
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
public MainWindow()
|
||||
{
|
||||
|
@@ -10,11 +10,17 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.collector" />
|
||||
<PackageReference Include="coverlet.collector">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="NUnit" />
|
||||
<PackageReference Include="NUnit.Analyzers" />
|
||||
<PackageReference Include="NUnit3TestAdapter"/>
|
||||
<PackageReference Include="NUnit.Analyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="NUnit3TestAdapter" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@@ -13,11 +13,11 @@ namespace ServiceLib.ViewModels;
|
||||
public class CheckUpdateViewModel : MyReactiveObject
|
||||
{
|
||||
private const string _geo = "GeoFiles";
|
||||
private string _v2rayN = ECoreType.v2rayN.ToString();
|
||||
private readonly string _v2rayN = ECoreType.v2rayN.ToString();
|
||||
private List<CheckUpdateModel> _lstUpdated = [];
|
||||
private static readonly string _tag = "CheckUpdateViewModel";
|
||||
|
||||
private IObservableCollection<CheckUpdateModel> _checkUpdateModel = new ObservableCollectionExtended<CheckUpdateModel>();
|
||||
public IObservableCollection<CheckUpdateModel> CheckUpdateModels => _checkUpdateModel;
|
||||
public IObservableCollection<CheckUpdateModel> CheckUpdateModels { get; } = new ObservableCollectionExtended<CheckUpdateModel>();
|
||||
public ReactiveCommand<Unit, Unit> CheckUpdateCmd { get; }
|
||||
[Reactive] public bool EnableCheckPreReleaseUpdate { get; set; }
|
||||
|
||||
@@ -26,9 +26,11 @@ public class CheckUpdateViewModel : MyReactiveObject
|
||||
_config = AppManager.Instance.Config;
|
||||
_updateView = updateView;
|
||||
|
||||
CheckUpdateCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
CheckUpdateCmd = ReactiveCommand.CreateFromTask(CheckUpdate);
|
||||
CheckUpdateCmd.ThrownExceptions.Subscribe(ex =>
|
||||
{
|
||||
await CheckUpdate();
|
||||
Logging.SaveLog(_tag, ex);
|
||||
_ = UpdateView(_v2rayN, ex.Message);
|
||||
});
|
||||
|
||||
EnableCheckPreReleaseUpdate = _config.CheckUpdateItem.CheckPreReleaseUpdate;
|
||||
@@ -43,20 +45,20 @@ public class CheckUpdateViewModel : MyReactiveObject
|
||||
|
||||
private void RefreshCheckUpdateItems()
|
||||
{
|
||||
_checkUpdateModel.Clear();
|
||||
CheckUpdateModels.Clear();
|
||||
|
||||
if (RuntimeInformation.ProcessArchitecture != Architecture.X86)
|
||||
{
|
||||
_checkUpdateModel.Add(GetCheckUpdateModel(_v2rayN));
|
||||
CheckUpdateModels.Add(GetCheckUpdateModel(_v2rayN));
|
||||
//Not Windows and under Win10
|
||||
if (!(Utils.IsWindows() && Environment.OSVersion.Version.Major < 10))
|
||||
{
|
||||
_checkUpdateModel.Add(GetCheckUpdateModel(ECoreType.Xray.ToString()));
|
||||
_checkUpdateModel.Add(GetCheckUpdateModel(ECoreType.mihomo.ToString()));
|
||||
_checkUpdateModel.Add(GetCheckUpdateModel(ECoreType.sing_box.ToString()));
|
||||
CheckUpdateModels.Add(GetCheckUpdateModel(ECoreType.Xray.ToString()));
|
||||
CheckUpdateModels.Add(GetCheckUpdateModel(ECoreType.mihomo.ToString()));
|
||||
CheckUpdateModels.Add(GetCheckUpdateModel(ECoreType.sing_box.ToString()));
|
||||
}
|
||||
}
|
||||
_checkUpdateModel.Add(GetCheckUpdateModel(_geo));
|
||||
CheckUpdateModels.Add(GetCheckUpdateModel(_geo));
|
||||
}
|
||||
|
||||
private CheckUpdateModel GetCheckUpdateModel(string coreType)
|
||||
@@ -71,7 +73,7 @@ public class CheckUpdateViewModel : MyReactiveObject
|
||||
|
||||
private async Task SaveSelectedCoreTypes()
|
||||
{
|
||||
_config.CheckUpdateItem.SelectedCoreTypes = _checkUpdateModel.Where(t => t.IsSelected == true).Select(t => t.CoreType ?? "").ToList();
|
||||
_config.CheckUpdateItem.SelectedCoreTypes = CheckUpdateModels.Where(t => t.IsSelected == true).Select(t => t.CoreType ?? "").ToList();
|
||||
await ConfigHandler.SaveConfig(_config);
|
||||
}
|
||||
|
||||
@@ -83,13 +85,13 @@ public class CheckUpdateViewModel : MyReactiveObject
|
||||
private async Task CheckUpdateTask()
|
||||
{
|
||||
_lstUpdated.Clear();
|
||||
_lstUpdated = _checkUpdateModel.Where(x => x.IsSelected == true)
|
||||
_lstUpdated = CheckUpdateModels.Where(x => x.IsSelected == true)
|
||||
.Select(x => new CheckUpdateModel() { CoreType = x.CoreType }).ToList();
|
||||
await SaveSelectedCoreTypes();
|
||||
|
||||
for (var k = _checkUpdateModel.Count - 1; k >= 0; k--)
|
||||
for (var k = CheckUpdateModels.Count - 1; k >= 0; k--)
|
||||
{
|
||||
var item = _checkUpdateModel[k];
|
||||
var item = CheckUpdateModels[k];
|
||||
if (item.IsSelected != true)
|
||||
{
|
||||
continue;
|
||||
@@ -320,7 +322,7 @@ public class CheckUpdateViewModel : MyReactiveObject
|
||||
|
||||
public async Task UpdateViewResult(CheckUpdateModel model)
|
||||
{
|
||||
var found = _checkUpdateModel.FirstOrDefault(t => t.CoreType == model.CoreType);
|
||||
var found = CheckUpdateModels.FirstOrDefault(t => t.CoreType == model.CoreType);
|
||||
if (found == null)
|
||||
{
|
||||
return;
|
||||
@@ -328,6 +330,6 @@ public class CheckUpdateViewModel : MyReactiveObject
|
||||
|
||||
var itemCopy = JsonUtils.DeepCopy(found);
|
||||
itemCopy.Remarks = model.Remarks;
|
||||
_checkUpdateModel.Replace(found, itemCopy);
|
||||
CheckUpdateModels.Replace(found, itemCopy);
|
||||
}
|
||||
}
|
||||
|
@@ -10,8 +10,7 @@ namespace ServiceLib.ViewModels;
|
||||
|
||||
public class ClashConnectionsViewModel : MyReactiveObject
|
||||
{
|
||||
private IObservableCollection<ClashConnectionModel> _connectionItems = new ObservableCollectionExtended<ClashConnectionModel>();
|
||||
public IObservableCollection<ClashConnectionModel> ConnectionItems => _connectionItems;
|
||||
public IObservableCollection<ClashConnectionModel> ConnectionItems { get; } = new ObservableCollectionExtended<ClashConnectionModel>();
|
||||
|
||||
[Reactive]
|
||||
public ClashConnectionModel SelectedSource { get; set; }
|
||||
@@ -74,7 +73,7 @@ public class ClashConnectionsViewModel : MyReactiveObject
|
||||
|
||||
public async Task RefreshConnections(List<ConnectionItem>? connections)
|
||||
{
|
||||
_connectionItems.Clear();
|
||||
ConnectionItems.Clear();
|
||||
|
||||
var dtNow = DateTime.Now;
|
||||
var lstModel = new List<ClashConnectionModel>();
|
||||
@@ -104,7 +103,7 @@ public class ClashConnectionsViewModel : MyReactiveObject
|
||||
return;
|
||||
}
|
||||
|
||||
_connectionItems.AddRange(lstModel);
|
||||
ConnectionItems.AddRange(lstModel);
|
||||
}
|
||||
|
||||
public async Task ClashConnectionClose(bool all)
|
||||
@@ -121,7 +120,7 @@ public class ClashConnectionsViewModel : MyReactiveObject
|
||||
}
|
||||
else
|
||||
{
|
||||
_connectionItems.Clear();
|
||||
ConnectionItems.Clear();
|
||||
}
|
||||
await ClashApiManager.Instance.ClashConnectionClose(id);
|
||||
await GetClashConnections();
|
||||
|
@@ -17,11 +17,8 @@ public class ClashProxiesViewModel : MyReactiveObject
|
||||
private Dictionary<string, ProvidersItem>? _providers;
|
||||
private readonly int _delayTimeout = 99999999;
|
||||
|
||||
private IObservableCollection<ClashProxyModel> _proxyGroups = new ObservableCollectionExtended<ClashProxyModel>();
|
||||
private IObservableCollection<ClashProxyModel> _proxyDetails = new ObservableCollectionExtended<ClashProxyModel>();
|
||||
|
||||
public IObservableCollection<ClashProxyModel> ProxyGroups => _proxyGroups;
|
||||
public IObservableCollection<ClashProxyModel> ProxyDetails => _proxyDetails;
|
||||
public IObservableCollection<ClashProxyModel> ProxyGroups { get; } = new ObservableCollectionExtended<ClashProxyModel>();
|
||||
public IObservableCollection<ClashProxyModel> ProxyDetails { get; } = new ObservableCollectionExtended<ClashProxyModel>();
|
||||
|
||||
[Reactive]
|
||||
public ClashProxyModel SelectedGroup { get; set; }
|
||||
@@ -182,7 +179,7 @@ public class ClashProxiesViewModel : MyReactiveObject
|
||||
}
|
||||
|
||||
var selectedName = SelectedGroup?.Name;
|
||||
_proxyGroups.Clear();
|
||||
ProxyGroups.Clear();
|
||||
|
||||
var proxyGroups = ClashApiManager.Instance.GetClashProxyGroups();
|
||||
if (proxyGroups != null && proxyGroups.Count > 0)
|
||||
@@ -198,7 +195,7 @@ public class ClashProxiesViewModel : MyReactiveObject
|
||||
{
|
||||
continue;
|
||||
}
|
||||
_proxyGroups.Add(new ClashProxyModel()
|
||||
ProxyGroups.Add(new ClashProxyModel()
|
||||
{
|
||||
Now = item.now,
|
||||
Name = item.name,
|
||||
@@ -214,12 +211,12 @@ public class ClashProxiesViewModel : MyReactiveObject
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var item = _proxyGroups.FirstOrDefault(t => t.Name == kv.Key);
|
||||
var item = ProxyGroups.FirstOrDefault(t => t.Name == kv.Key);
|
||||
if (item != null && item.Name.IsNotEmpty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
_proxyGroups.Add(new ClashProxyModel()
|
||||
ProxyGroups.Add(new ClashProxyModel()
|
||||
{
|
||||
Now = kv.Value.now,
|
||||
Name = kv.Key,
|
||||
@@ -227,15 +224,15 @@ public class ClashProxiesViewModel : MyReactiveObject
|
||||
});
|
||||
}
|
||||
|
||||
if (_proxyGroups != null && _proxyGroups.Count > 0)
|
||||
if (ProxyGroups != null && ProxyGroups.Count > 0)
|
||||
{
|
||||
if (selectedName != null && _proxyGroups.Any(t => t.Name == selectedName))
|
||||
if (selectedName != null && ProxyGroups.Any(t => t.Name == selectedName))
|
||||
{
|
||||
SelectedGroup = _proxyGroups.FirstOrDefault(t => t.Name == selectedName);
|
||||
SelectedGroup = ProxyGroups.FirstOrDefault(t => t.Name == selectedName);
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedGroup = _proxyGroups.First();
|
||||
SelectedGroup = ProxyGroups.First();
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -246,7 +243,7 @@ public class ClashProxiesViewModel : MyReactiveObject
|
||||
|
||||
private void RefreshProxyDetails(bool c)
|
||||
{
|
||||
_proxyDetails.Clear();
|
||||
ProxyDetails.Clear();
|
||||
if (!c)
|
||||
{
|
||||
return;
|
||||
@@ -299,7 +296,7 @@ public class ClashProxiesViewModel : MyReactiveObject
|
||||
default:
|
||||
break;
|
||||
}
|
||||
_proxyDetails.AddRange(lstDetails);
|
||||
ProxyDetails.AddRange(lstDetails);
|
||||
}
|
||||
|
||||
private ProxiesItem? TryGetProxy(string name)
|
||||
@@ -361,12 +358,12 @@ public class ClashProxiesViewModel : MyReactiveObject
|
||||
await ClashApiManager.Instance.ClashSetActiveProxy(name, nameNode);
|
||||
|
||||
selectedProxy.now = nameNode;
|
||||
var group = _proxyGroups.FirstOrDefault(it => it.Name == SelectedGroup.Name);
|
||||
var group = ProxyGroups.FirstOrDefault(it => it.Name == SelectedGroup.Name);
|
||||
if (group != null)
|
||||
{
|
||||
group.Now = nameNode;
|
||||
var group2 = JsonUtils.DeepCopy(group);
|
||||
_proxyGroups.Replace(group, group2);
|
||||
ProxyGroups.Replace(group, group2);
|
||||
|
||||
SelectedGroup = group2;
|
||||
}
|
||||
@@ -375,7 +372,7 @@ public class ClashProxiesViewModel : MyReactiveObject
|
||||
|
||||
private async Task ProxiesDelayTest(bool blAll = true)
|
||||
{
|
||||
ClashApiManager.Instance.ClashProxiesDelayTest(blAll, _proxyDetails.ToList(), async (item, result) =>
|
||||
ClashApiManager.Instance.ClashProxiesDelayTest(blAll, ProxyDetails.ToList(), async (item, result) =>
|
||||
{
|
||||
if (item == null || result.IsNullOrEmpty())
|
||||
{
|
||||
@@ -395,7 +392,7 @@ public class ClashProxiesViewModel : MyReactiveObject
|
||||
public async Task ProxiesDelayTestResult(SpeedTestResult result)
|
||||
{
|
||||
//UpdateHandler(false, $"{item.name}={result}");
|
||||
var detail = _proxyDetails.FirstOrDefault(it => it.Name == result.IndexId);
|
||||
var detail = ProxyDetails.FirstOrDefault(it => it.Name == result.IndexId);
|
||||
if (detail == null)
|
||||
{
|
||||
return;
|
||||
@@ -417,7 +414,7 @@ public class ClashProxiesViewModel : MyReactiveObject
|
||||
detail.Delay = _delayTimeout;
|
||||
detail.DelayName = string.Empty;
|
||||
}
|
||||
_proxyDetails.Replace(detail, JsonUtils.DeepCopy(detail));
|
||||
ProxyDetails.Replace(detail, JsonUtils.DeepCopy(detail));
|
||||
}
|
||||
|
||||
#endregion proxy function
|
||||
|
@@ -8,8 +8,8 @@ namespace ServiceLib.ViewModels;
|
||||
|
||||
public class MsgViewModel : MyReactiveObject
|
||||
{
|
||||
private ConcurrentQueue<string> _queueMsg = new();
|
||||
private int _numMaxMsg = 500;
|
||||
private readonly ConcurrentQueue<string> _queueMsg = new();
|
||||
private readonly int _numMaxMsg = 500;
|
||||
private bool _lastMsgFilterNotAvailable;
|
||||
private bool _blLockShow = false;
|
||||
|
||||
|
@@ -23,13 +23,9 @@ public class ProfilesViewModel : MyReactiveObject
|
||||
|
||||
#region ObservableCollection
|
||||
|
||||
private IObservableCollection<ProfileItemModel> _profileItems = new ObservableCollectionExtended<ProfileItemModel>();
|
||||
public IObservableCollection<ProfileItemModel> ProfileItems => _profileItems;
|
||||
public IObservableCollection<ProfileItemModel> ProfileItems { get; } = new ObservableCollectionExtended<ProfileItemModel>();
|
||||
|
||||
private IObservableCollection<SubItem> _subItems = new ObservableCollectionExtended<SubItem>();
|
||||
public IObservableCollection<SubItem> SubItems => _subItems;
|
||||
|
||||
private IObservableCollection<ComboItem> _servers = new ObservableCollectionExtended<ComboItem>();
|
||||
public IObservableCollection<SubItem> SubItems { get; } = new ObservableCollectionExtended<SubItem>();
|
||||
|
||||
[Reactive]
|
||||
public ProfileItemModel SelectedProfile { get; set; }
|
||||
@@ -293,7 +289,7 @@ public class ProfilesViewModel : MyReactiveObject
|
||||
NoticeManager.Instance.Enqueue(result.Delay);
|
||||
return;
|
||||
}
|
||||
var item = _profileItems.FirstOrDefault(it => it.IndexId == result.IndexId);
|
||||
var item = ProfileItems.FirstOrDefault(it => it.IndexId == result.IndexId);
|
||||
if (item == null)
|
||||
{
|
||||
return;
|
||||
@@ -323,7 +319,7 @@ public class ProfilesViewModel : MyReactiveObject
|
||||
|
||||
try
|
||||
{
|
||||
var item = _profileItems.FirstOrDefault(it => it.IndexId == update.IndexId);
|
||||
var item = ProfileItems.FirstOrDefault(it => it.IndexId == update.IndexId);
|
||||
if (item != null)
|
||||
{
|
||||
item.TodayDown = Utils.HumanFy(update.TodayDown);
|
||||
@@ -390,8 +386,8 @@ public class ProfilesViewModel : MyReactiveObject
|
||||
var lstModel = await GetProfileItemsEx(_config.SubIndexId, _serverFilter);
|
||||
_lstProfile = JsonUtils.Deserialize<List<ProfileItem>>(JsonUtils.Serialize(lstModel)) ?? [];
|
||||
|
||||
_profileItems.Clear();
|
||||
_profileItems.AddRange(lstModel);
|
||||
ProfileItems.Clear();
|
||||
ProfileItems.AddRange(lstModel);
|
||||
if (lstModel.Count > 0)
|
||||
{
|
||||
var selected = lstModel.FirstOrDefault(t => t.IndexId == _config.IndexId);
|
||||
@@ -410,21 +406,21 @@ public class ProfilesViewModel : MyReactiveObject
|
||||
|
||||
public async Task RefreshSubscriptions()
|
||||
{
|
||||
_subItems.Clear();
|
||||
SubItems.Clear();
|
||||
|
||||
_subItems.Add(new SubItem { Remarks = ResUI.AllGroupServers });
|
||||
SubItems.Add(new SubItem { Remarks = ResUI.AllGroupServers });
|
||||
|
||||
foreach (var item in await AppManager.Instance.SubItems())
|
||||
{
|
||||
_subItems.Add(item);
|
||||
SubItems.Add(item);
|
||||
}
|
||||
if (_config.SubIndexId != null && _subItems.FirstOrDefault(t => t.Id == _config.SubIndexId) != null)
|
||||
if (_config.SubIndexId != null && SubItems.FirstOrDefault(t => t.Id == _config.SubIndexId) != null)
|
||||
{
|
||||
SelectedSub = _subItems.FirstOrDefault(t => t.Id == _config.SubIndexId);
|
||||
SelectedSub = SubItems.FirstOrDefault(t => t.Id == _config.SubIndexId);
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedSub = _subItems.First();
|
||||
SelectedSub = SubItems.First();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -548,9 +544,9 @@ public class ProfilesViewModel : MyReactiveObject
|
||||
|
||||
await ConfigHandler.RemoveServers(_config, lstSelected);
|
||||
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
if (lstSelected.Count == _profileItems.Count)
|
||||
if (lstSelected.Count == ProfileItems.Count)
|
||||
{
|
||||
_profileItems.Clear();
|
||||
ProfileItems.Clear();
|
||||
}
|
||||
await RefreshServers();
|
||||
if (exists)
|
||||
@@ -740,7 +736,7 @@ public class ProfilesViewModel : MyReactiveObject
|
||||
|
||||
public async Task MoveServerTo(int startIndex, ProfileItemModel targetItem)
|
||||
{
|
||||
var targetIndex = _profileItems.IndexOf(targetItem);
|
||||
var targetIndex = ProfileItems.IndexOf(targetItem);
|
||||
if (startIndex >= 0 && targetIndex >= 0 && startIndex != targetIndex)
|
||||
{
|
||||
if (await ConfigHandler.MoveServer(_config, _lstProfile, startIndex, EMove.Position, targetIndex) == 0)
|
||||
@@ -754,7 +750,7 @@ public class ProfilesViewModel : MyReactiveObject
|
||||
{
|
||||
if (actionType == ESpeedActionType.Mixedtest)
|
||||
{
|
||||
SelectedProfiles = _profileItems;
|
||||
SelectedProfiles = ProfileItems;
|
||||
}
|
||||
var lstSelected = await GetProfileItems(false);
|
||||
if (lstSelected == null)
|
||||
|
@@ -13,9 +13,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
|
||||
|
||||
[Reactive]
|
||||
public RoutingItem SelectedRouting { get; set; }
|
||||
|
||||
private IObservableCollection<RulesItemModel> _rulesItems = new ObservableCollectionExtended<RulesItemModel>();
|
||||
public IObservableCollection<RulesItemModel> RulesItems => _rulesItems;
|
||||
public IObservableCollection<RulesItemModel> RulesItems { get; } = new ObservableCollectionExtended<RulesItemModel>();
|
||||
|
||||
[Reactive]
|
||||
public RulesItemModel SelectedSource { get; set; }
|
||||
@@ -101,7 +99,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
|
||||
|
||||
public void RefreshRulesItems()
|
||||
{
|
||||
_rulesItems.Clear();
|
||||
RulesItems.Clear();
|
||||
|
||||
foreach (var item in _rules)
|
||||
{
|
||||
@@ -118,7 +116,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
|
||||
Enabled = item.Enabled,
|
||||
Remarks = item.Remarks,
|
||||
};
|
||||
_rulesItems.Add(it);
|
||||
RulesItems.Add(it);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -9,8 +9,7 @@ public class RoutingSettingViewModel : MyReactiveObject
|
||||
{
|
||||
#region Reactive
|
||||
|
||||
private IObservableCollection<RoutingItemModel> _routingItems = new ObservableCollectionExtended<RoutingItemModel>();
|
||||
public IObservableCollection<RoutingItemModel> RoutingItems => _routingItems;
|
||||
public IObservableCollection<RoutingItemModel> RoutingItems { get; } = new ObservableCollectionExtended<RoutingItemModel>();
|
||||
|
||||
[Reactive]
|
||||
public RoutingItemModel SelectedSource { get; set; }
|
||||
@@ -82,7 +81,7 @@ public class RoutingSettingViewModel : MyReactiveObject
|
||||
|
||||
public async Task RefreshRoutingItems()
|
||||
{
|
||||
_routingItems.Clear();
|
||||
RoutingItems.Clear();
|
||||
|
||||
var routings = await AppManager.Instance.RoutingItems();
|
||||
foreach (var item in routings)
|
||||
@@ -98,7 +97,7 @@ public class RoutingSettingViewModel : MyReactiveObject
|
||||
CustomRulesetPath4Singbox = item.CustomRulesetPath4Singbox,
|
||||
Sort = item.Sort,
|
||||
};
|
||||
_routingItems.Add(it);
|
||||
RoutingItems.Add(it);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -13,11 +13,9 @@ public class StatusBarViewModel : MyReactiveObject
|
||||
{
|
||||
#region ObservableCollection
|
||||
|
||||
private IObservableCollection<RoutingItem> _routingItems = new ObservableCollectionExtended<RoutingItem>();
|
||||
public IObservableCollection<RoutingItem> RoutingItems => _routingItems;
|
||||
public IObservableCollection<RoutingItem> RoutingItems { get; } = new ObservableCollectionExtended<RoutingItem>();
|
||||
|
||||
private IObservableCollection<ComboItem> _servers = new ObservableCollectionExtended<ComboItem>();
|
||||
public IObservableCollection<ComboItem> Servers => _servers;
|
||||
public IObservableCollection<ComboItem> Servers { get; } = new ObservableCollectionExtended<ComboItem>();
|
||||
|
||||
[Reactive]
|
||||
public RoutingItem SelectedRouting { get; set; }
|
||||
@@ -295,7 +293,7 @@ public class StatusBarViewModel : MyReactiveObject
|
||||
{
|
||||
var lstModel = await AppManager.Instance.ProfileItems(_config.SubIndexId, "");
|
||||
|
||||
_servers.Clear();
|
||||
Servers.Clear();
|
||||
if (lstModel.Count > _config.GuiItem.TrayMenuServersLimit)
|
||||
{
|
||||
BlServers = false;
|
||||
@@ -309,7 +307,7 @@ public class StatusBarViewModel : MyReactiveObject
|
||||
string name = it.GetSummary();
|
||||
|
||||
var item = new ComboItem() { ID = it.IndexId, Text = name };
|
||||
_servers.Add(item);
|
||||
Servers.Add(item);
|
||||
if (_config.IndexId == it.IndexId)
|
||||
{
|
||||
SelectedServer = item;
|
||||
@@ -397,13 +395,13 @@ public class StatusBarViewModel : MyReactiveObject
|
||||
|
||||
public async Task RefreshRoutingsMenu()
|
||||
{
|
||||
_routingItems.Clear();
|
||||
RoutingItems.Clear();
|
||||
|
||||
BlRouting = true;
|
||||
var routings = await AppManager.Instance.RoutingItems();
|
||||
foreach (var item in routings)
|
||||
{
|
||||
_routingItems.Add(item);
|
||||
RoutingItems.Add(item);
|
||||
if (item.IsActive)
|
||||
{
|
||||
SelectedRouting = item;
|
||||
|
@@ -8,8 +8,7 @@ namespace ServiceLib.ViewModels;
|
||||
|
||||
public class SubSettingViewModel : MyReactiveObject
|
||||
{
|
||||
private IObservableCollection<SubItem> _subItems = new ObservableCollectionExtended<SubItem>();
|
||||
public IObservableCollection<SubItem> SubItems => _subItems;
|
||||
public IObservableCollection<SubItem> SubItems { get; } = new ObservableCollectionExtended<SubItem>();
|
||||
|
||||
[Reactive]
|
||||
public SubItem SelectedSource { get; set; }
|
||||
@@ -60,8 +59,8 @@ public class SubSettingViewModel : MyReactiveObject
|
||||
|
||||
public async Task RefreshSubItems()
|
||||
{
|
||||
_subItems.Clear();
|
||||
_subItems.AddRange(await AppManager.Instance.SubItems());
|
||||
SubItems.Clear();
|
||||
SubItems.AddRange(await AppManager.Instance.SubItems());
|
||||
}
|
||||
|
||||
public async Task EditSubAsync(bool blNew)
|
||||
|
@@ -22,4 +22,8 @@
|
||||
<Style Selector="ScrollViewer">
|
||||
<Setter Property="AllowAutoHide" Value="False" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="TabControl">
|
||||
<Setter Property="Theme" Value="{StaticResource LineTabControl}" />
|
||||
</Style>
|
||||
</Styles>
|
||||
|
@@ -59,7 +59,7 @@
|
||||
x:Name="cmbDirectDNS"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Width="300"
|
||||
Margin="{StaticResource Margin4}"
|
||||
Text="{Binding DirectDNS, Mode=TwoWay}" />
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
x:Name="cmbRemoteDNS"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Width="300"
|
||||
Margin="{StaticResource Margin4}"
|
||||
Text="{Binding RemoteDNS, Mode=TwoWay}" />
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
x:Name="cmbSBResolverDNS"
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Width="300"
|
||||
Margin="{StaticResource Margin4}"
|
||||
Text="{Binding SingboxOutboundsResolveDNS, Mode=TwoWay}" />
|
||||
<TextBlock
|
||||
@@ -108,7 +108,7 @@
|
||||
x:Name="cmbSBFinalResolverDNS"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Width="300"
|
||||
Margin="{StaticResource Margin4}"
|
||||
Text="{Binding SingboxFinalResolveDNS, Mode=TwoWay}" />
|
||||
<TextBlock
|
||||
|
@@ -130,10 +130,6 @@
|
||||
Width="*"
|
||||
Binding="{Binding Url}"
|
||||
Header="{x:Static resx:ResUI.LvUrl}" />
|
||||
<DataGridTextColumn
|
||||
Width="300"
|
||||
Binding="{Binding CustomIcon}"
|
||||
Header="{x:Static resx:ResUI.LvCustomIcon}" />
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</TabItem>
|
||||
|
@@ -80,7 +80,7 @@
|
||||
x:Name="cmbDirectDNS"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Width="300"
|
||||
Margin="{StaticResource Margin8}"
|
||||
IsEditable="True"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
@@ -96,7 +96,7 @@
|
||||
x:Name="cmbRemoteDNS"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Width="300"
|
||||
Margin="{StaticResource Margin8}"
|
||||
IsEditable="True"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
@@ -112,7 +112,7 @@
|
||||
x:Name="cmbSBResolverDNS"
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Width="300"
|
||||
Margin="{StaticResource Margin8}"
|
||||
IsEditable="True"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
@@ -136,7 +136,7 @@
|
||||
x:Name="cmbSBFinalResolverDNS"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Width="300"
|
||||
Margin="{StaticResource Margin8}"
|
||||
IsEditable="True"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
|
@@ -10,6 +10,9 @@ func RandBetween(from int64, to int64) int64 {
|
||||
if from == to {
|
||||
return from
|
||||
}
|
||||
if from > to {
|
||||
from, to = to, from
|
||||
}
|
||||
bigInt, _ := rand.Int(rand.Reader, big.NewInt(to-from))
|
||||
return from + bigInt.Int64()
|
||||
}
|
||||
|
@@ -19,7 +19,7 @@ import (
|
||||
var (
|
||||
Version_x byte = 25
|
||||
Version_y byte = 8
|
||||
Version_z byte = 29
|
||||
Version_z byte = 31
|
||||
)
|
||||
|
||||
var (
|
||||
|
@@ -74,7 +74,7 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
|
||||
}
|
||||
|
||||
if account.Encryption != "" {
|
||||
return nil, errors.New(`VLESS clients: "encryption" should not in inbound settings`)
|
||||
return nil, errors.New(`VLESS clients: "encryption" should not be in inbound settings`)
|
||||
}
|
||||
|
||||
user.Account = serial.ToTypedMessage(account)
|
||||
@@ -107,12 +107,21 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
|
||||
}
|
||||
config.Seconds = uint32(i)
|
||||
}
|
||||
for i := 3; i < len(s); i++ {
|
||||
if b, _ := base64.RawURLEncoding.DecodeString(s[i]); len(b) != 32 && len(b) != 64 {
|
||||
padding := 0
|
||||
for _, r := range s[3:] {
|
||||
if len(r) < 20 {
|
||||
padding += len(r) + 1
|
||||
continue
|
||||
}
|
||||
if b, _ := base64.RawURLEncoding.DecodeString(r); len(b) != 32 && len(b) != 64 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
config.Decryption = config.Decryption[27+len(s[2]):]
|
||||
if padding > 0 {
|
||||
config.Padding = config.Decryption[:padding-1]
|
||||
config.Decryption = config.Decryption[padding:]
|
||||
}
|
||||
return true
|
||||
}() && config.Decryption != "none" {
|
||||
if config.Decryption == "" {
|
||||
@@ -121,6 +130,10 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
|
||||
return nil, errors.New(`VLESS settings: unsupported "decryption": ` + config.Decryption)
|
||||
}
|
||||
|
||||
if config.Decryption != "none" && c.Fallbacks != nil {
|
||||
return nil, errors.New(`VLESS settings: "fallbacks" can not be used together with "decryption"`)
|
||||
}
|
||||
|
||||
for _, fb := range c.Fallbacks {
|
||||
var i uint16
|
||||
var s string
|
||||
@@ -250,12 +263,21 @@ func (c *VLessOutboundConfig) Build() (proto.Message, error) {
|
||||
default:
|
||||
return false
|
||||
}
|
||||
for i := 3; i < len(s); i++ {
|
||||
if b, _ := base64.RawURLEncoding.DecodeString(s[i]); len(b) != 32 && len(b) != 1184 {
|
||||
padding := 0
|
||||
for _, r := range s[3:] {
|
||||
if len(r) < 20 {
|
||||
padding += len(r) + 1
|
||||
continue
|
||||
}
|
||||
if b, _ := base64.RawURLEncoding.DecodeString(r); len(b) != 32 && len(b) != 1184 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
account.Encryption = account.Encryption[27+len(s[2]):]
|
||||
if padding > 0 {
|
||||
account.Padding = account.Encryption[:padding-1]
|
||||
account.Encryption = account.Encryption[padding:]
|
||||
}
|
||||
return true
|
||||
}() && account.Encryption != "none" {
|
||||
if account.Encryption == "" {
|
||||
|
@@ -20,6 +20,7 @@ func (a *Account) AsAccount() (protocol.Account, error) {
|
||||
Encryption: a.Encryption, // needs parser here?
|
||||
XorMode: a.XorMode,
|
||||
Seconds: a.Seconds,
|
||||
Padding: a.Padding,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -33,6 +34,7 @@ type MemoryAccount struct {
|
||||
Encryption string
|
||||
XorMode uint32
|
||||
Seconds uint32
|
||||
Padding string
|
||||
}
|
||||
|
||||
// Equals implements protocol.Account.Equals().
|
||||
@@ -51,5 +53,6 @@ func (a *MemoryAccount) ToProto() proto.Message {
|
||||
Encryption: a.Encryption,
|
||||
XorMode: a.XorMode,
|
||||
Seconds: a.Seconds,
|
||||
Padding: a.Padding,
|
||||
}
|
||||
}
|
||||
|
@@ -32,6 +32,7 @@ type Account struct {
|
||||
Encryption string `protobuf:"bytes,3,opt,name=encryption,proto3" json:"encryption,omitempty"`
|
||||
XorMode uint32 `protobuf:"varint,4,opt,name=xorMode,proto3" json:"xorMode,omitempty"`
|
||||
Seconds uint32 `protobuf:"varint,5,opt,name=seconds,proto3" json:"seconds,omitempty"`
|
||||
Padding string `protobuf:"bytes,6,opt,name=padding,proto3" json:"padding,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Account) Reset() {
|
||||
@@ -99,12 +100,19 @@ func (x *Account) GetSeconds() uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Account) GetPadding() string {
|
||||
if x != nil {
|
||||
return x.Padding
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_proxy_vless_account_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_proxy_vless_account_proto_rawDesc = []byte{
|
||||
0x0a, 0x19, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2f, 0x61, 0x63,
|
||||
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x78, 0x72, 0x61,
|
||||
0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x22, 0x81, 0x01,
|
||||
0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x22, 0x9b, 0x01,
|
||||
0x0a, 0x07, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x6c, 0x6f,
|
||||
0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x1e, 0x0a,
|
||||
@@ -113,12 +121,14 @@ var file_proxy_vless_account_proto_rawDesc = []byte{
|
||||
0x07, 0x78, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07,
|
||||
0x78, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x63, 0x6f, 0x6e,
|
||||
0x64, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64,
|
||||
0x73, 0x42, 0x52, 0x0a, 0x14, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x50, 0x01, 0x5a, 0x25, 0x67, 0x69, 0x74,
|
||||
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61,
|
||||
0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65,
|
||||
0x73, 0x73, 0xaa, 0x02, 0x10, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e,
|
||||
0x56, 0x6c, 0x65, 0x73, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x06, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x42, 0x52, 0x0a, 0x14, 0x63,
|
||||
0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c,
|
||||
0x65, 0x73, 0x73, 0x50, 0x01, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
|
||||
0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65,
|
||||
0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0xaa, 0x02, 0x10, 0x58,
|
||||
0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x56, 0x6c, 0x65, 0x73, 0x73, 0x62,
|
||||
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
@@ -15,4 +15,5 @@ message Account {
|
||||
string encryption = 3;
|
||||
uint32 xorMode = 4;
|
||||
uint32 seconds = 5;
|
||||
string padding = 6;
|
||||
}
|
||||
|
@@ -172,7 +172,7 @@ func DecodeResponseHeader(reader io.Reader, request *protocol.RequestHeader) (*A
|
||||
}
|
||||
|
||||
// XtlsRead filter and read xtls protocol
|
||||
func XtlsRead(reader buf.Reader, writer buf.Writer, timer *signal.ActivityTimer, conn net.Conn, peerCache *[]byte, input *bytes.Reader, rawInput *bytes.Buffer, trafficState *proxy.TrafficState, ob *session.Outbound, isUplink bool, ctx context.Context) error {
|
||||
func XtlsRead(reader buf.Reader, writer buf.Writer, timer *signal.ActivityTimer, conn net.Conn, input *bytes.Reader, rawInput *bytes.Buffer, trafficState *proxy.TrafficState, ob *session.Outbound, isUplink bool, ctx context.Context) error {
|
||||
err := func() error {
|
||||
for {
|
||||
if isUplink && trafficState.Inbound.UplinkReaderDirectCopy || !isUplink && trafficState.Outbound.DownlinkReaderDirectCopy {
|
||||
@@ -194,23 +194,17 @@ func XtlsRead(reader buf.Reader, writer buf.Writer, timer *signal.ActivityTimer,
|
||||
if !buffer.IsEmpty() {
|
||||
timer.Update()
|
||||
if isUplink && trafficState.Inbound.UplinkReaderDirectCopy || !isUplink && trafficState.Outbound.DownlinkReaderDirectCopy {
|
||||
// XTLS Vision processes struct Encryption Conn's peerCache or TLS Conn's input and rawInput
|
||||
if peerCache != nil {
|
||||
if len(*peerCache) != 0 {
|
||||
buffer = buf.MergeBytes(buffer, *peerCache)
|
||||
}
|
||||
} else {
|
||||
if inputBuffer, err := buf.ReadFrom(input); err == nil {
|
||||
if !inputBuffer.IsEmpty() {
|
||||
buffer, _ = buf.MergeMulti(buffer, inputBuffer)
|
||||
}
|
||||
}
|
||||
if rawInputBuffer, err := buf.ReadFrom(rawInput); err == nil {
|
||||
if !rawInputBuffer.IsEmpty() {
|
||||
buffer, _ = buf.MergeMulti(buffer, rawInputBuffer)
|
||||
}
|
||||
}
|
||||
// XTLS Vision processes TLS-like conn's input and rawInput
|
||||
if inputBuffer, err := buf.ReadFrom(input); err == nil && !inputBuffer.IsEmpty() {
|
||||
buffer, _ = buf.MergeMulti(buffer, inputBuffer)
|
||||
}
|
||||
if rawInputBuffer, err := buf.ReadFrom(rawInput); err == nil && !rawInputBuffer.IsEmpty() {
|
||||
buffer, _ = buf.MergeMulti(buffer, rawInputBuffer)
|
||||
}
|
||||
*input = bytes.Reader{} // release memory
|
||||
input = nil
|
||||
*rawInput = bytes.Buffer{} // release memory
|
||||
rawInput = nil
|
||||
}
|
||||
if werr := writer.WriteMultiBuffer(buffer); werr != nil {
|
||||
return werr
|
||||
|
@@ -10,7 +10,6 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common/crypto"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
"lukechampine.com/blake3"
|
||||
@@ -23,6 +22,8 @@ type ClientInstance struct {
|
||||
RelaysLength int
|
||||
XorMode uint32
|
||||
Seconds uint32
|
||||
PaddingLens [][3]int
|
||||
PaddingGaps [][3]int
|
||||
|
||||
RWLock sync.RWMutex
|
||||
Expire time.Time
|
||||
@@ -30,15 +31,13 @@ type ClientInstance struct {
|
||||
Ticket []byte
|
||||
}
|
||||
|
||||
func (i *ClientInstance) Init(nfsPKeysBytes [][]byte, xorMode, seconds uint32) (err error) {
|
||||
func (i *ClientInstance) Init(nfsPKeysBytes [][]byte, xorMode, seconds uint32, padding string) (err error) {
|
||||
if i.NfsPKeys != nil {
|
||||
err = errors.New("already initialized")
|
||||
return
|
||||
return errors.New("already initialized")
|
||||
}
|
||||
l := len(nfsPKeysBytes)
|
||||
if l == 0 {
|
||||
err = errors.New("empty nfsPKeysBytes")
|
||||
return
|
||||
return errors.New("empty nfsPKeysBytes")
|
||||
}
|
||||
i.NfsPKeys = make([]any, l)
|
||||
i.NfsPKeysBytes = nfsPKeysBytes
|
||||
@@ -60,7 +59,7 @@ func (i *ClientInstance) Init(nfsPKeysBytes [][]byte, xorMode, seconds uint32) (
|
||||
i.RelaysLength -= 32
|
||||
i.XorMode = xorMode
|
||||
i.Seconds = seconds
|
||||
return
|
||||
return ParsePadding(padding, &i.PaddingLens, &i.PaddingGaps)
|
||||
}
|
||||
|
||||
func (i *ClientInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||
@@ -71,7 +70,7 @@ func (i *ClientInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||
|
||||
ivAndRealysLength := 16 + i.RelaysLength
|
||||
pfsKeyExchangeLength := 18 + 1184 + 32 + 16
|
||||
paddingLength := int(crypto.RandBetween(100, 1000))
|
||||
paddingLength, paddingLens, paddingGaps := CreatPadding(i.PaddingLens, i.PaddingGaps)
|
||||
clientHello := make([]byte, ivAndRealysLength+pfsKeyExchangeLength+paddingLength)
|
||||
|
||||
iv := clientHello[:16]
|
||||
@@ -140,10 +139,18 @@ func (i *ClientInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||
nfsAEAD.Seal(padding[:0], nil, EncodeLength(paddingLength-18), nil)
|
||||
nfsAEAD.Seal(padding[:18], nil, padding[18:paddingLength-16], nil)
|
||||
|
||||
if _, err := conn.Write(clientHello); err != nil {
|
||||
return nil, err
|
||||
paddingLens[0] = ivAndRealysLength + pfsKeyExchangeLength + paddingLens[0]
|
||||
for i, l := range paddingLens { // sends padding in a fragmented way, to create variable traffic pattern, before inner VLESS flow takes control
|
||||
if l > 0 {
|
||||
if _, err := conn.Write(clientHello[:l]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clientHello = clientHello[l:]
|
||||
}
|
||||
if len(paddingGaps) > i {
|
||||
time.Sleep(paddingGaps[i])
|
||||
}
|
||||
}
|
||||
// padding can be sent in a fragmented way, to create variable traffic pattern, before inner VLESS flow takes control
|
||||
|
||||
encryptedPfsPublicKey := make([]byte, 1088+32+16)
|
||||
if _, err := io.ReadFull(conn, encryptedPfsPublicKey); err != nil {
|
||||
|
@@ -7,10 +7,12 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common/crypto"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
"lukechampine.com/blake3"
|
||||
@@ -31,15 +33,14 @@ type CommonConn struct {
|
||||
AEAD *AEAD
|
||||
PeerAEAD *AEAD
|
||||
PeerPadding []byte
|
||||
PeerInBytes []byte
|
||||
PeerCache []byte
|
||||
rawInput bytes.Buffer
|
||||
input bytes.Reader
|
||||
}
|
||||
|
||||
func NewCommonConn(conn net.Conn, useAES bool) *CommonConn {
|
||||
return &CommonConn{
|
||||
Conn: conn,
|
||||
UseAES: useAES,
|
||||
PeerInBytes: make([]byte, 5+17000), // no need to use sync.Pool, because we are always reading
|
||||
Conn: conn,
|
||||
UseAES: useAES,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,16 +100,14 @@ func (c *CommonConn) Read(b []byte) (int, error) {
|
||||
}
|
||||
c.PeerPadding = nil
|
||||
}
|
||||
if len(c.PeerCache) > 0 {
|
||||
n := copy(b, c.PeerCache)
|
||||
c.PeerCache = c.PeerCache[n:]
|
||||
return n, nil
|
||||
if c.input.Len() > 0 {
|
||||
return c.input.Read(b)
|
||||
}
|
||||
peerHeader := c.PeerInBytes[:5]
|
||||
if _, err := io.ReadFull(c.Conn, peerHeader); err != nil {
|
||||
peerHeader := [5]byte{}
|
||||
if _, err := io.ReadFull(c.Conn, peerHeader[:]); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
l, err := DecodeHeader(c.PeerInBytes[:5]) // l: 17~17000
|
||||
l, err := DecodeHeader(peerHeader[:]) // l: 17~17000
|
||||
if err != nil {
|
||||
if c.Client != nil && strings.Contains(err.Error(), "invalid header: ") { // client's 0-RTT
|
||||
c.Client.RWLock.Lock()
|
||||
@@ -121,7 +120,10 @@ func (c *CommonConn) Read(b []byte) (int, error) {
|
||||
return 0, err
|
||||
}
|
||||
c.Client = nil
|
||||
peerData := c.PeerInBytes[5 : 5+l]
|
||||
if c.rawInput.Cap() < l {
|
||||
c.rawInput.Grow(l) // no need to use sync.Pool, because we are always reading
|
||||
}
|
||||
peerData := c.rawInput.Bytes()[:l]
|
||||
if _, err := io.ReadFull(c.Conn, peerData); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -131,9 +133,9 @@ func (c *CommonConn) Read(b []byte) (int, error) {
|
||||
}
|
||||
var newAEAD *AEAD
|
||||
if bytes.Equal(c.PeerAEAD.Nonce[:], MaxNonce) {
|
||||
newAEAD = NewAEAD(c.PeerInBytes[:5+l], c.UnitedKey, c.UseAES)
|
||||
newAEAD = NewAEAD(append(peerHeader[:], peerData...), c.UnitedKey, c.UseAES)
|
||||
}
|
||||
_, err = c.PeerAEAD.Open(dst[:0], nil, peerData, peerHeader)
|
||||
_, err = c.PeerAEAD.Open(dst[:0], nil, peerData, peerHeader[:])
|
||||
if newAEAD != nil {
|
||||
c.PeerAEAD = newAEAD
|
||||
}
|
||||
@@ -141,7 +143,7 @@ func (c *CommonConn) Read(b []byte) (int, error) {
|
||||
return 0, err
|
||||
}
|
||||
if len(dst) > len(b) {
|
||||
c.PeerCache = dst[copy(b, dst):]
|
||||
c.input.Reset(dst[copy(b, dst):])
|
||||
dst = b // for len(dst)
|
||||
}
|
||||
return len(dst), nil
|
||||
@@ -213,7 +215,66 @@ func DecodeHeader(h []byte) (l int, err error) {
|
||||
l = 0
|
||||
}
|
||||
if l < 17 || l > 17000 { // TODO: TLSv1.3 max length
|
||||
err = errors.New("invalid header: ", fmt.Sprintf("%v", h[:5])) // DO NOT CHANGE: relied by client's Read()
|
||||
err = errors.New("invalid header: " + fmt.Sprintf("%v", h[:5])) // DO NOT CHANGE: relied by client's Read()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ParsePadding(padding string, paddingLens, paddingGaps *[][3]int) (err error) {
|
||||
if padding == "" {
|
||||
return
|
||||
}
|
||||
maxLen := 0
|
||||
for i, s := range strings.Split(padding, ".") {
|
||||
x := strings.Split(s, "-")
|
||||
if len(x) < 3 || x[0] == "" || x[1] == "" || x[2] == "" {
|
||||
return errors.New("invalid padding lenth/gap parameter: " + s)
|
||||
}
|
||||
y := [3]int{}
|
||||
if y[0], err = strconv.Atoi(x[0]); err != nil {
|
||||
return
|
||||
}
|
||||
if y[1], err = strconv.Atoi(x[1]); err != nil {
|
||||
return
|
||||
}
|
||||
if y[2], err = strconv.Atoi(x[2]); err != nil {
|
||||
return
|
||||
}
|
||||
if i == 0 && (y[0] < 100 || y[1] < 18+17 || y[2] < 18+17) {
|
||||
return errors.New("first padding length must not be smaller than 35")
|
||||
}
|
||||
if i%2 == 0 {
|
||||
*paddingLens = append(*paddingLens, y)
|
||||
maxLen += max(y[1], y[2])
|
||||
} else {
|
||||
*paddingGaps = append(*paddingGaps, y)
|
||||
}
|
||||
}
|
||||
if maxLen > 18+65535 {
|
||||
return errors.New("total padding length must not be larger than 65553")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func CreatPadding(paddingLens, paddingGaps [][3]int) (length int, lens []int, gaps []time.Duration) {
|
||||
if len(paddingLens) == 0 {
|
||||
paddingLens = [][3]int{{100, 111, 1111}, {50, 0, 3333}}
|
||||
paddingGaps = [][3]int{{75, 0, 111}}
|
||||
}
|
||||
for _, y := range paddingLens {
|
||||
l := 0
|
||||
if y[0] >= int(crypto.RandBetween(0, 100)) {
|
||||
l = int(crypto.RandBetween(int64(y[1]), int64(y[2])))
|
||||
}
|
||||
lens = append(lens, l)
|
||||
length += l
|
||||
}
|
||||
for _, y := range paddingGaps {
|
||||
g := 0
|
||||
if y[0] >= int(crypto.RandBetween(0, 100)) {
|
||||
g = int(crypto.RandBetween(int64(y[1]), int64(y[2])))
|
||||
}
|
||||
gaps = append(gaps, time.Duration(g)*time.Millisecond)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@@ -30,21 +30,21 @@ type ServerInstance struct {
|
||||
RelaysLength int
|
||||
XorMode uint32
|
||||
Seconds uint32
|
||||
PaddingLens [][3]int
|
||||
PaddingGaps [][3]int
|
||||
|
||||
RWLock sync.RWMutex
|
||||
Sessions map[[16]byte]*ServerSession
|
||||
Closed bool
|
||||
}
|
||||
|
||||
func (i *ServerInstance) Init(nfsSKeysBytes [][]byte, xorMode, seconds uint32) (err error) {
|
||||
func (i *ServerInstance) Init(nfsSKeysBytes [][]byte, xorMode, seconds uint32, padding string) (err error) {
|
||||
if i.NfsSKeys != nil {
|
||||
err = errors.New("already initialized")
|
||||
return
|
||||
return errors.New("already initialized")
|
||||
}
|
||||
l := len(nfsSKeysBytes)
|
||||
if l == 0 {
|
||||
err = errors.New("empty nfsSKeysBytes")
|
||||
return
|
||||
return errors.New("empty nfsSKeysBytes")
|
||||
}
|
||||
i.NfsSKeys = make([]any, l)
|
||||
i.NfsPKeysBytes = make([][]byte, l)
|
||||
@@ -88,7 +88,7 @@ func (i *ServerInstance) Init(nfsSKeysBytes [][]byte, xorMode, seconds uint32) (
|
||||
}
|
||||
}()
|
||||
}
|
||||
return
|
||||
return ParsePadding(padding, &i.PaddingLens, &i.PaddingGaps)
|
||||
}
|
||||
|
||||
func (i *ServerInstance) Close() (err error) {
|
||||
@@ -98,7 +98,7 @@ func (i *ServerInstance) Close() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||
func (i *ServerInstance) Handshake(conn net.Conn, fallback *[]byte) (*CommonConn, error) {
|
||||
if i.NfsSKeys == nil {
|
||||
return nil, errors.New("uninitialized")
|
||||
}
|
||||
@@ -108,6 +108,9 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||
if _, err := io.ReadFull(conn, ivAndRelays); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if fallback != nil {
|
||||
*fallback = append(*fallback, ivAndRelays...)
|
||||
}
|
||||
iv := ivAndRelays[:16]
|
||||
relays := ivAndRelays[16:]
|
||||
var nfsKey []byte
|
||||
@@ -157,6 +160,9 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||
if _, err := io.ReadFull(conn, encryptedLength); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if fallback != nil {
|
||||
*fallback = append(*fallback, encryptedLength...)
|
||||
}
|
||||
decryptedLength := make([]byte, 2)
|
||||
if _, err := nfsAEAD.Open(decryptedLength[:0], nil, encryptedLength, nil); err != nil {
|
||||
c.UseAES = !c.UseAES
|
||||
@@ -165,6 +171,9 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if fallback != nil {
|
||||
*fallback = nil
|
||||
}
|
||||
length := DecodeLength(decryptedLength)
|
||||
|
||||
if length == 32 {
|
||||
@@ -183,7 +192,7 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||
s := i.Sessions[[16]byte(ticket)]
|
||||
i.RWLock.RUnlock()
|
||||
if s == nil {
|
||||
noises := make([]byte, crypto.RandBetween(1268, 2268)) // matches 1-RTT's server hello length for "random", though it is not important, just for example
|
||||
noises := make([]byte, crypto.RandBetween(1279, 2279)) // matches 1-RTT's server hello length for "random", though it is not important, just for example
|
||||
var err error
|
||||
for err == nil {
|
||||
rand.Read(noises)
|
||||
@@ -237,25 +246,10 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||
c.UnitedKey = append(pfsKey, nfsKey...)
|
||||
c.AEAD = NewAEAD(pfsPublicKey, c.UnitedKey, c.UseAES)
|
||||
c.PeerAEAD = NewAEAD(encryptedPfsPublicKey[:1184+32], c.UnitedKey, c.UseAES)
|
||||
|
||||
ticket := make([]byte, 16)
|
||||
rand.Read(ticket)
|
||||
copy(ticket, EncodeLength(int(i.Seconds*4/5)))
|
||||
|
||||
pfsKeyExchangeLength := 1088 + 32 + 16
|
||||
encryptedTicketLength := 32
|
||||
paddingLength := int(crypto.RandBetween(100, 1000))
|
||||
serverHello := make([]byte, pfsKeyExchangeLength+encryptedTicketLength+paddingLength)
|
||||
nfsAEAD.Seal(serverHello[:0], MaxNonce, pfsPublicKey, nil)
|
||||
c.AEAD.Seal(serverHello[:pfsKeyExchangeLength], nil, ticket, nil)
|
||||
padding := serverHello[pfsKeyExchangeLength+encryptedTicketLength:]
|
||||
c.AEAD.Seal(padding[:0], nil, EncodeLength(paddingLength-18), nil)
|
||||
c.AEAD.Seal(padding[:18], nil, padding[18:paddingLength-16], nil)
|
||||
|
||||
if _, err := conn.Write(serverHello); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// padding can be sent in a fragmented way, to create variable traffic pattern, before inner VLESS flow takes control
|
||||
|
||||
if i.Seconds > 0 {
|
||||
i.RWLock.Lock()
|
||||
i.Sessions[[16]byte(ticket)] = &ServerSession{
|
||||
@@ -265,6 +259,29 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||
i.RWLock.Unlock()
|
||||
}
|
||||
|
||||
pfsKeyExchangeLength := 1088 + 32 + 16
|
||||
encryptedTicketLength := 32
|
||||
paddingLength, paddingLens, paddingGaps := CreatPadding(i.PaddingLens, i.PaddingGaps)
|
||||
serverHello := make([]byte, pfsKeyExchangeLength+encryptedTicketLength+paddingLength)
|
||||
nfsAEAD.Seal(serverHello[:0], MaxNonce, pfsPublicKey, nil)
|
||||
c.AEAD.Seal(serverHello[:pfsKeyExchangeLength], nil, ticket, nil)
|
||||
padding := serverHello[pfsKeyExchangeLength+encryptedTicketLength:]
|
||||
c.AEAD.Seal(padding[:0], nil, EncodeLength(paddingLength-18), nil)
|
||||
c.AEAD.Seal(padding[:18], nil, padding[18:paddingLength-16], nil)
|
||||
|
||||
paddingLens[0] = pfsKeyExchangeLength + encryptedTicketLength + paddingLens[0]
|
||||
for i, l := range paddingLens { // sends padding in a fragmented way, to create variable traffic pattern, before inner VLESS flow takes control
|
||||
if l > 0 {
|
||||
if _, err := conn.Write(serverHello[:l]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
serverHello = serverHello[l:]
|
||||
}
|
||||
if len(paddingGaps) > i {
|
||||
time.Sleep(paddingGaps[i])
|
||||
}
|
||||
}
|
||||
|
||||
// important: allows client sends padding slowly, eliminating 1-RTT's traffic pattern
|
||||
if _, err := io.ReadFull(conn, encryptedLength); err != nil {
|
||||
return nil, err
|
||||
|
@@ -116,6 +116,7 @@ type Config struct {
|
||||
Decryption string `protobuf:"bytes,3,opt,name=decryption,proto3" json:"decryption,omitempty"`
|
||||
XorMode uint32 `protobuf:"varint,4,opt,name=xorMode,proto3" json:"xorMode,omitempty"`
|
||||
Seconds uint32 `protobuf:"varint,5,opt,name=seconds,proto3" json:"seconds,omitempty"`
|
||||
Padding string `protobuf:"bytes,6,opt,name=padding,proto3" json:"padding,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Config) Reset() {
|
||||
@@ -183,6 +184,13 @@ func (x *Config) GetSeconds() uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetPadding() string {
|
||||
if x != nil {
|
||||
return x.Padding
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_proxy_vless_inbound_config_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_proxy_vless_inbound_config_proto_rawDesc = []byte{
|
||||
@@ -199,7 +207,7 @@ var file_proxy_vless_inbound_config_proto_rawDesc = []byte{
|
||||
0x68, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x74, 0x18, 0x05, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x78, 0x76, 0x65,
|
||||
0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x78, 0x76, 0x65, 0x72, 0x22, 0xd4, 0x01,
|
||||
0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x78, 0x76, 0x65, 0x72, 0x22, 0xee, 0x01,
|
||||
0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, 0x07, 0x63, 0x6c, 0x69, 0x65,
|
||||
0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x78, 0x72, 0x61, 0x79,
|
||||
0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,
|
||||
@@ -213,14 +221,16 @@ var file_proxy_vless_inbound_config_proto_rawDesc = []byte{
|
||||
0x12, 0x18, 0x0a, 0x07, 0x78, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28,
|
||||
0x0d, 0x52, 0x07, 0x78, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65,
|
||||
0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x73, 0x65, 0x63,
|
||||
0x6f, 0x6e, 0x64, 0x73, 0x42, 0x6a, 0x0a, 0x1c, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6e, 0x62,
|
||||
0x6f, 0x75, 0x6e, 0x64, 0x50, 0x01, 0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
|
||||
0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72,
|
||||
0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2f, 0x69, 0x6e,
|
||||
0x62, 0x6f, 0x75, 0x6e, 0x64, 0xaa, 0x02, 0x18, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f,
|
||||
0x78, 0x79, 0x2e, 0x56, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64,
|
||||
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x6f, 0x6e, 0x64, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x18,
|
||||
0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x42, 0x6a,
|
||||
0x0a, 0x1c, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79,
|
||||
0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x50, 0x01,
|
||||
0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c,
|
||||
0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78,
|
||||
0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2f, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0xaa,
|
||||
0x02, 0x18, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x56, 0x6c, 0x65,
|
||||
0x73, 0x73, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
@@ -24,4 +24,5 @@ message Config {
|
||||
string decryption = 3;
|
||||
uint32 xorMode = 4;
|
||||
uint32 seconds = 5;
|
||||
string padding = 6;
|
||||
}
|
||||
|
@@ -92,7 +92,7 @@ func New(ctx context.Context, config *Config, dc dns.Client, validator vless.Val
|
||||
nfsSKeysBytes = append(nfsSKeysBytes, b)
|
||||
}
|
||||
handler.decryption = &encryption.ServerInstance{}
|
||||
if err := handler.decryption.Init(nfsSKeysBytes, config.XorMode, config.Seconds); err != nil {
|
||||
if err := handler.decryption.Init(nfsSKeysBytes, config.XorMode, config.Seconds, config.Padding); err != nil {
|
||||
return nil, errors.New("failed to use decryption").Base(err).AtError()
|
||||
}
|
||||
}
|
||||
@@ -220,8 +220,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
|
||||
|
||||
if h.decryption != nil {
|
||||
var err error
|
||||
connection, err = h.decryption.Handshake(connection)
|
||||
if err != nil {
|
||||
if connection, err = h.decryption.Handshake(connection, nil); err != nil {
|
||||
return errors.New("ML-KEM-768 handshake failed").Base(err).AtInfo()
|
||||
}
|
||||
}
|
||||
@@ -491,7 +490,6 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
|
||||
// Flow: requestAddons.Flow,
|
||||
}
|
||||
|
||||
var peerCache *[]byte
|
||||
var input *bytes.Reader
|
||||
var rawInput *bytes.Buffer
|
||||
switch requestAddons.Flow {
|
||||
@@ -504,16 +502,15 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
|
||||
case protocol.RequestCommandMux:
|
||||
fallthrough // we will break Mux connections that contain TCP requests
|
||||
case protocol.RequestCommandTCP:
|
||||
if serverConn, ok := connection.(*encryption.CommonConn); ok {
|
||||
peerCache = &serverConn.PeerCache
|
||||
if _, ok := serverConn.Conn.(*encryption.XorConn); ok || !proxy.IsRAWTransport(iConn) {
|
||||
inbound.CanSpliceCopy = 3 // full-random xorConn / non-RAW transport can not use Linux Splice
|
||||
}
|
||||
break
|
||||
}
|
||||
var t reflect.Type
|
||||
var p uintptr
|
||||
if tlsConn, ok := iConn.(*tls.Conn); ok {
|
||||
if commonConn, ok := connection.(*encryption.CommonConn); ok {
|
||||
if _, ok := commonConn.Conn.(*encryption.XorConn); ok || !proxy.IsRAWTransport(iConn) {
|
||||
inbound.CanSpliceCopy = 3 // full-random xorConn / non-RAW transport can not use Linux Splice
|
||||
}
|
||||
t = reflect.TypeOf(commonConn).Elem()
|
||||
p = uintptr(unsafe.Pointer(commonConn))
|
||||
} else if tlsConn, ok := iConn.(*tls.Conn); ok {
|
||||
if tlsConn.ConnectionState().Version != gotls.VersionTLS13 {
|
||||
return errors.New(`failed to use `+requestAddons.Flow+`, found outer tls version `, tlsConn.ConnectionState().Version).AtWarning()
|
||||
}
|
||||
@@ -579,7 +576,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
|
||||
if requestAddons.Flow == vless.XRV {
|
||||
ctx1 := session.ContextWithInbound(ctx, nil) // TODO enable splice
|
||||
clientReader = proxy.NewVisionReader(clientReader, trafficState, true, ctx1)
|
||||
err = encoding.XtlsRead(clientReader, serverWriter, timer, connection, peerCache, input, rawInput, trafficState, nil, true, ctx1)
|
||||
err = encoding.XtlsRead(clientReader, serverWriter, timer, connection, input, rawInput, trafficState, nil, true, ctx1)
|
||||
} else {
|
||||
// from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBuffer
|
||||
err = buf.Copy(clientReader, serverWriter, buf.UpdateActivity(timer))
|
||||
|
@@ -77,7 +77,7 @@ func New(ctx context.Context, config *Config) (*Handler, error) {
|
||||
nfsPKeysBytes = append(nfsPKeysBytes, b)
|
||||
}
|
||||
handler.encryption = &encryption.ClientInstance{}
|
||||
if err := handler.encryption.Init(nfsPKeysBytes, a.XorMode, a.Seconds); err != nil {
|
||||
if err := handler.encryption.Init(nfsPKeysBytes, a.XorMode, a.Seconds, a.Padding); err != nil {
|
||||
return nil, errors.New("failed to use encryption").Base(err).AtError()
|
||||
}
|
||||
}
|
||||
@@ -118,8 +118,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||
|
||||
if h.encryption != nil {
|
||||
var err error
|
||||
conn, err = h.encryption.Handshake(conn)
|
||||
if err != nil {
|
||||
if conn, err = h.encryption.Handshake(conn); err != nil {
|
||||
return errors.New("ML-KEM-768 handshake failed").Base(err).AtInfo()
|
||||
}
|
||||
}
|
||||
@@ -146,7 +145,6 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||
Flow: account.Flow,
|
||||
}
|
||||
|
||||
var peerCache *[]byte
|
||||
var input *bytes.Reader
|
||||
var rawInput *bytes.Buffer
|
||||
allowUDP443 := false
|
||||
@@ -165,16 +163,15 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||
case protocol.RequestCommandMux:
|
||||
fallthrough // let server break Mux connections that contain TCP requests
|
||||
case protocol.RequestCommandTCP:
|
||||
if clientConn, ok := conn.(*encryption.CommonConn); ok {
|
||||
peerCache = &clientConn.PeerCache
|
||||
if _, ok := clientConn.Conn.(*encryption.XorConn); ok || !proxy.IsRAWTransport(iConn) {
|
||||
ob.CanSpliceCopy = 3 // full-random xorConn / non-RAW transport can not use Linux Splice
|
||||
}
|
||||
break
|
||||
}
|
||||
var t reflect.Type
|
||||
var p uintptr
|
||||
if tlsConn, ok := iConn.(*tls.Conn); ok {
|
||||
if commonConn, ok := conn.(*encryption.CommonConn); ok {
|
||||
if _, ok := commonConn.Conn.(*encryption.XorConn); ok || !proxy.IsRAWTransport(iConn) {
|
||||
ob.CanSpliceCopy = 3 // full-random xorConn / non-RAW transport can not use Linux Splice
|
||||
}
|
||||
t = reflect.TypeOf(commonConn).Elem()
|
||||
p = uintptr(unsafe.Pointer(commonConn))
|
||||
} else if tlsConn, ok := iConn.(*tls.Conn); ok {
|
||||
t = reflect.TypeOf(tlsConn.Conn).Elem()
|
||||
p = uintptr(unsafe.Pointer(tlsConn.Conn))
|
||||
} else if utlsConn, ok := iConn.(*tls.UConn); ok {
|
||||
@@ -306,7 +303,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||
}
|
||||
|
||||
if requestAddons.Flow == vless.XRV {
|
||||
err = encoding.XtlsRead(serverReader, clientWriter, timer, conn, peerCache, input, rawInput, trafficState, ob, false, ctx)
|
||||
err = encoding.XtlsRead(serverReader, clientWriter, timer, conn, input, rawInput, trafficState, ob, false, ctx)
|
||||
} else {
|
||||
// from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBuffer
|
||||
err = buf.Copy(serverReader, clientWriter, buf.UpdateActivity(timer))
|
||||
|
@@ -2288,6 +2288,10 @@ from .varzesh3 import Varzesh3IE
|
||||
from .vbox7 import Vbox7IE
|
||||
from .veo import VeoIE
|
||||
from .vesti import VestiIE
|
||||
from .vevo import (
|
||||
VevoIE,
|
||||
VevoPlaylistIE,
|
||||
)
|
||||
from .vgtv import (
|
||||
VGTVIE,
|
||||
BTArticleIE,
|
||||
|
@@ -90,6 +90,10 @@ class DisneyIE(InfoExtractor):
|
||||
webpage, 'embed data'), video_id)
|
||||
video_data = page_data['video']
|
||||
|
||||
for external in video_data.get('externals', []):
|
||||
if external.get('source') == 'vevo':
|
||||
return self.url_result('vevo:' + external['data_id'], 'Vevo')
|
||||
|
||||
video_id = video_data['id']
|
||||
title = video_data['title']
|
||||
|
||||
|
@@ -111,8 +111,12 @@ class MySpaceIE(InfoExtractor):
|
||||
search_data('stream-url'), search_data('hls-stream-url'),
|
||||
search_data('http-stream-url'))
|
||||
if not formats:
|
||||
vevo_id = search_data('vevo-id')
|
||||
youtube_id = search_data('youtube-id')
|
||||
if youtube_id:
|
||||
if vevo_id:
|
||||
self.to_screen(f'Vevo video detected: {vevo_id}')
|
||||
return self.url_result(f'vevo:{vevo_id}', ie='Vevo')
|
||||
elif youtube_id:
|
||||
self.to_screen(f'Youtube video detected: {youtube_id}')
|
||||
return self.url_result(youtube_id, ie='Youtube')
|
||||
else:
|
||||
|
352
yt-dlp/yt_dlp/extractor/vevo.py
Normal file
352
yt-dlp/yt_dlp/extractor/vevo.py
Normal file
@@ -0,0 +1,352 @@
|
||||
import json
|
||||
import re
|
||||
|
||||
from .common import InfoExtractor
|
||||
from ..networking.exceptions import HTTPError
|
||||
from ..utils import (
|
||||
ExtractorError,
|
||||
int_or_none,
|
||||
parse_iso8601,
|
||||
parse_qs,
|
||||
)
|
||||
|
||||
|
||||
class VevoBaseIE(InfoExtractor):
|
||||
def _extract_json(self, webpage, video_id):
|
||||
return self._parse_json(
|
||||
self._search_regex(
|
||||
r'window\.__INITIAL_STORE__\s*=\s*({.+?});\s*</script>',
|
||||
webpage, 'initial store'),
|
||||
video_id)
|
||||
|
||||
|
||||
class VevoIE(VevoBaseIE):
|
||||
"""
|
||||
Accepts urls from vevo.com or in the format 'vevo:{id}'
|
||||
(currently used by MTVIE and MySpaceIE)
|
||||
"""
|
||||
_VALID_URL = r'''(?x)
|
||||
(?:https?://(?:www\.)?vevo\.com/watch/(?!playlist|genre)(?:[^/]+/(?:[^/]+/)?)?|
|
||||
https?://cache\.vevo\.com/m/html/embed\.html\?video=|
|
||||
https?://videoplayer\.vevo\.com/embed/embedded\?videoId=|
|
||||
https?://embed\.vevo\.com/.*?[?&]isrc=|
|
||||
https?://tv\.vevo\.com/watch/artist/(?:[^/]+)/|
|
||||
vevo:)
|
||||
(?P<id>[^&?#]+)'''
|
||||
_EMBED_REGEX = [r'<iframe[^>]+?src=(["\'])(?P<url>(?:https?:)?//(?:cache\.)?vevo\.com/.+?)\1']
|
||||
|
||||
_TESTS = [{
|
||||
'url': 'http://www.vevo.com/watch/hurts/somebody-to-die-for/GB1101300280',
|
||||
'md5': '95ee28ee45e70130e3ab02b0f579ae23',
|
||||
'info_dict': {
|
||||
'id': 'GB1101300280',
|
||||
'ext': 'mp4',
|
||||
'title': 'Hurts - Somebody to Die For',
|
||||
'timestamp': 1372057200,
|
||||
'upload_date': '20130624',
|
||||
'uploader': 'Hurts',
|
||||
'track': 'Somebody to Die For',
|
||||
'artist': 'Hurts',
|
||||
'genre': 'Pop',
|
||||
},
|
||||
'expected_warnings': ['Unable to download SMIL file', 'Unable to download info'],
|
||||
}, {
|
||||
'note': 'v3 SMIL format',
|
||||
'url': 'http://www.vevo.com/watch/cassadee-pope/i-wish-i-could-break-your-heart/USUV71302923',
|
||||
'md5': 'f6ab09b034f8c22969020b042e5ac7fc',
|
||||
'info_dict': {
|
||||
'id': 'USUV71302923',
|
||||
'ext': 'mp4',
|
||||
'title': 'Cassadee Pope - I Wish I Could Break Your Heart',
|
||||
'timestamp': 1392796919,
|
||||
'upload_date': '20140219',
|
||||
'uploader': 'Cassadee Pope',
|
||||
'track': 'I Wish I Could Break Your Heart',
|
||||
'artist': 'Cassadee Pope',
|
||||
'genre': 'Country',
|
||||
},
|
||||
'expected_warnings': ['Unable to download SMIL file', 'Unable to download info'],
|
||||
}, {
|
||||
'note': 'Age-limited video',
|
||||
'url': 'https://www.vevo.com/watch/justin-timberlake/tunnel-vision-explicit/USRV81300282',
|
||||
'info_dict': {
|
||||
'id': 'USRV81300282',
|
||||
'ext': 'mp4',
|
||||
'title': 'Justin Timberlake - Tunnel Vision (Explicit)',
|
||||
'age_limit': 18,
|
||||
'timestamp': 1372888800,
|
||||
'upload_date': '20130703',
|
||||
'uploader': 'Justin Timberlake',
|
||||
'track': 'Tunnel Vision (Explicit)',
|
||||
'artist': 'Justin Timberlake',
|
||||
'genre': 'Pop',
|
||||
},
|
||||
'expected_warnings': ['Unable to download SMIL file', 'Unable to download info'],
|
||||
}, {
|
||||
'note': 'No video_info',
|
||||
'url': 'http://www.vevo.com/watch/k-camp-1/Till-I-Die/USUV71503000',
|
||||
'md5': '8b83cc492d72fc9cf74a02acee7dc1b0',
|
||||
'info_dict': {
|
||||
'id': 'USUV71503000',
|
||||
'ext': 'mp4',
|
||||
'title': 'K Camp ft. T.I. - Till I Die',
|
||||
'age_limit': 18,
|
||||
'timestamp': 1449468000,
|
||||
'upload_date': '20151207',
|
||||
'uploader': 'K Camp',
|
||||
'track': 'Till I Die',
|
||||
'artist': 'K Camp',
|
||||
'genre': 'Hip-Hop',
|
||||
},
|
||||
'expected_warnings': ['Unable to download SMIL file', 'Unable to download info'],
|
||||
}, {
|
||||
'note': 'Featured test',
|
||||
'url': 'https://www.vevo.com/watch/lemaitre/Wait/USUV71402190',
|
||||
'md5': 'd28675e5e8805035d949dc5cf161071d',
|
||||
'info_dict': {
|
||||
'id': 'USUV71402190',
|
||||
'ext': 'mp4',
|
||||
'title': 'Lemaitre ft. LoLo - Wait',
|
||||
'age_limit': 0,
|
||||
'timestamp': 1413432000,
|
||||
'upload_date': '20141016',
|
||||
'uploader': 'Lemaitre',
|
||||
'track': 'Wait',
|
||||
'artist': 'Lemaitre',
|
||||
'genre': 'Electronic',
|
||||
},
|
||||
'expected_warnings': ['Unable to download SMIL file', 'Unable to download info'],
|
||||
}, {
|
||||
'note': 'Only available via webpage',
|
||||
'url': 'http://www.vevo.com/watch/GBUV71600656',
|
||||
'md5': '67e79210613865b66a47c33baa5e37fe',
|
||||
'info_dict': {
|
||||
'id': 'GBUV71600656',
|
||||
'ext': 'mp4',
|
||||
'title': 'ABC - Viva Love',
|
||||
'age_limit': 0,
|
||||
'timestamp': 1461830400,
|
||||
'upload_date': '20160428',
|
||||
'uploader': 'ABC',
|
||||
'track': 'Viva Love',
|
||||
'artist': 'ABC',
|
||||
'genre': 'Pop',
|
||||
},
|
||||
'expected_warnings': ['Failed to download video versions info'],
|
||||
}, {
|
||||
# no genres available
|
||||
'url': 'http://www.vevo.com/watch/INS171400764',
|
||||
'only_matching': True,
|
||||
}, {
|
||||
# Another case available only via the webpage; using streams/streamsV3 formats
|
||||
# Geo-restricted to Netherlands/Germany
|
||||
'url': 'http://www.vevo.com/watch/boostee/pop-corn-clip-officiel/FR1A91600909',
|
||||
'only_matching': True,
|
||||
}, {
|
||||
'url': 'https://embed.vevo.com/?isrc=USH5V1923499&partnerId=4d61b777-8023-4191-9ede-497ed6c24647&partnerAdCode=',
|
||||
'only_matching': True,
|
||||
}, {
|
||||
'url': 'https://tv.vevo.com/watch/artist/janet-jackson/US0450100550',
|
||||
'only_matching': True,
|
||||
}]
|
||||
_VERSIONS = {
|
||||
0: 'youtube', # only in AuthenticateVideo videoVersions
|
||||
1: 'level3',
|
||||
2: 'akamai',
|
||||
3: 'level3',
|
||||
4: 'amazon',
|
||||
}
|
||||
|
||||
def _initialize_api(self, video_id):
|
||||
webpage = self._download_webpage(
|
||||
'https://accounts.vevo.com/token', None,
|
||||
note='Retrieving oauth token',
|
||||
errnote='Unable to retrieve oauth token',
|
||||
data=json.dumps({
|
||||
'client_id': 'SPupX1tvqFEopQ1YS6SS',
|
||||
'grant_type': 'urn:vevo:params:oauth:grant-type:anonymous',
|
||||
}).encode(),
|
||||
headers={
|
||||
'Content-Type': 'application/json',
|
||||
})
|
||||
|
||||
if re.search(r'(?i)THIS PAGE IS CURRENTLY UNAVAILABLE IN YOUR REGION', webpage):
|
||||
self.raise_geo_restricted(
|
||||
f'{self.IE_NAME} said: This page is currently unavailable in your region')
|
||||
|
||||
auth_info = self._parse_json(webpage, video_id)
|
||||
self._api_url_template = self.http_scheme() + '//apiv2.vevo.com/%s?token=' + auth_info['legacy_token']
|
||||
|
||||
def _call_api(self, path, *args, **kwargs):
|
||||
try:
|
||||
data = self._download_json(self._api_url_template % path, *args, **kwargs)
|
||||
except ExtractorError as e:
|
||||
if isinstance(e.cause, HTTPError):
|
||||
errors = self._parse_json(e.cause.response.read().decode(), None)['errors']
|
||||
error_message = ', '.join([error['message'] for error in errors])
|
||||
raise ExtractorError(f'{self.IE_NAME} said: {error_message}', expected=True)
|
||||
raise
|
||||
return data
|
||||
|
||||
def _real_extract(self, url):
|
||||
video_id = self._match_id(url)
|
||||
|
||||
self._initialize_api(video_id)
|
||||
|
||||
video_info = self._call_api(
|
||||
f'video/{video_id}', video_id, 'Downloading api video info',
|
||||
'Failed to download video info')
|
||||
|
||||
video_versions = self._call_api(
|
||||
f'video/{video_id}/streams', video_id,
|
||||
'Downloading video versions info',
|
||||
'Failed to download video versions info',
|
||||
fatal=False)
|
||||
|
||||
# Some videos are only available via webpage (e.g.
|
||||
# https://github.com/ytdl-org/youtube-dl/issues/9366)
|
||||
if not video_versions:
|
||||
webpage = self._download_webpage(url, video_id)
|
||||
json_data = self._extract_json(webpage, video_id)
|
||||
if 'streams' in json_data.get('default', {}):
|
||||
video_versions = json_data['default']['streams'][video_id][0]
|
||||
else:
|
||||
video_versions = [
|
||||
value
|
||||
for key, value in json_data['apollo']['data'].items()
|
||||
if key.startswith(f'{video_id}.streams')]
|
||||
|
||||
uploader = None
|
||||
artist = None
|
||||
featured_artist = None
|
||||
artists = video_info.get('artists')
|
||||
for curr_artist in artists:
|
||||
if curr_artist.get('role') == 'Featured':
|
||||
featured_artist = curr_artist['name']
|
||||
else:
|
||||
artist = uploader = curr_artist['name']
|
||||
|
||||
formats = []
|
||||
for video_version in video_versions:
|
||||
version = self._VERSIONS.get(video_version.get('version'), 'generic')
|
||||
version_url = video_version.get('url')
|
||||
if not version_url:
|
||||
continue
|
||||
|
||||
if '.ism' in version_url:
|
||||
continue
|
||||
elif '.mpd' in version_url:
|
||||
formats.extend(self._extract_mpd_formats(
|
||||
version_url, video_id, mpd_id=f'dash-{version}',
|
||||
note=f'Downloading {version} MPD information',
|
||||
errnote=f'Failed to download {version} MPD information',
|
||||
fatal=False))
|
||||
elif '.m3u8' in version_url:
|
||||
formats.extend(self._extract_m3u8_formats(
|
||||
version_url, video_id, 'mp4', 'm3u8_native',
|
||||
m3u8_id=f'hls-{version}',
|
||||
note=f'Downloading {version} m3u8 information',
|
||||
errnote=f'Failed to download {version} m3u8 information',
|
||||
fatal=False))
|
||||
else:
|
||||
m = re.search(r'''(?xi)
|
||||
_(?P<quality>[a-z0-9]+)
|
||||
_(?P<width>[0-9]+)x(?P<height>[0-9]+)
|
||||
_(?P<vcodec>[a-z0-9]+)
|
||||
_(?P<vbr>[0-9]+)
|
||||
_(?P<acodec>[a-z0-9]+)
|
||||
_(?P<abr>[0-9]+)
|
||||
\.(?P<ext>[a-z0-9]+)''', version_url)
|
||||
if not m:
|
||||
continue
|
||||
|
||||
formats.append({
|
||||
'url': version_url,
|
||||
'format_id': f'http-{version}-{video_version.get("quality") or m.group("quality")}',
|
||||
'vcodec': m.group('vcodec'),
|
||||
'acodec': m.group('acodec'),
|
||||
'vbr': int(m.group('vbr')),
|
||||
'abr': int(m.group('abr')),
|
||||
'ext': m.group('ext'),
|
||||
'width': int(m.group('width')),
|
||||
'height': int(m.group('height')),
|
||||
})
|
||||
|
||||
track = video_info['title']
|
||||
if featured_artist:
|
||||
artist = f'{artist} ft. {featured_artist}'
|
||||
title = f'{artist} - {track}' if artist else track
|
||||
|
||||
genres = video_info.get('genres')
|
||||
genre = (
|
||||
genres[0] if genres and isinstance(genres, list)
|
||||
and isinstance(genres[0], str) else None)
|
||||
|
||||
is_explicit = video_info.get('isExplicit')
|
||||
if is_explicit is True:
|
||||
age_limit = 18
|
||||
elif is_explicit is False:
|
||||
age_limit = 0
|
||||
else:
|
||||
age_limit = None
|
||||
|
||||
return {
|
||||
'id': video_id,
|
||||
'title': title,
|
||||
'formats': formats,
|
||||
'thumbnail': video_info.get('imageUrl') or video_info.get('thumbnailUrl'),
|
||||
'timestamp': parse_iso8601(video_info.get('releaseDate')),
|
||||
'uploader': uploader,
|
||||
'duration': int_or_none(video_info.get('duration')),
|
||||
'view_count': int_or_none(video_info.get('views', {}).get('total')),
|
||||
'age_limit': age_limit,
|
||||
'track': track,
|
||||
'artist': uploader,
|
||||
'genre': genre,
|
||||
}
|
||||
|
||||
|
||||
class VevoPlaylistIE(VevoBaseIE):
|
||||
_VALID_URL = r'https?://(?:www\.)?vevo\.com/watch/(?P<kind>playlist|genre)/(?P<id>[^/?#&]+)'
|
||||
|
||||
_TESTS = [{
|
||||
'url': 'http://www.vevo.com/watch/genre/rock',
|
||||
'info_dict': {
|
||||
'id': 'rock',
|
||||
'title': 'Rock',
|
||||
},
|
||||
'playlist_count': 20,
|
||||
}, {
|
||||
'url': 'http://www.vevo.com/watch/genre/rock?index=0',
|
||||
'only_matching': True,
|
||||
}]
|
||||
|
||||
def _real_extract(self, url):
|
||||
mobj = self._match_valid_url(url)
|
||||
playlist_id = mobj.group('id')
|
||||
playlist_kind = mobj.group('kind')
|
||||
|
||||
webpage = self._download_webpage(url, playlist_id)
|
||||
|
||||
qs = parse_qs(url)
|
||||
index = qs.get('index', [None])[0]
|
||||
|
||||
if index:
|
||||
video_id = self._search_regex(
|
||||
r'<meta[^>]+content=(["\'])vevo://video/(?P<id>.+?)\1[^>]*>',
|
||||
webpage, 'video id', default=None, group='id')
|
||||
if video_id:
|
||||
return self.url_result(f'vevo:{video_id}', VevoIE.ie_key())
|
||||
|
||||
playlists = self._extract_json(webpage, playlist_id)['default'][f'{playlist_kind}s']
|
||||
|
||||
playlist = (next(iter(playlists.values()))
|
||||
if playlist_kind == 'playlist' else playlists[playlist_id])
|
||||
|
||||
entries = [
|
||||
self.url_result(f'vevo:{src}', VevoIE.ie_key())
|
||||
for src in playlist['isrcs']]
|
||||
|
||||
return self.playlist_result(
|
||||
entries, playlist.get('playlistId') or playlist_id,
|
||||
playlist.get('name'), playlist.get('description'))
|
Reference in New Issue
Block a user