mirror of
https://github.com/bolucat/Archive.git
synced 2025-09-26 20:21:35 +08:00
Update On Tue Aug 12 20:40:50 CEST 2025
This commit is contained in:
1
.github/update.log
vendored
1
.github/update.log
vendored
@@ -1087,3 +1087,4 @@ Update On Fri Aug 8 20:38:56 CEST 2025
|
||||
Update On Sat Aug 9 20:40:00 CEST 2025
|
||||
Update On Sun Aug 10 20:40:02 CEST 2025
|
||||
Update On Mon Aug 11 20:43:53 CEST 2025
|
||||
Update On Tue Aug 12 20:40:42 CEST 2025
|
||||
|
@@ -3,14 +3,11 @@ package outbound
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/mihomo/common/convert"
|
||||
N "github.com/metacubex/mihomo/common/net"
|
||||
@@ -456,41 +453,11 @@ func NewVless(option VlessOption) (*Vless, error) {
|
||||
option: &option,
|
||||
}
|
||||
|
||||
if s := strings.SplitN(option.Encryption, "-", 4); len(s) == 4 && s[2] == "mlkem768client" {
|
||||
var minutes uint32
|
||||
if s[0] != "1rtt" {
|
||||
t := strings.TrimSuffix(s[0], "min")
|
||||
if t == s[0] {
|
||||
return nil, fmt.Errorf("invaild vless encryption value: %s", option.Encryption)
|
||||
}
|
||||
var i int
|
||||
i, err = strconv.Atoi(t)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invaild vless encryption value: %s", option.Encryption)
|
||||
}
|
||||
minutes = uint32(i)
|
||||
}
|
||||
var xor uint32
|
||||
switch s[1] {
|
||||
case "vless":
|
||||
case "aes128xor":
|
||||
xor = 1
|
||||
default:
|
||||
return nil, fmt.Errorf("invaild vless encryption value: %s", option.Encryption)
|
||||
}
|
||||
var b []byte
|
||||
b, err = base64.RawURLEncoding.DecodeString(s[3])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invaild vless encryption value: %s", option.Encryption)
|
||||
}
|
||||
if len(b) == encryption.MLKEM768ClientLength {
|
||||
v.encryption = &encryption.ClientInstance{}
|
||||
if err = v.encryption.Init(b, xor, time.Duration(minutes)*time.Minute); err != nil {
|
||||
return nil, fmt.Errorf("failed to use mlkem768seed: %w", err)
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("invaild vless encryption value: %s", option.Encryption)
|
||||
}
|
||||
v.encryption, err = encryption.NewClient(option.Encryption)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if v.encryption != nil {
|
||||
if option.Flow != "" {
|
||||
return nil, errors.New(`vless "encryption" doesn't support "flow" yet`)
|
||||
}
|
||||
|
@@ -10,11 +10,13 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"strconv"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
N "github.com/metacubex/mihomo/common/net"
|
||||
"github.com/metacubex/mihomo/common/pool"
|
||||
"github.com/metacubex/mihomo/common/utils"
|
||||
"github.com/metacubex/mihomo/component/ca"
|
||||
"github.com/metacubex/mihomo/component/dialer"
|
||||
@@ -30,7 +32,7 @@ import (
|
||||
)
|
||||
|
||||
var httpPath = "/inbound_test"
|
||||
var httpData = make([]byte, 10240)
|
||||
var httpData = make([]byte, 2*pool.RelayBufferSize)
|
||||
var remoteAddr = netip.MustParseAddr("1.2.3.4")
|
||||
var userUUID = utils.NewUUIDV4().String()
|
||||
var tlsCertificate, tlsPrivateKey, tlsFingerprint, _ = ca.NewRandomTLSKeyPair(ca.KeyPairTypeP256)
|
||||
@@ -134,14 +136,21 @@ func NewHttpTestTunnel() *TestTunnel {
|
||||
|
||||
r := chi.NewRouter()
|
||||
r.Get(httpPath, func(w http.ResponseWriter, r *http.Request) {
|
||||
render.Data(w, r, httpData)
|
||||
query := r.URL.Query()
|
||||
size, err := strconv.Atoi(query.Get("size"))
|
||||
if err != nil {
|
||||
render.Status(r, http.StatusBadRequest)
|
||||
render.PlainText(w, r, err.Error())
|
||||
return
|
||||
}
|
||||
render.Data(w, r, httpData[:size])
|
||||
})
|
||||
h2Server := &http2.Server{}
|
||||
server := http.Server{Handler: r}
|
||||
_ = http2.ConfigureServer(&server, h2Server)
|
||||
go server.Serve(ln)
|
||||
testFn := func(t *testing.T, proxy C.ProxyAdapter, proto string) {
|
||||
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s://%s%s", proto, remoteAddr, httpPath), nil)
|
||||
testFn := func(t *testing.T, proxy C.ProxyAdapter, proto string, size int) {
|
||||
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s://%s%s?size=%d", proto, remoteAddr, httpPath, size), nil)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
@@ -200,7 +209,7 @@ func NewHttpTestTunnel() *TestTunnel {
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
assert.Equal(t, httpData, data)
|
||||
assert.Equal(t, httpData[:size], data)
|
||||
}
|
||||
tunnel := &TestTunnel{
|
||||
HandleTCPConnFn: func(conn net.Conn, metadata *C.Metadata) {
|
||||
@@ -241,27 +250,30 @@ func NewHttpTestTunnel() *TestTunnel {
|
||||
},
|
||||
CloseFn: ln.Close,
|
||||
DoTestFn: func(t *testing.T, proxy C.ProxyAdapter) {
|
||||
|
||||
// Sequential testing for debugging
|
||||
t.Run("Sequential", func(t *testing.T) {
|
||||
testFn(t, proxy, "http")
|
||||
testFn(t, proxy, "https")
|
||||
testFn(t, proxy, "http", len(httpData))
|
||||
testFn(t, proxy, "https", len(httpData))
|
||||
})
|
||||
|
||||
// Concurrent testing to detect stress
|
||||
t.Run("Concurrent", func(t *testing.T) {
|
||||
wg := sync.WaitGroup{}
|
||||
const num = 50
|
||||
for i := 0; i < num; i++ {
|
||||
num := len(httpData) / 1024
|
||||
for i := 1; i <= num; i++ {
|
||||
i := i
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
testFn(t, proxy, "https")
|
||||
testFn(t, proxy, "https", i*1024)
|
||||
defer wg.Done()
|
||||
}()
|
||||
}
|
||||
for i := 0; i < num; i++ {
|
||||
for i := 1; i <= num; i++ {
|
||||
i := i
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
testFn(t, proxy, "http")
|
||||
testFn(t, proxy, "http", i*1024)
|
||||
defer wg.Done()
|
||||
}()
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var singMuxProtocolList = []string{"h2mux", "smux"} // don't test "yamux" because it has some confused bugs
|
||||
var singMuxProtocolList = []string{"smux"} // don't test "h2mux" and "yamux" because it has some confused bugs
|
||||
|
||||
// notCloseProxyAdapter is a proxy adapter that does not close the underlying outbound.ProxyAdapter.
|
||||
// The outbound.SingMux will close the underlying outbound.ProxyAdapter when it is closed, but we don't want to close it.
|
||||
|
@@ -2,15 +2,11 @@ package sing_vless
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/metacubex/mihomo/adapter/inbound"
|
||||
@@ -88,42 +84,11 @@ func New(config LC.VlessServer, tunnel C.Tunnel, additions ...inbound.Addition)
|
||||
|
||||
sl = &Listener{config: config, service: service}
|
||||
|
||||
if s := strings.SplitN(config.Decryption, "-", 4); len(s) == 4 && s[2] == "mlkem768seed" {
|
||||
var minutes uint32
|
||||
if s[0] != "1rtt" {
|
||||
t := strings.TrimSuffix(s[0], "min")
|
||||
if t == s[0] {
|
||||
return nil, fmt.Errorf("invaild vless decryption value: %s", config.Decryption)
|
||||
}
|
||||
var i int
|
||||
i, err = strconv.Atoi(t)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invaild vless decryption value: %s", config.Decryption)
|
||||
}
|
||||
minutes = uint32(i)
|
||||
}
|
||||
var xor uint32
|
||||
switch s[1] {
|
||||
case "vless":
|
||||
case "aes128xor":
|
||||
xor = 1
|
||||
default:
|
||||
return nil, fmt.Errorf("invaild vless decryption value: %s", config.Decryption)
|
||||
}
|
||||
var b []byte
|
||||
b, err = base64.RawURLEncoding.DecodeString(s[3])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invaild vless decryption value: %s", config.Decryption)
|
||||
}
|
||||
if len(b) == encryption.MLKEM768SeedLength {
|
||||
sl.decryption = &encryption.ServerInstance{}
|
||||
if err = sl.decryption.Init(b, xor, time.Duration(minutes)*time.Minute); err != nil {
|
||||
return nil, fmt.Errorf("failed to use mlkem768seed: %w", err)
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("invaild vless decryption value: %s", config.Decryption)
|
||||
}
|
||||
|
||||
sl.decryption, err = encryption.NewServer(config.Decryption)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if sl.decryption != nil {
|
||||
defer func() { // decryption must be closed to avoid the goroutine leak
|
||||
if err != nil {
|
||||
_ = sl.decryption.Close()
|
||||
|
@@ -4,7 +4,6 @@ import (
|
||||
"bytes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
@@ -13,7 +12,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/utls/mlkem"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
"golang.org/x/sys/cpu"
|
||||
)
|
||||
|
||||
@@ -30,19 +28,20 @@ var (
|
||||
var ClientCipher byte
|
||||
|
||||
func init() {
|
||||
if !HasAESGCMHardwareSupport {
|
||||
if HasAESGCMHardwareSupport {
|
||||
ClientCipher = 1
|
||||
}
|
||||
}
|
||||
|
||||
type ClientInstance struct {
|
||||
sync.RWMutex
|
||||
eKeyNfs *mlkem.EncapsulationKey768
|
||||
xor uint32
|
||||
minutes time.Duration
|
||||
expire time.Time
|
||||
baseKey []byte
|
||||
ticket []byte
|
||||
nfsEKey *mlkem.EncapsulationKey768
|
||||
nfsEKeyBytes []byte
|
||||
xor uint32
|
||||
minutes time.Duration
|
||||
expire time.Time
|
||||
baseKey []byte
|
||||
ticket []byte
|
||||
}
|
||||
|
||||
type ClientConn struct {
|
||||
@@ -58,19 +57,22 @@ type ClientConn struct {
|
||||
peerCache []byte
|
||||
}
|
||||
|
||||
func (i *ClientInstance) Init(eKeyNfsData []byte, xor uint32, minutes time.Duration) (err error) {
|
||||
i.eKeyNfs, err = mlkem.NewEncapsulationKey768(eKeyNfsData)
|
||||
i.xor = xor
|
||||
func (i *ClientInstance) Init(nfsEKeyBytes []byte, xor uint32, minutes time.Duration) (err error) {
|
||||
i.nfsEKey, err = mlkem.NewEncapsulationKey768(nfsEKeyBytes)
|
||||
if xor > 0 {
|
||||
i.nfsEKeyBytes = nfsEKeyBytes
|
||||
i.xor = xor
|
||||
}
|
||||
i.minutes = minutes
|
||||
return
|
||||
}
|
||||
|
||||
func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) {
|
||||
if i.eKeyNfs == nil {
|
||||
if i.nfsEKey == nil {
|
||||
return nil, errors.New("uninitialized")
|
||||
}
|
||||
if i.xor == 1 {
|
||||
conn = NewXorConn(conn, i.eKeyNfs.Bytes())
|
||||
if i.xor > 0 {
|
||||
conn = NewXorConn(conn, i.nfsEKeyBytes)
|
||||
}
|
||||
c := &ClientConn{Conn: conn}
|
||||
|
||||
@@ -86,18 +88,19 @@ func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) {
|
||||
i.RUnlock()
|
||||
}
|
||||
|
||||
nfsKey, encapsulatedNfsKey := i.eKeyNfs.Encapsulate()
|
||||
seed := make([]byte, 64)
|
||||
rand.Read(seed)
|
||||
dKeyPfs, _ := mlkem.NewDecapsulationKey768(seed)
|
||||
eKeyPfs := dKeyPfs.EncapsulationKey().Bytes()
|
||||
padding := randBetween(100, 1000)
|
||||
pfsDKeySeed := make([]byte, 64)
|
||||
rand.Read(pfsDKeySeed)
|
||||
pfsDKey, _ := mlkem.NewDecapsulationKey768(pfsDKeySeed)
|
||||
pfsEKeyBytes := pfsDKey.EncapsulationKey().Bytes()
|
||||
nfsKey, encapsulatedNfsKey := i.nfsEKey.Encapsulate()
|
||||
paddingLen := randBetween(100, 1000)
|
||||
|
||||
clientHello := make([]byte, 1088+1184+1+5+padding)
|
||||
copy(clientHello, encapsulatedNfsKey)
|
||||
copy(clientHello[1088:], eKeyPfs)
|
||||
clientHello[2272] = ClientCipher
|
||||
encodeHeader(clientHello[2273:], int(padding))
|
||||
clientHello := make([]byte, 1+1184+1088+5+paddingLen)
|
||||
clientHello[0] = ClientCipher
|
||||
copy(clientHello[1:], pfsEKeyBytes)
|
||||
copy(clientHello[1185:], encapsulatedNfsKey)
|
||||
EncodeHeader(clientHello[2273:], int(paddingLen))
|
||||
rand.Read(clientHello[2278:])
|
||||
|
||||
if _, err := c.Conn.Write(clientHello); err != nil {
|
||||
return nil, err
|
||||
@@ -111,17 +114,15 @@ func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) {
|
||||
encapsulatedPfsKey := peerServerHello[:1088]
|
||||
c.ticket = peerServerHello[1088:]
|
||||
|
||||
pfsKey, err := dKeyPfs.Decapsulate(encapsulatedPfsKey)
|
||||
pfsKey, err := pfsDKey.Decapsulate(encapsulatedPfsKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.baseKey = append(nfsKey, pfsKey...)
|
||||
c.baseKey = append(pfsKey, nfsKey...)
|
||||
|
||||
authKey := make([]byte, 32)
|
||||
hkdf.New(sha256.New, c.baseKey, encapsulatedNfsKey, eKeyPfs).Read(authKey)
|
||||
nonce := make([]byte, 12)
|
||||
VLESS, _ := newAead(ClientCipher, authKey).Open(nil, nonce, c.ticket, encapsulatedPfsKey)
|
||||
if !bytes.Equal(VLESS, []byte("VLESS")) { // TODO: more message
|
||||
nonce := [12]byte{ClientCipher}
|
||||
VLESS, _ := NewAead(ClientCipher, c.baseKey, encapsulatedPfsKey, encapsulatedNfsKey).Open(nil, nonce[:], c.ticket, pfsEKeyBytes)
|
||||
if !bytes.Equal(VLESS, []byte("VLESS")) { // TODO: more messages
|
||||
return nil, errors.New("invalid server")
|
||||
}
|
||||
|
||||
@@ -141,32 +142,39 @@ func (c *ClientConn) Write(b []byte) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
var data []byte
|
||||
if c.aead == nil {
|
||||
c.random = make([]byte, 32)
|
||||
rand.Read(c.random)
|
||||
key := make([]byte, 32)
|
||||
hkdf.New(sha256.New, c.baseKey, c.random, c.ticket).Read(key)
|
||||
c.aead = newAead(ClientCipher, key)
|
||||
c.nonce = make([]byte, 12)
|
||||
|
||||
data = make([]byte, 21+32+5+len(b)+16)
|
||||
copy(data, c.ticket)
|
||||
copy(data[21:], c.random)
|
||||
encodeHeader(data[53:], len(b)+16)
|
||||
c.aead.Seal(data[:58], c.nonce, b, data[53:58])
|
||||
} else {
|
||||
data = make([]byte, 5+len(b)+16)
|
||||
encodeHeader(data, len(b)+16)
|
||||
c.aead.Seal(data[:5], c.nonce, b, data[:5])
|
||||
}
|
||||
increaseNonce(c.nonce)
|
||||
if _, err := c.Conn.Write(data); err != nil {
|
||||
return 0, err
|
||||
for n := 0; n < len(b); {
|
||||
b := b[n:]
|
||||
if len(b) > 8192 {
|
||||
b = b[:8192] // for avoiding another copy() in server's Read()
|
||||
}
|
||||
n += len(b)
|
||||
if c.aead == nil {
|
||||
c.random = make([]byte, 32)
|
||||
rand.Read(c.random)
|
||||
c.aead = NewAead(ClientCipher, c.baseKey, c.random, c.ticket)
|
||||
c.nonce = make([]byte, 12)
|
||||
data = make([]byte, 21+32+5+len(b)+16)
|
||||
copy(data, c.ticket)
|
||||
copy(data[21:], c.random)
|
||||
EncodeHeader(data[53:], len(b)+16)
|
||||
c.aead.Seal(data[:58], c.nonce, b, data[53:58])
|
||||
} else {
|
||||
data = make([]byte, 5+len(b)+16)
|
||||
EncodeHeader(data, len(b)+16)
|
||||
c.aead.Seal(data[:5], c.nonce, b, data[:5])
|
||||
if bytes.Equal(c.nonce, MaxNonce) {
|
||||
c.aead = NewAead(ClientCipher, c.baseKey, data[5:], data[:5])
|
||||
}
|
||||
}
|
||||
IncreaseNonce(c.nonce)
|
||||
if _, err := c.Conn.Write(data); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
func (c *ClientConn) Read(b []byte) (int, error) { // after first Write()
|
||||
func (c *ClientConn) Read(b []byte) (int, error) {
|
||||
if len(b) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
@@ -177,11 +185,11 @@ func (c *ClientConn) Read(b []byte) (int, error) { // after first Write()
|
||||
if _, err := io.ReadFull(c.Conn, peerHeader); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
peerPadding, _ := decodeHeader(peerHeader)
|
||||
if peerPadding == 0 {
|
||||
peerPaddingLen, _ := DecodeHeader(peerHeader)
|
||||
if peerPaddingLen == 0 {
|
||||
break
|
||||
}
|
||||
if _, err := io.ReadFull(c.Conn, make([]byte, peerPadding)); err != nil {
|
||||
if _, err := io.ReadFull(c.Conn, make([]byte, peerPaddingLen)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
@@ -196,11 +204,9 @@ func (c *ClientConn) Read(b []byte) (int, error) { // after first Write()
|
||||
return 0, err
|
||||
}
|
||||
if c.random == nil {
|
||||
return 0, errors.New("can not Read() first")
|
||||
return 0, errors.New("empty c.random")
|
||||
}
|
||||
peerKey := make([]byte, 32)
|
||||
hkdf.New(sha256.New, c.baseKey, peerRandom, c.random).Read(peerKey)
|
||||
c.peerAead = newAead(ClientCipher, peerKey)
|
||||
c.peerAead = NewAead(ClientCipher, c.baseKey, peerRandom, c.random)
|
||||
c.peerNonce = make([]byte, 12)
|
||||
}
|
||||
if len(c.peerCache) != 0 {
|
||||
@@ -211,7 +217,7 @@ func (c *ClientConn) Read(b []byte) (int, error) { // after first Write()
|
||||
if _, err := io.ReadFull(c.Conn, peerHeader); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
peerLength, err := decodeHeader(peerHeader) // 17~17000
|
||||
peerLength, err := DecodeHeader(peerHeader) // 17~17000
|
||||
if err != nil {
|
||||
if c.instance != nil {
|
||||
c.instance.Lock()
|
||||
@@ -228,10 +234,17 @@ func (c *ClientConn) Read(b []byte) (int, error) { // after first Write()
|
||||
}
|
||||
dst := peerData[:peerLength-16]
|
||||
if len(dst) <= len(b) {
|
||||
dst = b[:len(dst)] // max=8192 is recommended for peer
|
||||
dst = b[:len(dst)] // avoids another copy()
|
||||
}
|
||||
var peerAead cipher.AEAD
|
||||
if bytes.Equal(c.peerNonce, MaxNonce) {
|
||||
peerAead = NewAead(ClientCipher, c.baseKey, peerData, peerHeader)
|
||||
}
|
||||
_, err = c.peerAead.Open(dst[:0], c.peerNonce, peerData, peerHeader)
|
||||
increaseNonce(c.peerNonce)
|
||||
if peerAead != nil {
|
||||
c.peerAead = peerAead
|
||||
}
|
||||
IncreaseNonce(c.peerNonce)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
@@ -1,17 +1,22 @@
|
||||
package encryption
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"math/big"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
)
|
||||
|
||||
func encodeHeader(b []byte, l int) {
|
||||
var MaxNonce = bytes.Repeat([]byte{255}, 12)
|
||||
|
||||
func EncodeHeader(b []byte, l int) {
|
||||
b[0] = 23
|
||||
b[1] = 3
|
||||
b[2] = 3
|
||||
@@ -19,10 +24,10 @@ func encodeHeader(b []byte, l int) {
|
||||
b[4] = byte(l)
|
||||
}
|
||||
|
||||
func decodeHeader(b []byte) (int, error) {
|
||||
func DecodeHeader(b []byte) (int, error) {
|
||||
if b[0] == 23 && b[1] == 3 && b[2] == 3 {
|
||||
l := int(b[3])<<8 | int(b[4])
|
||||
if l < 17 || l > 17000 { // TODO
|
||||
if l < 17 || l > 17000 { // TODO: TLSv1.3 max length
|
||||
return 0, errors.New("invalid length in record's header: " + strconv.Itoa(l))
|
||||
}
|
||||
return l, nil
|
||||
@@ -30,29 +35,24 @@ func decodeHeader(b []byte) (int, error) {
|
||||
return 0, errors.New("invalid record's header")
|
||||
}
|
||||
|
||||
func newAead(c byte, k []byte) cipher.AEAD {
|
||||
switch c {
|
||||
case 0:
|
||||
if block, err := aes.NewCipher(k); err == nil {
|
||||
aead, _ := cipher.NewGCM(block)
|
||||
return aead
|
||||
}
|
||||
case 1:
|
||||
aead, _ := chacha20poly1305.New(k)
|
||||
return aead
|
||||
func NewAead(c byte, secret, salt, info []byte) (aead cipher.AEAD) {
|
||||
key := make([]byte, 32)
|
||||
hkdf.New(sha256.New, secret, salt, info).Read(key)
|
||||
if c&1 == 1 {
|
||||
block, _ := aes.NewCipher(key)
|
||||
aead, _ = cipher.NewGCM(block)
|
||||
} else {
|
||||
aead, _ = chacha20poly1305.New(key)
|
||||
}
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
func increaseNonce(nonce []byte) {
|
||||
func IncreaseNonce(nonce []byte) {
|
||||
for i := 0; i < 12; i++ {
|
||||
nonce[11-i]++
|
||||
if nonce[11-i] != 0 {
|
||||
break
|
||||
}
|
||||
if i == 11 {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -2,4 +2,7 @@
|
||||
// https://github.com/XTLS/Xray-core/commit/f61c14e9c63dc41a8a09135db3aea337974f3f37
|
||||
// https://github.com/XTLS/Xray-core/commit/3e19bf9233bdd9bafc073a71c65b737cc1ffba5e
|
||||
// https://github.com/XTLS/Xray-core/commit/7ffb555fc8ec51bd1e3e60f26f1d6957984dba80
|
||||
// https://github.com/XTLS/Xray-core/commit/ec1cc35188c1a5f38a2ff75e88b5d043ffdc59da
|
||||
// https://github.com/XTLS/Xray-core/commit/5c611420487a92f931faefc01d4bf03869f477f6
|
||||
// https://github.com/XTLS/Xray-core/commit/23d7aad461d232bc5bed52dd6aaa731ecd88ad35
|
||||
package encryption
|
||||
|
99
clash-meta/transport/vless/encryption/factory.go
Normal file
99
clash-meta/transport/vless/encryption/factory.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package encryption
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// NewClient new client from encryption string
|
||||
// maybe return a nil *ClientInstance without any error, that means don't need to encrypt
|
||||
func NewClient(encryption string) (*ClientInstance, error) {
|
||||
switch encryption {
|
||||
case "", "none": // We will not reject empty string like xray-core does, because we need to ensure compatibility
|
||||
return nil, nil
|
||||
}
|
||||
if s := strings.SplitN(encryption, "-", 4); len(s) == 4 && s[2] == "mlkem768client" {
|
||||
var minutes uint32
|
||||
if s[0] != "1rtt" {
|
||||
t := strings.TrimSuffix(s[0], "min")
|
||||
if t == s[0] {
|
||||
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
|
||||
}
|
||||
i, err := strconv.Atoi(t)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
|
||||
}
|
||||
minutes = uint32(i)
|
||||
}
|
||||
var xor uint32
|
||||
switch s[1] {
|
||||
case "vless":
|
||||
case "aes128xor":
|
||||
xor = 1
|
||||
default:
|
||||
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
|
||||
}
|
||||
b, err := base64.RawURLEncoding.DecodeString(s[3])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
|
||||
}
|
||||
if len(b) == MLKEM768ClientLength {
|
||||
client := &ClientInstance{}
|
||||
if err = client.Init(b, xor, time.Duration(minutes)*time.Minute); err != nil {
|
||||
return nil, fmt.Errorf("failed to use mlkem768seed: %w", err)
|
||||
}
|
||||
return client, nil
|
||||
} else {
|
||||
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
|
||||
}
|
||||
|
||||
// NewServer new server from decryption string
|
||||
// maybe return a nil *ServerInstance without any error, that means don't need to decrypt
|
||||
func NewServer(decryption string) (*ServerInstance, error) {
|
||||
switch decryption {
|
||||
case "", "none": // We will not reject empty string like xray-core does, because we need to ensure compatibility
|
||||
return nil, nil
|
||||
}
|
||||
if s := strings.SplitN(decryption, "-", 4); len(s) == 4 && s[2] == "mlkem768seed" {
|
||||
var minutes uint32
|
||||
if s[0] != "1rtt" {
|
||||
t := strings.TrimSuffix(s[0], "min")
|
||||
if t == s[0] {
|
||||
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
|
||||
}
|
||||
i, err := strconv.Atoi(t)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
|
||||
}
|
||||
minutes = uint32(i)
|
||||
}
|
||||
var xor uint32
|
||||
switch s[1] {
|
||||
case "vless":
|
||||
case "aes128xor":
|
||||
xor = 1
|
||||
default:
|
||||
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
|
||||
}
|
||||
b, err := base64.RawURLEncoding.DecodeString(s[3])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
|
||||
}
|
||||
if len(b) == MLKEM768SeedLength {
|
||||
server := &ServerInstance{}
|
||||
if err = server.Init(b, xor, time.Duration(minutes)*time.Minute); err != nil {
|
||||
return nil, fmt.Errorf("failed to use mlkem768seed: %w", err)
|
||||
}
|
||||
return server, nil
|
||||
} else {
|
||||
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
|
||||
}
|
@@ -4,7 +4,6 @@ import (
|
||||
"bytes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
@@ -12,7 +11,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/utls/mlkem"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
)
|
||||
|
||||
type ServerSession struct {
|
||||
@@ -24,11 +22,12 @@ type ServerSession struct {
|
||||
|
||||
type ServerInstance struct {
|
||||
sync.RWMutex
|
||||
dKeyNfs *mlkem.DecapsulationKey768
|
||||
xor uint32
|
||||
minutes time.Duration
|
||||
sessions map[[21]byte]*ServerSession
|
||||
stop bool
|
||||
nfsDKey *mlkem.DecapsulationKey768
|
||||
nfsEKeyBytes []byte
|
||||
xor uint32
|
||||
minutes time.Duration
|
||||
sessions map[[21]byte]*ServerSession
|
||||
closed bool
|
||||
}
|
||||
|
||||
type ServerConn struct {
|
||||
@@ -44,9 +43,12 @@ type ServerConn struct {
|
||||
nonce []byte
|
||||
}
|
||||
|
||||
func (i *ServerInstance) Init(dKeyNfsData []byte, xor uint32, minutes time.Duration) (err error) {
|
||||
i.dKeyNfs, err = mlkem.NewDecapsulationKey768(dKeyNfsData)
|
||||
i.xor = xor
|
||||
func (i *ServerInstance) Init(nfsDKeySeed []byte, xor uint32, minutes time.Duration) (err error) {
|
||||
i.nfsDKey, err = mlkem.NewDecapsulationKey768(nfsDKeySeed)
|
||||
if xor > 0 {
|
||||
i.nfsEKeyBytes = i.nfsDKey.EncapsulationKey().Bytes()
|
||||
i.xor = xor
|
||||
}
|
||||
if minutes > 0 {
|
||||
i.minutes = minutes
|
||||
i.sessions = make(map[[21]byte]*ServerSession)
|
||||
@@ -55,12 +57,13 @@ func (i *ServerInstance) Init(dKeyNfsData []byte, xor uint32, minutes time.Durat
|
||||
time.Sleep(time.Minute)
|
||||
now := time.Now()
|
||||
i.Lock()
|
||||
if i.stop {
|
||||
if i.closed {
|
||||
i.Unlock()
|
||||
return
|
||||
}
|
||||
for index, session := range i.sessions {
|
||||
for ticket, session := range i.sessions {
|
||||
if now.After(session.expire) {
|
||||
delete(i.sessions, index)
|
||||
delete(i.sessions, ticket)
|
||||
}
|
||||
}
|
||||
i.Unlock()
|
||||
@@ -72,17 +75,17 @@ func (i *ServerInstance) Init(dKeyNfsData []byte, xor uint32, minutes time.Durat
|
||||
|
||||
func (i *ServerInstance) Close() (err error) {
|
||||
i.Lock()
|
||||
defer i.Unlock()
|
||||
i.stop = true
|
||||
i.closed = true
|
||||
i.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) {
|
||||
if i.dKeyNfs == nil {
|
||||
if i.nfsDKey == nil {
|
||||
return nil, errors.New("uninitialized")
|
||||
}
|
||||
if i.xor == 1 {
|
||||
conn = NewXorConn(conn, i.dKeyNfs.EncapsulationKey().Bytes())
|
||||
if i.xor > 0 {
|
||||
conn = NewXorConn(conn, i.nfsEKeyBytes)
|
||||
}
|
||||
c := &ServerConn{Conn: conn}
|
||||
|
||||
@@ -109,50 +112,49 @@ func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) {
|
||||
if _, err := io.ReadFull(c.Conn, peerHeader); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if l, _ := decodeHeader(peerHeader); l != 0 {
|
||||
c.Conn.Write(make([]byte, randBetween(100, 1000))) // make client do new handshake
|
||||
if l, _ := DecodeHeader(peerHeader); l != 0 {
|
||||
noise := make([]byte, randBetween(100, 1000))
|
||||
rand.Read(noise)
|
||||
c.Conn.Write(noise) // make client do new handshake
|
||||
return nil, errors.New("invalid ticket")
|
||||
}
|
||||
|
||||
peerClientHello := make([]byte, 1088+1184+1)
|
||||
peerClientHello := make([]byte, 1+1184+1088)
|
||||
copy(peerClientHello, peerTicketHello)
|
||||
copy(peerClientHello[53:], peerHeader)
|
||||
if _, err := io.ReadFull(c.Conn, peerClientHello[58:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
encapsulatedNfsKey := peerClientHello[:1088]
|
||||
eKeyPfsData := peerClientHello[1088:2272]
|
||||
c.cipher = peerClientHello[2272]
|
||||
if c.cipher != 0 && c.cipher != 1 {
|
||||
return nil, errors.New("invalid cipher")
|
||||
}
|
||||
c.cipher = peerClientHello[0]
|
||||
pfsEKeyBytes := peerClientHello[1:1185]
|
||||
encapsulatedNfsKey := peerClientHello[1185:2273]
|
||||
|
||||
nfsKey, err := i.dKeyNfs.Decapsulate(encapsulatedNfsKey)
|
||||
pfsEKey, err := mlkem.NewEncapsulationKey768(pfsEKeyBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
eKeyPfs, err := mlkem.NewEncapsulationKey768(eKeyPfsData)
|
||||
nfsKey, err := i.nfsDKey.Decapsulate(encapsulatedNfsKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pfsKey, encapsulatedPfsKey := eKeyPfs.Encapsulate()
|
||||
c.baseKey = append(nfsKey, pfsKey...)
|
||||
pfsKey, encapsulatedPfsKey := pfsEKey.Encapsulate()
|
||||
c.baseKey = append(pfsKey, nfsKey...)
|
||||
|
||||
authKey := make([]byte, 32)
|
||||
hkdf.New(sha256.New, c.baseKey, encapsulatedNfsKey, eKeyPfsData).Read(authKey)
|
||||
nonce := make([]byte, 12)
|
||||
c.ticket = newAead(c.cipher, authKey).Seal(nil, nonce, []byte("VLESS"), encapsulatedPfsKey)
|
||||
nonce := [12]byte{c.cipher}
|
||||
c.ticket = NewAead(c.cipher, c.baseKey, encapsulatedPfsKey, encapsulatedNfsKey).Seal(nil, nonce[:], []byte("VLESS"), pfsEKeyBytes)
|
||||
|
||||
padding := randBetween(100, 1000)
|
||||
paddingLen := randBetween(100, 1000)
|
||||
|
||||
serverHello := make([]byte, 1088+21+5+padding)
|
||||
serverHello := make([]byte, 1088+21+5+paddingLen)
|
||||
copy(serverHello, encapsulatedPfsKey)
|
||||
copy(serverHello[1088:], c.ticket)
|
||||
encodeHeader(serverHello[1109:], int(padding))
|
||||
EncodeHeader(serverHello[1109:], int(paddingLen))
|
||||
rand.Read(serverHello[1114:])
|
||||
|
||||
if _, err := c.Conn.Write(serverHello); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// we can send more padding if needed
|
||||
|
||||
if i.minutes > 0 {
|
||||
i.Lock()
|
||||
@@ -178,11 +180,11 @@ func (c *ServerConn) Read(b []byte) (int, error) {
|
||||
if _, err := io.ReadFull(c.Conn, peerHeader); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
peerPadding, _ := decodeHeader(peerHeader)
|
||||
if peerPadding == 0 {
|
||||
peerPaddingLen, _ := DecodeHeader(peerHeader)
|
||||
if peerPaddingLen == 0 {
|
||||
break
|
||||
}
|
||||
if _, err := io.ReadFull(c.Conn, make([]byte, peerPadding)); err != nil {
|
||||
if _, err := io.ReadFull(c.Conn, make([]byte, peerPaddingLen)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
@@ -199,9 +201,7 @@ func (c *ServerConn) Read(b []byte) (int, error) {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
peerKey := make([]byte, 32)
|
||||
hkdf.New(sha256.New, c.baseKey, c.peerRandom, c.ticket).Read(peerKey)
|
||||
c.peerAead = newAead(c.cipher, peerKey)
|
||||
c.peerAead = NewAead(c.cipher, c.baseKey, c.peerRandom, c.ticket)
|
||||
c.peerNonce = make([]byte, 12)
|
||||
}
|
||||
if len(c.peerCache) != 0 {
|
||||
@@ -212,7 +212,7 @@ func (c *ServerConn) Read(b []byte) (int, error) {
|
||||
if _, err := io.ReadFull(c.Conn, peerHeader); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
peerLength, err := decodeHeader(peerHeader) // 17~17000
|
||||
peerLength, err := DecodeHeader(peerHeader) // 17~17000
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -222,10 +222,17 @@ func (c *ServerConn) Read(b []byte) (int, error) {
|
||||
}
|
||||
dst := peerData[:peerLength-16]
|
||||
if len(dst) <= len(b) {
|
||||
dst = b[:len(dst)] // max=8192 is recommended for peer
|
||||
dst = b[:len(dst)] // avoids another copy()
|
||||
}
|
||||
var peerAead cipher.AEAD
|
||||
if bytes.Equal(c.peerNonce, MaxNonce) {
|
||||
peerAead = NewAead(c.cipher, c.baseKey, peerData, peerHeader)
|
||||
}
|
||||
_, err = c.peerAead.Open(dst[:0], c.peerNonce, peerData, peerHeader)
|
||||
increaseNonce(c.peerNonce)
|
||||
if peerAead != nil {
|
||||
c.peerAead = peerAead
|
||||
}
|
||||
IncreaseNonce(c.peerNonce)
|
||||
if err != nil {
|
||||
return 0, errors.New("error")
|
||||
}
|
||||
@@ -236,31 +243,39 @@ func (c *ServerConn) Read(b []byte) (int, error) {
|
||||
return len(dst), nil
|
||||
}
|
||||
|
||||
func (c *ServerConn) Write(b []byte) (int, error) { // after first Read()
|
||||
func (c *ServerConn) Write(b []byte) (int, error) {
|
||||
if len(b) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
var data []byte
|
||||
if c.aead == nil {
|
||||
if c.peerRandom == nil {
|
||||
return 0, errors.New("can not Write() first")
|
||||
for n := 0; n < len(b); {
|
||||
b := b[n:]
|
||||
if len(b) > 8192 {
|
||||
b = b[:8192] // for avoiding another copy() in client's Read()
|
||||
}
|
||||
n += len(b)
|
||||
if c.aead == nil {
|
||||
if c.peerRandom == nil {
|
||||
return 0, errors.New("empty c.peerRandom")
|
||||
}
|
||||
data = make([]byte, 32+5+len(b)+16)
|
||||
rand.Read(data[:32])
|
||||
c.aead = NewAead(c.cipher, c.baseKey, data[:32], c.peerRandom)
|
||||
c.nonce = make([]byte, 12)
|
||||
EncodeHeader(data[32:], len(b)+16)
|
||||
c.aead.Seal(data[:37], c.nonce, b, data[32:37])
|
||||
} else {
|
||||
data = make([]byte, 5+len(b)+16)
|
||||
EncodeHeader(data, len(b)+16)
|
||||
c.aead.Seal(data[:5], c.nonce, b, data[:5])
|
||||
if bytes.Equal(c.nonce, MaxNonce) {
|
||||
c.aead = NewAead(c.cipher, c.baseKey, data[5:], data[:5])
|
||||
}
|
||||
}
|
||||
IncreaseNonce(c.nonce)
|
||||
if _, err := c.Conn.Write(data); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
data = make([]byte, 32+5+len(b)+16)
|
||||
rand.Read(data[:32])
|
||||
key := make([]byte, 32)
|
||||
hkdf.New(sha256.New, c.baseKey, data[:32], c.peerRandom).Read(key)
|
||||
c.aead = newAead(c.cipher, key)
|
||||
c.nonce = make([]byte, 12)
|
||||
encodeHeader(data[32:], len(b)+16)
|
||||
c.aead.Seal(data[:37], c.nonce, b, data[32:37])
|
||||
} else {
|
||||
data = make([]byte, 5+len(b)+16)
|
||||
encodeHeader(data, len(b)+16)
|
||||
c.aead.Seal(data[:5], c.nonce, b, data[:5])
|
||||
}
|
||||
increaseNonce(c.nonce)
|
||||
if _, err := c.Conn.Write(data); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(b), nil
|
||||
}
|
||||
|
@@ -38,7 +38,7 @@ func (c *XorConn) Write(b []byte) (int, error) {
|
||||
return 0, err
|
||||
}
|
||||
if iv != nil {
|
||||
b = b[16:]
|
||||
b = b[16:] // for len(b)
|
||||
}
|
||||
return len(b), nil
|
||||
}
|
||||
|
@@ -22,6 +22,6 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/lodash-es": "4.17.12",
|
||||
"@types/react": "19.1.9"
|
||||
"@types/react": "19.1.10"
|
||||
}
|
||||
}
|
||||
|
@@ -59,9 +59,9 @@
|
||||
"@iconify/json": "2.2.371",
|
||||
"@monaco-editor/react": "4.7.0",
|
||||
"@tanstack/react-query": "5.84.2",
|
||||
"@tanstack/react-router": "1.131.3",
|
||||
"@tanstack/react-router-devtools": "1.131.3",
|
||||
"@tanstack/router-plugin": "1.131.3",
|
||||
"@tanstack/react-router": "1.131.5",
|
||||
"@tanstack/react-router-devtools": "1.131.5",
|
||||
"@tanstack/router-plugin": "1.131.5",
|
||||
"@tauri-apps/plugin-clipboard-manager": "2.3.0",
|
||||
"@tauri-apps/plugin-dialog": "2.3.0",
|
||||
"@tauri-apps/plugin-fs": "2.4.0",
|
||||
@@ -70,7 +70,7 @@
|
||||
"@tauri-apps/plugin-process": "2.3.0",
|
||||
"@tauri-apps/plugin-shell": "2.3.0",
|
||||
"@tauri-apps/plugin-updater": "2.9.0",
|
||||
"@types/react": "19.1.9",
|
||||
"@types/react": "19.1.10",
|
||||
"@types/react-dom": "19.1.7",
|
||||
"@types/validator": "13.15.2",
|
||||
"@vitejs/plugin-legacy": "7.2.1",
|
||||
|
@@ -19,7 +19,7 @@
|
||||
"@radix-ui/react-scroll-area": "1.2.9",
|
||||
"@tauri-apps/api": "2.6.0",
|
||||
"@types/d3": "7.4.3",
|
||||
"@types/react": "19.1.9",
|
||||
"@types/react": "19.1.10",
|
||||
"@vitejs/plugin-react": "4.7.0",
|
||||
"ahooks": "3.9.0",
|
||||
"d3": "7.9.0",
|
||||
|
@@ -2,10 +2,10 @@
|
||||
"manifest_version": 1,
|
||||
"latest": {
|
||||
"mihomo": "v1.19.12",
|
||||
"mihomo_alpha": "alpha-3a0d267",
|
||||
"mihomo_alpha": "alpha-eca5a27",
|
||||
"clash_rs": "v0.8.2",
|
||||
"clash_premium": "2023-09-05-gdcc8d87",
|
||||
"clash_rs_alpha": "0.8.2-alpha+sha.ec9134e"
|
||||
"clash_rs_alpha": "0.8.2-alpha+sha.df9f591"
|
||||
},
|
||||
"arch_template": {
|
||||
"mihomo": {
|
||||
@@ -69,5 +69,5 @@
|
||||
"linux-armv7hf": "clash-armv7-unknown-linux-gnueabihf"
|
||||
}
|
||||
},
|
||||
"updated_at": "2025-08-10T22:21:15.754Z"
|
||||
"updated_at": "2025-08-11T22:21:19.847Z"
|
||||
}
|
||||
|
356
clash-nyanpasu/pnpm-lock.yaml
generated
356
clash-nyanpasu/pnpm-lock.yaml
generated
@@ -204,8 +204,8 @@ importers:
|
||||
specifier: 4.17.12
|
||||
version: 4.17.12
|
||||
'@types/react':
|
||||
specifier: 19.1.9
|
||||
version: 19.1.9
|
||||
specifier: 19.1.10
|
||||
version: 19.1.10
|
||||
|
||||
frontend/nyanpasu:
|
||||
dependencies:
|
||||
@@ -220,7 +220,7 @@ importers:
|
||||
version: 3.2.2(react@19.1.1)
|
||||
'@emotion/styled':
|
||||
specifier: 11.14.1
|
||||
version: 11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1)
|
||||
version: 11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1)
|
||||
'@juggle/resize-observer':
|
||||
specifier: 3.4.0
|
||||
version: 3.4.0
|
||||
@@ -229,16 +229,16 @@ importers:
|
||||
version: 0.3.0
|
||||
'@mui/icons-material':
|
||||
specifier: 7.3.1
|
||||
version: 7.3.1(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@types/react@19.1.9)(react@19.1.1)
|
||||
version: 7.3.1(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@types/react@19.1.10)(react@19.1.1)
|
||||
'@mui/lab':
|
||||
specifier: 7.0.0-beta.16
|
||||
version: 7.0.0-beta.16(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@types/react@19.1.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
version: 7.0.0-beta.16(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
'@mui/material':
|
||||
specifier: 7.3.1
|
||||
version: 7.3.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
version: 7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
'@mui/x-date-pickers':
|
||||
specifier: 8.10.0
|
||||
version: 8.10.0(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@mui/system@7.3.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(dayjs@1.11.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
version: 8.10.0(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@mui/system@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(dayjs@1.11.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
'@nyanpasu/interface':
|
||||
specifier: workspace:^
|
||||
version: link:../interface
|
||||
@@ -250,7 +250,7 @@ importers:
|
||||
version: 4.1.11
|
||||
'@tanstack/router-zod-adapter':
|
||||
specifier: 1.81.5
|
||||
version: 1.81.5(@tanstack/react-router@1.131.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(zod@4.0.17)
|
||||
version: 1.81.5(@tanstack/react-router@1.131.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(zod@4.0.17)
|
||||
'@tauri-apps/api':
|
||||
specifier: 2.6.0
|
||||
version: 2.6.0
|
||||
@@ -280,19 +280,19 @@ importers:
|
||||
version: 25.3.4(typescript@5.9.2)
|
||||
jotai:
|
||||
specifier: 2.13.1
|
||||
version: 2.13.1(@babel/core@7.28.0)(@babel/template@7.27.2)(@types/react@19.1.9)(react@19.1.1)
|
||||
version: 2.13.1(@babel/core@7.28.0)(@babel/template@7.27.2)(@types/react@19.1.10)(react@19.1.1)
|
||||
json-schema:
|
||||
specifier: 0.4.0
|
||||
version: 0.4.0
|
||||
material-react-table:
|
||||
specifier: npm:@greenhat616/material-react-table@4.0.0
|
||||
version: '@greenhat616/material-react-table@4.0.0(ee367b75520587f2d436c002311f838b)'
|
||||
version: '@greenhat616/material-react-table@4.0.0(098023d060c5c1c6a96afb3bdaa051b0)'
|
||||
monaco-editor:
|
||||
specifier: 0.52.2
|
||||
version: 0.52.2
|
||||
mui-color-input:
|
||||
specifier: 7.0.0
|
||||
version: 7.0.0(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@types/react@19.1.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
version: 7.0.0(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
react:
|
||||
specifier: 19.1.1
|
||||
version: 19.1.1
|
||||
@@ -307,13 +307,13 @@ importers:
|
||||
version: 1.6.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
react-hook-form-mui:
|
||||
specifier: 7.6.2
|
||||
version: 7.6.2(dc790e3d871a3fe7e1d22dc6e321e397)
|
||||
version: 7.6.2(aea177882beb7723aeada5c99e57089b)
|
||||
react-i18next:
|
||||
specifier: 15.6.1
|
||||
version: 15.6.1(i18next@25.3.4(typescript@5.9.2))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)
|
||||
react-markdown:
|
||||
specifier: 10.1.0
|
||||
version: 10.1.0(@types/react@19.1.9)(react@19.1.1)
|
||||
version: 10.1.0(@types/react@19.1.10)(react@19.1.1)
|
||||
react-split-grid:
|
||||
specifier: 1.0.4
|
||||
version: 1.0.4(react@19.1.1)
|
||||
@@ -341,7 +341,7 @@ importers:
|
||||
version: 11.13.5
|
||||
'@emotion/react':
|
||||
specifier: 11.14.0
|
||||
version: 11.14.0(@types/react@19.1.9)(react@19.1.1)
|
||||
version: 11.14.0(@types/react@19.1.10)(react@19.1.1)
|
||||
'@iconify/json':
|
||||
specifier: 2.2.371
|
||||
version: 2.2.371
|
||||
@@ -352,14 +352,14 @@ importers:
|
||||
specifier: 5.84.2
|
||||
version: 5.84.2(react@19.1.1)
|
||||
'@tanstack/react-router':
|
||||
specifier: 1.131.3
|
||||
version: 1.131.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
specifier: 1.131.5
|
||||
version: 1.131.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
'@tanstack/react-router-devtools':
|
||||
specifier: 1.131.3
|
||||
version: 1.131.3(@tanstack/react-router@1.131.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@tanstack/router-core@1.131.3)(csstype@3.1.3)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(solid-js@1.9.5)(tiny-invariant@1.3.3)
|
||||
specifier: 1.131.5
|
||||
version: 1.131.5(@tanstack/react-router@1.131.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@tanstack/router-core@1.131.5)(csstype@3.1.3)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(solid-js@1.9.5)(tiny-invariant@1.3.3)
|
||||
'@tanstack/router-plugin':
|
||||
specifier: 1.131.3
|
||||
version: 1.131.3(@tanstack/react-router@1.131.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.0))
|
||||
specifier: 1.131.5
|
||||
version: 1.131.5(@tanstack/react-router@1.131.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.0))
|
||||
'@tauri-apps/plugin-clipboard-manager':
|
||||
specifier: 2.3.0
|
||||
version: 2.3.0
|
||||
@@ -385,11 +385,11 @@ importers:
|
||||
specifier: 2.9.0
|
||||
version: 2.9.0
|
||||
'@types/react':
|
||||
specifier: 19.1.9
|
||||
version: 19.1.9
|
||||
specifier: 19.1.10
|
||||
version: 19.1.10
|
||||
'@types/react-dom':
|
||||
specifier: 19.1.7
|
||||
version: 19.1.7(@types/react@19.1.9)
|
||||
version: 19.1.7(@types/react@19.1.10)
|
||||
'@types/validator':
|
||||
specifier: 13.15.2
|
||||
version: 13.15.2
|
||||
@@ -464,19 +464,19 @@ importers:
|
||||
version: 0.3.0
|
||||
'@mui/icons-material':
|
||||
specifier: 7.3.1
|
||||
version: 7.3.1(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@types/react@19.1.9)(react@19.1.1)
|
||||
version: 7.3.1(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@types/react@19.1.10)(react@19.1.1)
|
||||
'@mui/lab':
|
||||
specifier: 7.0.0-beta.16
|
||||
version: 7.0.0-beta.16(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@types/react@19.1.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
version: 7.0.0-beta.16(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
'@mui/material':
|
||||
specifier: 7.3.1
|
||||
version: 7.3.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
version: 7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(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.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
version: 1.1.9(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
'@radix-ui/react-scroll-area':
|
||||
specifier: 1.2.9
|
||||
version: 1.2.9(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
version: 1.2.9(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
'@tauri-apps/api':
|
||||
specifier: 2.6.0
|
||||
version: 2.6.0
|
||||
@@ -484,8 +484,8 @@ importers:
|
||||
specifier: 7.4.3
|
||||
version: 7.4.3
|
||||
'@types/react':
|
||||
specifier: 19.1.9
|
||||
version: 19.1.9
|
||||
specifier: 19.1.10
|
||||
version: 19.1.10
|
||||
'@vitejs/plugin-react':
|
||||
specifier: 4.7.0
|
||||
version: 4.7.0(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.0))
|
||||
@@ -525,7 +525,7 @@ importers:
|
||||
devDependencies:
|
||||
'@emotion/react':
|
||||
specifier: 11.14.0
|
||||
version: 11.14.0(@types/react@19.1.9)(react@19.1.1)
|
||||
version: 11.14.0(@types/react@19.1.10)(react@19.1.1)
|
||||
'@types/d3-interpolate-path':
|
||||
specifier: 2.0.3
|
||||
version: 2.0.3
|
||||
@@ -2939,16 +2939,16 @@ packages:
|
||||
peerDependencies:
|
||||
react: ^18 || ^19
|
||||
|
||||
'@tanstack/react-router-devtools@1.131.3':
|
||||
resolution: {integrity: sha512-opouR8dbBrDnkHdiyTVPb3rTfSRxWN+ZGi1YuEXNGUXgirvnpQUTWNP6kzDv1at29DxymwwfRRYD602MjZOOlA==}
|
||||
'@tanstack/react-router-devtools@1.131.5':
|
||||
resolution: {integrity: sha512-3LaEbWDYGnzw4J8DM7KX32qRslGrSt67Cg7uoAYReBfDlLpWVRnjpyG2fef7nHDqn5HaAuV0IbY1n6Duwp5IyQ==}
|
||||
engines: {node: '>=12'}
|
||||
peerDependencies:
|
||||
'@tanstack/react-router': ^1.131.3
|
||||
'@tanstack/react-router': ^1.131.5
|
||||
react: '>=18.0.0 || >=19.0.0'
|
||||
react-dom: '>=18.0.0 || >=19.0.0'
|
||||
|
||||
'@tanstack/react-router@1.131.3':
|
||||
resolution: {integrity: sha512-1wxsStYJai0/ssOQeO+8mh5VmMmJWIZOtIEEqDxIhQX4dHyurKl6khl34+qLtDvWFsj6zgiMwjID3GQj5SMz1w==}
|
||||
'@tanstack/react-router@1.131.5':
|
||||
resolution: {integrity: sha512-71suJGuCmrHN9PLLRUDB3CGnW5RNcEEfgfX616TOpKamHs977H8P4/75BgWPRWcLHCga/1kkA6c7bddCwZ35Fw==}
|
||||
engines: {node: '>=12'}
|
||||
peerDependencies:
|
||||
react: '>=18.0.0 || >=19.0.0'
|
||||
@@ -2973,15 +2973,15 @@ packages:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
|
||||
'@tanstack/router-core@1.131.3':
|
||||
resolution: {integrity: sha512-8sby4n2pgRJ7qZrFvuS6v21oBDHCRoFM8p+/HAIra2d32cD5wS5k9DeNltWFUAJFXdsdwpdGVA0iXSAc0WfIHw==}
|
||||
'@tanstack/router-core@1.131.5':
|
||||
resolution: {integrity: sha512-XVfZdnKNQbWfkQ6G7I9ml2wHp98Wy7wgTboP5SfrJHfOE+kPeHeZRJqF/pp5oqLZ2feBJqsDDKNWo9323L7sWQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
'@tanstack/router-devtools-core@1.131.3':
|
||||
resolution: {integrity: sha512-GQHVCE0ywJ0ajz9K52RLAhbDSuziD3MVRYYp5S5w2i31Mb9xkzITJe/CZ7WIgOuY+XQm18P8drbJnxdHTNOcWQ==}
|
||||
'@tanstack/router-devtools-core@1.131.5':
|
||||
resolution: {integrity: sha512-kH3cZz7UfnVQW9vMZJ/CAx15pu+iGkn10N4rRKBVWCEJZFPX3GZvYEwkeALHASsV0Io7yUvKDcWfPsc+UowyzQ==}
|
||||
engines: {node: '>=12'}
|
||||
peerDependencies:
|
||||
'@tanstack/router-core': ^1.131.3
|
||||
'@tanstack/router-core': ^1.131.5
|
||||
csstype: ^3.0.10
|
||||
solid-js: '>=1.9.5'
|
||||
tiny-invariant: ^1.3.3
|
||||
@@ -2989,16 +2989,16 @@ packages:
|
||||
csstype:
|
||||
optional: true
|
||||
|
||||
'@tanstack/router-generator@1.131.3':
|
||||
resolution: {integrity: sha512-sTsi9lSxBCpAExQWyh3k2X40biiRHjIRISxVvko5sPG/+NKYFjGaQwgXQ/1jiDSTqe8i7/b/2l94ZJPTa6VPxQ==}
|
||||
'@tanstack/router-generator@1.131.5':
|
||||
resolution: {integrity: sha512-5+/zyp/R9WN8tHNVIEYQZpRMzcsOrNH06HoPnPMiLiB9T4WsOLFJCcHdyso9ofGQq+hoxB4M9SUBXVBbJVWbSw==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
'@tanstack/router-plugin@1.131.3':
|
||||
resolution: {integrity: sha512-PLCjxTTHBf5H9TqH+jTNvgSnnCZhvrLj61C5XAtONA7NUv+Lh4xJ/u0nn83ZYb7uFM4rMg1JmpU5mG8iNbGHZw==}
|
||||
'@tanstack/router-plugin@1.131.5':
|
||||
resolution: {integrity: sha512-Px+GSijNv1cbSm74+U+kEbuSFsspL+/BakzytAJFdh2O4342G32tha1cMFMlzXbDd9SW1FaLWgu3VqNHjGIpOg==}
|
||||
engines: {node: '>=12'}
|
||||
peerDependencies:
|
||||
'@rsbuild/core': '>=1.0.2'
|
||||
'@tanstack/react-router': ^1.131.3
|
||||
'@tanstack/react-router': ^1.131.5
|
||||
vite: '>=5.0.0 || >=6.0.0'
|
||||
vite-plugin-solid: ^2.11.2
|
||||
webpack: '>=5.92.0'
|
||||
@@ -3360,8 +3360,8 @@ packages:
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
|
||||
'@types/react@19.1.9':
|
||||
resolution: {integrity: sha512-WmdoynAX8Stew/36uTSVMcLJJ1KRh6L3IZRx1PZ7qJtBqT3dYTgyDTx8H1qoRghErydW7xw9mSJ3wS//tCRpFA==}
|
||||
'@types/react@19.1.10':
|
||||
resolution: {integrity: sha512-EhBeSYX0Y6ye8pNebpKrwFJq7BoQ8J5SO6NlvNwwHjSj6adXJViPQrKlsyPw7hLBLvckEMO1yxeGdR82YBBlDg==}
|
||||
|
||||
'@types/responselike@1.0.3':
|
||||
resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==}
|
||||
@@ -9728,7 +9728,7 @@ snapshots:
|
||||
|
||||
'@emotion/memoize@0.9.0': {}
|
||||
|
||||
'@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1)':
|
||||
'@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.26.0
|
||||
'@emotion/babel-plugin': 11.13.5
|
||||
@@ -9740,7 +9740,7 @@ snapshots:
|
||||
hoist-non-react-statics: 3.3.2
|
||||
react: 19.1.1
|
||||
optionalDependencies:
|
||||
'@types/react': 19.1.9
|
||||
'@types/react': 19.1.10
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -9754,18 +9754,18 @@ snapshots:
|
||||
|
||||
'@emotion/sheet@1.4.0': {}
|
||||
|
||||
'@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1)':
|
||||
'@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.27.6
|
||||
'@emotion/babel-plugin': 11.13.5
|
||||
'@emotion/is-prop-valid': 1.3.0
|
||||
'@emotion/react': 11.14.0(@types/react@19.1.9)(react@19.1.1)
|
||||
'@emotion/react': 11.14.0(@types/react@19.1.10)(react@19.1.1)
|
||||
'@emotion/serialize': 1.3.3
|
||||
'@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.1.1)
|
||||
'@emotion/utils': 1.4.2
|
||||
react: 19.1.1
|
||||
optionalDependencies:
|
||||
'@types/react': 19.1.9
|
||||
'@types/react': 19.1.10
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -9911,13 +9911,13 @@ snapshots:
|
||||
|
||||
'@fastify/busboy@2.1.1': {}
|
||||
|
||||
'@greenhat616/material-react-table@4.0.0(ee367b75520587f2d436c002311f838b)':
|
||||
'@greenhat616/material-react-table@4.0.0(098023d060c5c1c6a96afb3bdaa051b0)':
|
||||
dependencies:
|
||||
'@emotion/react': 11.14.0(@types/react@19.1.9)(react@19.1.1)
|
||||
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1)
|
||||
'@mui/icons-material': 7.3.1(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@types/react@19.1.9)(react@19.1.1)
|
||||
'@mui/material': 7.3.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
'@mui/x-date-pickers': 8.10.0(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@mui/system@7.3.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(dayjs@1.11.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
'@emotion/react': 11.14.0(@types/react@19.1.10)(react@19.1.1)
|
||||
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1)
|
||||
'@mui/icons-material': 7.3.1(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@types/react@19.1.10)(react@19.1.1)
|
||||
'@mui/material': 7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
'@mui/x-date-pickers': 8.10.0(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@mui/system@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(dayjs@1.11.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
'@tanstack/match-sorter-utils': 8.19.4
|
||||
'@tanstack/react-table': 8.21.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
'@tanstack/react-virtual': 3.13.9(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
@@ -10059,39 +10059,39 @@ snapshots:
|
||||
|
||||
'@mui/core-downloads-tracker@7.3.1': {}
|
||||
|
||||
'@mui/icons-material@7.3.1(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@types/react@19.1.9)(react@19.1.1)':
|
||||
'@mui/icons-material@7.3.1(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@types/react@19.1.10)(react@19.1.1)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@mui/material': 7.3.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
'@mui/material': 7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
react: 19.1.1
|
||||
optionalDependencies:
|
||||
'@types/react': 19.1.9
|
||||
'@types/react': 19.1.10
|
||||
|
||||
'@mui/lab@7.0.0-beta.16(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@types/react@19.1.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
|
||||
'@mui/lab@7.0.0-beta.16(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@mui/material': 7.3.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
'@mui/system': 7.3.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1)
|
||||
'@mui/types': 7.4.5(@types/react@19.1.9)
|
||||
'@mui/utils': 7.3.1(@types/react@19.1.9)(react@19.1.1)
|
||||
'@mui/material': 7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
'@mui/system': 7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1)
|
||||
'@mui/types': 7.4.5(@types/react@19.1.10)
|
||||
'@mui/utils': 7.3.1(@types/react@19.1.10)(react@19.1.1)
|
||||
clsx: 2.1.1
|
||||
prop-types: 15.8.1
|
||||
react: 19.1.1
|
||||
react-dom: 19.1.1(react@19.1.1)
|
||||
optionalDependencies:
|
||||
'@emotion/react': 11.14.0(@types/react@19.1.9)(react@19.1.1)
|
||||
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1)
|
||||
'@types/react': 19.1.9
|
||||
'@emotion/react': 11.14.0(@types/react@19.1.10)(react@19.1.1)
|
||||
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1)
|
||||
'@types/react': 19.1.10
|
||||
|
||||
'@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
|
||||
'@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@mui/core-downloads-tracker': 7.3.1
|
||||
'@mui/system': 7.3.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1)
|
||||
'@mui/types': 7.4.5(@types/react@19.1.9)
|
||||
'@mui/utils': 7.3.1(@types/react@19.1.9)(react@19.1.1)
|
||||
'@mui/system': 7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1)
|
||||
'@mui/types': 7.4.5(@types/react@19.1.10)
|
||||
'@mui/utils': 7.3.1(@types/react@19.1.10)(react@19.1.1)
|
||||
'@popperjs/core': 2.11.8
|
||||
'@types/react-transition-group': 4.4.12(@types/react@19.1.9)
|
||||
'@types/react-transition-group': 4.4.12(@types/react@19.1.10)
|
||||
clsx: 2.1.1
|
||||
csstype: 3.1.3
|
||||
prop-types: 15.8.1
|
||||
@@ -10100,20 +10100,20 @@ snapshots:
|
||||
react-is: 19.1.1
|
||||
react-transition-group: 4.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
optionalDependencies:
|
||||
'@emotion/react': 11.14.0(@types/react@19.1.9)(react@19.1.1)
|
||||
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1)
|
||||
'@types/react': 19.1.9
|
||||
'@emotion/react': 11.14.0(@types/react@19.1.10)(react@19.1.1)
|
||||
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1)
|
||||
'@types/react': 19.1.10
|
||||
|
||||
'@mui/private-theming@7.3.1(@types/react@19.1.9)(react@19.1.1)':
|
||||
'@mui/private-theming@7.3.1(@types/react@19.1.10)(react@19.1.1)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@mui/utils': 7.3.1(@types/react@19.1.9)(react@19.1.1)
|
||||
'@mui/utils': 7.3.1(@types/react@19.1.10)(react@19.1.1)
|
||||
prop-types: 15.8.1
|
||||
react: 19.1.1
|
||||
optionalDependencies:
|
||||
'@types/react': 19.1.9
|
||||
'@types/react': 19.1.10
|
||||
|
||||
'@mui/styled-engine@7.3.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(react@19.1.1)':
|
||||
'@mui/styled-engine@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(react@19.1.1)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@emotion/cache': 11.14.0
|
||||
@@ -10123,67 +10123,67 @@ snapshots:
|
||||
prop-types: 15.8.1
|
||||
react: 19.1.1
|
||||
optionalDependencies:
|
||||
'@emotion/react': 11.14.0(@types/react@19.1.9)(react@19.1.1)
|
||||
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1)
|
||||
'@emotion/react': 11.14.0(@types/react@19.1.10)(react@19.1.1)
|
||||
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1)
|
||||
|
||||
'@mui/system@7.3.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1)':
|
||||
'@mui/system@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@mui/private-theming': 7.3.1(@types/react@19.1.9)(react@19.1.1)
|
||||
'@mui/styled-engine': 7.3.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(react@19.1.1)
|
||||
'@mui/types': 7.4.5(@types/react@19.1.9)
|
||||
'@mui/utils': 7.3.1(@types/react@19.1.9)(react@19.1.1)
|
||||
'@mui/private-theming': 7.3.1(@types/react@19.1.10)(react@19.1.1)
|
||||
'@mui/styled-engine': 7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(react@19.1.1)
|
||||
'@mui/types': 7.4.5(@types/react@19.1.10)
|
||||
'@mui/utils': 7.3.1(@types/react@19.1.10)(react@19.1.1)
|
||||
clsx: 2.1.1
|
||||
csstype: 3.1.3
|
||||
prop-types: 15.8.1
|
||||
react: 19.1.1
|
||||
optionalDependencies:
|
||||
'@emotion/react': 11.14.0(@types/react@19.1.9)(react@19.1.1)
|
||||
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1)
|
||||
'@types/react': 19.1.9
|
||||
'@emotion/react': 11.14.0(@types/react@19.1.10)(react@19.1.1)
|
||||
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1)
|
||||
'@types/react': 19.1.10
|
||||
|
||||
'@mui/types@7.4.5(@types/react@19.1.9)':
|
||||
'@mui/types@7.4.5(@types/react@19.1.10)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
optionalDependencies:
|
||||
'@types/react': 19.1.9
|
||||
'@types/react': 19.1.10
|
||||
|
||||
'@mui/utils@7.3.1(@types/react@19.1.9)(react@19.1.1)':
|
||||
'@mui/utils@7.3.1(@types/react@19.1.10)(react@19.1.1)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@mui/types': 7.4.5(@types/react@19.1.9)
|
||||
'@mui/types': 7.4.5(@types/react@19.1.10)
|
||||
'@types/prop-types': 15.7.15
|
||||
clsx: 2.1.1
|
||||
prop-types: 15.8.1
|
||||
react: 19.1.1
|
||||
react-is: 19.1.1
|
||||
optionalDependencies:
|
||||
'@types/react': 19.1.9
|
||||
'@types/react': 19.1.10
|
||||
|
||||
'@mui/x-date-pickers@8.10.0(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@mui/system@7.3.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(dayjs@1.11.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
|
||||
'@mui/x-date-pickers@8.10.0(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@mui/system@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(dayjs@1.11.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@mui/material': 7.3.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
'@mui/system': 7.3.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1)
|
||||
'@mui/utils': 7.3.1(@types/react@19.1.9)(react@19.1.1)
|
||||
'@mui/x-internals': 8.10.0(@types/react@19.1.9)(react@19.1.1)
|
||||
'@types/react-transition-group': 4.4.12(@types/react@19.1.9)
|
||||
'@mui/material': 7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
'@mui/system': 7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1)
|
||||
'@mui/utils': 7.3.1(@types/react@19.1.10)(react@19.1.1)
|
||||
'@mui/x-internals': 8.10.0(@types/react@19.1.10)(react@19.1.1)
|
||||
'@types/react-transition-group': 4.4.12(@types/react@19.1.10)
|
||||
clsx: 2.1.1
|
||||
prop-types: 15.8.1
|
||||
react: 19.1.1
|
||||
react-dom: 19.1.1(react@19.1.1)
|
||||
react-transition-group: 4.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
optionalDependencies:
|
||||
'@emotion/react': 11.14.0(@types/react@19.1.9)(react@19.1.1)
|
||||
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1)
|
||||
'@emotion/react': 11.14.0(@types/react@19.1.10)(react@19.1.1)
|
||||
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1)
|
||||
dayjs: 1.11.13
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
|
||||
'@mui/x-internals@8.10.0(@types/react@19.1.9)(react@19.1.1)':
|
||||
'@mui/x-internals@8.10.0(@types/react@19.1.10)(react@19.1.1)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@mui/utils': 7.3.1(@types/react@19.1.9)(react@19.1.1)
|
||||
'@mui/utils': 7.3.1(@types/react@19.1.10)(react@19.1.1)
|
||||
react: 19.1.1
|
||||
reselect: 5.1.1
|
||||
use-sync-external-store: 1.5.0(react@19.1.1)
|
||||
@@ -10578,88 +10578,88 @@ snapshots:
|
||||
|
||||
'@radix-ui/primitive@1.1.2': {}
|
||||
|
||||
'@radix-ui/react-compose-refs@1.1.2(@types/react@19.1.9)(react@19.1.1)':
|
||||
'@radix-ui/react-compose-refs@1.1.2(@types/react@19.1.10)(react@19.1.1)':
|
||||
dependencies:
|
||||
react: 19.1.1
|
||||
optionalDependencies:
|
||||
'@types/react': 19.1.9
|
||||
'@types/react': 19.1.10
|
||||
|
||||
'@radix-ui/react-context@1.1.2(@types/react@19.1.9)(react@19.1.1)':
|
||||
'@radix-ui/react-context@1.1.2(@types/react@19.1.10)(react@19.1.1)':
|
||||
dependencies:
|
||||
react: 19.1.1
|
||||
optionalDependencies:
|
||||
'@types/react': 19.1.9
|
||||
'@types/react': 19.1.10
|
||||
|
||||
'@radix-ui/react-direction@1.1.1(@types/react@19.1.9)(react@19.1.1)':
|
||||
'@radix-ui/react-direction@1.1.1(@types/react@19.1.10)(react@19.1.1)':
|
||||
dependencies:
|
||||
react: 19.1.1
|
||||
optionalDependencies:
|
||||
'@types/react': 19.1.9
|
||||
'@types/react': 19.1.10
|
||||
|
||||
'@radix-ui/react-portal@1.1.9(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(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.7(@types/react@19.1.10))(@types/react@19.1.10)(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.7(@types/react@19.1.9))(@types/react@19.1.9)(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.9)(react@19.1.1)
|
||||
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(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.10)(react@19.1.1)
|
||||
react: 19.1.1
|
||||
react-dom: 19.1.1(react@19.1.1)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.1.9
|
||||
'@types/react-dom': 19.1.7(@types/react@19.1.9)
|
||||
'@types/react': 19.1.10
|
||||
'@types/react-dom': 19.1.7(@types/react@19.1.10)
|
||||
|
||||
'@radix-ui/react-presence@1.1.4(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
|
||||
'@radix-ui/react-presence@1.1.4(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(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.9)(react@19.1.1)
|
||||
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.9)(react@19.1.1)
|
||||
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.10)(react@19.1.1)
|
||||
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.10)(react@19.1.1)
|
||||
react: 19.1.1
|
||||
react-dom: 19.1.1(react@19.1.1)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.1.9
|
||||
'@types/react-dom': 19.1.7(@types/react@19.1.9)
|
||||
'@types/react': 19.1.10
|
||||
'@types/react-dom': 19.1.7(@types/react@19.1.10)
|
||||
|
||||
'@radix-ui/react-primitive@2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(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.7(@types/react@19.1.10))(@types/react@19.1.10)(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.9)(react@19.1.1)
|
||||
'@radix-ui/react-slot': 1.2.3(@types/react@19.1.10)(react@19.1.1)
|
||||
react: 19.1.1
|
||||
react-dom: 19.1.1(react@19.1.1)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.1.9
|
||||
'@types/react-dom': 19.1.7(@types/react@19.1.9)
|
||||
'@types/react': 19.1.10
|
||||
'@types/react-dom': 19.1.7(@types/react@19.1.10)
|
||||
|
||||
'@radix-ui/react-scroll-area@1.2.9(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
|
||||
'@radix-ui/react-scroll-area@1.2.9(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(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.2
|
||||
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.9)(react@19.1.1)
|
||||
'@radix-ui/react-context': 1.1.2(@types/react@19.1.9)(react@19.1.1)
|
||||
'@radix-ui/react-direction': 1.1.1(@types/react@19.1.9)(react@19.1.1)
|
||||
'@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(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.7(@types/react@19.1.9))(@types/react@19.1.9)(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.9)(react@19.1.1)
|
||||
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.9)(react@19.1.1)
|
||||
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.10)(react@19.1.1)
|
||||
'@radix-ui/react-context': 1.1.2(@types/react@19.1.10)(react@19.1.1)
|
||||
'@radix-ui/react-direction': 1.1.1(@types/react@19.1.10)(react@19.1.1)
|
||||
'@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(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.7(@types/react@19.1.10))(@types/react@19.1.10)(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.10)(react@19.1.1)
|
||||
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.10)(react@19.1.1)
|
||||
react: 19.1.1
|
||||
react-dom: 19.1.1(react@19.1.1)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.1.9
|
||||
'@types/react-dom': 19.1.7(@types/react@19.1.9)
|
||||
'@types/react': 19.1.10
|
||||
'@types/react-dom': 19.1.7(@types/react@19.1.10)
|
||||
|
||||
'@radix-ui/react-slot@1.2.3(@types/react@19.1.9)(react@19.1.1)':
|
||||
'@radix-ui/react-slot@1.2.3(@types/react@19.1.10)(react@19.1.1)':
|
||||
dependencies:
|
||||
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.9)(react@19.1.1)
|
||||
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.10)(react@19.1.1)
|
||||
react: 19.1.1
|
||||
optionalDependencies:
|
||||
'@types/react': 19.1.9
|
||||
'@types/react': 19.1.10
|
||||
|
||||
'@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.1.9)(react@19.1.1)':
|
||||
'@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.1.10)(react@19.1.1)':
|
||||
dependencies:
|
||||
react: 19.1.1
|
||||
optionalDependencies:
|
||||
'@types/react': 19.1.9
|
||||
'@types/react': 19.1.10
|
||||
|
||||
'@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.1.9)(react@19.1.1)':
|
||||
'@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.1.10)(react@19.1.1)':
|
||||
dependencies:
|
||||
react: 19.1.1
|
||||
optionalDependencies:
|
||||
'@types/react': 19.1.9
|
||||
'@types/react': 19.1.10
|
||||
|
||||
'@rolldown/pluginutils@1.0.0-beta.27': {}
|
||||
|
||||
@@ -11040,10 +11040,10 @@ snapshots:
|
||||
'@tanstack/query-core': 5.83.1
|
||||
react: 19.1.1
|
||||
|
||||
'@tanstack/react-router-devtools@1.131.3(@tanstack/react-router@1.131.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@tanstack/router-core@1.131.3)(csstype@3.1.3)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(solid-js@1.9.5)(tiny-invariant@1.3.3)':
|
||||
'@tanstack/react-router-devtools@1.131.5(@tanstack/react-router@1.131.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@tanstack/router-core@1.131.5)(csstype@3.1.3)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(solid-js@1.9.5)(tiny-invariant@1.3.3)':
|
||||
dependencies:
|
||||
'@tanstack/react-router': 1.131.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
'@tanstack/router-devtools-core': 1.131.3(@tanstack/router-core@1.131.3)(csstype@3.1.3)(solid-js@1.9.5)(tiny-invariant@1.3.3)
|
||||
'@tanstack/react-router': 1.131.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
'@tanstack/router-devtools-core': 1.131.5(@tanstack/router-core@1.131.5)(csstype@3.1.3)(solid-js@1.9.5)(tiny-invariant@1.3.3)
|
||||
react: 19.1.1
|
||||
react-dom: 19.1.1(react@19.1.1)
|
||||
transitivePeerDependencies:
|
||||
@@ -11052,11 +11052,11 @@ snapshots:
|
||||
- solid-js
|
||||
- tiny-invariant
|
||||
|
||||
'@tanstack/react-router@1.131.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
|
||||
'@tanstack/react-router@1.131.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
|
||||
dependencies:
|
||||
'@tanstack/history': 1.131.2
|
||||
'@tanstack/react-store': 0.7.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
'@tanstack/router-core': 1.131.3
|
||||
'@tanstack/router-core': 1.131.5
|
||||
isbot: 5.1.28
|
||||
react: 19.1.1
|
||||
react-dom: 19.1.1(react@19.1.1)
|
||||
@@ -11082,7 +11082,7 @@ snapshots:
|
||||
react: 19.1.1
|
||||
react-dom: 19.1.1(react@19.1.1)
|
||||
|
||||
'@tanstack/router-core@1.131.3':
|
||||
'@tanstack/router-core@1.131.5':
|
||||
dependencies:
|
||||
'@tanstack/history': 1.131.2
|
||||
'@tanstack/store': 0.7.0
|
||||
@@ -11092,9 +11092,9 @@ snapshots:
|
||||
tiny-invariant: 1.3.3
|
||||
tiny-warning: 1.0.3
|
||||
|
||||
'@tanstack/router-devtools-core@1.131.3(@tanstack/router-core@1.131.3)(csstype@3.1.3)(solid-js@1.9.5)(tiny-invariant@1.3.3)':
|
||||
'@tanstack/router-devtools-core@1.131.5(@tanstack/router-core@1.131.5)(csstype@3.1.3)(solid-js@1.9.5)(tiny-invariant@1.3.3)':
|
||||
dependencies:
|
||||
'@tanstack/router-core': 1.131.3
|
||||
'@tanstack/router-core': 1.131.5
|
||||
clsx: 2.1.1
|
||||
goober: 2.1.16(csstype@3.1.3)
|
||||
solid-js: 1.9.5
|
||||
@@ -11102,9 +11102,9 @@ snapshots:
|
||||
optionalDependencies:
|
||||
csstype: 3.1.3
|
||||
|
||||
'@tanstack/router-generator@1.131.3':
|
||||
'@tanstack/router-generator@1.131.5':
|
||||
dependencies:
|
||||
'@tanstack/router-core': 1.131.3
|
||||
'@tanstack/router-core': 1.131.5
|
||||
'@tanstack/router-utils': 1.131.2
|
||||
'@tanstack/virtual-file-routes': 1.131.2
|
||||
prettier: 3.6.2
|
||||
@@ -11115,7 +11115,7 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@tanstack/router-plugin@1.131.3(@tanstack/react-router@1.131.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.0))':
|
||||
'@tanstack/router-plugin@1.131.5(@tanstack/react-router@1.131.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(vite@7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.0))':
|
||||
dependencies:
|
||||
'@babel/core': 7.28.0
|
||||
'@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.0)
|
||||
@@ -11123,8 +11123,8 @@ snapshots:
|
||||
'@babel/template': 7.27.2
|
||||
'@babel/traverse': 7.28.0
|
||||
'@babel/types': 7.28.1
|
||||
'@tanstack/router-core': 1.131.3
|
||||
'@tanstack/router-generator': 1.131.3
|
||||
'@tanstack/router-core': 1.131.5
|
||||
'@tanstack/router-generator': 1.131.5
|
||||
'@tanstack/router-utils': 1.131.2
|
||||
'@tanstack/virtual-file-routes': 1.131.2
|
||||
babel-dead-code-elimination: 1.0.10
|
||||
@@ -11132,7 +11132,7 @@ snapshots:
|
||||
unplugin: 2.3.5
|
||||
zod: 3.25.76
|
||||
optionalDependencies:
|
||||
'@tanstack/react-router': 1.131.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
'@tanstack/react-router': 1.131.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
vite: 7.1.1(@types/node@22.17.1)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@@ -11148,9 +11148,9 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@tanstack/router-zod-adapter@1.81.5(@tanstack/react-router@1.131.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(zod@4.0.17)':
|
||||
'@tanstack/router-zod-adapter@1.81.5(@tanstack/react-router@1.131.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(zod@4.0.17)':
|
||||
dependencies:
|
||||
'@tanstack/react-router': 1.131.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
'@tanstack/react-router': 1.131.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
zod: 4.0.17
|
||||
|
||||
'@tanstack/store@0.7.0': {}
|
||||
@@ -11496,15 +11496,15 @@ snapshots:
|
||||
|
||||
'@types/prop-types@15.7.15': {}
|
||||
|
||||
'@types/react-dom@19.1.7(@types/react@19.1.9)':
|
||||
'@types/react-dom@19.1.7(@types/react@19.1.10)':
|
||||
dependencies:
|
||||
'@types/react': 19.1.9
|
||||
'@types/react': 19.1.10
|
||||
|
||||
'@types/react-transition-group@4.4.12(@types/react@19.1.9)':
|
||||
'@types/react-transition-group@4.4.12(@types/react@19.1.10)':
|
||||
dependencies:
|
||||
'@types/react': 19.1.9
|
||||
'@types/react': 19.1.10
|
||||
|
||||
'@types/react@19.1.9':
|
||||
'@types/react@19.1.10':
|
||||
dependencies:
|
||||
csstype: 3.1.3
|
||||
|
||||
@@ -14464,11 +14464,11 @@ snapshots:
|
||||
|
||||
jju@1.4.0: {}
|
||||
|
||||
jotai@2.13.1(@babel/core@7.28.0)(@babel/template@7.27.2)(@types/react@19.1.9)(react@19.1.1):
|
||||
jotai@2.13.1(@babel/core@7.28.0)(@babel/template@7.27.2)(@types/react@19.1.10)(react@19.1.1):
|
||||
optionalDependencies:
|
||||
'@babel/core': 7.28.0
|
||||
'@babel/template': 7.27.2
|
||||
'@types/react': 19.1.9
|
||||
'@types/react': 19.1.10
|
||||
react: 19.1.1
|
||||
|
||||
js-cookie@2.2.1: {}
|
||||
@@ -15109,16 +15109,16 @@ snapshots:
|
||||
|
||||
muggle-string@0.4.1: {}
|
||||
|
||||
mui-color-input@7.0.0(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@types/react@19.1.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1):
|
||||
mui-color-input@7.0.0(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1):
|
||||
dependencies:
|
||||
'@ctrl/tinycolor': 4.1.0
|
||||
'@emotion/react': 11.14.0(@types/react@19.1.9)(react@19.1.1)
|
||||
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1)
|
||||
'@mui/material': 7.3.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
'@emotion/react': 11.14.0(@types/react@19.1.10)(react@19.1.1)
|
||||
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1)
|
||||
'@mui/material': 7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
react: 19.1.1
|
||||
react-dom: 19.1.1(react@19.1.1)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.1.9
|
||||
'@types/react': 19.1.10
|
||||
|
||||
nano-css@5.6.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1):
|
||||
dependencies:
|
||||
@@ -15704,14 +15704,14 @@ snapshots:
|
||||
react: 19.1.1
|
||||
react-dom: 19.1.1(react@19.1.1)
|
||||
|
||||
react-hook-form-mui@7.6.2(dc790e3d871a3fe7e1d22dc6e321e397):
|
||||
react-hook-form-mui@7.6.2(aea177882beb7723aeada5c99e57089b):
|
||||
dependencies:
|
||||
'@mui/material': 7.3.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
'@mui/material': 7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
react: 19.1.1
|
||||
react-hook-form: 7.52.1(react@19.1.1)
|
||||
optionalDependencies:
|
||||
'@mui/icons-material': 7.3.1(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@types/react@19.1.9)(react@19.1.1)
|
||||
'@mui/x-date-pickers': 8.10.0(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@mui/system@7.3.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(react@19.1.1))(@types/react@19.1.9)(dayjs@1.11.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
'@mui/icons-material': 7.3.1(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@types/react@19.1.10)(react@19.1.1)
|
||||
'@mui/x-date-pickers': 8.10.0(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@mui/system@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(dayjs@1.11.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
|
||||
react-hook-form@7.52.1(react@19.1.1):
|
||||
dependencies:
|
||||
@@ -15731,11 +15731,11 @@ snapshots:
|
||||
|
||||
react-is@19.1.1: {}
|
||||
|
||||
react-markdown@10.1.0(@types/react@19.1.9)(react@19.1.1):
|
||||
react-markdown@10.1.0(@types/react@19.1.10)(react@19.1.1):
|
||||
dependencies:
|
||||
'@types/hast': 3.0.4
|
||||
'@types/mdast': 4.0.3
|
||||
'@types/react': 19.1.9
|
||||
'@types/react': 19.1.10
|
||||
devlop: 1.1.0
|
||||
hast-util-to-jsx-runtime: 2.3.0
|
||||
html-url-attributes: 3.0.0
|
||||
|
@@ -51,6 +51,7 @@ image:
|
||||
make image EXTRA_IMAGE_NAME="<string>" # Add this to the output image filename (sanitized)
|
||||
make image DISABLED_SERVICES="<svc1> [<svc2> [<svc3> ..]]" # Which services in /etc/init.d/ should be disabled
|
||||
make image ADD_LOCAL_KEY=1 # store locally generated signing key in built images
|
||||
make image ROOTFS_PARTSIZE="<size>" # override the default rootfs partition size in MegaBytes
|
||||
|
||||
manifest:
|
||||
List "all" packages which get installed into the image.
|
||||
@@ -261,7 +262,8 @@ image:
|
||||
$(if $(FILES),USER_FILES="$(FILES)") \
|
||||
$(if $(PACKAGES),USER_PACKAGES="$(PACKAGES)") \
|
||||
$(if $(BIN_DIR),BIN_DIR="$(BIN_DIR)") \
|
||||
$(if $(DISABLED_SERVICES),DISABLED_SERVICES="$(DISABLED_SERVICES)"))
|
||||
$(if $(DISABLED_SERVICES),DISABLED_SERVICES="$(DISABLED_SERVICES)") \
|
||||
$(if $(ROOTFS_PARTSIZE),CONFIG_TARGET_ROOTFS_PARTSIZE="$(ROOTFS_PARTSIZE)"))
|
||||
|
||||
manifest: FORCE
|
||||
$(MAKE) -s _check_profile
|
||||
|
@@ -17,12 +17,10 @@ package testtool
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
mrand "math/rand"
|
||||
"net"
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/enfein/mieru/v3/pkg/common"
|
||||
@@ -32,12 +30,17 @@ import (
|
||||
func BufPipe() (net.Conn, net.Conn) {
|
||||
var buf1, buf2 bytes.Buffer
|
||||
var lock1, lock2 sync.Mutex
|
||||
cond1 := sync.NewCond(&lock1) // endpoint 1 has data to read
|
||||
cond2 := sync.NewCond(&lock2) // endpoint 2 has data to read
|
||||
|
||||
ep1 := &ioEndpoint{
|
||||
direction: forward,
|
||||
buf1: &buf1,
|
||||
buf2: &buf2,
|
||||
lock1: &lock1,
|
||||
lock2: &lock2,
|
||||
cond1: cond1,
|
||||
cond2: cond2,
|
||||
}
|
||||
ep2 := &ioEndpoint{
|
||||
direction: backward,
|
||||
@@ -45,7 +48,11 @@ func BufPipe() (net.Conn, net.Conn) {
|
||||
buf2: &buf2,
|
||||
lock1: &lock1,
|
||||
lock2: &lock2,
|
||||
cond1: cond1,
|
||||
cond2: cond2,
|
||||
}
|
||||
ep1.peer = ep2
|
||||
ep2.peer = ep1
|
||||
return ep1, ep2
|
||||
}
|
||||
|
||||
@@ -62,56 +69,83 @@ type ioEndpoint struct {
|
||||
buf2 *bytes.Buffer // backward writes to here
|
||||
lock1 *sync.Mutex // lock of buf1
|
||||
lock2 *sync.Mutex // lock of buf2
|
||||
closed bool
|
||||
cond1 *sync.Cond
|
||||
cond2 *sync.Cond
|
||||
closed atomic.Bool
|
||||
peer *ioEndpoint
|
||||
}
|
||||
|
||||
var _ net.Conn = &ioEndpoint{}
|
||||
|
||||
func (e *ioEndpoint) Read(b []byte) (n int, err error) {
|
||||
if e.closed {
|
||||
if e.closed.Load() {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
var buffer *bytes.Buffer
|
||||
var lock *sync.Mutex
|
||||
var cond *sync.Cond
|
||||
|
||||
if e.direction == forward {
|
||||
e.lock2.Lock()
|
||||
n, err = e.buf2.Read(b)
|
||||
e.lock2.Unlock()
|
||||
buffer = e.buf2
|
||||
lock = e.lock2
|
||||
cond = e.cond2
|
||||
} else {
|
||||
e.lock1.Lock()
|
||||
n, err = e.buf1.Read(b)
|
||||
e.lock1.Unlock()
|
||||
buffer = e.buf1
|
||||
lock = e.lock1
|
||||
cond = e.cond1
|
||||
}
|
||||
if errors.Is(err, io.EOF) {
|
||||
// io.ReadFull() with partial result will not fail.
|
||||
err = nil
|
||||
action := mrand.Intn(2)
|
||||
if action == 0 {
|
||||
// Allow the writer to catch up.
|
||||
runtime.Gosched()
|
||||
} else {
|
||||
time.Sleep(time.Microsecond)
|
||||
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
for buffer.Len() == 0 {
|
||||
if e.closed.Load() || e.peer.closed.Load() {
|
||||
return 0, io.EOF
|
||||
}
|
||||
cond.Wait()
|
||||
}
|
||||
return
|
||||
|
||||
return buffer.Read(b)
|
||||
}
|
||||
|
||||
func (e *ioEndpoint) Write(b []byte) (n int, err error) {
|
||||
if e.closed {
|
||||
if e.closed.Load() {
|
||||
return 0, io.ErrClosedPipe
|
||||
}
|
||||
|
||||
var buffer *bytes.Buffer
|
||||
var lock *sync.Mutex
|
||||
var cond *sync.Cond
|
||||
|
||||
if e.direction == forward {
|
||||
e.lock1.Lock()
|
||||
n, err = e.buf1.Write(b)
|
||||
e.lock1.Unlock()
|
||||
buffer = e.buf1
|
||||
lock = e.lock1
|
||||
cond = e.cond1
|
||||
} else {
|
||||
e.lock2.Lock()
|
||||
n, err = e.buf2.Write(b)
|
||||
e.lock2.Unlock()
|
||||
buffer = e.buf2
|
||||
lock = e.lock2
|
||||
cond = e.cond2
|
||||
}
|
||||
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
if e.peer.closed.Load() {
|
||||
return 0, io.ErrClosedPipe
|
||||
}
|
||||
|
||||
n, err = buffer.Write(b)
|
||||
cond.Signal()
|
||||
return
|
||||
}
|
||||
|
||||
func (e *ioEndpoint) Close() error {
|
||||
e.closed = true
|
||||
if e.closed.Swap(true) {
|
||||
return nil
|
||||
}
|
||||
e.cond1.Broadcast()
|
||||
e.cond2.Broadcast()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@@ -3,14 +3,11 @@ package outbound
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/mihomo/common/convert"
|
||||
N "github.com/metacubex/mihomo/common/net"
|
||||
@@ -456,41 +453,11 @@ func NewVless(option VlessOption) (*Vless, error) {
|
||||
option: &option,
|
||||
}
|
||||
|
||||
if s := strings.SplitN(option.Encryption, "-", 4); len(s) == 4 && s[2] == "mlkem768client" {
|
||||
var minutes uint32
|
||||
if s[0] != "1rtt" {
|
||||
t := strings.TrimSuffix(s[0], "min")
|
||||
if t == s[0] {
|
||||
return nil, fmt.Errorf("invaild vless encryption value: %s", option.Encryption)
|
||||
}
|
||||
var i int
|
||||
i, err = strconv.Atoi(t)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invaild vless encryption value: %s", option.Encryption)
|
||||
}
|
||||
minutes = uint32(i)
|
||||
}
|
||||
var xor uint32
|
||||
switch s[1] {
|
||||
case "vless":
|
||||
case "aes128xor":
|
||||
xor = 1
|
||||
default:
|
||||
return nil, fmt.Errorf("invaild vless encryption value: %s", option.Encryption)
|
||||
}
|
||||
var b []byte
|
||||
b, err = base64.RawURLEncoding.DecodeString(s[3])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invaild vless encryption value: %s", option.Encryption)
|
||||
}
|
||||
if len(b) == encryption.MLKEM768ClientLength {
|
||||
v.encryption = &encryption.ClientInstance{}
|
||||
if err = v.encryption.Init(b, xor, time.Duration(minutes)*time.Minute); err != nil {
|
||||
return nil, fmt.Errorf("failed to use mlkem768seed: %w", err)
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("invaild vless encryption value: %s", option.Encryption)
|
||||
}
|
||||
v.encryption, err = encryption.NewClient(option.Encryption)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if v.encryption != nil {
|
||||
if option.Flow != "" {
|
||||
return nil, errors.New(`vless "encryption" doesn't support "flow" yet`)
|
||||
}
|
||||
|
@@ -10,11 +10,13 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"strconv"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
N "github.com/metacubex/mihomo/common/net"
|
||||
"github.com/metacubex/mihomo/common/pool"
|
||||
"github.com/metacubex/mihomo/common/utils"
|
||||
"github.com/metacubex/mihomo/component/ca"
|
||||
"github.com/metacubex/mihomo/component/dialer"
|
||||
@@ -30,7 +32,7 @@ import (
|
||||
)
|
||||
|
||||
var httpPath = "/inbound_test"
|
||||
var httpData = make([]byte, 10240)
|
||||
var httpData = make([]byte, 2*pool.RelayBufferSize)
|
||||
var remoteAddr = netip.MustParseAddr("1.2.3.4")
|
||||
var userUUID = utils.NewUUIDV4().String()
|
||||
var tlsCertificate, tlsPrivateKey, tlsFingerprint, _ = ca.NewRandomTLSKeyPair(ca.KeyPairTypeP256)
|
||||
@@ -134,14 +136,21 @@ func NewHttpTestTunnel() *TestTunnel {
|
||||
|
||||
r := chi.NewRouter()
|
||||
r.Get(httpPath, func(w http.ResponseWriter, r *http.Request) {
|
||||
render.Data(w, r, httpData)
|
||||
query := r.URL.Query()
|
||||
size, err := strconv.Atoi(query.Get("size"))
|
||||
if err != nil {
|
||||
render.Status(r, http.StatusBadRequest)
|
||||
render.PlainText(w, r, err.Error())
|
||||
return
|
||||
}
|
||||
render.Data(w, r, httpData[:size])
|
||||
})
|
||||
h2Server := &http2.Server{}
|
||||
server := http.Server{Handler: r}
|
||||
_ = http2.ConfigureServer(&server, h2Server)
|
||||
go server.Serve(ln)
|
||||
testFn := func(t *testing.T, proxy C.ProxyAdapter, proto string) {
|
||||
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s://%s%s", proto, remoteAddr, httpPath), nil)
|
||||
testFn := func(t *testing.T, proxy C.ProxyAdapter, proto string, size int) {
|
||||
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s://%s%s?size=%d", proto, remoteAddr, httpPath, size), nil)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
@@ -200,7 +209,7 @@ func NewHttpTestTunnel() *TestTunnel {
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
assert.Equal(t, httpData, data)
|
||||
assert.Equal(t, httpData[:size], data)
|
||||
}
|
||||
tunnel := &TestTunnel{
|
||||
HandleTCPConnFn: func(conn net.Conn, metadata *C.Metadata) {
|
||||
@@ -241,27 +250,30 @@ func NewHttpTestTunnel() *TestTunnel {
|
||||
},
|
||||
CloseFn: ln.Close,
|
||||
DoTestFn: func(t *testing.T, proxy C.ProxyAdapter) {
|
||||
|
||||
// Sequential testing for debugging
|
||||
t.Run("Sequential", func(t *testing.T) {
|
||||
testFn(t, proxy, "http")
|
||||
testFn(t, proxy, "https")
|
||||
testFn(t, proxy, "http", len(httpData))
|
||||
testFn(t, proxy, "https", len(httpData))
|
||||
})
|
||||
|
||||
// Concurrent testing to detect stress
|
||||
t.Run("Concurrent", func(t *testing.T) {
|
||||
wg := sync.WaitGroup{}
|
||||
const num = 50
|
||||
for i := 0; i < num; i++ {
|
||||
num := len(httpData) / 1024
|
||||
for i := 1; i <= num; i++ {
|
||||
i := i
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
testFn(t, proxy, "https")
|
||||
testFn(t, proxy, "https", i*1024)
|
||||
defer wg.Done()
|
||||
}()
|
||||
}
|
||||
for i := 0; i < num; i++ {
|
||||
for i := 1; i <= num; i++ {
|
||||
i := i
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
testFn(t, proxy, "http")
|
||||
testFn(t, proxy, "http", i*1024)
|
||||
defer wg.Done()
|
||||
}()
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var singMuxProtocolList = []string{"h2mux", "smux"} // don't test "yamux" because it has some confused bugs
|
||||
var singMuxProtocolList = []string{"smux"} // don't test "h2mux" and "yamux" because it has some confused bugs
|
||||
|
||||
// notCloseProxyAdapter is a proxy adapter that does not close the underlying outbound.ProxyAdapter.
|
||||
// The outbound.SingMux will close the underlying outbound.ProxyAdapter when it is closed, but we don't want to close it.
|
||||
|
@@ -2,15 +2,11 @@ package sing_vless
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/metacubex/mihomo/adapter/inbound"
|
||||
@@ -88,42 +84,11 @@ func New(config LC.VlessServer, tunnel C.Tunnel, additions ...inbound.Addition)
|
||||
|
||||
sl = &Listener{config: config, service: service}
|
||||
|
||||
if s := strings.SplitN(config.Decryption, "-", 4); len(s) == 4 && s[2] == "mlkem768seed" {
|
||||
var minutes uint32
|
||||
if s[0] != "1rtt" {
|
||||
t := strings.TrimSuffix(s[0], "min")
|
||||
if t == s[0] {
|
||||
return nil, fmt.Errorf("invaild vless decryption value: %s", config.Decryption)
|
||||
}
|
||||
var i int
|
||||
i, err = strconv.Atoi(t)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invaild vless decryption value: %s", config.Decryption)
|
||||
}
|
||||
minutes = uint32(i)
|
||||
}
|
||||
var xor uint32
|
||||
switch s[1] {
|
||||
case "vless":
|
||||
case "aes128xor":
|
||||
xor = 1
|
||||
default:
|
||||
return nil, fmt.Errorf("invaild vless decryption value: %s", config.Decryption)
|
||||
}
|
||||
var b []byte
|
||||
b, err = base64.RawURLEncoding.DecodeString(s[3])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invaild vless decryption value: %s", config.Decryption)
|
||||
}
|
||||
if len(b) == encryption.MLKEM768SeedLength {
|
||||
sl.decryption = &encryption.ServerInstance{}
|
||||
if err = sl.decryption.Init(b, xor, time.Duration(minutes)*time.Minute); err != nil {
|
||||
return nil, fmt.Errorf("failed to use mlkem768seed: %w", err)
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("invaild vless decryption value: %s", config.Decryption)
|
||||
}
|
||||
|
||||
sl.decryption, err = encryption.NewServer(config.Decryption)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if sl.decryption != nil {
|
||||
defer func() { // decryption must be closed to avoid the goroutine leak
|
||||
if err != nil {
|
||||
_ = sl.decryption.Close()
|
||||
|
@@ -4,7 +4,6 @@ import (
|
||||
"bytes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
@@ -13,7 +12,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/utls/mlkem"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
"golang.org/x/sys/cpu"
|
||||
)
|
||||
|
||||
@@ -30,19 +28,20 @@ var (
|
||||
var ClientCipher byte
|
||||
|
||||
func init() {
|
||||
if !HasAESGCMHardwareSupport {
|
||||
if HasAESGCMHardwareSupport {
|
||||
ClientCipher = 1
|
||||
}
|
||||
}
|
||||
|
||||
type ClientInstance struct {
|
||||
sync.RWMutex
|
||||
eKeyNfs *mlkem.EncapsulationKey768
|
||||
xor uint32
|
||||
minutes time.Duration
|
||||
expire time.Time
|
||||
baseKey []byte
|
||||
ticket []byte
|
||||
nfsEKey *mlkem.EncapsulationKey768
|
||||
nfsEKeyBytes []byte
|
||||
xor uint32
|
||||
minutes time.Duration
|
||||
expire time.Time
|
||||
baseKey []byte
|
||||
ticket []byte
|
||||
}
|
||||
|
||||
type ClientConn struct {
|
||||
@@ -58,19 +57,22 @@ type ClientConn struct {
|
||||
peerCache []byte
|
||||
}
|
||||
|
||||
func (i *ClientInstance) Init(eKeyNfsData []byte, xor uint32, minutes time.Duration) (err error) {
|
||||
i.eKeyNfs, err = mlkem.NewEncapsulationKey768(eKeyNfsData)
|
||||
i.xor = xor
|
||||
func (i *ClientInstance) Init(nfsEKeyBytes []byte, xor uint32, minutes time.Duration) (err error) {
|
||||
i.nfsEKey, err = mlkem.NewEncapsulationKey768(nfsEKeyBytes)
|
||||
if xor > 0 {
|
||||
i.nfsEKeyBytes = nfsEKeyBytes
|
||||
i.xor = xor
|
||||
}
|
||||
i.minutes = minutes
|
||||
return
|
||||
}
|
||||
|
||||
func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) {
|
||||
if i.eKeyNfs == nil {
|
||||
if i.nfsEKey == nil {
|
||||
return nil, errors.New("uninitialized")
|
||||
}
|
||||
if i.xor == 1 {
|
||||
conn = NewXorConn(conn, i.eKeyNfs.Bytes())
|
||||
if i.xor > 0 {
|
||||
conn = NewXorConn(conn, i.nfsEKeyBytes)
|
||||
}
|
||||
c := &ClientConn{Conn: conn}
|
||||
|
||||
@@ -86,18 +88,19 @@ func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) {
|
||||
i.RUnlock()
|
||||
}
|
||||
|
||||
nfsKey, encapsulatedNfsKey := i.eKeyNfs.Encapsulate()
|
||||
seed := make([]byte, 64)
|
||||
rand.Read(seed)
|
||||
dKeyPfs, _ := mlkem.NewDecapsulationKey768(seed)
|
||||
eKeyPfs := dKeyPfs.EncapsulationKey().Bytes()
|
||||
padding := randBetween(100, 1000)
|
||||
pfsDKeySeed := make([]byte, 64)
|
||||
rand.Read(pfsDKeySeed)
|
||||
pfsDKey, _ := mlkem.NewDecapsulationKey768(pfsDKeySeed)
|
||||
pfsEKeyBytes := pfsDKey.EncapsulationKey().Bytes()
|
||||
nfsKey, encapsulatedNfsKey := i.nfsEKey.Encapsulate()
|
||||
paddingLen := randBetween(100, 1000)
|
||||
|
||||
clientHello := make([]byte, 1088+1184+1+5+padding)
|
||||
copy(clientHello, encapsulatedNfsKey)
|
||||
copy(clientHello[1088:], eKeyPfs)
|
||||
clientHello[2272] = ClientCipher
|
||||
encodeHeader(clientHello[2273:], int(padding))
|
||||
clientHello := make([]byte, 1+1184+1088+5+paddingLen)
|
||||
clientHello[0] = ClientCipher
|
||||
copy(clientHello[1:], pfsEKeyBytes)
|
||||
copy(clientHello[1185:], encapsulatedNfsKey)
|
||||
EncodeHeader(clientHello[2273:], int(paddingLen))
|
||||
rand.Read(clientHello[2278:])
|
||||
|
||||
if _, err := c.Conn.Write(clientHello); err != nil {
|
||||
return nil, err
|
||||
@@ -111,17 +114,15 @@ func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) {
|
||||
encapsulatedPfsKey := peerServerHello[:1088]
|
||||
c.ticket = peerServerHello[1088:]
|
||||
|
||||
pfsKey, err := dKeyPfs.Decapsulate(encapsulatedPfsKey)
|
||||
pfsKey, err := pfsDKey.Decapsulate(encapsulatedPfsKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.baseKey = append(nfsKey, pfsKey...)
|
||||
c.baseKey = append(pfsKey, nfsKey...)
|
||||
|
||||
authKey := make([]byte, 32)
|
||||
hkdf.New(sha256.New, c.baseKey, encapsulatedNfsKey, eKeyPfs).Read(authKey)
|
||||
nonce := make([]byte, 12)
|
||||
VLESS, _ := newAead(ClientCipher, authKey).Open(nil, nonce, c.ticket, encapsulatedPfsKey)
|
||||
if !bytes.Equal(VLESS, []byte("VLESS")) { // TODO: more message
|
||||
nonce := [12]byte{ClientCipher}
|
||||
VLESS, _ := NewAead(ClientCipher, c.baseKey, encapsulatedPfsKey, encapsulatedNfsKey).Open(nil, nonce[:], c.ticket, pfsEKeyBytes)
|
||||
if !bytes.Equal(VLESS, []byte("VLESS")) { // TODO: more messages
|
||||
return nil, errors.New("invalid server")
|
||||
}
|
||||
|
||||
@@ -141,32 +142,39 @@ func (c *ClientConn) Write(b []byte) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
var data []byte
|
||||
if c.aead == nil {
|
||||
c.random = make([]byte, 32)
|
||||
rand.Read(c.random)
|
||||
key := make([]byte, 32)
|
||||
hkdf.New(sha256.New, c.baseKey, c.random, c.ticket).Read(key)
|
||||
c.aead = newAead(ClientCipher, key)
|
||||
c.nonce = make([]byte, 12)
|
||||
|
||||
data = make([]byte, 21+32+5+len(b)+16)
|
||||
copy(data, c.ticket)
|
||||
copy(data[21:], c.random)
|
||||
encodeHeader(data[53:], len(b)+16)
|
||||
c.aead.Seal(data[:58], c.nonce, b, data[53:58])
|
||||
} else {
|
||||
data = make([]byte, 5+len(b)+16)
|
||||
encodeHeader(data, len(b)+16)
|
||||
c.aead.Seal(data[:5], c.nonce, b, data[:5])
|
||||
}
|
||||
increaseNonce(c.nonce)
|
||||
if _, err := c.Conn.Write(data); err != nil {
|
||||
return 0, err
|
||||
for n := 0; n < len(b); {
|
||||
b := b[n:]
|
||||
if len(b) > 8192 {
|
||||
b = b[:8192] // for avoiding another copy() in server's Read()
|
||||
}
|
||||
n += len(b)
|
||||
if c.aead == nil {
|
||||
c.random = make([]byte, 32)
|
||||
rand.Read(c.random)
|
||||
c.aead = NewAead(ClientCipher, c.baseKey, c.random, c.ticket)
|
||||
c.nonce = make([]byte, 12)
|
||||
data = make([]byte, 21+32+5+len(b)+16)
|
||||
copy(data, c.ticket)
|
||||
copy(data[21:], c.random)
|
||||
EncodeHeader(data[53:], len(b)+16)
|
||||
c.aead.Seal(data[:58], c.nonce, b, data[53:58])
|
||||
} else {
|
||||
data = make([]byte, 5+len(b)+16)
|
||||
EncodeHeader(data, len(b)+16)
|
||||
c.aead.Seal(data[:5], c.nonce, b, data[:5])
|
||||
if bytes.Equal(c.nonce, MaxNonce) {
|
||||
c.aead = NewAead(ClientCipher, c.baseKey, data[5:], data[:5])
|
||||
}
|
||||
}
|
||||
IncreaseNonce(c.nonce)
|
||||
if _, err := c.Conn.Write(data); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
func (c *ClientConn) Read(b []byte) (int, error) { // after first Write()
|
||||
func (c *ClientConn) Read(b []byte) (int, error) {
|
||||
if len(b) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
@@ -177,11 +185,11 @@ func (c *ClientConn) Read(b []byte) (int, error) { // after first Write()
|
||||
if _, err := io.ReadFull(c.Conn, peerHeader); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
peerPadding, _ := decodeHeader(peerHeader)
|
||||
if peerPadding == 0 {
|
||||
peerPaddingLen, _ := DecodeHeader(peerHeader)
|
||||
if peerPaddingLen == 0 {
|
||||
break
|
||||
}
|
||||
if _, err := io.ReadFull(c.Conn, make([]byte, peerPadding)); err != nil {
|
||||
if _, err := io.ReadFull(c.Conn, make([]byte, peerPaddingLen)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
@@ -196,11 +204,9 @@ func (c *ClientConn) Read(b []byte) (int, error) { // after first Write()
|
||||
return 0, err
|
||||
}
|
||||
if c.random == nil {
|
||||
return 0, errors.New("can not Read() first")
|
||||
return 0, errors.New("empty c.random")
|
||||
}
|
||||
peerKey := make([]byte, 32)
|
||||
hkdf.New(sha256.New, c.baseKey, peerRandom, c.random).Read(peerKey)
|
||||
c.peerAead = newAead(ClientCipher, peerKey)
|
||||
c.peerAead = NewAead(ClientCipher, c.baseKey, peerRandom, c.random)
|
||||
c.peerNonce = make([]byte, 12)
|
||||
}
|
||||
if len(c.peerCache) != 0 {
|
||||
@@ -211,7 +217,7 @@ func (c *ClientConn) Read(b []byte) (int, error) { // after first Write()
|
||||
if _, err := io.ReadFull(c.Conn, peerHeader); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
peerLength, err := decodeHeader(peerHeader) // 17~17000
|
||||
peerLength, err := DecodeHeader(peerHeader) // 17~17000
|
||||
if err != nil {
|
||||
if c.instance != nil {
|
||||
c.instance.Lock()
|
||||
@@ -228,10 +234,17 @@ func (c *ClientConn) Read(b []byte) (int, error) { // after first Write()
|
||||
}
|
||||
dst := peerData[:peerLength-16]
|
||||
if len(dst) <= len(b) {
|
||||
dst = b[:len(dst)] // max=8192 is recommended for peer
|
||||
dst = b[:len(dst)] // avoids another copy()
|
||||
}
|
||||
var peerAead cipher.AEAD
|
||||
if bytes.Equal(c.peerNonce, MaxNonce) {
|
||||
peerAead = NewAead(ClientCipher, c.baseKey, peerData, peerHeader)
|
||||
}
|
||||
_, err = c.peerAead.Open(dst[:0], c.peerNonce, peerData, peerHeader)
|
||||
increaseNonce(c.peerNonce)
|
||||
if peerAead != nil {
|
||||
c.peerAead = peerAead
|
||||
}
|
||||
IncreaseNonce(c.peerNonce)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
@@ -1,17 +1,22 @@
|
||||
package encryption
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"math/big"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
)
|
||||
|
||||
func encodeHeader(b []byte, l int) {
|
||||
var MaxNonce = bytes.Repeat([]byte{255}, 12)
|
||||
|
||||
func EncodeHeader(b []byte, l int) {
|
||||
b[0] = 23
|
||||
b[1] = 3
|
||||
b[2] = 3
|
||||
@@ -19,10 +24,10 @@ func encodeHeader(b []byte, l int) {
|
||||
b[4] = byte(l)
|
||||
}
|
||||
|
||||
func decodeHeader(b []byte) (int, error) {
|
||||
func DecodeHeader(b []byte) (int, error) {
|
||||
if b[0] == 23 && b[1] == 3 && b[2] == 3 {
|
||||
l := int(b[3])<<8 | int(b[4])
|
||||
if l < 17 || l > 17000 { // TODO
|
||||
if l < 17 || l > 17000 { // TODO: TLSv1.3 max length
|
||||
return 0, errors.New("invalid length in record's header: " + strconv.Itoa(l))
|
||||
}
|
||||
return l, nil
|
||||
@@ -30,29 +35,24 @@ func decodeHeader(b []byte) (int, error) {
|
||||
return 0, errors.New("invalid record's header")
|
||||
}
|
||||
|
||||
func newAead(c byte, k []byte) cipher.AEAD {
|
||||
switch c {
|
||||
case 0:
|
||||
if block, err := aes.NewCipher(k); err == nil {
|
||||
aead, _ := cipher.NewGCM(block)
|
||||
return aead
|
||||
}
|
||||
case 1:
|
||||
aead, _ := chacha20poly1305.New(k)
|
||||
return aead
|
||||
func NewAead(c byte, secret, salt, info []byte) (aead cipher.AEAD) {
|
||||
key := make([]byte, 32)
|
||||
hkdf.New(sha256.New, secret, salt, info).Read(key)
|
||||
if c&1 == 1 {
|
||||
block, _ := aes.NewCipher(key)
|
||||
aead, _ = cipher.NewGCM(block)
|
||||
} else {
|
||||
aead, _ = chacha20poly1305.New(key)
|
||||
}
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
func increaseNonce(nonce []byte) {
|
||||
func IncreaseNonce(nonce []byte) {
|
||||
for i := 0; i < 12; i++ {
|
||||
nonce[11-i]++
|
||||
if nonce[11-i] != 0 {
|
||||
break
|
||||
}
|
||||
if i == 11 {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -2,4 +2,7 @@
|
||||
// https://github.com/XTLS/Xray-core/commit/f61c14e9c63dc41a8a09135db3aea337974f3f37
|
||||
// https://github.com/XTLS/Xray-core/commit/3e19bf9233bdd9bafc073a71c65b737cc1ffba5e
|
||||
// https://github.com/XTLS/Xray-core/commit/7ffb555fc8ec51bd1e3e60f26f1d6957984dba80
|
||||
// https://github.com/XTLS/Xray-core/commit/ec1cc35188c1a5f38a2ff75e88b5d043ffdc59da
|
||||
// https://github.com/XTLS/Xray-core/commit/5c611420487a92f931faefc01d4bf03869f477f6
|
||||
// https://github.com/XTLS/Xray-core/commit/23d7aad461d232bc5bed52dd6aaa731ecd88ad35
|
||||
package encryption
|
||||
|
99
mihomo/transport/vless/encryption/factory.go
Normal file
99
mihomo/transport/vless/encryption/factory.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package encryption
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// NewClient new client from encryption string
|
||||
// maybe return a nil *ClientInstance without any error, that means don't need to encrypt
|
||||
func NewClient(encryption string) (*ClientInstance, error) {
|
||||
switch encryption {
|
||||
case "", "none": // We will not reject empty string like xray-core does, because we need to ensure compatibility
|
||||
return nil, nil
|
||||
}
|
||||
if s := strings.SplitN(encryption, "-", 4); len(s) == 4 && s[2] == "mlkem768client" {
|
||||
var minutes uint32
|
||||
if s[0] != "1rtt" {
|
||||
t := strings.TrimSuffix(s[0], "min")
|
||||
if t == s[0] {
|
||||
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
|
||||
}
|
||||
i, err := strconv.Atoi(t)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
|
||||
}
|
||||
minutes = uint32(i)
|
||||
}
|
||||
var xor uint32
|
||||
switch s[1] {
|
||||
case "vless":
|
||||
case "aes128xor":
|
||||
xor = 1
|
||||
default:
|
||||
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
|
||||
}
|
||||
b, err := base64.RawURLEncoding.DecodeString(s[3])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
|
||||
}
|
||||
if len(b) == MLKEM768ClientLength {
|
||||
client := &ClientInstance{}
|
||||
if err = client.Init(b, xor, time.Duration(minutes)*time.Minute); err != nil {
|
||||
return nil, fmt.Errorf("failed to use mlkem768seed: %w", err)
|
||||
}
|
||||
return client, nil
|
||||
} else {
|
||||
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
|
||||
}
|
||||
|
||||
// NewServer new server from decryption string
|
||||
// maybe return a nil *ServerInstance without any error, that means don't need to decrypt
|
||||
func NewServer(decryption string) (*ServerInstance, error) {
|
||||
switch decryption {
|
||||
case "", "none": // We will not reject empty string like xray-core does, because we need to ensure compatibility
|
||||
return nil, nil
|
||||
}
|
||||
if s := strings.SplitN(decryption, "-", 4); len(s) == 4 && s[2] == "mlkem768seed" {
|
||||
var minutes uint32
|
||||
if s[0] != "1rtt" {
|
||||
t := strings.TrimSuffix(s[0], "min")
|
||||
if t == s[0] {
|
||||
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
|
||||
}
|
||||
i, err := strconv.Atoi(t)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
|
||||
}
|
||||
minutes = uint32(i)
|
||||
}
|
||||
var xor uint32
|
||||
switch s[1] {
|
||||
case "vless":
|
||||
case "aes128xor":
|
||||
xor = 1
|
||||
default:
|
||||
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
|
||||
}
|
||||
b, err := base64.RawURLEncoding.DecodeString(s[3])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
|
||||
}
|
||||
if len(b) == MLKEM768SeedLength {
|
||||
server := &ServerInstance{}
|
||||
if err = server.Init(b, xor, time.Duration(minutes)*time.Minute); err != nil {
|
||||
return nil, fmt.Errorf("failed to use mlkem768seed: %w", err)
|
||||
}
|
||||
return server, nil
|
||||
} else {
|
||||
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
|
||||
}
|
@@ -4,7 +4,6 @@ import (
|
||||
"bytes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
@@ -12,7 +11,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/utls/mlkem"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
)
|
||||
|
||||
type ServerSession struct {
|
||||
@@ -24,11 +22,12 @@ type ServerSession struct {
|
||||
|
||||
type ServerInstance struct {
|
||||
sync.RWMutex
|
||||
dKeyNfs *mlkem.DecapsulationKey768
|
||||
xor uint32
|
||||
minutes time.Duration
|
||||
sessions map[[21]byte]*ServerSession
|
||||
stop bool
|
||||
nfsDKey *mlkem.DecapsulationKey768
|
||||
nfsEKeyBytes []byte
|
||||
xor uint32
|
||||
minutes time.Duration
|
||||
sessions map[[21]byte]*ServerSession
|
||||
closed bool
|
||||
}
|
||||
|
||||
type ServerConn struct {
|
||||
@@ -44,9 +43,12 @@ type ServerConn struct {
|
||||
nonce []byte
|
||||
}
|
||||
|
||||
func (i *ServerInstance) Init(dKeyNfsData []byte, xor uint32, minutes time.Duration) (err error) {
|
||||
i.dKeyNfs, err = mlkem.NewDecapsulationKey768(dKeyNfsData)
|
||||
i.xor = xor
|
||||
func (i *ServerInstance) Init(nfsDKeySeed []byte, xor uint32, minutes time.Duration) (err error) {
|
||||
i.nfsDKey, err = mlkem.NewDecapsulationKey768(nfsDKeySeed)
|
||||
if xor > 0 {
|
||||
i.nfsEKeyBytes = i.nfsDKey.EncapsulationKey().Bytes()
|
||||
i.xor = xor
|
||||
}
|
||||
if minutes > 0 {
|
||||
i.minutes = minutes
|
||||
i.sessions = make(map[[21]byte]*ServerSession)
|
||||
@@ -55,12 +57,13 @@ func (i *ServerInstance) Init(dKeyNfsData []byte, xor uint32, minutes time.Durat
|
||||
time.Sleep(time.Minute)
|
||||
now := time.Now()
|
||||
i.Lock()
|
||||
if i.stop {
|
||||
if i.closed {
|
||||
i.Unlock()
|
||||
return
|
||||
}
|
||||
for index, session := range i.sessions {
|
||||
for ticket, session := range i.sessions {
|
||||
if now.After(session.expire) {
|
||||
delete(i.sessions, index)
|
||||
delete(i.sessions, ticket)
|
||||
}
|
||||
}
|
||||
i.Unlock()
|
||||
@@ -72,17 +75,17 @@ func (i *ServerInstance) Init(dKeyNfsData []byte, xor uint32, minutes time.Durat
|
||||
|
||||
func (i *ServerInstance) Close() (err error) {
|
||||
i.Lock()
|
||||
defer i.Unlock()
|
||||
i.stop = true
|
||||
i.closed = true
|
||||
i.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) {
|
||||
if i.dKeyNfs == nil {
|
||||
if i.nfsDKey == nil {
|
||||
return nil, errors.New("uninitialized")
|
||||
}
|
||||
if i.xor == 1 {
|
||||
conn = NewXorConn(conn, i.dKeyNfs.EncapsulationKey().Bytes())
|
||||
if i.xor > 0 {
|
||||
conn = NewXorConn(conn, i.nfsEKeyBytes)
|
||||
}
|
||||
c := &ServerConn{Conn: conn}
|
||||
|
||||
@@ -109,50 +112,49 @@ func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) {
|
||||
if _, err := io.ReadFull(c.Conn, peerHeader); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if l, _ := decodeHeader(peerHeader); l != 0 {
|
||||
c.Conn.Write(make([]byte, randBetween(100, 1000))) // make client do new handshake
|
||||
if l, _ := DecodeHeader(peerHeader); l != 0 {
|
||||
noise := make([]byte, randBetween(100, 1000))
|
||||
rand.Read(noise)
|
||||
c.Conn.Write(noise) // make client do new handshake
|
||||
return nil, errors.New("invalid ticket")
|
||||
}
|
||||
|
||||
peerClientHello := make([]byte, 1088+1184+1)
|
||||
peerClientHello := make([]byte, 1+1184+1088)
|
||||
copy(peerClientHello, peerTicketHello)
|
||||
copy(peerClientHello[53:], peerHeader)
|
||||
if _, err := io.ReadFull(c.Conn, peerClientHello[58:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
encapsulatedNfsKey := peerClientHello[:1088]
|
||||
eKeyPfsData := peerClientHello[1088:2272]
|
||||
c.cipher = peerClientHello[2272]
|
||||
if c.cipher != 0 && c.cipher != 1 {
|
||||
return nil, errors.New("invalid cipher")
|
||||
}
|
||||
c.cipher = peerClientHello[0]
|
||||
pfsEKeyBytes := peerClientHello[1:1185]
|
||||
encapsulatedNfsKey := peerClientHello[1185:2273]
|
||||
|
||||
nfsKey, err := i.dKeyNfs.Decapsulate(encapsulatedNfsKey)
|
||||
pfsEKey, err := mlkem.NewEncapsulationKey768(pfsEKeyBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
eKeyPfs, err := mlkem.NewEncapsulationKey768(eKeyPfsData)
|
||||
nfsKey, err := i.nfsDKey.Decapsulate(encapsulatedNfsKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pfsKey, encapsulatedPfsKey := eKeyPfs.Encapsulate()
|
||||
c.baseKey = append(nfsKey, pfsKey...)
|
||||
pfsKey, encapsulatedPfsKey := pfsEKey.Encapsulate()
|
||||
c.baseKey = append(pfsKey, nfsKey...)
|
||||
|
||||
authKey := make([]byte, 32)
|
||||
hkdf.New(sha256.New, c.baseKey, encapsulatedNfsKey, eKeyPfsData).Read(authKey)
|
||||
nonce := make([]byte, 12)
|
||||
c.ticket = newAead(c.cipher, authKey).Seal(nil, nonce, []byte("VLESS"), encapsulatedPfsKey)
|
||||
nonce := [12]byte{c.cipher}
|
||||
c.ticket = NewAead(c.cipher, c.baseKey, encapsulatedPfsKey, encapsulatedNfsKey).Seal(nil, nonce[:], []byte("VLESS"), pfsEKeyBytes)
|
||||
|
||||
padding := randBetween(100, 1000)
|
||||
paddingLen := randBetween(100, 1000)
|
||||
|
||||
serverHello := make([]byte, 1088+21+5+padding)
|
||||
serverHello := make([]byte, 1088+21+5+paddingLen)
|
||||
copy(serverHello, encapsulatedPfsKey)
|
||||
copy(serverHello[1088:], c.ticket)
|
||||
encodeHeader(serverHello[1109:], int(padding))
|
||||
EncodeHeader(serverHello[1109:], int(paddingLen))
|
||||
rand.Read(serverHello[1114:])
|
||||
|
||||
if _, err := c.Conn.Write(serverHello); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// we can send more padding if needed
|
||||
|
||||
if i.minutes > 0 {
|
||||
i.Lock()
|
||||
@@ -178,11 +180,11 @@ func (c *ServerConn) Read(b []byte) (int, error) {
|
||||
if _, err := io.ReadFull(c.Conn, peerHeader); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
peerPadding, _ := decodeHeader(peerHeader)
|
||||
if peerPadding == 0 {
|
||||
peerPaddingLen, _ := DecodeHeader(peerHeader)
|
||||
if peerPaddingLen == 0 {
|
||||
break
|
||||
}
|
||||
if _, err := io.ReadFull(c.Conn, make([]byte, peerPadding)); err != nil {
|
||||
if _, err := io.ReadFull(c.Conn, make([]byte, peerPaddingLen)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
@@ -199,9 +201,7 @@ func (c *ServerConn) Read(b []byte) (int, error) {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
peerKey := make([]byte, 32)
|
||||
hkdf.New(sha256.New, c.baseKey, c.peerRandom, c.ticket).Read(peerKey)
|
||||
c.peerAead = newAead(c.cipher, peerKey)
|
||||
c.peerAead = NewAead(c.cipher, c.baseKey, c.peerRandom, c.ticket)
|
||||
c.peerNonce = make([]byte, 12)
|
||||
}
|
||||
if len(c.peerCache) != 0 {
|
||||
@@ -212,7 +212,7 @@ func (c *ServerConn) Read(b []byte) (int, error) {
|
||||
if _, err := io.ReadFull(c.Conn, peerHeader); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
peerLength, err := decodeHeader(peerHeader) // 17~17000
|
||||
peerLength, err := DecodeHeader(peerHeader) // 17~17000
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -222,10 +222,17 @@ func (c *ServerConn) Read(b []byte) (int, error) {
|
||||
}
|
||||
dst := peerData[:peerLength-16]
|
||||
if len(dst) <= len(b) {
|
||||
dst = b[:len(dst)] // max=8192 is recommended for peer
|
||||
dst = b[:len(dst)] // avoids another copy()
|
||||
}
|
||||
var peerAead cipher.AEAD
|
||||
if bytes.Equal(c.peerNonce, MaxNonce) {
|
||||
peerAead = NewAead(c.cipher, c.baseKey, peerData, peerHeader)
|
||||
}
|
||||
_, err = c.peerAead.Open(dst[:0], c.peerNonce, peerData, peerHeader)
|
||||
increaseNonce(c.peerNonce)
|
||||
if peerAead != nil {
|
||||
c.peerAead = peerAead
|
||||
}
|
||||
IncreaseNonce(c.peerNonce)
|
||||
if err != nil {
|
||||
return 0, errors.New("error")
|
||||
}
|
||||
@@ -236,31 +243,39 @@ func (c *ServerConn) Read(b []byte) (int, error) {
|
||||
return len(dst), nil
|
||||
}
|
||||
|
||||
func (c *ServerConn) Write(b []byte) (int, error) { // after first Read()
|
||||
func (c *ServerConn) Write(b []byte) (int, error) {
|
||||
if len(b) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
var data []byte
|
||||
if c.aead == nil {
|
||||
if c.peerRandom == nil {
|
||||
return 0, errors.New("can not Write() first")
|
||||
for n := 0; n < len(b); {
|
||||
b := b[n:]
|
||||
if len(b) > 8192 {
|
||||
b = b[:8192] // for avoiding another copy() in client's Read()
|
||||
}
|
||||
n += len(b)
|
||||
if c.aead == nil {
|
||||
if c.peerRandom == nil {
|
||||
return 0, errors.New("empty c.peerRandom")
|
||||
}
|
||||
data = make([]byte, 32+5+len(b)+16)
|
||||
rand.Read(data[:32])
|
||||
c.aead = NewAead(c.cipher, c.baseKey, data[:32], c.peerRandom)
|
||||
c.nonce = make([]byte, 12)
|
||||
EncodeHeader(data[32:], len(b)+16)
|
||||
c.aead.Seal(data[:37], c.nonce, b, data[32:37])
|
||||
} else {
|
||||
data = make([]byte, 5+len(b)+16)
|
||||
EncodeHeader(data, len(b)+16)
|
||||
c.aead.Seal(data[:5], c.nonce, b, data[:5])
|
||||
if bytes.Equal(c.nonce, MaxNonce) {
|
||||
c.aead = NewAead(c.cipher, c.baseKey, data[5:], data[:5])
|
||||
}
|
||||
}
|
||||
IncreaseNonce(c.nonce)
|
||||
if _, err := c.Conn.Write(data); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
data = make([]byte, 32+5+len(b)+16)
|
||||
rand.Read(data[:32])
|
||||
key := make([]byte, 32)
|
||||
hkdf.New(sha256.New, c.baseKey, data[:32], c.peerRandom).Read(key)
|
||||
c.aead = newAead(c.cipher, key)
|
||||
c.nonce = make([]byte, 12)
|
||||
encodeHeader(data[32:], len(b)+16)
|
||||
c.aead.Seal(data[:37], c.nonce, b, data[32:37])
|
||||
} else {
|
||||
data = make([]byte, 5+len(b)+16)
|
||||
encodeHeader(data, len(b)+16)
|
||||
c.aead.Seal(data[:5], c.nonce, b, data[:5])
|
||||
}
|
||||
increaseNonce(c.nonce)
|
||||
if _, err := c.Conn.Write(data); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(b), nil
|
||||
}
|
||||
|
@@ -38,7 +38,7 @@ func (c *XorConn) Write(b []byte) (int, error) {
|
||||
return 0, err
|
||||
}
|
||||
if iv != nil {
|
||||
b = b[16:]
|
||||
b = b[16:] // for len(b)
|
||||
}
|
||||
return len(b), nil
|
||||
}
|
||||
|
54
shadowsocks-rust/Cargo.lock
generated
54
shadowsocks-rust/Cargo.lock
generated
@@ -579,18 +579,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.43"
|
||||
version = "4.5.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50fd97c9dc2399518aa331917ac6f274280ec5eb34e555dd291899745c48ec6f"
|
||||
checksum = "1c1f056bae57e3e54c3375c41ff79619ddd13460a17d7438712bd0d83fda4ff8"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.43"
|
||||
version = "4.5.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c35b5830294e1fa0462034af85cc95225a4cb07092c088c55bda3147cfcd8f65"
|
||||
checksum = "b3e7f4214277f3c7aa526a59dd3fbe306a370daee1f8b7b8c987069cd8e888a8"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -800,7 +800,7 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e"
|
||||
dependencies = [
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1004,7 +1004,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1403,7 +1403,7 @@ dependencies = [
|
||||
"ring",
|
||||
"rustls",
|
||||
"serde",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.14",
|
||||
"tinyvec",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
@@ -1431,7 +1431,7 @@ dependencies = [
|
||||
"rustls",
|
||||
"serde",
|
||||
"smallvec",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.14",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tracing",
|
||||
@@ -1942,7 +1942,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets 0.53.2",
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2449,7 +2449,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.14",
|
||||
"ucd-trie",
|
||||
]
|
||||
|
||||
@@ -2668,7 +2668,7 @@ dependencies = [
|
||||
"rustc-hash",
|
||||
"rustls",
|
||||
"socket2 0.5.10",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.14",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"web-time",
|
||||
@@ -2689,7 +2689,7 @@ dependencies = [
|
||||
"rustls",
|
||||
"rustls-pki-types",
|
||||
"slab",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.14",
|
||||
"tinyvec",
|
||||
"tracing",
|
||||
"web-time",
|
||||
@@ -2706,7 +2706,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"socket2 0.5.10",
|
||||
"tracing",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2806,7 +2806,7 @@ checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b"
|
||||
dependencies = [
|
||||
"getrandom 0.2.16",
|
||||
"libredox",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3021,7 +3021,7 @@ dependencies = [
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3320,7 +3320,7 @@ dependencies = [
|
||||
"shadowsocks-crypto",
|
||||
"socket2 0.6.0",
|
||||
"spin",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.14",
|
||||
"tokio",
|
||||
"tokio-tfo",
|
||||
"trait-variant",
|
||||
@@ -3387,7 +3387,7 @@ dependencies = [
|
||||
"snmalloc-rs",
|
||||
"sysexits",
|
||||
"tcmalloc",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.14",
|
||||
"time",
|
||||
"tokio",
|
||||
"tracing",
|
||||
@@ -3436,7 +3436,7 @@ dependencies = [
|
||||
"smoltcp",
|
||||
"socket2 0.6.0",
|
||||
"spin",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.14",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tokio-rustls",
|
||||
@@ -3703,7 +3703,7 @@ dependencies = [
|
||||
"getrandom 0.3.3",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3727,11 +3727,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.12"
|
||||
version = "2.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
||||
checksum = "0b0949c3a6c842cbde3f1686d6eea5a010516deb7085f79db747562d4102f41e"
|
||||
dependencies = [
|
||||
"thiserror-impl 2.0.12",
|
||||
"thiserror-impl 2.0.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3747,9 +3747,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.12"
|
||||
version = "2.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
||||
checksum = "cc5b44b4ab9c2fdd0e0512e6bece8388e214c0749f5862b114cc5b7a25daf227"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -4053,7 +4053,7 @@ dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"nix",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.14",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"windows-sys 0.60.2",
|
||||
@@ -4343,7 +4343,7 @@ version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4737,7 +4737,7 @@ dependencies = [
|
||||
"futures",
|
||||
"libloading",
|
||||
"log",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.14",
|
||||
"windows-sys 0.60.2",
|
||||
"winreg 0.55.0",
|
||||
]
|
||||
|
@@ -475,30 +475,30 @@ if singbox_tags:find("with_utls") then
|
||||
o:value("firefox")
|
||||
o:value("edge")
|
||||
o:value("safari")
|
||||
-- o:value("360")
|
||||
o:value("360")
|
||||
o:value("qq")
|
||||
o:value("ios")
|
||||
-- o:value("android")
|
||||
o:value("android")
|
||||
o:value("random")
|
||||
-- o:value("randomized")
|
||||
o:value("randomized")
|
||||
o.default = "chrome"
|
||||
o:depends({ [_n("tls")] = true, [_n("utls")] = true })
|
||||
o:depends({ [_n("utls")] = true })
|
||||
|
||||
-- [[ REALITY部分 ]] --
|
||||
o = s:option(Flag, _n("reality"), translate("REALITY"))
|
||||
o.default = 0
|
||||
o:depends({ [_n("protocol")] = "vless", [_n("utls")] = true })
|
||||
o:depends({ [_n("protocol")] = "vmess", [_n("utls")] = true })
|
||||
o:depends({ [_n("protocol")] = "shadowsocks", [_n("utls")] = true })
|
||||
o:depends({ [_n("protocol")] = "socks", [_n("utls")] = true })
|
||||
o:depends({ [_n("protocol")] = "trojan", [_n("utls")] = true })
|
||||
o:depends({ [_n("protocol")] = "anytls", [_n("utls")] = true })
|
||||
o:depends({ [_n("protocol")] = "vless", [_n("tls")] = true })
|
||||
o:depends({ [_n("protocol")] = "vmess", [_n("tls")] = true })
|
||||
o:depends({ [_n("protocol")] = "shadowsocks", [_n("tls")] = true })
|
||||
o:depends({ [_n("protocol")] = "socks", [_n("tls")] = true })
|
||||
o:depends({ [_n("protocol")] = "trojan", [_n("tls")] = true })
|
||||
o:depends({ [_n("protocol")] = "anytls", [_n("tls")] = true })
|
||||
|
||||
o = s:option(Value, _n("reality_publicKey"), translate("Public Key"))
|
||||
o:depends({ [_n("utls")] = true, [_n("reality")] = true })
|
||||
o:depends({ [_n("reality")] = true })
|
||||
|
||||
o = s:option(Value, _n("reality_shortId"), translate("Short Id"))
|
||||
o:depends({ [_n("utls")] = true, [_n("reality")] = true })
|
||||
o:depends({ [_n("reality")] = true })
|
||||
end
|
||||
|
||||
o = s:option(ListValue, _n("transport"), translate("Transport"))
|
||||
|
@@ -17,6 +17,9 @@ local xray_fragment = ucursor:get_all("shadowsocksr", "@global_xray_fragment[0]"
|
||||
local xray_noise = ucursor:get_all("shadowsocksr", "@xray_noise_packets[0]") or {}
|
||||
local outbound_settings = nil
|
||||
|
||||
local node_id = server_section
|
||||
local remarks = server.alias or ""
|
||||
|
||||
function vmess_vless()
|
||||
outbound_settings = {
|
||||
vnext = {
|
||||
@@ -238,7 +241,7 @@ end
|
||||
rawSettings = (server.transport == "raw" or server.transport == "tcp") and {
|
||||
-- tcp
|
||||
header = {
|
||||
type = server.tcp_guise or "none",
|
||||
type = server.tcp_guise,
|
||||
request = (server.tcp_guise == "http") and {
|
||||
-- request
|
||||
path = {server.http_path} or {"/"},
|
||||
@@ -317,7 +320,8 @@ end
|
||||
tcpMptcp = (server.mptcp == "1") and true or nil, -- MPTCP
|
||||
Penetrate = (server.mptcp == "1") and true or nil, -- Penetrate MPTCP
|
||||
tcpcongestion = server.custom_tcpcongestion, -- 连接服务器节点的 TCP 拥塞控制算法
|
||||
dialerProxy = (xray_fragment.fragment == "1" or xray_fragment.noise == "1") and "dialerproxy" or nil
|
||||
dialerProxy = (xray_fragment.fragment == "1" or xray_fragment.noise == "1") and
|
||||
((remarks ~= nil and remarks ~= "") and (node_id .. "." .. remarks) or node_id) or nil
|
||||
}
|
||||
} or nil,
|
||||
mux = (server.v2ray_protocol ~= "wireguard") and {
|
||||
@@ -334,7 +338,7 @@ end
|
||||
if xray_fragment.fragment ~= "0" or (xray_fragment.noise ~= "0" and xray_noise.enabled ~= "0") then
|
||||
table.insert(Xray.outbounds, {
|
||||
protocol = "freedom",
|
||||
tag = "dialerproxy",
|
||||
tag = (remarks ~= nil and remarks ~= "") and (node_id .. "." .. remarks) or node_id,
|
||||
settings = {
|
||||
domainStrategy = (xray_fragment.noise == "1" and xray_noise.enabled == "1") and xray_noise.domainStrategy,
|
||||
fragment = (xray_fragment.fragment == "1") and {
|
||||
|
@@ -142,6 +142,7 @@ public class CoreTypeItem
|
||||
public class TunModeItem
|
||||
{
|
||||
public bool EnableTun { get; set; }
|
||||
public bool AutoRoute { get; set; } = true;
|
||||
public bool StrictRoute { get; set; } = true;
|
||||
public string Stack { get; set; }
|
||||
public int Mtu { get; set; }
|
||||
|
@@ -1,17 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
@@ -26,36 +26,36 @@
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
@@ -1099,13 +1099,13 @@
|
||||
<value>Отмена тестирования...</value>
|
||||
</data>
|
||||
<data name="TransportRequestHostTip5" xml:space="preserve">
|
||||
<value>*gRPC Authority</value>
|
||||
<value>* gRPC Authority (HTTP/2 псевдозаголовок :authority)</value>
|
||||
</data>
|
||||
<data name="menuAddHttpServer" xml:space="preserve">
|
||||
<value>Добавить сервер [HTTP]</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableFragmentTips" xml:space="preserve">
|
||||
<value>which conflicts with the group previous proxy</value>
|
||||
<value>что конфликтует с предыдущим прокси группы</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
||||
<value>Включить фрагментацию (Fragment)</value>
|
||||
@@ -1318,13 +1318,13 @@
|
||||
<value>Пароль sudo системы</value>
|
||||
</data>
|
||||
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
|
||||
<value>The password will be validated via the command line. If a validation error causes the application to malfunction, please restart the application. The password will not be stored and must be entered again after each restart.</value>
|
||||
<value>Пароль sudo будет проверен в терминале. Если из-за ошибки проверки приложение начнёт работать некорректно, перезапустите его. Пароль не сохраняется — его нужно вводить после каждого перезапуска.</value>
|
||||
</data>
|
||||
<data name="TransportHeaderTypeTip5" xml:space="preserve">
|
||||
<value>*XHTTP-режим</value>
|
||||
</data>
|
||||
<data name="TransportExtraTip" xml:space="preserve">
|
||||
<value>Дополнительный XHTTP сырой JSON, формат: { XHTTPObject }</value>
|
||||
<value>Дополнительный „сырой“ JSON для XHTTP, формат: { XHTTP Object }</value>
|
||||
</data>
|
||||
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
|
||||
<value>Скрыть в трее при закрытии окна</value>
|
||||
@@ -1393,10 +1393,10 @@
|
||||
<value>URL для тестирования текущего соединения</value>
|
||||
</data>
|
||||
<data name="TbRuleOutboundTagTip" xml:space="preserve">
|
||||
<value>Can fill in the configuration remarks, please make sure it exist and are unique</value>
|
||||
<value>Можно указать название (Remarks) из конфигурации, убедитесь, что оно существует и уникально</value>
|
||||
</data>
|
||||
<data name="SudoIncorrectPasswordTip" xml:space="preserve">
|
||||
<value>Incorrect password, please try again.</value>
|
||||
<value>Неверный пароль, попробуйте ещё раз.</value>
|
||||
</data>
|
||||
<data name="TbMldsa65Verify" xml:space="preserve">
|
||||
<value>Mldsa65Verify</value>
|
||||
@@ -1405,99 +1405,99 @@
|
||||
<value>Добавить сервер [Anytls]</value>
|
||||
</data>
|
||||
<data name="TbRemoteDNS" xml:space="preserve">
|
||||
<value>Remote DNS</value>
|
||||
<value>Удалённый DNS</value>
|
||||
</data>
|
||||
<data name="TbDomesticDNS" xml:space="preserve">
|
||||
<value>Domestic DNS</value>
|
||||
<value>Внутренний DNS</value>
|
||||
</data>
|
||||
<data name="TbSBOutboundsResolverDNS" xml:space="preserve">
|
||||
<value>Outbound DNS Resolution (sing-box)</value>
|
||||
<value>Резолвер DNS для исходящих (sing-box)</value>
|
||||
</data>
|
||||
<data name="TbSBOutboundDomainResolve" xml:space="preserve">
|
||||
<value>Resolve Outbound Domains</value>
|
||||
<value>Разрешать домены для исходящих соединений</value>
|
||||
</data>
|
||||
<data name="TbSBDoHResolverServer" xml:space="preserve">
|
||||
<value>sing-box DoH Resolver Server</value>
|
||||
<value>Сервер DoH-резолвера (sing-box)</value>
|
||||
</data>
|
||||
<data name="TbSBFallbackDNSResolve" xml:space="preserve">
|
||||
<value>Fallback DNS Resolution, Suggest IP</value>
|
||||
<value>Резервное DNS-разрешение (рекомендуется указывать IP)</value>
|
||||
</data>
|
||||
<data name="TbXrayFreedomResolveStrategy" xml:space="preserve">
|
||||
<value>xray Freedom Resolution Strategy</value>
|
||||
<value>Стратегия резолвинга Freedom (Xray)</value>
|
||||
</data>
|
||||
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
|
||||
<value>sing-box Direct Resolution Strategy</value>
|
||||
<value>Стратегия прямого резолвинга (sing-box)</value>
|
||||
</data>
|
||||
<data name="TbSBRemoteResolveStrategy" xml:space="preserve">
|
||||
<value>sing-box Remote Resolution Strategy</value>
|
||||
<value>Стратегия удалённого резолвинга (sing-box)</value>
|
||||
</data>
|
||||
<data name="TbAddCommonDNSHosts" xml:space="preserve">
|
||||
<value>Add Common DNS Hosts</value>
|
||||
<value>Добавить стандартные записи hosts (DNS)</value>
|
||||
</data>
|
||||
<data name="TbSBDoHOverride" xml:space="preserve">
|
||||
<value>The sing-box DoH resolution server can be overwritten</value>
|
||||
<value>Сервер DoH-резолвера sing-box можно переопределить</value>
|
||||
</data>
|
||||
<data name="TbFakeIP" xml:space="preserve">
|
||||
<value>FakeIP</value>
|
||||
</data>
|
||||
<data name="TbBlockSVCBHTTPSQueries" xml:space="preserve">
|
||||
<value>Block SVCB and HTTPS Queries</value>
|
||||
<value>Блокировать DNS-запросы SVCB и HTTPS</value>
|
||||
</data>
|
||||
<data name="TbDNSHostsConfig" xml:space="preserve">
|
||||
<value>DNS Hosts: ("domain1 ip1 ip2" per line)</value>
|
||||
<value>DNS hosts: (каждая строка в формате "domain1 ip1 ip2")</value>
|
||||
</data>
|
||||
<data name="TbApplyProxyDomainsOnly" xml:space="preserve">
|
||||
<value>Apply to Proxy Domains Only</value>
|
||||
<value>Применять только к доменам через прокси</value>
|
||||
</data>
|
||||
<data name="ThBasicDNSSettings" xml:space="preserve">
|
||||
<value>Basic DNS Settings</value>
|
||||
<value>Базовые настройки DNS</value>
|
||||
</data>
|
||||
<data name="ThAdvancedDNSSettings" xml:space="preserve">
|
||||
<value>Advanced DNS Settings</value>
|
||||
<value>Расширенные настройки DNS</value>
|
||||
</data>
|
||||
<data name="TbValidateDirectExpectedIPs" xml:space="preserve">
|
||||
<value>Validate Regional Domain IPs</value>
|
||||
<value>Проверять IP-адреса региональных доменов</value>
|
||||
</data>
|
||||
<data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve">
|
||||
<value>When configured, validates IPs returned for regional domains (e.g., geosite:cn), returning only expected IPs</value>
|
||||
<value>При включении проверяет IP-адреса, возвращаемые для региональных доменов (например, geosite:cn), и оставляет только ожидаемые IP-адреса</value>
|
||||
</data>
|
||||
<data name="TbCustomDNSEnable" xml:space="preserve">
|
||||
<value>Enable Custom DNS</value>
|
||||
<value>Включить пользовательский DNS</value>
|
||||
</data>
|
||||
<data name="TbCustomDNSEnabledPageInvalid" xml:space="preserve">
|
||||
<value>Custom DNS Enabled, This Page's Settings Invalid</value>
|
||||
<value>Включён пользовательский DNS — настройки на этой странице не применяются</value>
|
||||
</data>
|
||||
<data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve">
|
||||
<value>Prevent domain-based routing rules from failing</value>
|
||||
<value>Предотвращает сбои доменных правил маршрутизации</value>
|
||||
</data>
|
||||
<data name="FillCorrectConfigTemplateText" xml:space="preserve">
|
||||
<value>Please fill in the correct config template</value>
|
||||
<value>Пожалуйста, заполните корректный шаблон конфигурации</value>
|
||||
</data>
|
||||
<data name="menuFullConfigTemplate" xml:space="preserve">
|
||||
<value>Full Config Template Setting</value>
|
||||
<value>Настройка полного шаблона конфигурации</value>
|
||||
</data>
|
||||
<data name="TbFullConfigTemplateEnable" xml:space="preserve">
|
||||
<value>Enable Full Config Template</value>
|
||||
<value>Включить полный шаблон конфигурации</value>
|
||||
</data>
|
||||
<data name="TbRayFullConfigTemplate" xml:space="preserve">
|
||||
<value>v2ray Full Config Template</value>
|
||||
<value>Полный шаблон конфигурации v2ray</value>
|
||||
</data>
|
||||
<data name="TbRayFullConfigTemplateDesc" xml:space="preserve">
|
||||
<value>Add Outbound Config Only, routing.balancers and routing.rules.outboundTag, Click to view the document</value>
|
||||
<value>Добавляет только конфигурацию исходящих (outbound), а также routing.balancers и routing.rules.outboundTag. Нажмите, чтобы открыть документ</value>
|
||||
</data>
|
||||
<data name="TbAddProxyProtocolOutboundOnly" xml:space="preserve">
|
||||
<value>Do Not Add Non-Proxy Protocol Outbound</value>
|
||||
<value>Не добавлять исходящие для непрокси-протоколов</value>
|
||||
</data>
|
||||
<data name="TbSetUpstreamProxyDetour" xml:space="preserve">
|
||||
<value>Set Upstream Proxy Tag</value>
|
||||
<value>Задать тег верхнего прокси (upstream)</value>
|
||||
</data>
|
||||
<data name="TbSBFullConfigTemplate" xml:space="preserve">
|
||||
<value>sing-box Full Config Template</value>
|
||||
<value>Полный шаблон конфигурации sing-box</value>
|
||||
</data>
|
||||
<data name="TbSBFullConfigTemplateDesc" xml:space="preserve">
|
||||
<value>Add Outbound and Endpoint Config Only, Click to view the document</value>
|
||||
<value>Добавляет только конфигурацию Outbound и Endpoint. Нажмите, чтобы открыть документ</value>
|
||||
</data>
|
||||
<data name="TbFullConfigTemplateDesc" xml:space="preserve">
|
||||
<value>This feature is intended for advanced users and those with special requirements. Once enabled, it will ignore the Core's basic settings, DNS settings, and routing settings. You must ensure that the system proxy port, traffic statistics, and other related configurations are set correctly — everything will be configured by you.</value>
|
||||
<value>Эта функция предназначена для продвинутых пользователей и особых случаев. После включения игнорируются базовые настройки ядра, DNS и маршрутизации. Вы должны самостоятельно корректно задать порт системного прокси, учёт трафика и другие связанные параметры — всё настраивается вручную.</value>
|
||||
</data>
|
||||
</root>
|
||||
</root>
|
||||
|
@@ -80,8 +80,7 @@ public class CoreConfigSingboxService
|
||||
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
||||
ret.Success = true;
|
||||
|
||||
var fullConfigTemplate = await AppHandler.Instance.GetFullConfigTemplateItem(ECoreType.sing_box);
|
||||
ret.Data = await ApplyFullConfigTemplate(fullConfigTemplate, singboxConfig);
|
||||
ret.Data = await ApplyFullConfigTemplate(singboxConfig);
|
||||
return ret;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -434,8 +433,7 @@ public class CoreConfigSingboxService
|
||||
|
||||
ret.Success = true;
|
||||
|
||||
var fullConfigTemplate = await AppHandler.Instance.GetFullConfigTemplateItem(ECoreType.sing_box);
|
||||
ret.Data = await ApplyFullConfigTemplate(fullConfigTemplate, singboxConfig);
|
||||
ret.Data = await ApplyFullConfigTemplate(singboxConfig);
|
||||
return ret;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -628,6 +626,7 @@ public class CoreConfigSingboxService
|
||||
var tunInbound = JsonUtils.Deserialize<Inbound4Sbox>(EmbedUtils.GetEmbedText(Global.TunSingboxInboundFileName)) ?? new Inbound4Sbox { };
|
||||
tunInbound.interface_name = Utils.IsOSX() ? $"utun{new Random().Next(99)}" : "singbox_tun";
|
||||
tunInbound.mtu = _config.TunModeItem.Mtu;
|
||||
tunInbound.auto_route = _config.TunModeItem.AutoRoute;
|
||||
tunInbound.strict_route = _config.TunModeItem.StrictRoute;
|
||||
tunInbound.stack = _config.TunModeItem.Stack;
|
||||
if (_config.TunModeItem.EnableIPv6Address == false)
|
||||
@@ -2218,15 +2217,16 @@ public class CoreConfigSingboxService
|
||||
return 0;
|
||||
}
|
||||
|
||||
private async Task<string> ApplyFullConfigTemplate(FullConfigTemplateItem fullConfigTemplate, SingboxConfig singboxConfig)
|
||||
private async Task<string> ApplyFullConfigTemplate(SingboxConfig singboxConfig)
|
||||
{
|
||||
var fullConfigTemplateItem = fullConfigTemplate.Config;
|
||||
if (_config.TunModeItem.EnableTun)
|
||||
var fullConfigTemplate = await AppHandler.Instance.GetFullConfigTemplateItem(ECoreType.sing_box);
|
||||
if (fullConfigTemplate == null || !fullConfigTemplate.Enabled)
|
||||
{
|
||||
fullConfigTemplateItem = fullConfigTemplate.TunConfig;
|
||||
return JsonUtils.Serialize(singboxConfig);
|
||||
}
|
||||
|
||||
if (!fullConfigTemplate.Enabled || fullConfigTemplateItem.IsNullOrEmpty())
|
||||
var fullConfigTemplateItem = _config.TunModeItem.EnableTun ? fullConfigTemplate.TunConfig : fullConfigTemplate.Config;
|
||||
if (fullConfigTemplateItem.IsNullOrEmpty())
|
||||
{
|
||||
return JsonUtils.Serialize(singboxConfig);
|
||||
}
|
||||
|
@@ -3,7 +3,6 @@ using System.Net.NetworkInformation;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization;
|
||||
using ServiceLib.Models;
|
||||
|
||||
namespace ServiceLib.Services.CoreConfig;
|
||||
|
||||
@@ -69,9 +68,7 @@ public class CoreConfigV2rayService
|
||||
|
||||
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
||||
ret.Success = true;
|
||||
|
||||
var fullConfigTemplate = await AppHandler.Instance.GetFullConfigTemplateItem(ECoreType.Xray);
|
||||
ret.Data = await ApplyFullConfigTemplate(fullConfigTemplate, v2rayConfig);
|
||||
ret.Data = await ApplyFullConfigTemplate(v2rayConfig);
|
||||
return ret;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -201,8 +198,7 @@ public class CoreConfigV2rayService
|
||||
|
||||
ret.Success = true;
|
||||
|
||||
var fullConfigTemplate = await AppHandler.Instance.GetFullConfigTemplateItem(ECoreType.Xray);
|
||||
ret.Data = await ApplyFullConfigTemplate(fullConfigTemplate, v2rayConfig, true);
|
||||
ret.Data = await ApplyFullConfigTemplate(v2rayConfig, true);
|
||||
return ret;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -1844,9 +1840,10 @@ public class CoreConfigV2rayService
|
||||
return await Task.FromResult(0);
|
||||
}
|
||||
|
||||
private async Task<string> ApplyFullConfigTemplate(FullConfigTemplateItem fullConfigTemplate, V2rayConfig v2rayConfig, bool handleBalancerAndRules = false)
|
||||
private async Task<string> ApplyFullConfigTemplate(V2rayConfig v2rayConfig, bool handleBalancerAndRules = false)
|
||||
{
|
||||
if (!fullConfigTemplate.Enabled || fullConfigTemplate.Config.IsNullOrEmpty())
|
||||
var fullConfigTemplate = await AppHandler.Instance.GetFullConfigTemplateItem(ECoreType.Xray);
|
||||
if (fullConfigTemplate == null || !fullConfigTemplate.Enabled || fullConfigTemplate.Config.IsNullOrEmpty())
|
||||
{
|
||||
return JsonUtils.Serialize(v2rayConfig);
|
||||
}
|
||||
@@ -1918,6 +1915,7 @@ public class CoreConfigV2rayService
|
||||
}
|
||||
customOutboundsNode.Add(JsonUtils.DeepCopy(outbound));
|
||||
}
|
||||
fullConfigTemplateNode["outbounds"] = customOutboundsNode;
|
||||
|
||||
return await Task.FromResult(JsonUtils.Serialize(fullConfigTemplateNode));
|
||||
}
|
||||
|
@@ -84,6 +84,7 @@ public class OptionSettingViewModel : MyReactiveObject
|
||||
|
||||
#region Tun mode
|
||||
|
||||
[Reactive] public bool TunAutoRoute { get; set; }
|
||||
[Reactive] public bool TunStrictRoute { get; set; }
|
||||
[Reactive] public string TunStack { get; set; }
|
||||
[Reactive] public int TunMtu { get; set; }
|
||||
@@ -201,6 +202,7 @@ public class OptionSettingViewModel : MyReactiveObject
|
||||
|
||||
#region Tun mode
|
||||
|
||||
TunAutoRoute = _config.TunModeItem.AutoRoute;
|
||||
TunStrictRoute = _config.TunModeItem.StrictRoute;
|
||||
TunStack = _config.TunModeItem.Stack;
|
||||
TunMtu = _config.TunModeItem.Mtu;
|
||||
@@ -354,6 +356,7 @@ public class OptionSettingViewModel : MyReactiveObject
|
||||
_config.SystemProxyItem.SystemProxyAdvancedProtocol = systemProxyAdvancedProtocol;
|
||||
|
||||
//tun mode
|
||||
_config.TunModeItem.AutoRoute = TunAutoRoute;
|
||||
_config.TunModeItem.StrictRoute = TunStrictRoute;
|
||||
_config.TunModeItem.Stack = TunStack;
|
||||
_config.TunModeItem.Mtu = TunMtu;
|
||||
|
@@ -729,71 +729,84 @@
|
||||
Margin="{StaticResource Margin4}"
|
||||
ColumnDefinitions="Auto,Auto,Auto"
|
||||
DockPanel.Dock="Top"
|
||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
|
||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="Auto Route" />
|
||||
<ToggleSwitch
|
||||
x:Name="togAutoRoute"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="Strict Route" />
|
||||
<ToggleSwitch
|
||||
x:Name="togStrictRoute"
|
||||
Grid.Row="2"
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="Stack" />
|
||||
<ComboBox
|
||||
x:Name="cmbStack"
|
||||
Grid.Row="3"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Row="5"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="Mtu" />
|
||||
<ComboBox
|
||||
x:Name="cmbMtu"
|
||||
Grid.Row="4"
|
||||
Grid.Row="5"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="5"
|
||||
Grid.Row="6"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsEnableExInbound}" />
|
||||
<ToggleSwitch
|
||||
x:Name="togEnableExInbound"
|
||||
Grid.Row="5"
|
||||
Grid.Row="6"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="6"
|
||||
Grid.Row="7"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsEnableIPv6Address}" />
|
||||
<ToggleSwitch
|
||||
x:Name="togEnableIPv6Address"
|
||||
Grid.Row="6"
|
||||
Grid.Row="7"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
|
@@ -105,6 +105,7 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
|
||||
this.Bind(ViewModel, vm => vm.systemProxyAdvancedProtocol, v => v.cmbsystemProxyAdvancedProtocol.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.systemProxyExceptions, v => v.txtsystemProxyExceptions.Text).DisposeWith(disposables);
|
||||
|
||||
this.Bind(ViewModel, vm => vm.TunAutoRoute, v => v.togAutoRoute.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.TunStrictRoute, v => v.togStrictRoute.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.TunStack, v => v.cmbStack.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.TunMtu, v => v.cmbMtu.SelectedValue).DisposeWith(disposables);
|
||||
|
@@ -1023,6 +1023,7 @@
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
@@ -1036,9 +1037,9 @@
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="Strict Route" />
|
||||
Text="Auto Route" />
|
||||
<ToggleButton
|
||||
x:Name="togStrictRoute"
|
||||
x:Name="togAutoRoute"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin8}"
|
||||
@@ -1050,10 +1051,24 @@
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="Strict Route" />
|
||||
<ToggleButton
|
||||
x:Name="togStrictRoute"
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin8}"
|
||||
HorizontalAlignment="Left" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="Stack" />
|
||||
<ComboBox
|
||||
x:Name="cmbStack"
|
||||
Grid.Row="3"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
@@ -1061,7 +1076,7 @@
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Row="5"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
@@ -1069,7 +1084,7 @@
|
||||
Text="Mtu" />
|
||||
<ComboBox
|
||||
x:Name="cmbMtu"
|
||||
Grid.Row="4"
|
||||
Grid.Row="5"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
@@ -1077,7 +1092,7 @@
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="5"
|
||||
Grid.Row="6"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
@@ -1085,13 +1100,13 @@
|
||||
Text="{x:Static resx:ResUI.TbSettingsEnableExInbound}" />
|
||||
<ToggleButton
|
||||
x:Name="togEnableExInbound"
|
||||
Grid.Row="5"
|
||||
Grid.Row="6"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin8}"
|
||||
HorizontalAlignment="Left" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="6"
|
||||
Grid.Row="7"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
@@ -1099,7 +1114,7 @@
|
||||
Text="{x:Static resx:ResUI.TbSettingsEnableIPv6Address}" />
|
||||
<ToggleButton
|
||||
x:Name="togEnableIPv6Address"
|
||||
Grid.Row="6"
|
||||
Grid.Row="7"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin8}"
|
||||
HorizontalAlignment="Left" />
|
||||
@@ -1231,3 +1246,4 @@
|
||||
</TabControl>
|
||||
</DockPanel>
|
||||
</base:WindowBase>
|
||||
|
||||
|
@@ -117,6 +117,7 @@ public partial class OptionSettingWindow
|
||||
this.Bind(ViewModel, vm => vm.systemProxyAdvancedProtocol, v => v.cmbsystemProxyAdvancedProtocol.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.systemProxyExceptions, v => v.txtsystemProxyExceptions.Text).DisposeWith(disposables);
|
||||
|
||||
this.Bind(ViewModel, vm => vm.TunAutoRoute, v => v.togAutoRoute.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.TunStrictRoute, v => v.togStrictRoute.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.TunStack, v => v.cmbStack.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.TunMtu, v => v.cmbMtu.Text).DisposeWith(disposables);
|
||||
|
@@ -12,8 +12,8 @@ android {
|
||||
applicationId = "com.v2ray.ang"
|
||||
minSdk = 21
|
||||
targetSdk = 35
|
||||
versionCode = 666
|
||||
versionName = "1.10.16"
|
||||
versionCode = 667
|
||||
versionName = "1.10.17"
|
||||
multiDexEnabled = true
|
||||
|
||||
val abiFilterList = (properties["ABI_FILTERS"] as? String)?.split(';')
|
||||
|
@@ -27,6 +27,7 @@ object AppConfig {
|
||||
const val PREF_VPN_DNS = "pref_vpn_dns"
|
||||
const val PREF_VPN_BYPASS_LAN = "pref_vpn_bypass_lan"
|
||||
const val PREF_VPN_INTERFACE_ADDRESS_CONFIG_INDEX = "pref_vpn_interface_address_config_index"
|
||||
const val PREF_VPN_MTU = "pref_vpn_mtu"
|
||||
const val PREF_ROUTING_DOMAIN_STRATEGY = "pref_routing_domain_strategy"
|
||||
const val PREF_ROUTING_RULESET = "pref_routing_ruleset"
|
||||
const val PREF_MUX_ENABLED = "pref_mux_enabled"
|
||||
|
@@ -370,4 +370,11 @@ object SettingsManager {
|
||||
val selectedIndex = MmkvManager.decodeSettingsString(AppConfig.PREF_VPN_INTERFACE_ADDRESS_CONFIG_INDEX, "0")?.toInt()
|
||||
return VpnInterfaceAddressConfig.getConfigByIndex(selectedIndex ?: 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the VPN MTU from settings, defaulting to AppConfig.VPN_MTU.
|
||||
*/
|
||||
fun getVpnMtu(): Int {
|
||||
return Utils.parseInt(MmkvManager.decodeSettingsString(AppConfig.PREF_VPN_MTU), AppConfig.VPN_MTU)
|
||||
}
|
||||
}
|
||||
|
@@ -4,7 +4,6 @@ import android.content.Context
|
||||
import android.os.ParcelFileDescriptor
|
||||
import android.util.Log
|
||||
import com.v2ray.ang.AppConfig
|
||||
import com.v2ray.ang.AppConfig.VPN_MTU
|
||||
import com.v2ray.ang.handler.MmkvManager
|
||||
import com.v2ray.ang.handler.SettingsManager
|
||||
import java.io.File
|
||||
@@ -60,7 +59,7 @@ class TProxyService(
|
||||
val vpnConfig = SettingsManager.getCurrentVpnInterfaceAddressConfig()
|
||||
return buildString {
|
||||
appendLine("tunnel:")
|
||||
appendLine(" mtu: $VPN_MTU")
|
||||
appendLine(" mtu: ${SettingsManager.getVpnMtu()}")
|
||||
appendLine(" ipv4: ${vpnConfig.ipv4Client}")
|
||||
|
||||
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_PREFER_IPV6) == true) {
|
||||
|
@@ -6,7 +6,6 @@ import android.net.LocalSocketAddress
|
||||
import android.os.ParcelFileDescriptor
|
||||
import android.util.Log
|
||||
import com.v2ray.ang.AppConfig
|
||||
import com.v2ray.ang.AppConfig.VPN_MTU
|
||||
import com.v2ray.ang.handler.MmkvManager
|
||||
import com.v2ray.ang.handler.SettingsManager
|
||||
import com.v2ray.ang.util.Utils
|
||||
@@ -42,7 +41,7 @@ class Tun2SocksService(
|
||||
"--netif-ipaddr", vpnConfig.ipv4Router,
|
||||
"--netif-netmask", "255.255.255.252",
|
||||
"--socks-server-addr", "${AppConfig.LOOPBACK}:${socksPort}",
|
||||
"--tunmtu", VPN_MTU.toString(),
|
||||
"--tunmtu", SettingsManager.getVpnMtu().toString(),
|
||||
"--sock-path", "sock_path",
|
||||
"--enable-udprelay",
|
||||
"--loglevel", "notice"
|
||||
|
@@ -17,7 +17,6 @@ import android.util.Log
|
||||
import androidx.annotation.RequiresApi
|
||||
import com.v2ray.ang.AppConfig
|
||||
import com.v2ray.ang.AppConfig.LOOPBACK
|
||||
import com.v2ray.ang.AppConfig.VPN_MTU
|
||||
import com.v2ray.ang.BuildConfig
|
||||
import com.v2ray.ang.handler.MmkvManager
|
||||
import com.v2ray.ang.handler.NotificationManager
|
||||
@@ -185,7 +184,7 @@ class V2RayVpnService : VpnService(), ServiceControl {
|
||||
val bypassLan = SettingsManager.routingRulesetsBypassLan()
|
||||
|
||||
// Configure IPv4 settings
|
||||
builder.setMtu(VPN_MTU)
|
||||
builder.setMtu(SettingsManager.getVpnMtu())
|
||||
builder.addAddress(vpnConfig.ipv4Client, 30)
|
||||
|
||||
// Configure routing rules
|
||||
|
@@ -45,6 +45,7 @@ class SettingsActivity : BaseActivity() {
|
||||
private val vpnDns by lazy { findPreference<EditTextPreference>(AppConfig.PREF_VPN_DNS) }
|
||||
private val vpnBypassLan by lazy { findPreference<ListPreference>(AppConfig.PREF_VPN_BYPASS_LAN) }
|
||||
private val vpnInterfaceAddress by lazy { findPreference<ListPreference>(AppConfig.PREF_VPN_INTERFACE_ADDRESS_CONFIG_INDEX) }
|
||||
private val vpnMtu by lazy { findPreference<EditTextPreference>(AppConfig.PREF_VPN_MTU) }
|
||||
|
||||
private val mux by lazy { findPreference<CheckBoxPreference>(AppConfig.PREF_MUX_ENABLED) }
|
||||
private val muxConcurrency by lazy { findPreference<EditTextPreference>(AppConfig.PREF_MUX_CONCURRENCY) }
|
||||
@@ -93,6 +94,12 @@ class SettingsActivity : BaseActivity() {
|
||||
true
|
||||
}
|
||||
|
||||
vpnMtu?.setOnPreferenceChangeListener { _, any ->
|
||||
val nval = any as String
|
||||
vpnMtu?.summary = if (TextUtils.isEmpty(nval)) AppConfig.VPN_MTU.toString() else nval
|
||||
true
|
||||
}
|
||||
|
||||
mux?.setOnPreferenceChangeListener { _, newValue ->
|
||||
updateMux(newValue as Boolean)
|
||||
true
|
||||
@@ -196,6 +203,7 @@ class SettingsActivity : BaseActivity() {
|
||||
appendHttpProxy?.isChecked = MmkvManager.decodeSettingsBool(AppConfig.PREF_APPEND_HTTP_PROXY, false)
|
||||
localDnsPort?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_LOCAL_DNS_PORT, AppConfig.PORT_LOCAL_DNS)
|
||||
vpnDns?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_VPN_DNS, AppConfig.DNS_VPN)
|
||||
vpnMtu?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_VPN_MTU, AppConfig.VPN_MTU.toString())
|
||||
|
||||
updateMux(MmkvManager.decodeSettingsBool(AppConfig.PREF_MUX_ENABLED, false))
|
||||
mux?.isChecked = MmkvManager.decodeSettingsBool(AppConfig.PREF_MUX_ENABLED, false)
|
||||
@@ -229,6 +237,7 @@ class SettingsActivity : BaseActivity() {
|
||||
listOf(
|
||||
localDnsPort,
|
||||
vpnDns,
|
||||
vpnMtu,
|
||||
muxConcurrency,
|
||||
muxXudpConcurrency,
|
||||
fragmentLength,
|
||||
@@ -298,6 +307,7 @@ class SettingsActivity : BaseActivity() {
|
||||
vpnDns?.isEnabled = vpn
|
||||
vpnBypassLan?.isEnabled = vpn
|
||||
vpnInterfaceAddress?.isEnabled = vpn
|
||||
vpnMtu?.isEnabled = vpn
|
||||
if (vpn) {
|
||||
updateLocalDns(
|
||||
MmkvManager.decodeSettingsBool(
|
||||
|
@@ -81,6 +81,7 @@
|
||||
<string name="server_lab_preshared_key">PreSharedKey(optional)</string>
|
||||
<string name="server_lab_short_id" translatable="false">المعرّف القصير</string>
|
||||
<string name="server_lab_spider_x" translatable="false">SpiderX</string>
|
||||
<string name="server_lab_mldsa65_verify">Mldsa65Verify</string>
|
||||
<string name="server_lab_secret_key" translatable="false">المفتاح السري</string>
|
||||
<string name="server_lab_reserved">محجوز (اختياري)</string>
|
||||
<string name="server_lab_local_address">العنوان المحلي (اختياري IPv4/IPv6، مفصولة بفواصل)</string>
|
||||
@@ -183,6 +184,7 @@
|
||||
<string name="title_pref_vpn_bypass_lan">Does VPN bypass LAN</string>
|
||||
|
||||
<string name="title_pref_vpn_interface_address">VPN Interface Address</string>
|
||||
<string name="title_pref_vpn_mtu">VPN MTU (default 1500)</string>
|
||||
|
||||
<string name="title_pref_domestic_dns">DNS المحلي (اختياري)</string>
|
||||
<string name="summary_pref_domestic_dns">DNS</string>
|
||||
|
@@ -184,6 +184,7 @@
|
||||
<string name="title_pref_vpn_bypass_lan">Does VPN bypass LAN</string>
|
||||
|
||||
<string name="title_pref_vpn_interface_address">VPN Interface Address</string>
|
||||
<string name="title_pref_vpn_mtu">VPN MTU (default 1500)</string>
|
||||
|
||||
<string name="title_pref_domestic_dns">ঘরোয়া DNS (ঐচ্ছিক)</string>
|
||||
<string name="summary_pref_domestic_dns">DNS</string>
|
||||
|
@@ -184,6 +184,7 @@
|
||||
<string name="title_pref_vpn_bypass_lan">VPN ز شبکه مهلی اگوڌرته؟</string>
|
||||
|
||||
<string name="title_pref_vpn_interface_address">نشۊوی رابت VPN</string>
|
||||
<string name="title_pref_vpn_mtu">VPN MTU (default 1500)</string>
|
||||
|
||||
<string name="title_pref_domestic_dns">DNS منی (اختیاری)</string>
|
||||
<string name="summary_pref_domestic_dns">DNS</string>
|
||||
|
@@ -182,6 +182,7 @@
|
||||
<string name="title_pref_vpn_bypass_lan">آیا VPN از شبکه محلی عبور می کند؟</string>
|
||||
|
||||
<string name="title_pref_vpn_interface_address">آدرس واسط VPN</string>
|
||||
<string name="title_pref_vpn_mtu">VPN MTU (default 1500)</string>
|
||||
|
||||
<string name="title_pref_domestic_dns">DNS داخلی (اختیاری)</string>
|
||||
<string name="summary_pref_domestic_dns">DNS</string>
|
||||
|
@@ -183,6 +183,7 @@
|
||||
<string name="title_pref_vpn_bypass_lan">VPN обходит LAN</string>
|
||||
|
||||
<string name="title_pref_vpn_interface_address">Адрес интерфейса VPN</string>
|
||||
<string name="title_pref_vpn_mtu">VPN MTU (default 1500)</string>
|
||||
|
||||
<string name="title_pref_domestic_dns">Внутренняя DNS (необязательно)</string>
|
||||
<string name="summary_pref_domestic_dns">DNS</string>
|
||||
|
@@ -184,6 +184,7 @@
|
||||
<string name="title_pref_vpn_bypass_lan">Does VPN bypass LAN</string>
|
||||
|
||||
<string name="title_pref_vpn_interface_address">VPN Interface Address</string>
|
||||
<string name="title_pref_vpn_mtu">VPN MTU (default 1500)</string>
|
||||
|
||||
<string name="title_pref_domestic_dns">DNS nội địa (Không bắt buộc)</string>
|
||||
<string name="summary_pref_domestic_dns">DNS</string>
|
||||
|
@@ -181,6 +181,7 @@
|
||||
<string name="title_pref_vpn_bypass_lan">VPN 是否绕过局域网</string>
|
||||
|
||||
<string name="title_pref_vpn_interface_address">VPN 接口地址</string>
|
||||
<string name="title_pref_vpn_mtu">VPN MTU (默认 1500)</string>
|
||||
|
||||
<string name="title_pref_domestic_dns">境内 DNS (可选)</string>
|
||||
<string name="summary_pref_domestic_dns">DNS</string>
|
||||
|
@@ -183,6 +183,7 @@
|
||||
<string name="title_pref_vpn_bypass_lan">VPN 是否繞過區域網</string>
|
||||
|
||||
<string name="title_pref_vpn_interface_address">VPN 介面位址</string>
|
||||
<string name="title_pref_vpn_mtu">VPN MTU (預設 1500)</string>
|
||||
|
||||
<string name="summary_pref_domestic_dns">DNS</string>
|
||||
<string name="title_pref_domestic_dns">境内 DNS (可选)</string>
|
||||
|
@@ -185,6 +185,7 @@
|
||||
<string name="title_pref_vpn_bypass_lan">Does VPN bypass LAN</string>
|
||||
|
||||
<string name="title_pref_vpn_interface_address">VPN Interface Address</string>
|
||||
<string name="title_pref_vpn_mtu">VPN MTU (default 1500)</string>
|
||||
|
||||
<string name="title_pref_domestic_dns">Domestic DNS (Optional)</string>
|
||||
<string name="summary_pref_domestic_dns">DNS</string>
|
||||
|
@@ -72,6 +72,12 @@
|
||||
android:summary="%s"
|
||||
android:title="@string/title_pref_vpn_interface_address" />
|
||||
|
||||
<EditTextPreference
|
||||
android:inputType="number"
|
||||
android:key="pref_vpn_mtu"
|
||||
android:summary="1500"
|
||||
android:title="@string/title_pref_vpn_mtu" />
|
||||
|
||||
<CheckBoxPreference
|
||||
android:key="pref_use_hev_tunnel"
|
||||
android:summary="@string/summary_pref_use_hev_tunnel"
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
from .common import PostProcessor
|
||||
from ..utils import (
|
||||
@@ -54,8 +55,8 @@ class XAttrMetadataPP(PostProcessor):
|
||||
if infoname == 'upload_date':
|
||||
value = hyphenate_date(value)
|
||||
elif xattrname == 'com.apple.metadata:kMDItemWhereFroms':
|
||||
# NTFS ADS doesn't support colons in names
|
||||
if os.name == 'nt':
|
||||
# Colon in xattr name throws errors on Windows/NTFS and Linux
|
||||
if sys.platform != 'darwin':
|
||||
continue
|
||||
value = self.APPLE_PLIST_TEMPLATE % value
|
||||
write_xattr(info['filepath'], xattrname, value.encode())
|
||||
|
Reference in New Issue
Block a user