diff --git a/.github/update.log b/.github/update.log index 2847358d01..445c4cf68e 100644 --- a/.github/update.log +++ b/.github/update.log @@ -1086,3 +1086,4 @@ Update On Thu Aug 7 20:45:01 CEST 2025 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 diff --git a/clash-meta/adapter/outbound/vless.go b/clash-meta/adapter/outbound/vless.go index bacc41fc6f..de3c24ab75 100644 --- a/clash-meta/adapter/outbound/vless.go +++ b/clash-meta/adapter/outbound/vless.go @@ -456,7 +456,7 @@ func NewVless(option VlessOption) (*Vless, error) { option: &option, } - if s := strings.Split(option.Encryption, "-mlkem768client-"); len(s) == 2 { + 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") @@ -470,14 +470,22 @@ func NewVless(option VlessOption) (*Vless, error) { } 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[1]) + b, err = base64.RawURLEncoding.DecodeString(s[3]) if err != nil { return nil, fmt.Errorf("invaild vless encryption value: %s", option.Encryption) } - if len(b) == 1184 { + if len(b) == encryption.MLKEM768ClientLength { v.encryption = &encryption.ClientInstance{} - if err = v.encryption.Init(b, time.Duration(minutes)*time.Minute); err != nil { + 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 { diff --git a/clash-meta/common/convert/converter.go b/clash-meta/common/convert/converter.go index a4f89f37cc..c69a258a4f 100644 --- a/clash-meta/common/convert/converter.go +++ b/clash-meta/common/convert/converter.go @@ -221,6 +221,9 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) { if flow := query.Get("flow"); flow != "" { vless["flow"] = strings.ToLower(flow) } + if encryption := query.Get("encryption"); encryption != "" { + vless["encryption"] = encryption + } proxies = append(proxies, vless) case "vmess": diff --git a/clash-meta/component/generater/cmd.go b/clash-meta/component/generater/cmd.go index c34c890443..2bd5b9cd86 100644 --- a/clash-meta/component/generater/cmd.go +++ b/clash-meta/component/generater/cmd.go @@ -46,16 +46,16 @@ func Main(args []string) { } fmt.Println("Config:", configBase64) fmt.Println("Key:", keyPem) - case "vless-mlkem768-keypair": + case "vless-mlkem768": var seed string if len(args) > 1 { seed = args[1] } - seedBase64, pubBase64, err := encryption.GenMLKEM768(seed) + seedBase64, clientBase64, err := encryption.GenMLKEM768(seed) if err != nil { panic(err) } fmt.Println("Seed: " + seedBase64) - fmt.Println("Client: " + pubBase64) + fmt.Println("Client: " + clientBase64) } } diff --git a/clash-meta/component/tls/reality.go b/clash-meta/component/tls/reality.go index 41c14781bb..2de5c46e73 100644 --- a/clash-meta/component/tls/reality.go +++ b/clash-meta/component/tls/reality.go @@ -170,7 +170,7 @@ type realityVerifier struct { //var pOffset = utils.MustOK(reflect.TypeOf((*utls.Conn)(nil)).Elem().FieldByName("peerCertificates")).Offset func (c *realityVerifier) VerifyPeerCertificate(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { - //log.Debugln("REALITY localAddr: %v\t is using X25519MLKEM768 for TLS' communication: %v", c.RemoteAddr(), c.HandshakeState.ServerHello.SelectedGroup == utls.X25519MLKEM768) + log.Debugln("REALITY localAddr: %v is using X25519MLKEM768 for TLS' communication: %v", c.RemoteAddr(), c.HandshakeState.ServerHello.ServerShare.Group == utls.X25519MLKEM768) //p, _ := reflect.TypeOf(c.Conn).Elem().FieldByName("peerCertificates") //certs := *(*[]*x509.Certificate)(unsafe.Add(unsafe.Pointer(c.Conn), pOffset)) certs := c.Conn.PeerCertificates() diff --git a/clash-meta/component/wildcard/wildcard.go b/clash-meta/component/wildcard/wildcard.go index 7fcf15ba34..c22eedf90a 100644 --- a/clash-meta/component/wildcard/wildcard.go +++ b/clash-meta/component/wildcard/wildcard.go @@ -1,3 +1,12 @@ +// Package wildcard modified IGLOU-EU/go-wildcard to support: +// +// `*` matches zero or more characters +// `?` matches exactly one character +// +// The original go-wildcard library used `.` to match exactly one character, and `?` to match zero or one character. +// `.` is a valid delimiter in domain name matching and should not be used as a wildcard. +// The `?` matching logic strictly matches only one character in most scenarios. +// So, the `?` matching logic in the original go-wildcard library has been removed and its wildcard `.` has been replaced with `?`. package wildcard // copy and modified from https://github.com/IGLOU-EU/go-wildcard/tree/ce22b7af48e487517a492d3727d9386492043e21 @@ -16,12 +25,10 @@ func Match(pattern, s string) bool { } func matchByString(pattern, s string) bool { - var lastErotemeCluster byte - var patternIndex, sIndex, lastStar, lastEroteme int + var patternIndex, sIndex, lastStar int patternLen := len(pattern) sLen := len(s) star := -1 - eroteme := -1 Loop: if sIndex >= sLen { @@ -38,14 +45,8 @@ Loop: return false } switch pattern[patternIndex] { - // Removed dot matching as it conflicts with dot in domains. - // case '.': - // It matches any single character. So, we don't need to check anything. case '?': - // '?' matches one character. Store its position and match exactly one character in the string. - eroteme = patternIndex - lastEroteme = sIndex - lastErotemeCluster = byte(s[sIndex]) + // It matches any single character. So, we don't need to check anything. case '*': // '*' matches zero or more characters. Store its position and increment the pattern index. star = patternIndex @@ -53,15 +54,8 @@ Loop: patternIndex++ goto Loop default: - // If the characters don't match, check if there was a previous '?' or '*' to backtrack. + // If the characters don't match, check if there was a previous '*' to backtrack. if pattern[patternIndex] != s[sIndex] { - if eroteme != -1 { - patternIndex = eroteme + 1 - sIndex = lastEroteme - eroteme = -1 - goto Loop - } - if star != -1 { patternIndex = star + 1 lastStar++ @@ -71,29 +65,18 @@ Loop: return false } - - // If the characters match, check if it was not the same to validate the eroteme. - if eroteme != -1 && lastErotemeCluster != byte(s[sIndex]) { - eroteme = -1 - } } patternIndex++ sIndex++ goto Loop - // Check if the remaining pattern characters are '*' or '?', which can match the end of the string. + // Check if the remaining pattern characters are '*', which can match the end of the string. checkPattern: if patternIndex < patternLen { if pattern[patternIndex] == '*' { patternIndex++ goto checkPattern - } else if pattern[patternIndex] == '?' { - if sIndex >= sLen { - sIndex-- - } - patternIndex++ - goto checkPattern } } diff --git a/clash-meta/component/wildcard/wildcard_test.go b/clash-meta/component/wildcard/wildcard_test.go index 719a4979ff..64b833d79d 100644 --- a/clash-meta/component/wildcard/wildcard_test.go +++ b/clash-meta/component/wildcard/wildcard_test.go @@ -25,31 +25,17 @@ func TestMatch(t *testing.T) { {"", "", true}, {"", "*", true}, {"", "**", true}, - {"", "?", true}, - {"", "??", true}, - {"", "?*", true}, - {"", "*?", true}, - {"", ".", false}, - {"", ".?", false}, - {"", "?.", false}, - {"", ".*", false}, - {"", "*.", false}, - {"", "*.?", false}, - {"", "?.*", false}, + {"", "?", false}, + {"", "?*", false}, + {"", "*?", false}, {"a", "", false}, {"a", "a", true}, {"a", "*", true}, {"a", "**", true}, {"a", "?", true}, - {"a", "??", true}, - {"a", ".", false}, - {"a", ".?", false}, - {"a", "?.", false}, - {"a", ".*", false}, - {"a", "*.", false}, - {"a", "*.?", false}, - {"a", "?.*", false}, + {"a", "?*", true}, + {"a", "*?", true}, {"match the exact string", "match the exact string", true}, {"do not match a different string", "this is a different string", false}, @@ -68,22 +54,27 @@ func TestMatch(t *testing.T) { {"match a string with a ?", "match ? string with a ?", true}, {"match a string with a ? at the beginning", "?atch a string with a ? at the beginning", true}, - {"match a string with two ?", "match a string with two ??", true}, - {"match a optional char with a ?", "match a optional? char with a ?", true}, - {"match a optional char with a ?", "match a optional? char with a ?", true}, - {"do not match a string with extra and a ?", "do not match ? string with extra and a ? like this", false}, + {"match a string with two ?", "match a ??ring with two ?", true}, + {"do not match a string with extra ?", "do not match a string with extra ??", false}, - {"do not match a string with a .", "do not match . string with a .", false}, - {"do not match a string with a . at the beginning", "do not .atch a string with a . at the beginning", false}, - {"do not match a string with two .", "do not match a ..ring with two .", false}, - {"do not match a string with extra .", "do not match a string with extra ..", false}, + {"abc.edf.hjg", "abc.edf.hjg", true}, + {"abc.edf.hjg", "ab.cedf.hjg", false}, + {"abc.edf.hjg", "abc.edfh.jg", false}, + {"abc.edf.hjg", "abc.edf.hjq", false}, - {"A big brown fox jumps over the lazy dog, with all there wildcards friends", ". big?brown fox jumps over * wildcard. friend??", false}, - {"A big brown fox fails to jump over the lazy dog, with all there wildcards friends", ". big?brown fox jumps over * wildcard. friend??", false}, + {"abc.edf.hjg", "abc.*.hjg", true}, + {"abc.edf.hjg", "abc.*.hjq", false}, + {"abc.edf.hjg", "abc*hjg", true}, + {"abc.edf.hjg", "abc*hjq", false}, + {"abc.edf.hjg", "a*g", true}, + {"abc.edf.hjg", "a*q", false}, - {"domain a.b.c", "domain a.b.c", true}, - {"domain adb.c", "domain a.b.c", false}, - {"aaaa", "a*a", true}, + {"abc.edf.hjg", "ab?.edf.hjg", true}, + {"abc.edf.hjg", "?b?.edf.hjg", true}, + {"abc.edf.hjg", "??c.edf.hjg", true}, + {"abc.edf.hjg", "a??.edf.hjg", true}, + {"abc.edf.hjg", "ab??.edf.hjg", false}, + {"abc.edf.hjg", "??.edf.hjg", false}, } for i, c := range cases { @@ -96,10 +87,106 @@ func TestMatch(t *testing.T) { } } +func match(pattern, name string) bool { // https://research.swtch.com/glob + px := 0 + nx := 0 + nextPx := 0 + nextNx := 0 + for px < len(pattern) || nx < len(name) { + if px < len(pattern) { + c := pattern[px] + switch c { + default: // ordinary character + if nx < len(name) && name[nx] == c { + px++ + nx++ + continue + } + case '?': // single-character wildcard + if nx < len(name) { + px++ + nx++ + continue + } + case '*': // zero-or-more-character wildcard + // Try to match at nx. + // If that doesn't work out, + // restart at nx+1 next. + nextPx = px + nextNx = nx + 1 + px++ + continue + } + } + // Mismatch. Maybe restart. + if 0 < nextNx && nextNx <= len(name) { + px = nextPx + nx = nextNx + continue + } + return false + } + // Matched all of pattern to all of name. Success. + return true +} + func FuzzMatch(f *testing.F) { - f.Fuzz(func(t *testing.T, s string) { - if !Match(string(s), string(s)) { - t.Fatalf("%s does not match %s", s, s) + f.Fuzz(func(t *testing.T, pattern, name string) { + result1 := Match(pattern, name) + result2 := match(pattern, name) + if result1 != result2 { + t.Fatalf("Match failed for pattern `%s` and name `%s`", pattern, name) + } + }) +} + +func BenchmarkMatch(b *testing.B) { + cases := []struct { + s string + pattern string + result bool + }{ + {"abc.edf.hjg", "abc.edf.hjg", true}, + {"abc.edf.hjg", "ab.cedf.hjg", false}, + {"abc.edf.hjg", "abc.edfh.jg", false}, + {"abc.edf.hjg", "abc.edf.hjq", false}, + + {"abc.edf.hjg", "abc.*.hjg", true}, + {"abc.edf.hjg", "abc.*.hjq", false}, + {"abc.edf.hjg", "abc*hjg", true}, + {"abc.edf.hjg", "abc*hjq", false}, + {"abc.edf.hjg", "a*g", true}, + {"abc.edf.hjg", "a*q", false}, + + {"abc.edf.hjg", "ab?.edf.hjg", true}, + {"abc.edf.hjg", "?b?.edf.hjg", true}, + {"abc.edf.hjg", "??c.edf.hjg", true}, + {"abc.edf.hjg", "a??.edf.hjg", true}, + {"abc.edf.hjg", "ab??.edf.hjg", false}, + {"abc.edf.hjg", "??.edf.hjg", false}, + + {"r4.cdn-aa-wow-this-is-long-a1.video-yajusenpai1145141919810-oh-hell-yeah-this-is-also-very-long-and-sukka-the-fox-has-a-very-big-fluffy-fox-tail-ao-wu-ao-wu-regex-and-wildcard-both-might-have-deadly-back-tracing-issue-be-careful-or-use-linear-matching.com", "*.cdn-*-*.video**.com", true}, + } + + b.Run("Match", func(b *testing.B) { + for i := 0; i < b.N; i++ { + for _, c := range cases { + result := Match(c.pattern, c.s) + if c.result != result { + b.Errorf("Test %d: Expected `%v`, found `%v`; With Pattern: `%s` and String: `%s`", i+1, c.result, result, c.pattern, c.s) + } + } + } + }) + + b.Run("match", func(b *testing.B) { + for i := 0; i < b.N; i++ { + for _, c := range cases { + result := match(c.pattern, c.s) + if c.result != result { + b.Errorf("Test %d: Expected `%v`, found `%v`; With Pattern: `%s` and String: `%s`", i+1, c.result, result, c.pattern, c.s) + } + } } }) } diff --git a/clash-meta/docs/config.yaml b/clash-meta/docs/config.yaml index 6790294673..245b38e8b0 100644 --- a/clash-meta/docs/config.yaml +++ b/clash-meta/docs/config.yaml @@ -638,7 +638,8 @@ proxies: # socks5 port: 443 uuid: uuid network: tcp - encryption: "8min-mlkem768client-bas64RawURLEncoding" # 复用八分钟后协商新的 sharedKey,需小于服务端的值 + encryption: "8min-vless-mlkem768client-bas64RawURLEncoding" # 复用八分钟后协商新的 sharedKey,需小于服务端的值 + # encryption: "8min-aes128xor-mlkem768client-bas64RawURLEncoding" tls: false #可以不开启tls udp: true @@ -1346,7 +1347,8 @@ listeners: flow: xtls-rprx-vision # ws-path: "/" # 如果不为空则开启 websocket 传输层 # grpc-service-name: "GunService" # 如果不为空则开启 grpc 传输层 - # decryption: "10min-mlkem768seed-bas64RawURLEncoding" # 同时允许 1-RTT 模式与十分钟复用的 0-RTT 模式, 后面base64字符串可由可由 mihomo generate vless-mlkem768 命令生成 + # decryption: "10min-vless-mlkem768seed-bas64RawURLEncoding" # 同时允许 1-RTT 模式与十分钟复用的 0-RTT 模式, 后面base64字符串可由可由 mihomo generate vless-mlkem768 命令生成 + # decryption: "10min-aes128xor-mlkem768seed-bas64RawURLEncoding" # 下面两项如果填写则开启 tls(需要同时填写) # certificate: ./server.crt # private-key: ./server.key diff --git a/clash-meta/go.mod b/clash-meta/go.mod index 11d10c9a0f..4ba0c3ebbe 100644 --- a/clash-meta/go.mod +++ b/clash-meta/go.mod @@ -35,7 +35,7 @@ require ( github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f github.com/metacubex/smux v0.0.0-20250503055512-501391591dee github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4 - github.com/metacubex/utls v1.8.1-0.20250810142204-d0e55ab2e852 + github.com/metacubex/utls v1.8.1-0.20250811145843-49b4f106169a github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181 github.com/miekg/dns v1.1.63 // lastest version compatible with golang1.20 github.com/mroth/weightedrand/v2 v2.1.0 diff --git a/clash-meta/go.sum b/clash-meta/go.sum index 2adede6cbc..92d52230d4 100644 --- a/clash-meta/go.sum +++ b/clash-meta/go.sum @@ -139,10 +139,8 @@ github.com/metacubex/smux v0.0.0-20250503055512-501391591dee h1:lp6hJ+4wCLZu113a github.com/metacubex/smux v0.0.0-20250503055512-501391591dee/go.mod h1:4bPD8HWx9jPJ9aE4uadgyN7D1/Wz3KmPy+vale8sKLE= github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4 h1:j1VRTiC9JLR4nUbSikx9OGdu/3AgFDqgcLj4GoqyQkc= github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw= -github.com/metacubex/utls v1.8.0 h1:mSYi6FMnmc5riARl5UZDmWVy710z+P5b7xuGW0lV9ac= -github.com/metacubex/utls v1.8.0/go.mod h1:FdjYzVfCtgtna19hX0ER1Xsa5uJInwdQ4IcaaI98lEQ= -github.com/metacubex/utls v1.8.1-0.20250810142204-d0e55ab2e852 h1:MLHUGmASNH7/AeoGmSrVM2RutRZAqIDSbQWBp0P7ItE= -github.com/metacubex/utls v1.8.1-0.20250810142204-d0e55ab2e852/go.mod h1:FdjYzVfCtgtna19hX0ER1Xsa5uJInwdQ4IcaaI98lEQ= +github.com/metacubex/utls v1.8.1-0.20250811145843-49b4f106169a h1:IIzlVmDoB4+7b0BUcLZaY5+AirhhLFep3PhwkAFMRnQ= +github.com/metacubex/utls v1.8.1-0.20250811145843-49b4f106169a/go.mod h1:FdjYzVfCtgtna19hX0ER1Xsa5uJInwdQ4IcaaI98lEQ= github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181 h1:hJLQviGySBuaynlCwf/oYgIxbVbGRUIKZCxdya9YrbQ= github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181/go.mod h1:phewKljNYiTVT31Gcif8RiCKnTUOgVWFJjccqYM8s+Y= github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY= diff --git a/clash-meta/listener/inbound/vless_test.go b/clash-meta/listener/inbound/vless_test.go index fdb8905ce9..2ef1212669 100644 --- a/clash-meta/listener/inbound/vless_test.go +++ b/clash-meta/listener/inbound/vless_test.go @@ -89,18 +89,29 @@ func TestInboundVless_TLS(t *testing.T) { } func TestInboundVless_Encryption(t *testing.T) { - seedBase64, pubBase64, err := encryption.GenMLKEM768("") + seedBase64, clientBase64, err := encryption.GenMLKEM768("") if err != nil { t.Fatal(err) return } - inboundOptions := inbound.VlessOption{ - Decryption: "10min-mlkem768seed-" + seedBase64, - } - outboundOptions := outbound.VlessOption{ - Encryption: "8min-mlkem768client-" + pubBase64, - } - testInboundVless(t, inboundOptions, outboundOptions) + t.Run("-vless-", func(t *testing.T) { + inboundOptions := inbound.VlessOption{ + Decryption: "10min-vless-mlkem768seed-" + seedBase64, + } + outboundOptions := outbound.VlessOption{ + Encryption: "8min-vless-mlkem768client-" + clientBase64, + } + testInboundVless(t, inboundOptions, outboundOptions) + }) + t.Run("-aes128xor-", func(t *testing.T) { + inboundOptions := inbound.VlessOption{ + Decryption: "10min-aes128xor-mlkem768seed-" + seedBase64, + } + outboundOptions := outbound.VlessOption{ + Encryption: "8min-aes128xor-mlkem768client-" + clientBase64, + } + testInboundVless(t, inboundOptions, outboundOptions) + }) } func TestInboundVless_Wss1(t *testing.T) { diff --git a/clash-meta/listener/sing_vless/server.go b/clash-meta/listener/sing_vless/server.go index a81650839b..41f23f34d8 100644 --- a/clash-meta/listener/sing_vless/server.go +++ b/clash-meta/listener/sing_vless/server.go @@ -88,7 +88,7 @@ func New(config LC.VlessServer, tunnel C.Tunnel, additions ...inbound.Addition) sl = &Listener{config: config, service: service} - if s := strings.Split(config.Decryption, "-mlkem768seed-"); len(s) == 2 { + 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") @@ -102,14 +102,22 @@ func New(config LC.VlessServer, tunnel C.Tunnel, additions ...inbound.Addition) } 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[1]) + b, err = base64.RawURLEncoding.DecodeString(s[3]) if err != nil { return nil, fmt.Errorf("invaild vless decryption value: %s", config.Decryption) } - if len(b) == 64 { + if len(b) == encryption.MLKEM768SeedLength { sl.decryption = &encryption.ServerInstance{} - if err = sl.decryption.Init(b, time.Duration(minutes)*time.Minute); err != nil { + 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 { diff --git a/clash-meta/transport/vless/encryption/client.go b/clash-meta/transport/vless/encryption/client.go index b0b44857f1..8453ea613d 100644 --- a/clash-meta/transport/vless/encryption/client.go +++ b/clash-meta/transport/vless/encryption/client.go @@ -38,17 +38,18 @@ func init() { type ClientInstance struct { sync.RWMutex eKeyNfs *mlkem.EncapsulationKey768 + xor uint32 minutes time.Duration expire time.Time baseKey []byte - reuse []byte + ticket []byte } type ClientConn struct { net.Conn instance *ClientInstance baseKey []byte - reuse []byte + ticket []byte random []byte aead cipher.AEAD nonce []byte @@ -57,8 +58,9 @@ type ClientConn struct { peerCache []byte } -func (i *ClientInstance) Init(eKeyNfsData []byte, minutes time.Duration) (err error) { +func (i *ClientInstance) Init(eKeyNfsData []byte, xor uint32, minutes time.Duration) (err error) { i.eKeyNfs, err = mlkem.NewEncapsulationKey768(eKeyNfsData) + i.xor = xor i.minutes = minutes return } @@ -67,6 +69,9 @@ func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) { if i.eKeyNfs == nil { return nil, errors.New("uninitialized") } + if i.xor == 1 { + conn = NewXorConn(conn, i.eKeyNfs.Bytes()) + } c := &ClientConn{Conn: conn} if i.minutes > 0 { @@ -74,7 +79,7 @@ func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) { if time.Now().Before(i.expire) { c.instance = i c.baseKey = i.baseKey - c.reuse = i.reuse + c.ticket = i.ticket i.RUnlock() return c, nil } @@ -104,7 +109,7 @@ func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) { return nil, err } encapsulatedPfsKey := peerServerHello[:1088] - c.reuse = peerServerHello[1088:] + c.ticket = peerServerHello[1088:] pfsKey, err := dKeyPfs.Decapsulate(encapsulatedPfsKey) if err != nil { @@ -115,7 +120,7 @@ func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) { 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.reuse, encapsulatedPfsKey) + VLESS, _ := newAead(ClientCipher, authKey).Open(nil, nonce, c.ticket, encapsulatedPfsKey) if !bytes.Equal(VLESS, []byte("VLESS")) { // TODO: more message return nil, errors.New("invalid server") } @@ -124,7 +129,7 @@ func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) { i.Lock() i.expire = time.Now().Add(i.minutes) i.baseKey = c.baseKey - i.reuse = c.reuse + i.ticket = c.ticket i.Unlock() } @@ -140,12 +145,12 @@ func (c *ClientConn) Write(b []byte) (int, error) { c.random = make([]byte, 32) rand.Read(c.random) key := make([]byte, 32) - hkdf.New(sha256.New, c.baseKey, c.random, c.reuse).Read(key) + 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.reuse) + 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]) @@ -210,7 +215,7 @@ func (c *ClientConn) Read(b []byte) (int, error) { // after first Write() if err != nil { if c.instance != nil { c.instance.Lock() - if bytes.Equal(c.reuse, c.instance.reuse) { + if bytes.Equal(c.ticket, c.instance.ticket) { c.instance.expire = time.Now() // expired } c.instance.Unlock() diff --git a/clash-meta/transport/vless/encryption/doc.go b/clash-meta/transport/vless/encryption/doc.go index dbb00211d4..af531f8b99 100644 --- a/clash-meta/transport/vless/encryption/doc.go +++ b/clash-meta/transport/vless/encryption/doc.go @@ -1,3 +1,5 @@ // Package encryption copy and modify from xray-core // https://github.com/XTLS/Xray-core/commit/f61c14e9c63dc41a8a09135db3aea337974f3f37 +// https://github.com/XTLS/Xray-core/commit/3e19bf9233bdd9bafc073a71c65b737cc1ffba5e +// https://github.com/XTLS/Xray-core/commit/7ffb555fc8ec51bd1e3e60f26f1d6957984dba80 package encryption diff --git a/clash-meta/transport/vless/encryption/key.go b/clash-meta/transport/vless/encryption/key.go index 46b53163d3..69b5289579 100644 --- a/clash-meta/transport/vless/encryption/key.go +++ b/clash-meta/transport/vless/encryption/key.go @@ -8,7 +8,10 @@ import ( "github.com/metacubex/utls/mlkem" ) -func GenMLKEM768(seedStr string) (seedBase64, pubBase64 string, err error) { +const MLKEM768SeedLength = mlkem.SeedSize +const MLKEM768ClientLength = mlkem.EncapsulationKeySize768 + +func GenMLKEM768(seedStr string) (seedBase64, clientBase64 string, err error) { var seed [64]byte if len(seedStr) > 0 { s, _ := base64.RawURLEncoding.DecodeString(seedStr) @@ -27,6 +30,6 @@ func GenMLKEM768(seedStr string) (seedBase64, pubBase64 string, err error) { key, _ := mlkem.NewDecapsulationKey768(seed[:]) pub := key.EncapsulationKey() seedBase64 = base64.RawURLEncoding.EncodeToString(seed[:]) - pubBase64 = base64.RawURLEncoding.EncodeToString(pub.Bytes()) + clientBase64 = base64.RawURLEncoding.EncodeToString(pub.Bytes()) return } diff --git a/clash-meta/transport/vless/encryption/server.go b/clash-meta/transport/vless/encryption/server.go index 667b813fa6..6874445960 100644 --- a/clash-meta/transport/vless/encryption/server.go +++ b/clash-meta/transport/vless/encryption/server.go @@ -25,6 +25,7 @@ type ServerSession struct { type ServerInstance struct { sync.RWMutex dKeyNfs *mlkem.DecapsulationKey768 + xor uint32 minutes time.Duration sessions map[[21]byte]*ServerSession stop bool @@ -34,7 +35,7 @@ type ServerConn struct { net.Conn cipher byte baseKey []byte - reuse []byte + ticket []byte peerRandom []byte peerAead cipher.AEAD peerNonce []byte @@ -43,8 +44,9 @@ type ServerConn struct { nonce []byte } -func (i *ServerInstance) Init(dKeyNfsData []byte, minutes time.Duration) (err error) { +func (i *ServerInstance) Init(dKeyNfsData []byte, xor uint32, minutes time.Duration) (err error) { i.dKeyNfs, err = mlkem.NewDecapsulationKey768(dKeyNfsData) + i.xor = xor if minutes > 0 { i.minutes = minutes i.sessions = make(map[[21]byte]*ServerSession) @@ -79,22 +81,25 @@ func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) { if i.dKeyNfs == nil { return nil, errors.New("uninitialized") } + if i.xor == 1 { + conn = NewXorConn(conn, i.dKeyNfs.EncapsulationKey().Bytes()) + } c := &ServerConn{Conn: conn} - peerReuseHello := make([]byte, 21+32) - if _, err := io.ReadFull(c.Conn, peerReuseHello); err != nil { + peerTicketHello := make([]byte, 21+32) + if _, err := io.ReadFull(c.Conn, peerTicketHello); err != nil { return nil, err } if i.minutes > 0 { i.RLock() - s := i.sessions[[21]byte(peerReuseHello)] + s := i.sessions[[21]byte(peerTicketHello)] i.RUnlock() if s != nil { - if _, replay := s.randoms.LoadOrStore([32]byte(peerReuseHello[21:]), true); !replay { + if _, replay := s.randoms.LoadOrStore([32]byte(peerTicketHello[21:]), true); !replay { c.cipher = s.cipher c.baseKey = s.baseKey - c.reuse = peerReuseHello[:21] - c.peerRandom = peerReuseHello[21:] + c.ticket = peerTicketHello[:21] + c.peerRandom = peerTicketHello[21:] return c, nil } } @@ -106,11 +111,11 @@ func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) { } if l, _ := decodeHeader(peerHeader); l != 0 { c.Conn.Write(make([]byte, randBetween(100, 1000))) // make client do new handshake - return nil, errors.New("invalid reuse") + return nil, errors.New("invalid ticket") } peerClientHello := make([]byte, 1088+1184+1) - copy(peerClientHello, peerReuseHello) + copy(peerClientHello, peerTicketHello) copy(peerClientHello[53:], peerHeader) if _, err := io.ReadFull(c.Conn, peerClientHello[58:]); err != nil { return nil, err @@ -136,13 +141,13 @@ func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) { authKey := make([]byte, 32) hkdf.New(sha256.New, c.baseKey, encapsulatedNfsKey, eKeyPfsData).Read(authKey) nonce := make([]byte, 12) - c.reuse = newAead(c.cipher, authKey).Seal(nil, nonce, []byte("VLESS"), encapsulatedPfsKey) + c.ticket = newAead(c.cipher, authKey).Seal(nil, nonce, []byte("VLESS"), encapsulatedPfsKey) padding := randBetween(100, 1000) serverHello := make([]byte, 1088+21+5+padding) copy(serverHello, encapsulatedPfsKey) - copy(serverHello[1088:], c.reuse) + copy(serverHello[1088:], c.ticket) encodeHeader(serverHello[1109:], int(padding)) if _, err := c.Conn.Write(serverHello); err != nil { @@ -151,7 +156,7 @@ func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) { if i.minutes > 0 { i.Lock() - i.sessions[[21]byte(c.reuse)] = &ServerSession{ + i.sessions[[21]byte(c.ticket)] = &ServerSession{ expire: time.Now().Add(i.minutes), cipher: c.cipher, baseKey: c.baseKey, @@ -181,12 +186,12 @@ func (c *ServerConn) Read(b []byte) (int, error) { return 0, err } } - peerIndex := make([]byte, 21) - copy(peerIndex, peerHeader) - if _, err := io.ReadFull(c.Conn, peerIndex[5:]); err != nil { + peerTicket := make([]byte, 21) + copy(peerTicket, peerHeader) + if _, err := io.ReadFull(c.Conn, peerTicket[5:]); err != nil { return 0, err } - if !bytes.Equal(peerIndex, c.reuse) { + if !bytes.Equal(peerTicket, c.ticket) { return 0, errors.New("naughty boy") } c.peerRandom = make([]byte, 32) @@ -195,7 +200,7 @@ func (c *ServerConn) Read(b []byte) (int, error) { } } peerKey := make([]byte, 32) - hkdf.New(sha256.New, c.baseKey, c.peerRandom, c.reuse).Read(peerKey) + hkdf.New(sha256.New, c.baseKey, c.peerRandom, c.ticket).Read(peerKey) c.peerAead = newAead(c.cipher, peerKey) c.peerNonce = make([]byte, 12) } diff --git a/clash-meta/transport/vless/encryption/xor.go b/clash-meta/transport/vless/encryption/xor.go new file mode 100644 index 0000000000..c8af2112d1 --- /dev/null +++ b/clash-meta/transport/vless/encryption/xor.go @@ -0,0 +1,63 @@ +package encryption + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "io" + "net" +) + +type XorConn struct { + net.Conn + key []byte + ctr cipher.Stream + peerCtr cipher.Stream +} + +func NewXorConn(conn net.Conn, key []byte) *XorConn { + return &XorConn{Conn: conn, key: key[:16]} +} + +func (c *XorConn) Write(b []byte) (int, error) { + if len(b) == 0 { + return 0, nil + } + var iv []byte + if c.ctr == nil { + block, _ := aes.NewCipher(c.key) + iv = make([]byte, 16) + rand.Read(iv) + c.ctr = cipher.NewCTR(block, iv) + } + c.ctr.XORKeyStream(b, b) // caller MUST discard b + if iv != nil { + b = append(iv, b...) + } + if _, err := c.Conn.Write(b); err != nil { + return 0, err + } + if iv != nil { + b = b[16:] + } + return len(b), nil +} + +func (c *XorConn) Read(b []byte) (int, error) { + if len(b) == 0 { + return 0, nil + } + if c.peerCtr == nil { + peerIv := make([]byte, 16) + if _, err := io.ReadFull(c.Conn, peerIv); err != nil { + return 0, err + } + block, _ := aes.NewCipher(c.key) + c.peerCtr = cipher.NewCTR(block, peerIv) + } + n, err := c.Conn.Read(b) + if n > 0 { + c.peerCtr.XORKeyStream(b[:n], b[:n]) + } + return n, err +} diff --git a/clash-nyanpasu/frontend/nyanpasu/package.json b/clash-nyanpasu/frontend/nyanpasu/package.json index fcd4645bc1..a0180324b1 100644 --- a/clash-nyanpasu/frontend/nyanpasu/package.json +++ b/clash-nyanpasu/frontend/nyanpasu/package.json @@ -32,8 +32,8 @@ "country-emoji": "1.5.6", "dayjs": "1.11.13", "framer-motion": "12.23.12", - "i18next": "25.3.2", - "jotai": "2.13.0", + "i18next": "25.3.4", + "jotai": "2.13.1", "json-schema": "0.4.0", "material-react-table": "3.2.1", "monaco-editor": "0.52.2", @@ -56,12 +56,12 @@ "@csstools/normalize.css": "12.1.1", "@emotion/babel-plugin": "11.13.5", "@emotion/react": "11.14.0", - "@iconify/json": "2.2.368", + "@iconify/json": "2.2.371", "@monaco-editor/react": "4.7.0", "@tanstack/react-query": "5.84.2", - "@tanstack/react-router": "1.131.2", - "@tanstack/react-router-devtools": "1.131.2", - "@tanstack/router-plugin": "1.131.2", + "@tanstack/react-router": "1.131.3", + "@tanstack/react-router-devtools": "1.131.3", + "@tanstack/router-plugin": "1.131.3", "@tauri-apps/plugin-clipboard-manager": "2.3.0", "@tauri-apps/plugin-dialog": "2.3.0", "@tauri-apps/plugin-fs": "2.4.0", diff --git a/clash-nyanpasu/manifest/version.json b/clash-nyanpasu/manifest/version.json index 6e18f3057d..5806920903 100644 --- a/clash-nyanpasu/manifest/version.json +++ b/clash-nyanpasu/manifest/version.json @@ -2,10 +2,10 @@ "manifest_version": 1, "latest": { "mihomo": "v1.19.12", - "mihomo_alpha": "alpha-e89af72", + "mihomo_alpha": "alpha-3a0d267", "clash_rs": "v0.8.2", "clash_premium": "2023-09-05-gdcc8d87", - "clash_rs_alpha": "0.8.2-alpha+sha.2a8be3b" + "clash_rs_alpha": "0.8.2-alpha+sha.ec9134e" }, "arch_template": { "mihomo": { @@ -69,5 +69,5 @@ "linux-armv7hf": "clash-armv7-unknown-linux-gnueabihf" } }, - "updated_at": "2025-08-09T22:21:17.093Z" + "updated_at": "2025-08-10T22:21:15.754Z" } diff --git a/clash-nyanpasu/pnpm-lock.yaml b/clash-nyanpasu/pnpm-lock.yaml index e66eb8ae92..3c01034409 100644 --- a/clash-nyanpasu/pnpm-lock.yaml +++ b/clash-nyanpasu/pnpm-lock.yaml @@ -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.2(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.3(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 @@ -276,11 +276,11 @@ importers: specifier: 12.23.12 version: 12.23.12(@emotion/is-prop-valid@1.3.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) i18next: - specifier: 25.3.2 - version: 25.3.2(typescript@5.9.2) + specifier: 25.3.4 + version: 25.3.4(typescript@5.9.2) jotai: - specifier: 2.13.0 - version: 2.13.0(@babel/core@7.28.0)(@babel/template@7.27.2)(@types/react@19.1.9)(react@19.1.1) + 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) json-schema: specifier: 0.4.0 version: 0.4.0 @@ -310,7 +310,7 @@ importers: version: 7.6.2(dc790e3d871a3fe7e1d22dc6e321e397) react-i18next: specifier: 15.6.1 - version: 15.6.1(i18next@25.3.2(typescript@5.9.2))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + 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) @@ -343,8 +343,8 @@ importers: specifier: 11.14.0 version: 11.14.0(@types/react@19.1.9)(react@19.1.1) '@iconify/json': - specifier: 2.2.368 - version: 2.2.368 + specifier: 2.2.371 + version: 2.2.371 '@monaco-editor/react': specifier: 4.7.0 version: 4.7.0(monaco-editor@0.52.2)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -352,14 +352,14 @@ importers: specifier: 5.84.2 version: 5.84.2(react@19.1.1) '@tanstack/react-router': - specifier: 1.131.2 - version: 1.131.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + specifier: 1.131.3 + version: 1.131.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@tanstack/react-router-devtools': - specifier: 1.131.2 - version: 1.131.2(@tanstack/react-router@1.131.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@tanstack/router-core@1.131.2)(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.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) '@tanstack/router-plugin': - specifier: 1.131.2 - version: 1.131.2(@tanstack/react-router@1.131.2(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.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)) '@tauri-apps/plugin-clipboard-manager': specifier: 2.3.0 version: 2.3.0 @@ -509,7 +509,7 @@ importers: version: 6.0.0(react@19.1.1) react-i18next: specifier: 15.6.1 - version: 15.6.1(i18next@25.3.2(typescript@5.9.2))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + 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-use: specifier: 17.6.0 version: 17.6.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -1753,8 +1753,8 @@ packages: '@vue/compiler-sfc': optional: true - '@iconify/json@2.2.368': - resolution: {integrity: sha512-gxz87+V5og+kYRc8jp0YTGgImusCpZLFVd+QzIVH5GwA9zQlMe2BvK9dhIHKSWlP1fZ7blKFKVqKKaCNoRCEiA==} + '@iconify/json@2.2.371': + resolution: {integrity: sha512-dnamUrgw8aDWz4STJOkQqqCz6GB2s7SHGPIY84EVQmlVXzM7T1V7L/csdL7r9bkA3AHF80+OXhx7o4FXn5L8pQ==} '@iconify/types@2.0.0': resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} @@ -2939,16 +2939,16 @@ packages: peerDependencies: react: ^18 || ^19 - '@tanstack/react-router-devtools@1.131.2': - resolution: {integrity: sha512-7bJxZadqjZrsdPOcKWp2+sc8cnARHzrgr/eduDJDA79Rbx7qEh8TgAY7xFqlhShCxA90/a9y4wastbs8AqnDcA==} + '@tanstack/react-router-devtools@1.131.3': + resolution: {integrity: sha512-opouR8dbBrDnkHdiyTVPb3rTfSRxWN+ZGi1YuEXNGUXgirvnpQUTWNP6kzDv1at29DxymwwfRRYD602MjZOOlA==} engines: {node: '>=12'} peerDependencies: - '@tanstack/react-router': ^1.131.2 + '@tanstack/react-router': ^1.131.3 react: '>=18.0.0 || >=19.0.0' react-dom: '>=18.0.0 || >=19.0.0' - '@tanstack/react-router@1.131.2': - resolution: {integrity: sha512-MGkCPA/7HJ9UWIV27CtKb5i3Sizxywx43/h+ifrEC+2guzQR8yBcI4ibwMmSpsuGqKOzkuvX63RU1SVeCwbg+g==} + '@tanstack/react-router@1.131.3': + resolution: {integrity: sha512-1wxsStYJai0/ssOQeO+8mh5VmMmJWIZOtIEEqDxIhQX4dHyurKl6khl34+qLtDvWFsj6zgiMwjID3GQj5SMz1w==} 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.2': - resolution: {integrity: sha512-ITnzlVk9iZTYNe/1FuLnDhpDTml1PfzUZk3V5He+NrXIcPGd2h3plZlv6NqZirNZMhCoiX52jfM1BOHGuy1ICA==} + '@tanstack/router-core@1.131.3': + resolution: {integrity: sha512-8sby4n2pgRJ7qZrFvuS6v21oBDHCRoFM8p+/HAIra2d32cD5wS5k9DeNltWFUAJFXdsdwpdGVA0iXSAc0WfIHw==} engines: {node: '>=12'} - '@tanstack/router-devtools-core@1.131.2': - resolution: {integrity: sha512-j7++EKhxbB8JXFqbZH6fRdCge7pujqrFrBEDDpNX7ms3EY7MwAcqiYE4qqY9YsJhfAOjZWo+88KEzX9AB09twA==} + '@tanstack/router-devtools-core@1.131.3': + resolution: {integrity: sha512-GQHVCE0ywJ0ajz9K52RLAhbDSuziD3MVRYYp5S5w2i31Mb9xkzITJe/CZ7WIgOuY+XQm18P8drbJnxdHTNOcWQ==} engines: {node: '>=12'} peerDependencies: - '@tanstack/router-core': ^1.131.2 + '@tanstack/router-core': ^1.131.3 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.2': - resolution: {integrity: sha512-A4IW5zmAV5NrRCf2UXONA47DLSEXPaIcPL3Bmi6MHmA8QaWkoJ7frTHv82QE8DmyIsv2Y7c0CfINy2K5KET0Tw==} + '@tanstack/router-generator@1.131.3': + resolution: {integrity: sha512-sTsi9lSxBCpAExQWyh3k2X40biiRHjIRISxVvko5sPG/+NKYFjGaQwgXQ/1jiDSTqe8i7/b/2l94ZJPTa6VPxQ==} engines: {node: '>=12'} - '@tanstack/router-plugin@1.131.2': - resolution: {integrity: sha512-+F/Orgn4Gi69WBn+aRmVezGbbk/Uk8viciSkdeQUpU2cWtMSR2EFU2xjFG06YCTX92jcZAYfSRQMpStncRXJQQ==} + '@tanstack/router-plugin@1.131.3': + resolution: {integrity: sha512-PLCjxTTHBf5H9TqH+jTNvgSnnCZhvrLj61C5XAtONA7NUv+Lh4xJ/u0nn83ZYb7uFM4rMg1JmpU5mG8iNbGHZw==} engines: {node: '>=12'} peerDependencies: '@rsbuild/core': '>=1.0.2' - '@tanstack/react-router': ^1.131.2 + '@tanstack/react-router': ^1.131.3 vite: '>=5.0.0 || >=6.0.0' vite-plugin-solid: ^2.11.2 webpack: '>=5.92.0' @@ -5482,8 +5482,8 @@ packages: hyphenate-style-name@1.1.0: resolution: {integrity: sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==} - i18next@25.3.2: - resolution: {integrity: sha512-JSnbZDxRVbphc5jiptxr3o2zocy5dEqpVm9qCGdJwRNO+9saUJS0/u4LnM/13C23fUEWxAylPqKU/NpMV/IjqA==} + i18next@25.3.4: + resolution: {integrity: sha512-AHklEYFLiRRxW1Cb6zE9lfnEtYvsydRC8nRS3RSKGX3zCqZ8nLZwMaUsrb80YuccPNv2RNokDL8LkTNnp+6mDw==} peerDependencies: typescript: ^5 peerDependenciesMeta: @@ -5896,8 +5896,8 @@ packages: jju@1.4.0: resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} - jotai@2.13.0: - resolution: {integrity: sha512-H43zXdanNTdpfOEJ4NVbm4hgmrctpXLZagjJNcqAywhUv+sTE7esvFjwm5oBg/ywT9Qw63lIkM6fjrhFuW8UDg==} + jotai@2.13.1: + resolution: {integrity: sha512-cRsw6kFeGC9Z/D3egVKrTXRweycZ4z/k7i2MrfCzPYsL9SIWcPXTyqv258/+Ay8VUEcihNiE/coBLE6Kic6b8A==} engines: {node: '>=12.20.0'} peerDependencies: '@babel/core': '>=7.0.0' @@ -9953,7 +9953,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@iconify/json@2.2.368': + '@iconify/json@2.2.371': dependencies: '@iconify/types': 2.0.0 pathe: 1.1.2 @@ -11040,10 +11040,10 @@ snapshots: '@tanstack/query-core': 5.83.1 react: 19.1.1 - '@tanstack/react-router-devtools@1.131.2(@tanstack/react-router@1.131.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@tanstack/router-core@1.131.2)(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.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)': dependencies: - '@tanstack/react-router': 1.131.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@tanstack/router-devtools-core': 1.131.2(@tanstack/router-core@1.131.2)(csstype@3.1.3)(solid-js@1.9.5)(tiny-invariant@1.3.3) + '@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) 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.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@tanstack/react-router@1.131.3(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.2 + '@tanstack/router-core': 1.131.3 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.2': + '@tanstack/router-core@1.131.3': 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.2(@tanstack/router-core@1.131.2)(csstype@3.1.3)(solid-js@1.9.5)(tiny-invariant@1.3.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)': dependencies: - '@tanstack/router-core': 1.131.2 + '@tanstack/router-core': 1.131.3 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.2': + '@tanstack/router-generator@1.131.3': dependencies: - '@tanstack/router-core': 1.131.2 + '@tanstack/router-core': 1.131.3 '@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.2(@tanstack/react-router@1.131.2(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.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))': 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.2 - '@tanstack/router-generator': 1.131.2 + '@tanstack/router-core': 1.131.3 + '@tanstack/router-generator': 1.131.3 '@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.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@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) transitivePeerDependencies: - supports-color @@ -11148,9 +11148,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@tanstack/router-zod-adapter@1.81.5(@tanstack/react-router@1.131.2(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.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(zod@4.0.17)': dependencies: - '@tanstack/react-router': 1.131.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@tanstack/react-router': 1.131.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1) zod: 4.0.17 '@tanstack/store@0.7.0': {} @@ -14090,9 +14090,9 @@ snapshots: hyphenate-style-name@1.1.0: {} - i18next@25.3.2(typescript@5.9.2): + i18next@25.3.4(typescript@5.9.2): dependencies: - '@babel/runtime': 7.27.6 + '@babel/runtime': 7.28.2 optionalDependencies: typescript: 5.9.2 @@ -14464,7 +14464,7 @@ snapshots: jju@1.4.0: {} - jotai@2.13.0(@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.9)(react@19.1.1): optionalDependencies: '@babel/core': 7.28.0 '@babel/template': 7.27.2 @@ -15717,11 +15717,11 @@ snapshots: dependencies: react: 19.1.1 - react-i18next@15.6.1(i18next@25.3.2(typescript@5.9.2))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2): + react-i18next@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): dependencies: '@babel/runtime': 7.27.6 html-parse-stringify: 3.0.1 - i18next: 25.3.2(typescript@5.9.2) + i18next: 25.3.4(typescript@5.9.2) react: 19.1.1 optionalDependencies: react-dom: 19.1.1(react@19.1.1) diff --git a/lede/target/linux/generic/config-6.12 b/lede/target/linux/generic/config-6.12 index 5ce2341951..46a4aac7ad 100644 --- a/lede/target/linux/generic/config-6.12 +++ b/lede/target/linux/generic/config-6.12 @@ -2304,6 +2304,7 @@ CONFIG_HARDEN_BRANCH_HISTORY=y # CONFIG_HI6421V600_IRQ is not set # CONFIG_HI8435 is not set # CONFIG_HIBERNATION is not set +# CONFIG_HIBERNATION_COMP_LZ4 is not set # CONFIG_HID is not set # CONFIG_HIDRAW is not set # CONFIG_HID_A4TECH is not set diff --git a/lede/target/linux/loongarch64/Makefile b/lede/target/linux/loongarch64/Makefile index f8401c2b2e..950880e22b 100644 --- a/lede/target/linux/loongarch64/Makefile +++ b/lede/target/linux/loongarch64/Makefile @@ -11,6 +11,7 @@ FEATURES:=audio display ext4 pcie boot-part rootfs-part rtc usb targz SUBTARGETS:=generic KERNEL_PATCHVER:=6.6 +KERNEL_TESTING_PATCHVER:=6.12 KERNELNAME:=vmlinuz.efi dtbs diff --git a/lede/target/linux/loongarch64/config-6.12 b/lede/target/linux/loongarch64/config-6.12 new file mode 100644 index 0000000000..36a3a1e825 --- /dev/null +++ b/lede/target/linux/loongarch64/config-6.12 @@ -0,0 +1,754 @@ +# CONFIG_16KB_2LEVEL is not set +CONFIG_16KB_3LEVEL=y +# CONFIG_4KB_3LEVEL is not set +# CONFIG_4KB_4LEVEL is not set +CONFIG_64BIT=y +# CONFIG_64KB_2LEVEL is not set +# CONFIG_64KB_3LEVEL is not set +CONFIG_AC97_BUS=y +CONFIG_ACPI=y +CONFIG_ACPI_AC=y +CONFIG_ACPI_BATTERY=y +# CONFIG_ACPI_BGRT is not set +CONFIG_ACPI_BUTTON=y +CONFIG_ACPI_CONTAINER=y +CONFIG_ACPI_CPU_FREQ_PSS=y +# CONFIG_ACPI_DEBUG is not set +# CONFIG_ACPI_DEBUGGER is not set +# CONFIG_ACPI_DOCK is not set +# CONFIG_ACPI_EC_DEBUGFS is not set +CONFIG_ACPI_FAN=y +# CONFIG_ACPI_FFH is not set +CONFIG_ACPI_GENERIC_GSI=y +CONFIG_ACPI_HOTPLUG_CPU=y +CONFIG_ACPI_I2C_OPREGION=y +CONFIG_ACPI_MCFG=y +CONFIG_ACPI_NHLT=y +# CONFIG_ACPI_PCI_SLOT is not set +# CONFIG_ACPI_PFRUT is not set +CONFIG_ACPI_PPTT=y +CONFIG_ACPI_PROCESSOR=y +CONFIG_ACPI_PROCESSOR_IDLE=y +CONFIG_ACPI_SLEEP=y +# CONFIG_ACPI_SPCR_TABLE is not set +CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT=y +CONFIG_ACPI_TABLE_UPGRADE=y +# CONFIG_ACPI_TAD is not set +CONFIG_ACPI_THERMAL=y +CONFIG_ACPI_THERMAL_LIB=y +CONFIG_ACPI_VIDEO=y +CONFIG_APERTURE_HELPERS=y +CONFIG_ARCH_DISABLE_KASAN_INLINE=y +CONFIG_ARCH_DMA_ADDR_T_64BIT=y +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +# CONFIG_ARCH_IOREMAP is not set +CONFIG_ARCH_KEEP_MEMBLOCK=y +CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y +CONFIG_ARCH_MIGHT_HAVE_PC_SERIO=y +CONFIG_ARCH_MMAP_RND_BITS=12 +CONFIG_ARCH_MMAP_RND_BITS_MAX=18 +CONFIG_ARCH_MMAP_RND_BITS_MIN=12 +CONFIG_ARCH_SELECT_MEMORY_MODEL=y +CONFIG_ARCH_SPARSEMEM_ENABLE=y +CONFIG_ARCH_STACKWALK=y +CONFIG_ARCH_STRICT_ALIGN=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARCH_WANTS_NO_INSTR=y +CONFIG_ARCH_WANTS_THP_SWAP=y +# CONFIG_ARCH_WRITECOMBINE is not set +CONFIG_ASN1=y +CONFIG_ASSOCIATIVE_ARRAY=y +CONFIG_ATA=y +CONFIG_ATA_ACPI=y +CONFIG_ATA_FORCE=y +# CONFIG_ATA_SFF is not set +CONFIG_ATA_VERBOSE_ERROR=y +CONFIG_AUDIT=y +CONFIG_AUDITSYSCALL=y +CONFIG_AUDIT_GENERIC=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BLK_CGROUP=y +CONFIG_BLK_CGROUP_IOCOST=y +CONFIG_BLK_CGROUP_RWSTAT=y +CONFIG_BLK_DEBUG_FS=y +CONFIG_BLK_DEV_BSGLIB=y +CONFIG_BLK_DEV_BSG_COMMON=y +CONFIG_BLK_DEV_INTEGRITY=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_NVME=y +CONFIG_BLK_DEV_SD=y +CONFIG_BLK_DEV_SR=y +CONFIG_BLK_DEV_THROTTLING=y +CONFIG_BLK_DEV_ZONED=y +CONFIG_BLK_MQ_PCI=y +CONFIG_BLK_PM=y +CONFIG_BLK_RQ_ALLOC_TIME=y +CONFIG_BLK_SED_OPAL=y +CONFIG_BLK_WBT=y +CONFIG_BLK_WBT_MQ=y +CONFIG_BLOCK_LEGACY_AUTOLOAD=y +CONFIG_BOOT_PRINTK_DELAY=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +CONFIG_BUFFER_HEAD=y +CONFIG_BUG_ON_DATA_CORRUPTION=y +# CONFIG_BUILTIN_DTB is not set +CONFIG_CACHESTAT_SYSCALL=y +CONFIG_CDROM=y +CONFIG_CFS_BANDWIDTH=y +CONFIG_CGROUPS=y +# CONFIG_CGROUP_BPF is not set +# CONFIG_CGROUP_CPUACCT is not set +# CONFIG_CGROUP_DEBUG is not set +# CONFIG_CGROUP_DEVICE is not set +# CONFIG_CGROUP_FREEZER is not set +# CONFIG_CGROUP_HUGETLB is not set +# CONFIG_CGROUP_NET_CLASSID is not set +# CONFIG_CGROUP_NET_PRIO is not set +# CONFIG_CGROUP_PIDS is not set +# CONFIG_CGROUP_RDMA is not set +CONFIG_CGROUP_SCHED=y +CONFIG_CHECKPOINT_RESTORE=y +CONFIG_CHR_DEV_SG=y +CONFIG_CLZ_TAB=y +CONFIG_CMA=y +CONFIG_CMA_ALIGNMENT=8 +CONFIG_CMA_AREAS=7 +# CONFIG_CMA_DEBUGFS is not set +CONFIG_CMA_SIZE_MBYTES=16 +# CONFIG_CMA_SIZE_SEL_MAX is not set +CONFIG_CMA_SIZE_SEL_MBYTES=y +# CONFIG_CMA_SIZE_SEL_MIN is not set +# CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set +# CONFIG_CMA_SYSFS is not set +CONFIG_CMDLINE_BOOTLOADER=y +CONFIG_COMMON_CLK=y +# CONFIG_COMMON_CLK_LOONGSON2 is not set +CONFIG_COMPACT_UNEVICTABLE_DEFAULT=1 +# CONFIG_COMPAT_32BIT_TIME is not set +CONFIG_CONNECTOR=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_CONTEXT_TRACKING=y +CONFIG_CONTEXT_TRACKING_IDLE=y +CONFIG_CONTIG_ALLOC=y +CONFIG_COREDUMP=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_CPUSETS=y +# CONFIG_CPUSETS_V1 is not set +CONFIG_CPU_HAS_FPU=y +CONFIG_CPU_HAS_LASX=y +CONFIG_CPU_HAS_LBT=y +CONFIG_CPU_HAS_LSX=y +CONFIG_CPU_HAS_PREFETCH=y +CONFIG_CPU_ISOLATION=y +CONFIG_CPU_MITIGATIONS=y +CONFIG_CPU_PM=y +CONFIG_CPU_RMAP=y +CONFIG_CRC16=y +CONFIG_CRC64=y +CONFIG_CRC64_ROCKSOFT=y +CONFIG_CRC_T10DIF=y +CONFIG_CROSS_MEMORY_ATTACH=y +CONFIG_CRYPTO_CBC=y +CONFIG_CRYPTO_CRC32=y +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_CRC32_LOONGARCH is not set +CONFIG_CRYPTO_CRC64_ROCKSOFT=y +CONFIG_CRYPTO_CRCT10DIF=y +CONFIG_CRYPTO_ECB=y +CONFIG_CRYPTO_HMAC=y +CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y +CONFIG_CRYPTO_LIB_GF128MUL=y +CONFIG_CRYPTO_LIB_POLY1305_RSIZE=1 +CONFIG_CRYPTO_LIB_SHA1=y +CONFIG_CRYPTO_LIB_SHA256=y +CONFIG_CRYPTO_LIB_UTILS=y +CONFIG_CRYPTO_LZO=y +CONFIG_CRYPTO_MD5=y +CONFIG_CRYPTO_RNG=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_RSA=y +CONFIG_CRYPTO_SHA1=y +CONFIG_CRYPTO_SHA256=y +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_LIST=y +CONFIG_DEBUG_MEMORY_INIT=y +CONFIG_DEBUG_MISC=y +CONFIG_DETECT_HUNG_TASK=y +# CONFIG_DEVFREQ_GOV_PASSIVE is not set +# CONFIG_DEVFREQ_GOV_PERFORMANCE is not set +# CONFIG_DEVFREQ_GOV_POWERSAVE is not set +# CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND is not set +# CONFIG_DEVFREQ_GOV_USERSPACE is not set +CONFIG_DEVFREQ_THERMAL=y +CONFIG_DEVMEM=y +CONFIG_DEVTMPFS=y +CONFIG_DMA_CMA=y +CONFIG_DMA_NEED_SYNC=y +CONFIG_DMA_SHARED_BUFFER=y +CONFIG_DMI=y +CONFIG_DMIID=y +CONFIG_DMI_SCAN_MACHINE_NON_EFI_FALLBACK=y +CONFIG_DMI_SYSFS=y +CONFIG_DRM=y +CONFIG_DRM_BRIDGE=y +CONFIG_DRM_FBDEV_EMULATION=y +CONFIG_DRM_FBDEV_OVERALLOC=100 +CONFIG_DRM_KMS_HELPER=y +CONFIG_DRM_LOAD_EDID_FIRMWARE=y +CONFIG_DRM_LOONGSON=y +CONFIG_DRM_PANEL=y +CONFIG_DRM_PANEL_BRIDGE=y +CONFIG_DRM_PANEL_ORIENTATION_QUIRKS=y +CONFIG_DRM_TTM=y +CONFIG_DRM_TTM_HELPER=y +# CONFIG_DRM_WERROR is not set +CONFIG_DTC=y +CONFIG_DUMMY_CONSOLE=y +CONFIG_EFI=y +CONFIG_EFIVAR_FS=m +# CONFIG_EFI_BOOTLOADER_CONTROL is not set +# CONFIG_EFI_CAPSULE_LOADER is not set +# CONFIG_EFI_COCO_SECRET is not set +CONFIG_EFI_CUSTOM_SSDT_OVERLAYS=y +# CONFIG_EFI_DISABLE_PCI_DMA is not set +# CONFIG_EFI_DISABLE_RUNTIME is not set +CONFIG_EFI_EARLYCON=y +CONFIG_EFI_ESRT=y +CONFIG_EFI_GENERIC_STUB=y +CONFIG_EFI_RUNTIME_WRAPPERS=y +CONFIG_EFI_STUB=y +# CONFIG_EFI_TEST is not set +CONFIG_EFI_ZBOOT=y +CONFIG_ELF_CORE=y +CONFIG_ENCRYPTED_KEYS=y +CONFIG_EXCLUSIVE_SYSTEM_RAM=y +CONFIG_EXPORTFS_BLOCK_OPS=y +CONFIG_EXT4_FS=y +CONFIG_FAILOVER=y +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_FANOTIFY=y +CONFIG_FB=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_IMAGEBLIT=y +CONFIG_FB_CORE=y +CONFIG_FB_DEFERRED_IO=y +CONFIG_FB_DEVICE=y +CONFIG_FB_EFI=y +CONFIG_FB_IOMEM_FOPS=y +CONFIG_FB_IOMEM_HELPERS=y +CONFIG_FB_MODE_HELPERS=y +CONFIG_FB_SIMPLE=y +CONFIG_FB_SYSMEM_FOPS=y +CONFIG_FB_SYSMEM_HELPERS=y +CONFIG_FB_SYSMEM_HELPERS_DEFERRED=y +CONFIG_FB_SYS_COPYAREA=y +CONFIG_FB_SYS_FILLRECT=y +CONFIG_FB_SYS_IMAGEBLIT=y +CONFIG_FB_TILEBLITTING=y +CONFIG_FHANDLE=y +CONFIG_FIRMWARE_EDID=y +CONFIG_FIRMWARE_TABLE=y +CONFIG_FIX_EARLYCON_MEM=y +# CONFIG_FLATMEM_MANUAL is not set +CONFIG_FONTS=y +# CONFIG_FONT_10x18 is not set +# CONFIG_FONT_6x10 is not set +# CONFIG_FONT_6x11 is not set +# CONFIG_FONT_7x14 is not set +CONFIG_FONT_8x16=y +CONFIG_FONT_8x8=y +# CONFIG_FONT_ACORN_8x8 is not set +# CONFIG_FONT_MINI_4x6 is not set +# CONFIG_FONT_PEARL_8x8 is not set +# CONFIG_FONT_SUN12x22 is not set +# CONFIG_FONT_SUN8x16 is not set +CONFIG_FONT_SUPPORT=y +CONFIG_FONT_TER16x32=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y +CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y +CONFIG_FREEZER=y +CONFIG_FS_IOMAP=y +CONFIG_FS_MBCACHE=y +CONFIG_FUNCTION_ALIGNMENT=0 +CONFIG_FW_CACHE=y +CONFIG_FW_LOADER_PAGED_BUF=y +CONFIG_FW_LOADER_SYSFS=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_GENERIC_BUG=y +CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CMOS_UPDATE=y +CONFIG_GENERIC_CPU_AUTOPROBE=y +CONFIG_GENERIC_CPU_DEVICES=y +CONFIG_GENERIC_CPU_VULNERABILITIES=y +CONFIG_GENERIC_CSUM=y +CONFIG_GENERIC_ENTRY=y +CONFIG_GENERIC_GETTIMEOFDAY=y +CONFIG_GENERIC_IOREMAP=y +CONFIG_GENERIC_IRQ_CHIP=y +CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y +CONFIG_GENERIC_IRQ_MATRIX_ALLOCATOR=y +CONFIG_GENERIC_IRQ_MIGRATION=y +CONFIG_GENERIC_IRQ_MULTI_HANDLER=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_LIB_ASHLDI3=y +CONFIG_GENERIC_LIB_ASHRDI3=y +CONFIG_GENERIC_LIB_CMPDI2=y +CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED=y +CONFIG_GENERIC_LIB_LSHRDI3=y +CONFIG_GENERIC_LIB_UCMPDI2=y +CONFIG_GENERIC_MSI_IRQ=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_PHY=y +CONFIG_GENERIC_SCHED_CLOCK=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_GENERIC_STRNCPY_FROM_USER=y +CONFIG_GENERIC_STRNLEN_USER=y +CONFIG_GENERIC_TIME_VSYSCALL=y +CONFIG_GLOB=y +CONFIG_GPIO_ACPI=y +CONFIG_GPIO_CDEV=y +CONFIG_GPIO_CDEV_V1=y +# CONFIG_GPIO_LOONGSON_64BIT is not set +CONFIG_GROUP_SCHED_WEIGHT=y +CONFIG_HAMRADIO=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HDMI=y +CONFIG_HIBERNATE_CALLBACKS=y +CONFIG_HIBERNATION=y +CONFIG_HIBERNATION_COMP_LZO=y +CONFIG_HIBERNATION_DEF_COMP="lzo" +CONFIG_HIBERNATION_SNAPSHOT_DEV=y +CONFIG_HID=y +CONFIG_HIDRAW=y +CONFIG_HID_GENERIC=y +CONFIG_HID_SUPPORT=y +CONFIG_HOTPLUG_CPU=y +CONFIG_HUGETLBFS=y +CONFIG_HUGETLB_PAGE=y +CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP=y +CONFIG_HWMON=y +CONFIG_HZ=250 +# CONFIG_HZ_100 is not set +CONFIG_HZ_250=y +CONFIG_HZ_PERIODIC=y +CONFIG_I2C=y +CONFIG_I2C_ALGOBIT=y +# CONFIG_I2C_AMD_MP2 is not set +CONFIG_I2C_BOARDINFO=y +# CONFIG_I2C_LS2X is not set +# CONFIG_I2C_ZHAOXIN is not set +CONFIG_INITRAMFS_PRESERVE_MTIME=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_INIT_STACK_ALL_ZERO=y +# CONFIG_INIT_STACK_NONE is not set +CONFIG_INPUT=y +CONFIG_INPUT_KEYBOARD=y +CONFIG_INPUT_LEDS=y +# CONFIG_INPUT_MISC is not set +CONFIG_INPUT_MOUSE=y +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +CONFIG_INPUT_SPARSEKMAP=y +# CONFIG_IOMMUFD is not set +# CONFIG_IOMMU_DEBUGFS is not set +CONFIG_IOMMU_SUPPORT=y +CONFIG_IO_URING=y +CONFIG_IRQCHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_DOMAIN_HIERARCHY=y +CONFIG_IRQ_FASTEOI_HIERARCHY_HANDLERS=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_LOONGARCH_CPU=y +CONFIG_IRQ_MSI_LIB=y +CONFIG_IRQ_POLL=y +CONFIG_IRQ_WORK=y +# CONFIG_ISCSI_IBFT is not set +CONFIG_ISO9660_FS=y +CONFIG_JBD2=y +CONFIG_JUMP_LABEL=y +CONFIG_KALLSYMS=y +CONFIG_KCMP=y +CONFIG_KERNEL_GZIP=y +CONFIG_KEYS=y +CONFIG_KSM=y +CONFIG_L1_CACHE_SHIFT=6 +CONFIG_LEDS_TRIGGER_CPU=y +CONFIG_LEDS_TRIGGER_DISK=y +CONFIG_LEDS_TRIGGER_MTD=y +CONFIG_LEDS_TRIGGER_PANIC=y +CONFIG_LEGACY_TIOCSTI=y +CONFIG_LIBFDT=y +CONFIG_LOCKUP_DETECTOR=y +CONFIG_LOCK_DEBUGGING_SUPPORT=y +CONFIG_LOCK_SPIN_ON_OWNER=y +CONFIG_LOONGARCH=y +CONFIG_LOONGARCH_PLATFORM_DEVICES=y +# CONFIG_LOONGSON2_GUTS is not set +# CONFIG_LOONGSON2_PM is not set +# CONFIG_LOONGSON2_THERMAL is not set +CONFIG_LOONGSON_EIOINTC=y +CONFIG_LOONGSON_HTVEC=y +CONFIG_LOONGSON_LAPTOP=y +CONFIG_LOONGSON_LIOINTC=y +CONFIG_LOONGSON_PCH_LPC=y +CONFIG_LOONGSON_PCH_MSI=y +CONFIG_LOONGSON_PCH_PIC=y +CONFIG_LSM="landlock,lockdown,yama,loadpin,safesetid,integrity,apparmor,selinux,smack,tomoyo,bpf" +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_MACH_LOONGSON64=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x01b6 +CONFIG_MAGIC_SYSRQ_SERIAL=y +# CONFIG_MEMCG is not set +CONFIG_MEMORY=y +CONFIG_MEMORY_ISOLATION=y +CONFIG_MIGRATION=y +CONFIG_MMU_GATHER_MERGE_VMAS=y +CONFIG_MMU_LAZY_TLB_REFCOUNT=y +CONFIG_MODULES_USE_ELF_RELA=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_MOUSE_BCM5974 is not set +# CONFIG_MOUSE_CYAPA is not set +CONFIG_MOUSE_PS2=y +CONFIG_MOUSE_PS2_ALPS=y +CONFIG_MOUSE_PS2_BYD=y +CONFIG_MOUSE_PS2_CYPRESS=y +# CONFIG_MOUSE_PS2_ELANTECH is not set +CONFIG_MOUSE_PS2_LOGIPS2PP=y +CONFIG_MOUSE_PS2_SMBUS=y +CONFIG_MOUSE_PS2_SYNAPTICS=y +CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS=y +# CONFIG_MOUSE_PS2_TOUCHKIT is not set +CONFIG_MOUSE_PS2_TRACKPOINT=y +# CONFIG_MOUSE_SERIAL is not set +# CONFIG_MOUSE_VSXXXAA is not set +# CONFIG_MOXA_INTELLIO is not set +# CONFIG_MOXA_SMARTIO is not set +CONFIG_MPILIB=y +CONFIG_MQ_IOSCHED_DEADLINE=y +CONFIG_MUTEX_SPIN_ON_OWNER=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NEED_PER_CPU_EMBED_FIRST_CHUNK=y +CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK=y +# CONFIG_NET_CLS_CGROUP is not set +CONFIG_NET_DEVMEM=y +CONFIG_NET_EGRESS=y +CONFIG_NET_FAILOVER=y +CONFIG_NET_FLOW_LIMIT=y +CONFIG_NET_INGRESS=y +CONFIG_NET_PTP_CLASSIFY=y +CONFIG_NET_XGRESS=y +CONFIG_NLS=y +CONFIG_NR_CPUS=64 +CONFIG_NVMEM=y +CONFIG_NVMEM_LAYOUTS=y +CONFIG_NVMEM_SYSFS=y +CONFIG_NVME_CORE=y +CONFIG_NVME_HWMON=y +CONFIG_NVME_MULTIPATH=y +CONFIG_NVME_VERBOSE_ERRORS=y +# CONFIG_N_HDLC is not set +CONFIG_OF=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_GPIO=y +CONFIG_OF_IRQ=y +CONFIG_OF_KOBJ=y +CONFIG_OID_REGISTRY=y +CONFIG_PADATA=y +CONFIG_PAGE_EXTENSION=y +CONFIG_PAGE_POISONING=y +CONFIG_PAGE_POOL=y +CONFIG_PAGE_POOL_STATS=y +CONFIG_PAGE_REPORTING=y +CONFIG_PAGE_SHIFT=14 +CONFIG_PAGE_SIZE_16KB=y +CONFIG_PAGE_SIZE_LESS_THAN_256KB=y +CONFIG_PAGE_SIZE_LESS_THAN_64KB=y +# CONFIG_PANIC_ON_OOPS is not set +CONFIG_PANIC_ON_OOPS_VALUE=0 +CONFIG_PATA_TIMINGS=y +CONFIG_PCI=y +CONFIG_PCIEAER=y +CONFIG_PCIEASPM=y +CONFIG_PCIEASPM_DEFAULT=y +# CONFIG_PCIEASPM_PERFORMANCE is not set +# CONFIG_PCIEASPM_POWERSAVE is not set +# CONFIG_PCIEASPM_POWER_SUPERSAVE is not set +CONFIG_PCIEPORTBUS=y +CONFIG_PCIE_DPC=y +# CONFIG_PCIE_EDR is not set +CONFIG_PCIE_PME=y +CONFIG_PCIE_PTM=y +CONFIG_PCI_ATS=y +CONFIG_PCI_DOMAINS=y +CONFIG_PCI_DOMAINS_GENERIC=y +CONFIG_PCI_ECAM=y +CONFIG_PCI_IOV=y +CONFIG_PCI_LABEL=y +CONFIG_PCI_LOONGSON=y +CONFIG_PCI_MSI=y +CONFIG_PCI_MSI_ARCH_FALLBACKS=y +CONFIG_PCI_REALLOC_ENABLE_AUTO=y +CONFIG_PERF_USE_VMALLOC=y +CONFIG_PGTABLE_3LEVEL=y +CONFIG_PGTABLE_HAS_HUGE_LEAVES=y +CONFIG_PGTABLE_LEVELS=3 +CONFIG_PHYS_ADDR_T_64BIT=y +CONFIG_PM=y +# CONFIG_PMIC_OPREGION is not set +CONFIG_PM_ADVANCED_DEBUG=y +CONFIG_PM_CLK=y +CONFIG_PM_DEBUG=y +CONFIG_PM_DEVFREQ=y +# CONFIG_PM_DEVFREQ_EVENT is not set +CONFIG_PM_OPP=y +CONFIG_PM_SLEEP=y +CONFIG_PM_SLEEP_DEBUG=y +CONFIG_PM_SLEEP_SMP=y +CONFIG_PM_STD_PARTITION="" +# CONFIG_PM_TEST_SUSPEND is not set +CONFIG_PNP=y +CONFIG_PNPACPI=y +# CONFIG_PNP_DEBUG_MESSAGES is not set +CONFIG_POSIX_MQUEUE=y +CONFIG_POSIX_MQUEUE_SYSCTL=y +CONFIG_POWER_SUPPLY=y +CONFIG_POWER_SUPPLY_HWMON=y +CONFIG_PPS=y +# CONFIG_PREEMPT_NONE is not set +CONFIG_PREEMPT_VOLUNTARY=y +CONFIG_PREEMPT_VOLUNTARY_BUILD=y +CONFIG_PRINTK_TIME=y +CONFIG_PROC_CHILDREN=y +CONFIG_PROC_EVENTS=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_PROC_PID_CPUSET=y +CONFIG_PTP_1588_CLOCK=y +CONFIG_PTP_1588_CLOCK_OPTIONAL=y +CONFIG_QUEUED_RWLOCKS=y +CONFIG_QUEUED_SPINLOCKS=y +CONFIG_RANDSTRUCT_NONE=y +CONFIG_RAS=y +CONFIG_RATIONAL=y +# CONFIG_RAVE_SP_CORE is not set +CONFIG_REGMAP=y +CONFIG_REGMAP_I2C=y +CONFIG_REGMAP_MMIO=y +CONFIG_REGMAP_SPI=y +CONFIG_RELAY=y +CONFIG_RELOCATABLE=y +CONFIG_RESET_ATTACK_MITIGATION=y +CONFIG_RFS_ACCEL=y +CONFIG_RPS=y +CONFIG_RSEQ=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_LOONGSON=y +CONFIG_RTC_I2C_AND_SPI=y +# CONFIG_RT_GROUP_SCHED is not set +CONFIG_RWSEM_SPIN_ON_OWNER=y +CONFIG_SATA_AHCI=y +CONFIG_SATA_HOST=y +# CONFIG_SATA_ZPODD is not set +CONFIG_SCHEDSTATS=y +CONFIG_SCHED_AUTOGROUP=y +# CONFIG_SCHED_CORE is not set +CONFIG_SCHED_DEBUG=y +CONFIG_SCHED_INFO=y +CONFIG_SCHED_MM_CID=y +CONFIG_SCHED_SMT=y +CONFIG_SCREEN_INFO=y +CONFIG_SCSI=y +CONFIG_SCSI_COMMON=y +CONFIG_SECCOMP=y +CONFIG_SECCOMP_FILTER=y +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_MANY_PORTS=y +CONFIG_SERIAL_8250_NR_UARTS=4 +CONFIG_SERIAL_8250_PCI=y +CONFIG_SERIAL_8250_PCILIB=y +CONFIG_SERIAL_8250_PERICOM=y +CONFIG_SERIAL_8250_PNP=y +CONFIG_SERIAL_8250_RSA=y +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_DEV_BUS=y +CONFIG_SERIAL_DEV_CTRL_TTYPORT=y +CONFIG_SERIAL_MCTRL_GPIO=y +CONFIG_SERIAL_NONSTANDARD=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIO=y +CONFIG_SERIO_I8042=y +CONFIG_SERIO_LIBPS2=y +CONFIG_SERIO_SERPORT=y +CONFIG_SGL_ALLOC=y +CONFIG_SG_POOL=y +CONFIG_SMP=y +CONFIG_SND=y +CONFIG_SND_AC97_CODEC=y +CONFIG_SND_COMPRESS_OFFLOAD=y +CONFIG_SND_CTL_LED=y +CONFIG_SND_DYNAMIC_MINORS=y +CONFIG_SND_HDA=y +# CONFIG_SND_HDA_CODEC_ANALOG is not set +# CONFIG_SND_HDA_CODEC_CA0110 is not set +# CONFIG_SND_HDA_CODEC_CA0132 is not set +# CONFIG_SND_HDA_CODEC_CIRRUS is not set +# CONFIG_SND_HDA_CODEC_CMEDIA is not set +CONFIG_SND_HDA_CODEC_CONEXANT=y +CONFIG_SND_HDA_CODEC_HDMI=y +# CONFIG_SND_HDA_CODEC_REALTEK is not set +# CONFIG_SND_HDA_CODEC_SI3054 is not set +# CONFIG_SND_HDA_CODEC_SIGMATEL is not set +# CONFIG_SND_HDA_CODEC_VIA is not set +CONFIG_SND_HDA_CORE=y +CONFIG_SND_HDA_GENERIC=y +CONFIG_SND_HDA_GENERIC_LEDS=y +CONFIG_SND_HDA_HWDEP=y +# CONFIG_SND_HDA_INPUT_BEEP is not set +CONFIG_SND_HDA_INTEL=y +# CONFIG_SND_HDA_PATCH_LOADER is not set +# CONFIG_SND_HDA_RECONFIG is not set +CONFIG_SND_HWDEP=y +CONFIG_SND_INTEL_DSP_CONFIG=y +CONFIG_SND_INTEL_NHLT=y +CONFIG_SND_INTEL_SOUNDWIRE_ACPI=y +CONFIG_SND_JACK=y +CONFIG_SND_JACK_INPUT_DEV=y +CONFIG_SND_MIXER_OSS=y +CONFIG_SND_PCM=y +CONFIG_SND_PCM_OSS=y +CONFIG_SND_PCM_TIMER=y +CONFIG_SND_RAWMIDI=y +CONFIG_SND_SEQUENCER=y +CONFIG_SND_SEQUENCER_OSS=y +CONFIG_SND_SEQ_DEVICE=y +CONFIG_SND_SEQ_DUMMY=y +CONFIG_SND_SEQ_MIDI=y +CONFIG_SND_SEQ_MIDI_EVENT=y +CONFIG_SND_SEQ_VIRMIDI=y +CONFIG_SND_SOC=y +CONFIG_SND_SOC_AC97_BUS=y +CONFIG_SND_SOC_AC97_CODEC=y +CONFIG_SND_SOC_I2C_AND_SPI=y +CONFIG_SND_SOC_LOONGSON_CARD=y +CONFIG_SND_SOC_LOONGSON_I2S_PCI=y +CONFIG_SND_TIMER=y +CONFIG_SND_VIRMIDI=y +CONFIG_SND_VMASTER=y +CONFIG_SOCK_RX_QUEUE_MAPPING=y +CONFIG_SOFTLOCKUP_DETECTOR=y +CONFIG_SOUND=y +CONFIG_SOUND_OSS_CORE=y +CONFIG_SOUND_OSS_CORE_PRECLAIM=y +CONFIG_SPARSEMEM=y +CONFIG_SPARSEMEM_EXTREME=y +CONFIG_SPARSEMEM_MANUAL=y +CONFIG_SPARSEMEM_VMEMMAP=y +CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y +CONFIG_SPARSE_IRQ=y +CONFIG_SPI=y +CONFIG_SPI_DYNAMIC=y +CONFIG_SPI_LOONGSON_CORE=y +CONFIG_SPI_LOONGSON_PCI=y +CONFIG_SPI_LOONGSON_PLATFORM=y +CONFIG_SPI_MASTER=y +CONFIG_SPI_MEM=y +CONFIG_SPI_SPIDEV=y +CONFIG_SPLIT_PTE_PTLOCKS=y +# CONFIG_SQUASHFS_COMPILE_DECOMP_MULTI_PERCPU is not set +CONFIG_SQUASHFS_COMPILE_DECOMP_SINGLE=y +CONFIG_SQUASHFS_DECOMP_SINGLE=y +CONFIG_STACKPROTECTOR=y +CONFIG_STACKPROTECTOR_STRONG=y +CONFIG_STACKTRACE=y +CONFIG_STRICT_DEVMEM=y +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_SWIOTLB=y +CONFIG_SYNC_FILE=y +CONFIG_SYSCTL_ARCH_UNALIGN_ALLOW=y +CONFIG_SYSCTL_ARCH_UNALIGN_NO_WARN=y +CONFIG_SYSCTL_EXCEPTION_TRACE=y +CONFIG_SYSFB=y +# CONFIG_SYSFB_SIMPLEFB is not set +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_TASK_XACCT=y +CONFIG_THERMAL=y +CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y +CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0 +CONFIG_THERMAL_GOV_BANG_BANG=y +CONFIG_THERMAL_GOV_FAIR_SHARE=y +CONFIG_THERMAL_GOV_STEP_WISE=y +CONFIG_THERMAL_GOV_USER_SPACE=y +CONFIG_THERMAL_HWMON=y +CONFIG_THERMAL_OF=y +CONFIG_THERMAL_STATISTICS=y +CONFIG_THP_SWAP=y +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_TMPFS_INODE64=y +CONFIG_TRANSPARENT_HUGEPAGE=y +CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y +# CONFIG_TRANSPARENT_HUGEPAGE_MADVISE is not set +# CONFIG_TRANSPARENT_HUGEPAGE_NEVER is not set +CONFIG_TREE_RCU=y +CONFIG_TREE_SRCU=y +CONFIG_UCS2_STRING=y +# CONFIG_UEVENT_HELPER is not set +# CONFIG_UNWINDER_GUESS is not set +# CONFIG_UNWINDER_ORC is not set +CONFIG_UNWINDER_PROLOGUE=y +CONFIG_USB=y +CONFIG_USB_COMMON=y +CONFIG_USB_EHCI_HCD=y +# CONFIG_USB_EHCI_HCD_PLATFORM is not set +CONFIG_USB_EHCI_PCI=y +CONFIG_USB_HID=y +CONFIG_USB_HIDDEV=y +# CONFIG_USB_LJCA is not set +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_HCD_PCI=y +# CONFIG_USB_OHCI_HCD_PLATFORM is not set +CONFIG_USB_PCI=y +CONFIG_USB_STORAGE=y +CONFIG_USB_SUPPORT=y +CONFIG_USB_UAS=y +# CONFIG_USB_UHCI_HCD is not set +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_XHCI_PCI=y +# CONFIG_USB_XHCI_PLATFORM is not set +CONFIG_USERFAULTFD=y +CONFIG_USER_STACKTRACE_SUPPORT=y +CONFIG_USE_PERCPU_NUMA_NODE_ID=y +CONFIG_VDSO_GETRANDOM=y +CONFIG_VGA_ARB=y +CONFIG_VGA_ARB_MAX_GPUS=16 +CONFIG_VIDEO=y +CONFIG_VIRTIO_VSOCKETS_COMMON=y +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_VSOCKETS=y +CONFIG_VSOCKETS_LOOPBACK=y +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_VT_CONSOLE_SLEEP=y +CONFIG_VT_HW_CONSOLE_BINDING=y +CONFIG_XARRAY_MULTI=y +CONFIG_XPS=y +CONFIG_XXHASH=y +# CONFIG_ZONEFS_FS is not set +CONFIG_ZONE_DMA32=y diff --git a/mieru/Makefile b/mieru/Makefile index 51854fc3ab..f15e27bf5e 100644 --- a/mieru/Makefile +++ b/mieru/Makefile @@ -32,7 +32,7 @@ PROJECT_NAME=$(shell basename "${ROOT}") # - pkg/version/current.go # # Use `tools/bump_version.sh` script to change all those files at one shot. -VERSION="3.17.1" +VERSION="3.18.0" # Build binaries and installation packages. .PHONY: build diff --git a/mieru/build/package/mieru/amd64/debian/DEBIAN/control b/mieru/build/package/mieru/amd64/debian/DEBIAN/control index 4cadf07b63..eaf38c09ac 100755 --- a/mieru/build/package/mieru/amd64/debian/DEBIAN/control +++ b/mieru/build/package/mieru/amd64/debian/DEBIAN/control @@ -1,5 +1,5 @@ Package: mieru -Version: 3.17.1 +Version: 3.18.0 Section: net Priority: optional Architecture: amd64 diff --git a/mieru/build/package/mieru/amd64/rpm/mieru.spec b/mieru/build/package/mieru/amd64/rpm/mieru.spec index 8b3e881054..5b3966e408 100644 --- a/mieru/build/package/mieru/amd64/rpm/mieru.spec +++ b/mieru/build/package/mieru/amd64/rpm/mieru.spec @@ -1,5 +1,5 @@ Name: mieru -Version: 3.17.1 +Version: 3.18.0 Release: 1%{?dist} Summary: Mieru proxy client License: GPLv3+ diff --git a/mieru/build/package/mieru/arm64/debian/DEBIAN/control b/mieru/build/package/mieru/arm64/debian/DEBIAN/control index e6b0bcf9b7..6672f65241 100755 --- a/mieru/build/package/mieru/arm64/debian/DEBIAN/control +++ b/mieru/build/package/mieru/arm64/debian/DEBIAN/control @@ -1,5 +1,5 @@ Package: mieru -Version: 3.17.1 +Version: 3.18.0 Section: net Priority: optional Architecture: arm64 diff --git a/mieru/build/package/mieru/arm64/rpm/mieru.spec b/mieru/build/package/mieru/arm64/rpm/mieru.spec index 8b3e881054..5b3966e408 100644 --- a/mieru/build/package/mieru/arm64/rpm/mieru.spec +++ b/mieru/build/package/mieru/arm64/rpm/mieru.spec @@ -1,5 +1,5 @@ Name: mieru -Version: 3.17.1 +Version: 3.18.0 Release: 1%{?dist} Summary: Mieru proxy client License: GPLv3+ diff --git a/mieru/build/package/mita/amd64/debian/DEBIAN/control b/mieru/build/package/mita/amd64/debian/DEBIAN/control index f607b0cae3..791594c112 100755 --- a/mieru/build/package/mita/amd64/debian/DEBIAN/control +++ b/mieru/build/package/mita/amd64/debian/DEBIAN/control @@ -1,5 +1,5 @@ Package: mita -Version: 3.17.1 +Version: 3.18.0 Section: net Priority: optional Architecture: amd64 diff --git a/mieru/build/package/mita/amd64/rpm/mita.spec b/mieru/build/package/mita/amd64/rpm/mita.spec index b5e3a5bb95..3f69efdc35 100644 --- a/mieru/build/package/mita/amd64/rpm/mita.spec +++ b/mieru/build/package/mita/amd64/rpm/mita.spec @@ -1,5 +1,5 @@ Name: mita -Version: 3.17.1 +Version: 3.18.0 Release: 1%{?dist} Summary: Mieru proxy server License: GPLv3+ diff --git a/mieru/build/package/mita/arm64/debian/DEBIAN/control b/mieru/build/package/mita/arm64/debian/DEBIAN/control index 324ab1cac3..31a84db354 100755 --- a/mieru/build/package/mita/arm64/debian/DEBIAN/control +++ b/mieru/build/package/mita/arm64/debian/DEBIAN/control @@ -1,5 +1,5 @@ Package: mita -Version: 3.17.1 +Version: 3.18.0 Section: net Priority: optional Architecture: arm64 diff --git a/mieru/build/package/mita/arm64/rpm/mita.spec b/mieru/build/package/mita/arm64/rpm/mita.spec index 9d67f9c33e..0102e93ed6 100644 --- a/mieru/build/package/mita/arm64/rpm/mita.spec +++ b/mieru/build/package/mita/arm64/rpm/mita.spec @@ -1,5 +1,5 @@ Name: mita -Version: 3.17.1 +Version: 3.18.0 Release: 1%{?dist} Summary: Mieru proxy server License: GPLv3+ diff --git a/mieru/configs/examples/client_config.json b/mieru/configs/examples/client_config.json index dc72b09bbc..eb6ad065f9 100644 --- a/mieru/configs/examples/client_config.json +++ b/mieru/configs/examples/client_config.json @@ -25,7 +25,8 @@ "mtu": 1400, "multiplexing": { "level": "MULTIPLEXING_HIGH" - } + }, + "handshakeMode": "HANDSHAKE_NO_WAIT" } ], "activeProfile": "default", diff --git a/mieru/configs/examples/client_config_with_socks5_authentication.json b/mieru/configs/examples/client_config_with_socks5_authentication.json index dcfaa70233..7f28c82f30 100644 --- a/mieru/configs/examples/client_config_with_socks5_authentication.json +++ b/mieru/configs/examples/client_config_with_socks5_authentication.json @@ -25,7 +25,8 @@ "mtu": 1400, "multiplexing": { "level": "MULTIPLEXING_HIGH" - } + }, + "handshakeMode": "HANDSHAKE_NO_WAIT" } ], "activeProfile": "default", diff --git a/mieru/docs/client-install.md b/mieru/docs/client-install.md index 3bf230abf2..f7654e9539 100644 --- a/mieru/docs/client-install.md +++ b/mieru/docs/client-install.md @@ -46,7 +46,8 @@ An example of client configuration is as follows. "mtu": 1400, "multiplexing": { "level": "MULTIPLEXING_HIGH" - } + }, + "handshakeMode": "HANDSHAKE_NO_WAIT" } ], "activeProfile": "default", @@ -67,11 +68,12 @@ Please use a text editor to modify the following fields. 4. [Optional] If you have registered a domain name for the proxy server, please fill in the domain name in `profiles` -> `servers` -> `domainName`. Otherwise, do not modify this property. 5. Fill in `profiles` -> `servers` -> `portBindings` -> `port` with the TCP or UDP port number that mita is listening to. The port number must be the same as the one set in the proxy server. If you want to listen to a range of consecutive port numbers, you can also use the `portRange` property instead. 6. [Optional] Specify a value between 1280 and 1400 for the `profiles` -> `mtu` property. The default value is 1400. This value must be the same as proxy server. -7. [Optional] If you want to adjust the frequency of multiplexing, you can set a value for the `profiles` -> `multiplexing` -> `level` property. The values you can use here include `MULTIPLEXING_OFF`, `MULTIPLEXING_LOW`, `MULTIPLEXING_MIDDLE`, and `MULTIPLEXING_HIGH`. `MULTIPLEXING_OFF` will disable multiplexing, and the default value is `MULTIPLEXING_LOW`. -8. Please specify a value between 1025 and 65535 for the `rpcPort` property. -9. Please specify a value between 1025 and 65535 for the `socks5Port` property. This port cannot be the same as `rpcPort`. -10. [Optional] If the client needs to provide proxy services to other devices on the LAN, set the `socks5ListenLAN` property to `true`. -11. [Optional] If you want to enable HTTP / HTTPS proxy, Please specify a value between 1025 and 65535 for the `httpProxyPort` property. This port cannot be the same as `rpcPort` or `socks5Port`. If the client needs to provide HTTP / HTTPS proxy services to other devices on the LAN, set the `httpProxyListenLAN` property to `true`. If you want to disable HTTP / HTTPS proxy, please delete `httpProxyPort` and `httpProxyListenLAN` property. +7. [Optional] If you want to adjust the frequency of multiplexing, you can set a value for the `profiles` -> `multiplexing` -> `level` property. The values you can use here include `MULTIPLEXING_OFF`, `MULTIPLEXING_LOW`, `MULTIPLEXING_MIDDLE`, and `MULTIPLEXING_HIGH`. `MULTIPLEXING_OFF` will disable multiplexing. The default value is `MULTIPLEXING_LOW`. +8. [Optional] If you want to enable 0-RTT handshake, you can set the value of `profiles` -> `handshakeMode` property to `HANDSHAKE_NO_WAIT`, otherwise set it to `HANDSHAKE_STANDARD`. The default value is `HANDSHAKE_STANDARD`. +9. Please specify a value between 1025 and 65535 for the `rpcPort` property. +10. Please specify a value between 1025 and 65535 for the `socks5Port` property. This port cannot be the same as `rpcPort`. +11. [Optional] If the client needs to provide proxy services to other devices on the LAN, set the `socks5ListenLAN` property to `true`. +12. [Optional] If you want to enable HTTP / HTTPS proxy, Please specify a value between 1025 and 65535 for the `httpProxyPort` property. This port cannot be the same as `rpcPort` or `socks5Port`. If the client needs to provide HTTP / HTTPS proxy services to other devices on the LAN, set the `httpProxyListenLAN` property to `true`. If you want to disable HTTP / HTTPS proxy, please delete `httpProxyPort` and `httpProxyListenLAN` property. If you have multiple proxy servers installed, or one server listening on multiple ports, you can add them all to the client settings. Each time a new connection is created, mieru will randomly select one of the servers and one of the ports. **If you are using multiple servers, make sure that each server has the mita proxy service started.** diff --git a/mieru/docs/client-install.zh_CN.md b/mieru/docs/client-install.zh_CN.md index 0000524bb5..a3409ae3e4 100644 --- a/mieru/docs/client-install.zh_CN.md +++ b/mieru/docs/client-install.zh_CN.md @@ -46,7 +46,8 @@ mieru apply config "mtu": 1400, "multiplexing": { "level": "MULTIPLEXING_HIGH" - } + }, + "handshakeMode": "HANDSHAKE_NO_WAIT" } ], "activeProfile": "default", @@ -68,10 +69,11 @@ mieru apply config 5. 在 `profiles` -> `servers` -> `portBindings` -> `port` 中填写 mita 监听的 TCP 或 UDP 端口号。这个端口号必须与代理服务器中的设置相同。如果想要监听连续的端口号,也可以改为使用 `portRange` 属性。 6. 【可选】请为 `profiles` -> `mtu` 属性中指定一个从 1280 到 1400 之间的值。默认值为 1400。这个值必须与代理服务器相同。 7. 【可选】如果想要调整多路复用的频率,是更多地创建新连接,还是更多地重用旧连接,可以为 `profiles` -> `multiplexing` -> `level` 属性设定一个值。这里可以使用的值包括 `MULTIPLEXING_OFF`, `MULTIPLEXING_LOW`, `MULTIPLEXING_MIDDLE`, `MULTIPLEXING_HIGH`。其中 `MULTIPLEXING_OFF` 会关闭多路复用功能。默认值为 `MULTIPLEXING_LOW`。 -8. 请为 `rpcPort` 属性指定一个从 1025 到 65535 之间的数值。 -9. 请为 `socks5Port` 属性指定一个从 1025 到 65535 之间的数值。该端口不能与 `rpcPort` 相同。 -10. 【可选】如果客户端需要为局域网中的其他设备提供代理服务,请将 `socks5ListenLAN` 属性设置为 `true`。 -11. 【可选】如果要启动 HTTP / HTTPS 代理,请为 `httpProxyPort` 属性指定一个从 1025 到 65535 之间的数值。该端口不能与 `rpcPort` 和 `socks5Port` 相同。如果需要为局域网中的其他设备提供 HTTP / HTTPS 代理,请将 `httpProxyListenLAN` 属性设置为 `true`。如果不需要 HTTP / HTTPS 代理,请删除 `httpProxyPort` 和 `httpProxyListenLAN` 属性。 +8. 【可选】如果想开启 0-RTT 握手,请将 `profiles` -> `handshakeMode` 属性的值设置为 `HANDSHAKE_NO_WAIT`,否则请设置为 `HANDSHAKE_STANDARD`。默认值为 `HANDSHAKE_STANDARD`。 +9. 请为 `rpcPort` 属性指定一个从 1025 到 65535 之间的数值。 +10. 请为 `socks5Port` 属性指定一个从 1025 到 65535 之间的数值。该端口不能与 `rpcPort` 相同。 +11. 【可选】如果客户端需要为局域网中的其他设备提供代理服务,请将 `socks5ListenLAN` 属性设置为 `true`。 +12. 【可选】如果要启动 HTTP / HTTPS 代理,请为 `httpProxyPort` 属性指定一个从 1025 到 65535 之间的数值。该端口不能与 `rpcPort` 和 `socks5Port` 相同。如果需要为局域网中的其他设备提供 HTTP / HTTPS 代理,请将 `httpProxyListenLAN` 属性设置为 `true`。如果不需要 HTTP / HTTPS 代理,请删除 `httpProxyPort` 和 `httpProxyListenLAN` 属性。 如果你安装了多台代理服务器,或者一台服务器监听多个端口,可以把它们都添加到客户端设置中。每次发起新的连接时,mieru 会随机选取其中的一台服务器和一个端口。**如果使用了多台服务器,请确保每一台服务器都启动了 mita 代理服务。** diff --git a/mieru/docs/server-install.md b/mieru/docs/server-install.md index d0cd2b3105..84fa43b997 100644 --- a/mieru/docs/server-install.md +++ b/mieru/docs/server-install.md @@ -18,32 +18,32 @@ Or you can manually install and configure proxy server using the steps below. ```sh # Debian / Ubuntu - X86_64 -curl -LSO https://github.com/enfein/mieru/releases/download/v3.17.1/mita_3.17.1_amd64.deb +curl -LSO https://github.com/enfein/mieru/releases/download/v3.18.0/mita_3.18.0_amd64.deb # Debian / Ubuntu - ARM 64 -curl -LSO https://github.com/enfein/mieru/releases/download/v3.17.1/mita_3.17.1_arm64.deb +curl -LSO https://github.com/enfein/mieru/releases/download/v3.18.0/mita_3.18.0_arm64.deb # RedHat / CentOS / Rocky Linux - X86_64 -curl -LSO https://github.com/enfein/mieru/releases/download/v3.17.1/mita-3.17.1-1.x86_64.rpm +curl -LSO https://github.com/enfein/mieru/releases/download/v3.18.0/mita-3.18.0-1.x86_64.rpm # RedHat / CentOS / Rocky Linux - ARM 64 -curl -LSO https://github.com/enfein/mieru/releases/download/v3.17.1/mita-3.17.1-1.aarch64.rpm +curl -LSO https://github.com/enfein/mieru/releases/download/v3.18.0/mita-3.18.0-1.aarch64.rpm ``` ## Install mita package ```sh # Debian / Ubuntu - X86_64 -sudo dpkg -i mita_3.17.1_amd64.deb +sudo dpkg -i mita_3.18.0_amd64.deb # Debian / Ubuntu - ARM 64 -sudo dpkg -i mita_3.17.1_arm64.deb +sudo dpkg -i mita_3.18.0_arm64.deb # RedHat / CentOS / Rocky Linux - X86_64 -sudo rpm -Uvh --force mita-3.17.1-1.x86_64.rpm +sudo rpm -Uvh --force mita-3.18.0-1.x86_64.rpm # RedHat / CentOS / Rocky Linux - ARM 64 -sudo rpm -Uvh --force mita-3.17.1-1.aarch64.rpm +sudo rpm -Uvh --force mita-3.18.0-1.aarch64.rpm ``` Those instructions can also be used to upgrade the version of mita software package. @@ -230,7 +230,7 @@ Below is an example to configure a proxy chain. ``` 1. In the `egress` -> `proxies` property, list the information of outbound proxy servers. The current version only supports socks5 outbound, so the value of `protocol` must be set to `SOCKS5_PROXY_PROTOCOL`. If the outbound proxy server requires socks5 username and password authentication, please fill in the `socks5Authentication` property. Otherwise, please remove the `socks5Authentication` property. -2. In the `egress` -> `rules` property, list outbound rules. Outbound actions include `DIRECT`, `PROXY` and `REJECT`. `proxyNames` must be set if `PROXY` action is used. `proxyNames` needs to point to proxies that exist in `egress` -> `proxies` property. +2. In the `egress` -> `rules` property, list outbound rules. From begin to end, the first rule that matches IP address range or DNS suffix is executed. Use wildcard `"*"` to match all IP addresses or domain names. Outbound actions include `DIRECT`, `PROXY` and `REJECT`. `proxyNames` must be set if `PROXY` action is used. `proxyNames` needs to point to proxies that exist in `egress` -> `proxies` property. The default outbound action is `DIRECT`. If you want to turn off the outbound proxy feature, simply set the `egress` property to an empty value `{}`. diff --git a/mieru/docs/server-install.zh_CN.md b/mieru/docs/server-install.zh_CN.md index ead9dd6a37..cb4cd136c6 100644 --- a/mieru/docs/server-install.zh_CN.md +++ b/mieru/docs/server-install.zh_CN.md @@ -18,32 +18,32 @@ sudo python3 setup.py --lang=zh ```sh # Debian / Ubuntu - X86_64 -curl -LSO https://github.com/enfein/mieru/releases/download/v3.17.1/mita_3.17.1_amd64.deb +curl -LSO https://github.com/enfein/mieru/releases/download/v3.18.0/mita_3.18.0_amd64.deb # Debian / Ubuntu - ARM 64 -curl -LSO https://github.com/enfein/mieru/releases/download/v3.17.1/mita_3.17.1_arm64.deb +curl -LSO https://github.com/enfein/mieru/releases/download/v3.18.0/mita_3.18.0_arm64.deb # RedHat / CentOS / Rocky Linux - X86_64 -curl -LSO https://github.com/enfein/mieru/releases/download/v3.17.1/mita-3.17.1-1.x86_64.rpm +curl -LSO https://github.com/enfein/mieru/releases/download/v3.18.0/mita-3.18.0-1.x86_64.rpm # RedHat / CentOS / Rocky Linux - ARM 64 -curl -LSO https://github.com/enfein/mieru/releases/download/v3.17.1/mita-3.17.1-1.aarch64.rpm +curl -LSO https://github.com/enfein/mieru/releases/download/v3.18.0/mita-3.18.0-1.aarch64.rpm ``` ## 安装 mita 软件包 ```sh # Debian / Ubuntu - X86_64 -sudo dpkg -i mita_3.17.1_amd64.deb +sudo dpkg -i mita_3.18.0_amd64.deb # Debian / Ubuntu - ARM 64 -sudo dpkg -i mita_3.17.1_arm64.deb +sudo dpkg -i mita_3.18.0_arm64.deb # RedHat / CentOS / Rocky Linux - X86_64 -sudo rpm -Uvh --force mita-3.17.1-1.x86_64.rpm +sudo rpm -Uvh --force mita-3.18.0-1.x86_64.rpm # RedHat / CentOS / Rocky Linux - ARM 64 -sudo rpm -Uvh --force mita-3.17.1-1.aarch64.rpm +sudo rpm -Uvh --force mita-3.18.0-1.aarch64.rpm ``` 上述指令也可以用来升级 mita 软件包的版本。 @@ -230,7 +230,7 @@ mieru 客户端 -> GFW -> mita 服务器 -> cloudflare 代理客户端 -> cloudf ``` 1. 在 `egress` -> `proxies` 属性中列举出站代理服务器的信息。当前版本只支持 socks5 出站,因此 `protocol` 的值必须设定为 `SOCKS5_PROXY_PROTOCOL`。如果出站代理服务器需要 socks5 用户名和密码验证,请填写 `socks5Authentication` 属性。否则,请删除 `socks5Authentication` 属性。 -2. 在 `egress` -> `rules` 属性中列举出站规则。出站行为包括 `DIRECT`,`PROXY` 和 `REJECT`。其中 `PROXY` 行为必须指定 `proxyNames`。`proxyNames` 需要指向 `egress` -> `proxies` 属性中存在的代理。 +2. 在 `egress` -> `rules` 属性中列举出站规则。从上到下,第一条匹配 IP 地址段或 DNS 后缀的规则会被执行。使用通配符 `"*"` 匹配所有 IP 地址或域名。出站行为包括 `DIRECT`,`PROXY` 和 `REJECT`。其中 `PROXY` 行为必须指定 `proxyNames`。`proxyNames` 需要指向 `egress` -> `proxies` 属性中存在的代理。默认的出站行为是 `DIRECT`。 如果想要关闭出站代理功能,将 `egress` 属性设置为空 `{}` 即可。 diff --git a/mieru/pkg/version/current.go b/mieru/pkg/version/current.go index 031b65d56d..d015b427f4 100644 --- a/mieru/pkg/version/current.go +++ b/mieru/pkg/version/current.go @@ -16,5 +16,5 @@ package version const ( - AppVersion = "3.17.1" + AppVersion = "3.18.0" ) diff --git a/mieru/tools/setup.py b/mieru/tools/setup.py index 997b9a315f..fc813f0b6a 100755 --- a/mieru/tools/setup.py +++ b/mieru/tools/setup.py @@ -461,7 +461,8 @@ class ClientConfig: 'servers': [{ 'ipAddress': '', 'portBindings': [{}], - }] + }], + 'handshakeMode': 'HANDSHAKE_NO_WAIT' }], 'activeProfile': 'default', 'rpcPort': 0, diff --git a/mihomo/adapter/outbound/vless.go b/mihomo/adapter/outbound/vless.go index bacc41fc6f..de3c24ab75 100644 --- a/mihomo/adapter/outbound/vless.go +++ b/mihomo/adapter/outbound/vless.go @@ -456,7 +456,7 @@ func NewVless(option VlessOption) (*Vless, error) { option: &option, } - if s := strings.Split(option.Encryption, "-mlkem768client-"); len(s) == 2 { + 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") @@ -470,14 +470,22 @@ func NewVless(option VlessOption) (*Vless, error) { } 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[1]) + b, err = base64.RawURLEncoding.DecodeString(s[3]) if err != nil { return nil, fmt.Errorf("invaild vless encryption value: %s", option.Encryption) } - if len(b) == 1184 { + if len(b) == encryption.MLKEM768ClientLength { v.encryption = &encryption.ClientInstance{} - if err = v.encryption.Init(b, time.Duration(minutes)*time.Minute); err != nil { + 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 { diff --git a/mihomo/common/convert/converter.go b/mihomo/common/convert/converter.go index a4f89f37cc..c69a258a4f 100644 --- a/mihomo/common/convert/converter.go +++ b/mihomo/common/convert/converter.go @@ -221,6 +221,9 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) { if flow := query.Get("flow"); flow != "" { vless["flow"] = strings.ToLower(flow) } + if encryption := query.Get("encryption"); encryption != "" { + vless["encryption"] = encryption + } proxies = append(proxies, vless) case "vmess": diff --git a/mihomo/component/generater/cmd.go b/mihomo/component/generater/cmd.go index c34c890443..2bd5b9cd86 100644 --- a/mihomo/component/generater/cmd.go +++ b/mihomo/component/generater/cmd.go @@ -46,16 +46,16 @@ func Main(args []string) { } fmt.Println("Config:", configBase64) fmt.Println("Key:", keyPem) - case "vless-mlkem768-keypair": + case "vless-mlkem768": var seed string if len(args) > 1 { seed = args[1] } - seedBase64, pubBase64, err := encryption.GenMLKEM768(seed) + seedBase64, clientBase64, err := encryption.GenMLKEM768(seed) if err != nil { panic(err) } fmt.Println("Seed: " + seedBase64) - fmt.Println("Client: " + pubBase64) + fmt.Println("Client: " + clientBase64) } } diff --git a/mihomo/component/tls/reality.go b/mihomo/component/tls/reality.go index 41c14781bb..2de5c46e73 100644 --- a/mihomo/component/tls/reality.go +++ b/mihomo/component/tls/reality.go @@ -170,7 +170,7 @@ type realityVerifier struct { //var pOffset = utils.MustOK(reflect.TypeOf((*utls.Conn)(nil)).Elem().FieldByName("peerCertificates")).Offset func (c *realityVerifier) VerifyPeerCertificate(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { - //log.Debugln("REALITY localAddr: %v\t is using X25519MLKEM768 for TLS' communication: %v", c.RemoteAddr(), c.HandshakeState.ServerHello.SelectedGroup == utls.X25519MLKEM768) + log.Debugln("REALITY localAddr: %v is using X25519MLKEM768 for TLS' communication: %v", c.RemoteAddr(), c.HandshakeState.ServerHello.ServerShare.Group == utls.X25519MLKEM768) //p, _ := reflect.TypeOf(c.Conn).Elem().FieldByName("peerCertificates") //certs := *(*[]*x509.Certificate)(unsafe.Add(unsafe.Pointer(c.Conn), pOffset)) certs := c.Conn.PeerCertificates() diff --git a/mihomo/component/wildcard/wildcard.go b/mihomo/component/wildcard/wildcard.go index 7fcf15ba34..c22eedf90a 100644 --- a/mihomo/component/wildcard/wildcard.go +++ b/mihomo/component/wildcard/wildcard.go @@ -1,3 +1,12 @@ +// Package wildcard modified IGLOU-EU/go-wildcard to support: +// +// `*` matches zero or more characters +// `?` matches exactly one character +// +// The original go-wildcard library used `.` to match exactly one character, and `?` to match zero or one character. +// `.` is a valid delimiter in domain name matching and should not be used as a wildcard. +// The `?` matching logic strictly matches only one character in most scenarios. +// So, the `?` matching logic in the original go-wildcard library has been removed and its wildcard `.` has been replaced with `?`. package wildcard // copy and modified from https://github.com/IGLOU-EU/go-wildcard/tree/ce22b7af48e487517a492d3727d9386492043e21 @@ -16,12 +25,10 @@ func Match(pattern, s string) bool { } func matchByString(pattern, s string) bool { - var lastErotemeCluster byte - var patternIndex, sIndex, lastStar, lastEroteme int + var patternIndex, sIndex, lastStar int patternLen := len(pattern) sLen := len(s) star := -1 - eroteme := -1 Loop: if sIndex >= sLen { @@ -38,14 +45,8 @@ Loop: return false } switch pattern[patternIndex] { - // Removed dot matching as it conflicts with dot in domains. - // case '.': - // It matches any single character. So, we don't need to check anything. case '?': - // '?' matches one character. Store its position and match exactly one character in the string. - eroteme = patternIndex - lastEroteme = sIndex - lastErotemeCluster = byte(s[sIndex]) + // It matches any single character. So, we don't need to check anything. case '*': // '*' matches zero or more characters. Store its position and increment the pattern index. star = patternIndex @@ -53,15 +54,8 @@ Loop: patternIndex++ goto Loop default: - // If the characters don't match, check if there was a previous '?' or '*' to backtrack. + // If the characters don't match, check if there was a previous '*' to backtrack. if pattern[patternIndex] != s[sIndex] { - if eroteme != -1 { - patternIndex = eroteme + 1 - sIndex = lastEroteme - eroteme = -1 - goto Loop - } - if star != -1 { patternIndex = star + 1 lastStar++ @@ -71,29 +65,18 @@ Loop: return false } - - // If the characters match, check if it was not the same to validate the eroteme. - if eroteme != -1 && lastErotemeCluster != byte(s[sIndex]) { - eroteme = -1 - } } patternIndex++ sIndex++ goto Loop - // Check if the remaining pattern characters are '*' or '?', which can match the end of the string. + // Check if the remaining pattern characters are '*', which can match the end of the string. checkPattern: if patternIndex < patternLen { if pattern[patternIndex] == '*' { patternIndex++ goto checkPattern - } else if pattern[patternIndex] == '?' { - if sIndex >= sLen { - sIndex-- - } - patternIndex++ - goto checkPattern } } diff --git a/mihomo/component/wildcard/wildcard_test.go b/mihomo/component/wildcard/wildcard_test.go index 719a4979ff..64b833d79d 100644 --- a/mihomo/component/wildcard/wildcard_test.go +++ b/mihomo/component/wildcard/wildcard_test.go @@ -25,31 +25,17 @@ func TestMatch(t *testing.T) { {"", "", true}, {"", "*", true}, {"", "**", true}, - {"", "?", true}, - {"", "??", true}, - {"", "?*", true}, - {"", "*?", true}, - {"", ".", false}, - {"", ".?", false}, - {"", "?.", false}, - {"", ".*", false}, - {"", "*.", false}, - {"", "*.?", false}, - {"", "?.*", false}, + {"", "?", false}, + {"", "?*", false}, + {"", "*?", false}, {"a", "", false}, {"a", "a", true}, {"a", "*", true}, {"a", "**", true}, {"a", "?", true}, - {"a", "??", true}, - {"a", ".", false}, - {"a", ".?", false}, - {"a", "?.", false}, - {"a", ".*", false}, - {"a", "*.", false}, - {"a", "*.?", false}, - {"a", "?.*", false}, + {"a", "?*", true}, + {"a", "*?", true}, {"match the exact string", "match the exact string", true}, {"do not match a different string", "this is a different string", false}, @@ -68,22 +54,27 @@ func TestMatch(t *testing.T) { {"match a string with a ?", "match ? string with a ?", true}, {"match a string with a ? at the beginning", "?atch a string with a ? at the beginning", true}, - {"match a string with two ?", "match a string with two ??", true}, - {"match a optional char with a ?", "match a optional? char with a ?", true}, - {"match a optional char with a ?", "match a optional? char with a ?", true}, - {"do not match a string with extra and a ?", "do not match ? string with extra and a ? like this", false}, + {"match a string with two ?", "match a ??ring with two ?", true}, + {"do not match a string with extra ?", "do not match a string with extra ??", false}, - {"do not match a string with a .", "do not match . string with a .", false}, - {"do not match a string with a . at the beginning", "do not .atch a string with a . at the beginning", false}, - {"do not match a string with two .", "do not match a ..ring with two .", false}, - {"do not match a string with extra .", "do not match a string with extra ..", false}, + {"abc.edf.hjg", "abc.edf.hjg", true}, + {"abc.edf.hjg", "ab.cedf.hjg", false}, + {"abc.edf.hjg", "abc.edfh.jg", false}, + {"abc.edf.hjg", "abc.edf.hjq", false}, - {"A big brown fox jumps over the lazy dog, with all there wildcards friends", ". big?brown fox jumps over * wildcard. friend??", false}, - {"A big brown fox fails to jump over the lazy dog, with all there wildcards friends", ". big?brown fox jumps over * wildcard. friend??", false}, + {"abc.edf.hjg", "abc.*.hjg", true}, + {"abc.edf.hjg", "abc.*.hjq", false}, + {"abc.edf.hjg", "abc*hjg", true}, + {"abc.edf.hjg", "abc*hjq", false}, + {"abc.edf.hjg", "a*g", true}, + {"abc.edf.hjg", "a*q", false}, - {"domain a.b.c", "domain a.b.c", true}, - {"domain adb.c", "domain a.b.c", false}, - {"aaaa", "a*a", true}, + {"abc.edf.hjg", "ab?.edf.hjg", true}, + {"abc.edf.hjg", "?b?.edf.hjg", true}, + {"abc.edf.hjg", "??c.edf.hjg", true}, + {"abc.edf.hjg", "a??.edf.hjg", true}, + {"abc.edf.hjg", "ab??.edf.hjg", false}, + {"abc.edf.hjg", "??.edf.hjg", false}, } for i, c := range cases { @@ -96,10 +87,106 @@ func TestMatch(t *testing.T) { } } +func match(pattern, name string) bool { // https://research.swtch.com/glob + px := 0 + nx := 0 + nextPx := 0 + nextNx := 0 + for px < len(pattern) || nx < len(name) { + if px < len(pattern) { + c := pattern[px] + switch c { + default: // ordinary character + if nx < len(name) && name[nx] == c { + px++ + nx++ + continue + } + case '?': // single-character wildcard + if nx < len(name) { + px++ + nx++ + continue + } + case '*': // zero-or-more-character wildcard + // Try to match at nx. + // If that doesn't work out, + // restart at nx+1 next. + nextPx = px + nextNx = nx + 1 + px++ + continue + } + } + // Mismatch. Maybe restart. + if 0 < nextNx && nextNx <= len(name) { + px = nextPx + nx = nextNx + continue + } + return false + } + // Matched all of pattern to all of name. Success. + return true +} + func FuzzMatch(f *testing.F) { - f.Fuzz(func(t *testing.T, s string) { - if !Match(string(s), string(s)) { - t.Fatalf("%s does not match %s", s, s) + f.Fuzz(func(t *testing.T, pattern, name string) { + result1 := Match(pattern, name) + result2 := match(pattern, name) + if result1 != result2 { + t.Fatalf("Match failed for pattern `%s` and name `%s`", pattern, name) + } + }) +} + +func BenchmarkMatch(b *testing.B) { + cases := []struct { + s string + pattern string + result bool + }{ + {"abc.edf.hjg", "abc.edf.hjg", true}, + {"abc.edf.hjg", "ab.cedf.hjg", false}, + {"abc.edf.hjg", "abc.edfh.jg", false}, + {"abc.edf.hjg", "abc.edf.hjq", false}, + + {"abc.edf.hjg", "abc.*.hjg", true}, + {"abc.edf.hjg", "abc.*.hjq", false}, + {"abc.edf.hjg", "abc*hjg", true}, + {"abc.edf.hjg", "abc*hjq", false}, + {"abc.edf.hjg", "a*g", true}, + {"abc.edf.hjg", "a*q", false}, + + {"abc.edf.hjg", "ab?.edf.hjg", true}, + {"abc.edf.hjg", "?b?.edf.hjg", true}, + {"abc.edf.hjg", "??c.edf.hjg", true}, + {"abc.edf.hjg", "a??.edf.hjg", true}, + {"abc.edf.hjg", "ab??.edf.hjg", false}, + {"abc.edf.hjg", "??.edf.hjg", false}, + + {"r4.cdn-aa-wow-this-is-long-a1.video-yajusenpai1145141919810-oh-hell-yeah-this-is-also-very-long-and-sukka-the-fox-has-a-very-big-fluffy-fox-tail-ao-wu-ao-wu-regex-and-wildcard-both-might-have-deadly-back-tracing-issue-be-careful-or-use-linear-matching.com", "*.cdn-*-*.video**.com", true}, + } + + b.Run("Match", func(b *testing.B) { + for i := 0; i < b.N; i++ { + for _, c := range cases { + result := Match(c.pattern, c.s) + if c.result != result { + b.Errorf("Test %d: Expected `%v`, found `%v`; With Pattern: `%s` and String: `%s`", i+1, c.result, result, c.pattern, c.s) + } + } + } + }) + + b.Run("match", func(b *testing.B) { + for i := 0; i < b.N; i++ { + for _, c := range cases { + result := match(c.pattern, c.s) + if c.result != result { + b.Errorf("Test %d: Expected `%v`, found `%v`; With Pattern: `%s` and String: `%s`", i+1, c.result, result, c.pattern, c.s) + } + } } }) } diff --git a/mihomo/docs/config.yaml b/mihomo/docs/config.yaml index 6790294673..245b38e8b0 100644 --- a/mihomo/docs/config.yaml +++ b/mihomo/docs/config.yaml @@ -638,7 +638,8 @@ proxies: # socks5 port: 443 uuid: uuid network: tcp - encryption: "8min-mlkem768client-bas64RawURLEncoding" # 复用八分钟后协商新的 sharedKey,需小于服务端的值 + encryption: "8min-vless-mlkem768client-bas64RawURLEncoding" # 复用八分钟后协商新的 sharedKey,需小于服务端的值 + # encryption: "8min-aes128xor-mlkem768client-bas64RawURLEncoding" tls: false #可以不开启tls udp: true @@ -1346,7 +1347,8 @@ listeners: flow: xtls-rprx-vision # ws-path: "/" # 如果不为空则开启 websocket 传输层 # grpc-service-name: "GunService" # 如果不为空则开启 grpc 传输层 - # decryption: "10min-mlkem768seed-bas64RawURLEncoding" # 同时允许 1-RTT 模式与十分钟复用的 0-RTT 模式, 后面base64字符串可由可由 mihomo generate vless-mlkem768 命令生成 + # decryption: "10min-vless-mlkem768seed-bas64RawURLEncoding" # 同时允许 1-RTT 模式与十分钟复用的 0-RTT 模式, 后面base64字符串可由可由 mihomo generate vless-mlkem768 命令生成 + # decryption: "10min-aes128xor-mlkem768seed-bas64RawURLEncoding" # 下面两项如果填写则开启 tls(需要同时填写) # certificate: ./server.crt # private-key: ./server.key diff --git a/mihomo/go.mod b/mihomo/go.mod index 11d10c9a0f..4ba0c3ebbe 100644 --- a/mihomo/go.mod +++ b/mihomo/go.mod @@ -35,7 +35,7 @@ require ( github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f github.com/metacubex/smux v0.0.0-20250503055512-501391591dee github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4 - github.com/metacubex/utls v1.8.1-0.20250810142204-d0e55ab2e852 + github.com/metacubex/utls v1.8.1-0.20250811145843-49b4f106169a github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181 github.com/miekg/dns v1.1.63 // lastest version compatible with golang1.20 github.com/mroth/weightedrand/v2 v2.1.0 diff --git a/mihomo/go.sum b/mihomo/go.sum index 2adede6cbc..92d52230d4 100644 --- a/mihomo/go.sum +++ b/mihomo/go.sum @@ -139,10 +139,8 @@ github.com/metacubex/smux v0.0.0-20250503055512-501391591dee h1:lp6hJ+4wCLZu113a github.com/metacubex/smux v0.0.0-20250503055512-501391591dee/go.mod h1:4bPD8HWx9jPJ9aE4uadgyN7D1/Wz3KmPy+vale8sKLE= github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4 h1:j1VRTiC9JLR4nUbSikx9OGdu/3AgFDqgcLj4GoqyQkc= github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw= -github.com/metacubex/utls v1.8.0 h1:mSYi6FMnmc5riARl5UZDmWVy710z+P5b7xuGW0lV9ac= -github.com/metacubex/utls v1.8.0/go.mod h1:FdjYzVfCtgtna19hX0ER1Xsa5uJInwdQ4IcaaI98lEQ= -github.com/metacubex/utls v1.8.1-0.20250810142204-d0e55ab2e852 h1:MLHUGmASNH7/AeoGmSrVM2RutRZAqIDSbQWBp0P7ItE= -github.com/metacubex/utls v1.8.1-0.20250810142204-d0e55ab2e852/go.mod h1:FdjYzVfCtgtna19hX0ER1Xsa5uJInwdQ4IcaaI98lEQ= +github.com/metacubex/utls v1.8.1-0.20250811145843-49b4f106169a h1:IIzlVmDoB4+7b0BUcLZaY5+AirhhLFep3PhwkAFMRnQ= +github.com/metacubex/utls v1.8.1-0.20250811145843-49b4f106169a/go.mod h1:FdjYzVfCtgtna19hX0ER1Xsa5uJInwdQ4IcaaI98lEQ= github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181 h1:hJLQviGySBuaynlCwf/oYgIxbVbGRUIKZCxdya9YrbQ= github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181/go.mod h1:phewKljNYiTVT31Gcif8RiCKnTUOgVWFJjccqYM8s+Y= github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY= diff --git a/mihomo/listener/inbound/vless_test.go b/mihomo/listener/inbound/vless_test.go index fdb8905ce9..2ef1212669 100644 --- a/mihomo/listener/inbound/vless_test.go +++ b/mihomo/listener/inbound/vless_test.go @@ -89,18 +89,29 @@ func TestInboundVless_TLS(t *testing.T) { } func TestInboundVless_Encryption(t *testing.T) { - seedBase64, pubBase64, err := encryption.GenMLKEM768("") + seedBase64, clientBase64, err := encryption.GenMLKEM768("") if err != nil { t.Fatal(err) return } - inboundOptions := inbound.VlessOption{ - Decryption: "10min-mlkem768seed-" + seedBase64, - } - outboundOptions := outbound.VlessOption{ - Encryption: "8min-mlkem768client-" + pubBase64, - } - testInboundVless(t, inboundOptions, outboundOptions) + t.Run("-vless-", func(t *testing.T) { + inboundOptions := inbound.VlessOption{ + Decryption: "10min-vless-mlkem768seed-" + seedBase64, + } + outboundOptions := outbound.VlessOption{ + Encryption: "8min-vless-mlkem768client-" + clientBase64, + } + testInboundVless(t, inboundOptions, outboundOptions) + }) + t.Run("-aes128xor-", func(t *testing.T) { + inboundOptions := inbound.VlessOption{ + Decryption: "10min-aes128xor-mlkem768seed-" + seedBase64, + } + outboundOptions := outbound.VlessOption{ + Encryption: "8min-aes128xor-mlkem768client-" + clientBase64, + } + testInboundVless(t, inboundOptions, outboundOptions) + }) } func TestInboundVless_Wss1(t *testing.T) { diff --git a/mihomo/listener/sing_vless/server.go b/mihomo/listener/sing_vless/server.go index a81650839b..41f23f34d8 100644 --- a/mihomo/listener/sing_vless/server.go +++ b/mihomo/listener/sing_vless/server.go @@ -88,7 +88,7 @@ func New(config LC.VlessServer, tunnel C.Tunnel, additions ...inbound.Addition) sl = &Listener{config: config, service: service} - if s := strings.Split(config.Decryption, "-mlkem768seed-"); len(s) == 2 { + 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") @@ -102,14 +102,22 @@ func New(config LC.VlessServer, tunnel C.Tunnel, additions ...inbound.Addition) } 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[1]) + b, err = base64.RawURLEncoding.DecodeString(s[3]) if err != nil { return nil, fmt.Errorf("invaild vless decryption value: %s", config.Decryption) } - if len(b) == 64 { + if len(b) == encryption.MLKEM768SeedLength { sl.decryption = &encryption.ServerInstance{} - if err = sl.decryption.Init(b, time.Duration(minutes)*time.Minute); err != nil { + 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 { diff --git a/mihomo/transport/vless/encryption/client.go b/mihomo/transport/vless/encryption/client.go index b0b44857f1..8453ea613d 100644 --- a/mihomo/transport/vless/encryption/client.go +++ b/mihomo/transport/vless/encryption/client.go @@ -38,17 +38,18 @@ func init() { type ClientInstance struct { sync.RWMutex eKeyNfs *mlkem.EncapsulationKey768 + xor uint32 minutes time.Duration expire time.Time baseKey []byte - reuse []byte + ticket []byte } type ClientConn struct { net.Conn instance *ClientInstance baseKey []byte - reuse []byte + ticket []byte random []byte aead cipher.AEAD nonce []byte @@ -57,8 +58,9 @@ type ClientConn struct { peerCache []byte } -func (i *ClientInstance) Init(eKeyNfsData []byte, minutes time.Duration) (err error) { +func (i *ClientInstance) Init(eKeyNfsData []byte, xor uint32, minutes time.Duration) (err error) { i.eKeyNfs, err = mlkem.NewEncapsulationKey768(eKeyNfsData) + i.xor = xor i.minutes = minutes return } @@ -67,6 +69,9 @@ func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) { if i.eKeyNfs == nil { return nil, errors.New("uninitialized") } + if i.xor == 1 { + conn = NewXorConn(conn, i.eKeyNfs.Bytes()) + } c := &ClientConn{Conn: conn} if i.minutes > 0 { @@ -74,7 +79,7 @@ func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) { if time.Now().Before(i.expire) { c.instance = i c.baseKey = i.baseKey - c.reuse = i.reuse + c.ticket = i.ticket i.RUnlock() return c, nil } @@ -104,7 +109,7 @@ func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) { return nil, err } encapsulatedPfsKey := peerServerHello[:1088] - c.reuse = peerServerHello[1088:] + c.ticket = peerServerHello[1088:] pfsKey, err := dKeyPfs.Decapsulate(encapsulatedPfsKey) if err != nil { @@ -115,7 +120,7 @@ func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) { 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.reuse, encapsulatedPfsKey) + VLESS, _ := newAead(ClientCipher, authKey).Open(nil, nonce, c.ticket, encapsulatedPfsKey) if !bytes.Equal(VLESS, []byte("VLESS")) { // TODO: more message return nil, errors.New("invalid server") } @@ -124,7 +129,7 @@ func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) { i.Lock() i.expire = time.Now().Add(i.minutes) i.baseKey = c.baseKey - i.reuse = c.reuse + i.ticket = c.ticket i.Unlock() } @@ -140,12 +145,12 @@ func (c *ClientConn) Write(b []byte) (int, error) { c.random = make([]byte, 32) rand.Read(c.random) key := make([]byte, 32) - hkdf.New(sha256.New, c.baseKey, c.random, c.reuse).Read(key) + 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.reuse) + 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]) @@ -210,7 +215,7 @@ func (c *ClientConn) Read(b []byte) (int, error) { // after first Write() if err != nil { if c.instance != nil { c.instance.Lock() - if bytes.Equal(c.reuse, c.instance.reuse) { + if bytes.Equal(c.ticket, c.instance.ticket) { c.instance.expire = time.Now() // expired } c.instance.Unlock() diff --git a/mihomo/transport/vless/encryption/doc.go b/mihomo/transport/vless/encryption/doc.go index dbb00211d4..af531f8b99 100644 --- a/mihomo/transport/vless/encryption/doc.go +++ b/mihomo/transport/vless/encryption/doc.go @@ -1,3 +1,5 @@ // Package encryption copy and modify from xray-core // https://github.com/XTLS/Xray-core/commit/f61c14e9c63dc41a8a09135db3aea337974f3f37 +// https://github.com/XTLS/Xray-core/commit/3e19bf9233bdd9bafc073a71c65b737cc1ffba5e +// https://github.com/XTLS/Xray-core/commit/7ffb555fc8ec51bd1e3e60f26f1d6957984dba80 package encryption diff --git a/mihomo/transport/vless/encryption/key.go b/mihomo/transport/vless/encryption/key.go index 46b53163d3..69b5289579 100644 --- a/mihomo/transport/vless/encryption/key.go +++ b/mihomo/transport/vless/encryption/key.go @@ -8,7 +8,10 @@ import ( "github.com/metacubex/utls/mlkem" ) -func GenMLKEM768(seedStr string) (seedBase64, pubBase64 string, err error) { +const MLKEM768SeedLength = mlkem.SeedSize +const MLKEM768ClientLength = mlkem.EncapsulationKeySize768 + +func GenMLKEM768(seedStr string) (seedBase64, clientBase64 string, err error) { var seed [64]byte if len(seedStr) > 0 { s, _ := base64.RawURLEncoding.DecodeString(seedStr) @@ -27,6 +30,6 @@ func GenMLKEM768(seedStr string) (seedBase64, pubBase64 string, err error) { key, _ := mlkem.NewDecapsulationKey768(seed[:]) pub := key.EncapsulationKey() seedBase64 = base64.RawURLEncoding.EncodeToString(seed[:]) - pubBase64 = base64.RawURLEncoding.EncodeToString(pub.Bytes()) + clientBase64 = base64.RawURLEncoding.EncodeToString(pub.Bytes()) return } diff --git a/mihomo/transport/vless/encryption/server.go b/mihomo/transport/vless/encryption/server.go index 667b813fa6..6874445960 100644 --- a/mihomo/transport/vless/encryption/server.go +++ b/mihomo/transport/vless/encryption/server.go @@ -25,6 +25,7 @@ type ServerSession struct { type ServerInstance struct { sync.RWMutex dKeyNfs *mlkem.DecapsulationKey768 + xor uint32 minutes time.Duration sessions map[[21]byte]*ServerSession stop bool @@ -34,7 +35,7 @@ type ServerConn struct { net.Conn cipher byte baseKey []byte - reuse []byte + ticket []byte peerRandom []byte peerAead cipher.AEAD peerNonce []byte @@ -43,8 +44,9 @@ type ServerConn struct { nonce []byte } -func (i *ServerInstance) Init(dKeyNfsData []byte, minutes time.Duration) (err error) { +func (i *ServerInstance) Init(dKeyNfsData []byte, xor uint32, minutes time.Duration) (err error) { i.dKeyNfs, err = mlkem.NewDecapsulationKey768(dKeyNfsData) + i.xor = xor if minutes > 0 { i.minutes = minutes i.sessions = make(map[[21]byte]*ServerSession) @@ -79,22 +81,25 @@ func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) { if i.dKeyNfs == nil { return nil, errors.New("uninitialized") } + if i.xor == 1 { + conn = NewXorConn(conn, i.dKeyNfs.EncapsulationKey().Bytes()) + } c := &ServerConn{Conn: conn} - peerReuseHello := make([]byte, 21+32) - if _, err := io.ReadFull(c.Conn, peerReuseHello); err != nil { + peerTicketHello := make([]byte, 21+32) + if _, err := io.ReadFull(c.Conn, peerTicketHello); err != nil { return nil, err } if i.minutes > 0 { i.RLock() - s := i.sessions[[21]byte(peerReuseHello)] + s := i.sessions[[21]byte(peerTicketHello)] i.RUnlock() if s != nil { - if _, replay := s.randoms.LoadOrStore([32]byte(peerReuseHello[21:]), true); !replay { + if _, replay := s.randoms.LoadOrStore([32]byte(peerTicketHello[21:]), true); !replay { c.cipher = s.cipher c.baseKey = s.baseKey - c.reuse = peerReuseHello[:21] - c.peerRandom = peerReuseHello[21:] + c.ticket = peerTicketHello[:21] + c.peerRandom = peerTicketHello[21:] return c, nil } } @@ -106,11 +111,11 @@ func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) { } if l, _ := decodeHeader(peerHeader); l != 0 { c.Conn.Write(make([]byte, randBetween(100, 1000))) // make client do new handshake - return nil, errors.New("invalid reuse") + return nil, errors.New("invalid ticket") } peerClientHello := make([]byte, 1088+1184+1) - copy(peerClientHello, peerReuseHello) + copy(peerClientHello, peerTicketHello) copy(peerClientHello[53:], peerHeader) if _, err := io.ReadFull(c.Conn, peerClientHello[58:]); err != nil { return nil, err @@ -136,13 +141,13 @@ func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) { authKey := make([]byte, 32) hkdf.New(sha256.New, c.baseKey, encapsulatedNfsKey, eKeyPfsData).Read(authKey) nonce := make([]byte, 12) - c.reuse = newAead(c.cipher, authKey).Seal(nil, nonce, []byte("VLESS"), encapsulatedPfsKey) + c.ticket = newAead(c.cipher, authKey).Seal(nil, nonce, []byte("VLESS"), encapsulatedPfsKey) padding := randBetween(100, 1000) serverHello := make([]byte, 1088+21+5+padding) copy(serverHello, encapsulatedPfsKey) - copy(serverHello[1088:], c.reuse) + copy(serverHello[1088:], c.ticket) encodeHeader(serverHello[1109:], int(padding)) if _, err := c.Conn.Write(serverHello); err != nil { @@ -151,7 +156,7 @@ func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) { if i.minutes > 0 { i.Lock() - i.sessions[[21]byte(c.reuse)] = &ServerSession{ + i.sessions[[21]byte(c.ticket)] = &ServerSession{ expire: time.Now().Add(i.minutes), cipher: c.cipher, baseKey: c.baseKey, @@ -181,12 +186,12 @@ func (c *ServerConn) Read(b []byte) (int, error) { return 0, err } } - peerIndex := make([]byte, 21) - copy(peerIndex, peerHeader) - if _, err := io.ReadFull(c.Conn, peerIndex[5:]); err != nil { + peerTicket := make([]byte, 21) + copy(peerTicket, peerHeader) + if _, err := io.ReadFull(c.Conn, peerTicket[5:]); err != nil { return 0, err } - if !bytes.Equal(peerIndex, c.reuse) { + if !bytes.Equal(peerTicket, c.ticket) { return 0, errors.New("naughty boy") } c.peerRandom = make([]byte, 32) @@ -195,7 +200,7 @@ func (c *ServerConn) Read(b []byte) (int, error) { } } peerKey := make([]byte, 32) - hkdf.New(sha256.New, c.baseKey, c.peerRandom, c.reuse).Read(peerKey) + hkdf.New(sha256.New, c.baseKey, c.peerRandom, c.ticket).Read(peerKey) c.peerAead = newAead(c.cipher, peerKey) c.peerNonce = make([]byte, 12) } diff --git a/mihomo/transport/vless/encryption/xor.go b/mihomo/transport/vless/encryption/xor.go new file mode 100644 index 0000000000..c8af2112d1 --- /dev/null +++ b/mihomo/transport/vless/encryption/xor.go @@ -0,0 +1,63 @@ +package encryption + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "io" + "net" +) + +type XorConn struct { + net.Conn + key []byte + ctr cipher.Stream + peerCtr cipher.Stream +} + +func NewXorConn(conn net.Conn, key []byte) *XorConn { + return &XorConn{Conn: conn, key: key[:16]} +} + +func (c *XorConn) Write(b []byte) (int, error) { + if len(b) == 0 { + return 0, nil + } + var iv []byte + if c.ctr == nil { + block, _ := aes.NewCipher(c.key) + iv = make([]byte, 16) + rand.Read(iv) + c.ctr = cipher.NewCTR(block, iv) + } + c.ctr.XORKeyStream(b, b) // caller MUST discard b + if iv != nil { + b = append(iv, b...) + } + if _, err := c.Conn.Write(b); err != nil { + return 0, err + } + if iv != nil { + b = b[16:] + } + return len(b), nil +} + +func (c *XorConn) Read(b []byte) (int, error) { + if len(b) == 0 { + return 0, nil + } + if c.peerCtr == nil { + peerIv := make([]byte, 16) + if _, err := io.ReadFull(c.Conn, peerIv); err != nil { + return 0, err + } + block, _ := aes.NewCipher(c.key) + c.peerCtr = cipher.NewCTR(block, peerIv) + } + n, err := c.Conn.Read(b) + if n > 0 { + c.peerCtr.XORKeyStream(b[:n], b[:n]) + } + return n, err +} diff --git a/openwrt-packages/luci-app-amlogic/Makefile b/openwrt-packages/luci-app-amlogic/Makefile index 21109ad94d..6f901e9989 100644 --- a/openwrt-packages/luci-app-amlogic/Makefile +++ b/openwrt-packages/luci-app-amlogic/Makefile @@ -16,7 +16,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-amlogic -PKG_VERSION:=3.1.265 +PKG_VERSION:=3.1.266 PKG_RELEASE:=1 PKG_LICENSE:=GPL-2.0 License diff --git a/openwrt-packages/luci-app-amlogic/root/usr/share/amlogic/amlogic_check_kernel.sh b/openwrt-packages/luci-app-amlogic/root/usr/share/amlogic/amlogic_check_kernel.sh index e55c38bcdf..3b051493cc 100755 --- a/openwrt-packages/luci-app-amlogic/root/usr/share/amlogic/amlogic_check_kernel.sh +++ b/openwrt-packages/luci-app-amlogic/root/usr/share/amlogic/amlogic_check_kernel.sh @@ -173,7 +173,7 @@ check_kernel() { latest_version="$( curl -fsSL -m 10 \ ${kernel_api}/releases/expanded_assets/kernel_${kernel_tag} | - grep -oE "${main_line_version}\.[0-9]+.*\.tar\.gz" | sed 's/.tar.gz//' | + grep -oE "${main_line_version}\.[0-9]+\.tar\.gz" | sed 's/.tar.gz//' | sort -urV | head -n 1 )" [[ -n "${latest_version}" ]] || tolog "02.03 No kernel available, please use another kernel branch." "1" diff --git a/openwrt-packages/luci-theme-argon/Makefile b/openwrt-packages/luci-theme-argon/Makefile index ab1b92f74c..82d9253956 100644 --- a/openwrt-packages/luci-theme-argon/Makefile +++ b/openwrt-packages/luci-theme-argon/Makefile @@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=Argon Theme -LUCI_DEPENDS:=+curl +jsonfilter +LUCI_DEPENDS:=+wget +jsonfilter PKG_VERSION:=2.4.3 PKG_RELEASE:=20250722 diff --git a/openwrt-packages/luci-theme-argon/luasrc/view/themes/argon/sysauth.htm b/openwrt-packages/luci-theme-argon/luasrc/view/themes/argon/sysauth.htm index 8e55042f25..73d38dbe86 100644 --- a/openwrt-packages/luci-theme-argon/luasrc/view/themes/argon/sysauth.htm +++ b/openwrt-packages/luci-theme-argon/luasrc/view/themes/argon/sysauth.htm @@ -27,7 +27,6 @@ local fs = require "nixio.fs" local nutil = require "nixio.util" local json = require "luci.jsonc" - local sys = require "luci.sys" local uci = require 'luci.model.uci'.cursor() -- Fetch Local Background Media @@ -74,9 +73,9 @@ if fs.access("/etc/config/argon") then local online_wallpaper = uci:get_first('argon', 'global', 'online_wallpaper') or (uci:get_first('argon', 'global', 'bing_background') == '1' and 'bing') if (online_wallpaper and online_wallpaper ~= "none") then - local picurl = sys.exec("/usr/libexec/argon/online_wallpaper") - if (picurl and picurl ~= '') then - return picurl, "Image", "" + local picurl = util.ubus("luci.argon_wallpaper", "get_url") or {} + if (picurl.url and picurl.url ~= '') then + return picurl.url, "Image", "" end end end diff --git a/openwrt-packages/luci-theme-argon/root/usr/libexec/argon/online_wallpaper b/openwrt-packages/luci-theme-argon/root/usr/libexec/argon/online_wallpaper deleted file mode 100755 index 01bcae2303..0000000000 --- a/openwrt-packages/luci-theme-argon/root/usr/libexec/argon/online_wallpaper +++ /dev/null @@ -1,105 +0,0 @@ -#!/bin/sh -# author jjm2473 - -# the script will be excuted when `argon.@global[0].bing_background == '1'` -# defaults to 'bing' to be compatible with old config -WEB_PIC_SRC=$(uci -q get argon.@global[0].online_wallpaper || echo 'bing') -# For now, the next two variables are used for wallhaven.cc with specified Tag ID -# API_KEY if user has an account with wallhaven and want to use their apikey to allow for more images -# EXACT_RESO is used for exact resolution by default, if not use 'atleast' instead of 'resolutions' -API_KEY=$(uci -q get argon.@global[0].use_api_key) -EXACT_RESO=$(uci -q get argon.@global[0].use_exact_resolution || echo '1') -CACHE=/var/run/argon_${WEB_PIC_SRC}.url -WRLOCK=/var/lock/argon_${WEB_PIC_SRC}.lock - -fetch_pic_url() { - case $WEB_PIC_SRC in - bing) - local picpath=$(curl -fks --max-time 3 \ - "https://www.bing.com/HPImageArchive.aspx?format=js&n=1" | - jsonfilter -qe '@.images[0].url') - [ -n "${picpath}" ] && echo "//www.bing.com${picpath}" - ;; - unsplash) - if [ -z "$API_KEY" ]; then - curl -fks --max-time 3 \ - "https://source.unsplash.com/1920x1080/daily?wallpapers" | - sed -E 's#^.*href="([^?]+)\?.*$#\1?fm=jpg\&fit=crop\&w=1920\&h=1080#' - else - curl -fks --max-time 3 \ - "https://api.unsplash.com/photos/random?client_id=${API_KEY}" | - jsonfilter -qe '@["urls"]["regular"]' - fi - ;; - unsplash_*) - local collection_id=${WEB_PIC_SRC#unsplash_} - if [ -z "$API_KEY" ]; then - curl -fks --max-time 3 \ - "https://source.unsplash.com/collection/${collection_id}/1920x1080" | - sed -E 's#^.*href="([^?]+)\?.*$#\1?fm=jpg\&fit=crop\&w=1920\&h=1080#' - else - curl -fks --max-time 3 \ - "https://api.unsplash.com/photos/random?client_id=${API_KEY}&collections=${collection_id}" | - jsonfilter -qe '@["urls"]["regular"]' - fi - ;; - wallhaven) - curl -fks --max-time 3 \ - "https://wallhaven.cc/api/v1/search?resolutions=1920x1080&sorting=random" | - jsonfilter -qe '@.data[0].path' - ;; - wallhaven_*) - local tag_id=${WEB_PIC_SRC#wallhaven_} - local has_api_key="" - [ -n "$API_KEY" ] && has_api_key="apikey=$API_KEY&" || has_api_key="" - local use_reso="resolutions" - [ "$EXACT_RESO" -eq "1" ] && use_reso='resolutions' || use_reso='atleast' - curl -fks --max-time 3 \ - "https://wallhaven.cc/api/v1/search?${has_api_key}q=id%3A${tag_id}&${use_reso}=1920x1080&sorting=random" | - jsonfilter -qe '@.data[0].path' - ;; - esac -} - -try_update() { - local lock="$WRLOCK" - exec 200>$lock - - if flock -n 200 >/dev/null 2>&1; then - local picurl=$(fetch_pic_url) - if [[ "$WEB_PIC_SRC" == wallhave* ]] ; then - curl -fks --max-time 3 --url "${picurl}" > /dev/null - fi - if [ -n "$picurl" ]; then - echo "${picurl}" | tee "$CACHE" - else - if [ -s "$CACHE" ]; then - cat "$CACHE" - else - touch "$CACHE" - fi - fi - flock -u 200 >/dev/null 2>&1 - elif [ -s "$CACHE" ]; then - cat "$CACHE" - fi -} - -get_url() { - if [ -f "$CACHE" ]; then - local idle_t=$(($(date '+%s') - $(date -r "$CACHE" '+%s' 2>/dev/null || echo '0'))) - if [ -s "$CACHE" ]; then - if [ $idle_t -le 43200 ]; then - cat "$CACHE" - return - fi - else - if [ $idle_t -le 120 ]; then - return - fi - fi - fi - try_update -} - -get_url diff --git a/openwrt-packages/luci-theme-argon/root/usr/libexec/rpcd/luci.argon_wallpaper b/openwrt-packages/luci-theme-argon/root/usr/libexec/rpcd/luci.argon_wallpaper new file mode 100755 index 0000000000..530121f5e8 --- /dev/null +++ b/openwrt-packages/luci-theme-argon/root/usr/libexec/rpcd/luci.argon_wallpaper @@ -0,0 +1,129 @@ +#!/bin/sh +# author jjm2473 + +. /lib/functions.sh +. /usr/share/libubox/jshn.sh + +# the script will be excuted when `argon.@global[0].bing_background == '1'` +# defaults to 'bing' to be compatible with old config +WEB_PIC_SRC="$(uci -q get argon.@global[0].online_wallpaper || echo 'bing')" + +# For now, the next two variables are used for wallhaven.cc with specified Tag ID +# API_KEY if user has an account with wallhaven and want to use their apikey to allow for more images +# EXACT_RESO is used for exact resolution by default, if not use 'atleast' instead of 'resolutions' +API_KEY="$(uci -q get argon.@global[0].use_api_key)" +EXACT_RESO="$(uci -q get argon.@global[0].use_exact_resolution || echo '1')" + +CACHE="/var/run/argon_${WEB_PIC_SRC}.url" +WRLOCK="/var/lock/argon_${WEB_PIC_SRC}.lock" + +fetch_pic_url() { + case "$WEB_PIC_SRC" in + bing) + local picpath="$(wget -T3 -qO- \ + "https://www.bing.com/HPImageArchive.aspx?format=js&n=1" | + jsonfilter -qe '@.images[0].url' | + sed 's/1920x1080/UHD/g')" + [ -n "${picpath}" ] && echo "//www.bing.com${picpath}" + ;; + unsplash) + if [ -z "$API_KEY" ]; then + local pic_id="$(wget -T3 --spider \ + "https://source.unsplash.com/1920x1080/daily?wallpapers" 2>&1 | + grep -Eo "photo-\w+-\w+" | head -n1)" + [ -n "${pic_id}" ] && echo "https://images.unsplash.com/${pic_id}?fm=jpg&fit=crop&w=1920&h=1080" + else + wget -T3 -qO- "https://api.unsplash.com/photos/random?client_id=${API_KEY}" | + jsonfilter -qe '@["urls"]["regular"]' + fi + ;; + unsplash_*) + local collection_id="${WEB_PIC_SRC#unsplash_}" + if [ -z "$API_KEY" ]; then + local pic_id="$(wget -T3 --spider \ + "https://source.unsplash.com/collection/${collection_id}/1920x1080" 2>&1 | + grep -Eo "photo-\w+-\w+" | head -n1)" + [ -n "${pic_id}" ] && echo "https://images.unsplash.com/${pic_id}?fm=jpg&fit=crop&w=1920&h=1080" + else + wget -T3 -qO- \ + "https://api.unsplash.com/photos/random?client_id=${API_KEY}&collections=${collection_id}" | + jsonfilter -qe '@["urls"]["regular"]' + fi + ;; + wallhaven) + wget -T3 -qO- \ + "https://wallhaven.cc/api/v1/search?resolutions=1920x1080&sorting=random" | + jsonfilter -qe "@.data[0].path" + ;; + wallhaven_*) + local tag_id="${WEB_PIC_SRC#wallhaven_}" + local use_reso="resolutions" + [ "$EXACT_RESO" -eq "1" ] || use_reso="atleast" + [ -z "$API_KEY" ] || API_KEY="apikey=$API_KEY&" + wget -T3 -qO- \ + "https://wallhaven.cc/api/v1/search?${API_KEY}q=id%3A${tag_id}&${use_reso}=1920x1080&sorting=random" | + jsonfilter -qe '@.data[0].path' + ;; + esac +} + +try_update() { + local lock="$WRLOCK" + exec 200>"$lock" + + if flock -n 200 >"/dev/null" 2>&1; then + local picurl="$(fetch_pic_url)" + if [ -n "$picurl" ]; then + echo "$picurl" | tee "$CACHE" + else + if [ -s "$CACHE" ]; then + cat "$CACHE" + else + touch "$CACHE" + fi + fi + flock -u 200 >"/dev/null" 2>&1 + elif [ -s "$CACHE" ]; then + cat "$CACHE" + fi +} + +case "$1" in +"list") + json_init + json_add_object "get_url" + json_close_object + json_dump + json_cleanup + ;; +"call") + case "$2" in + "get_url") + read -r input + if [ -f "$CACHE" ]; then + idle_t="$(($(date '+%s') - $(date -r "$CACHE" '+%s' 2>/dev/null || echo '0')))" + if [ -s "$CACHE" ]; then + if [ "$idle_t" -le "43200" ]; then + json_init + json_add_string "url" "$(cat "$CACHE")" + json_dump + json_cleanup + return 0 + fi + else + if [ "$idle_t" -le 120 ]; then + echo '{ "url": "" }' + return 1 + fi + fi + fi + + json_init + json_add_string "url" "$(try_update)" + json_dump + json_cleanup + return 0 + ;; + esac + ;; +esac diff --git a/openwrt-packages/quickstart/Makefile b/openwrt-packages/quickstart/Makefile index ce53267d23..55cadb112e 100644 --- a/openwrt-packages/quickstart/Makefile +++ b/openwrt-packages/quickstart/Makefile @@ -10,11 +10,11 @@ include $(TOPDIR)/rules.mk PKG_ARCH_quickstart:=$(ARCH) PKG_NAME:=quickstart -PKG_VERSION:=0.11.1 +PKG_VERSION:=0.11.2 PKG_RELEASE:=1 PKG_SOURCE:=$(PKG_NAME)-binary-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://github.com/linkease/istore-packages/releases/download/prebuilt/ -PKG_HASH:=5f59e69dc1a8dc5606e53936cc7a34f6d37c9102cbf378576420ef9fdd7e7d2f +PKG_HASH:=b22f430f08fb12739179e4b983133afb84a37be1e702d5b4e9fb30b1e701824d PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-binary-$(PKG_VERSION) diff --git a/openwrt-passwall/luci-app-passwall/luasrc/model/cbi/passwall/client/type/sing-box.lua b/openwrt-passwall/luci-app-passwall/luasrc/model/cbi/passwall/client/type/sing-box.lua index eed90f6b6f..84223800d1 100644 --- a/openwrt-passwall/luci-app-passwall/luasrc/model/cbi/passwall/client/type/sing-box.lua +++ b/openwrt-passwall/luci-app-passwall/luasrc/model/cbi/passwall/client/type/sing-box.lua @@ -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")) diff --git a/pingtunnel/client.go b/pingtunnel/client.go index 6531e0cf3e..113d4f26a6 100644 --- a/pingtunnel/client.go +++ b/pingtunnel/client.go @@ -21,7 +21,7 @@ const ( func NewClient(addr string, server string, target string, timeout int, key int, tcpmode int, tcpmode_buffersize int, tcpmode_maxwin int, tcpmode_resend_timems int, tcpmode_compress int, - tcpmode_stat int, open_sock5 int, maxconn int, sock5_filter *func(addr string) bool) (*Client, error) { + tcpmode_stat int, open_sock5 int, maxconn int, sock5_filter *func(addr string) bool, cryptoConfig *CryptoConfig) (*Client, error) { var ipaddr *net.UDPAddr var tcpaddr *net.TCPAddr @@ -67,6 +67,7 @@ func NewClient(addr string, server string, target string, timeout int, key int, maxconn: maxconn, pongTime: time.Now(), sock5_filter: sock5_filter, + cryptoConfig: cryptoConfig, }, nil } @@ -92,6 +93,7 @@ type Client struct { open_sock5 int sock5_filter *func(addr string) bool + cryptoConfig *CryptoConfig ipaddr *net.UDPAddr tcpaddr *net.TCPAddr @@ -214,7 +216,7 @@ func (p *Client) Run() error { recv := make(chan *Packet, 10000) p.recvcontrol = make(chan int, 1) - go recvICMP(&p.workResultLock, &p.exit, *p.conn, recv) + go recvICMP(&p.workResultLock, &p.exit, *p.conn, recv, p.cryptoConfig) go func() { defer common.CrashLog() @@ -346,7 +348,7 @@ func (p *Client) AcceptTcpConn(conn *net.TCPConn, targetAddr string) { sendICMP(p.id, p.sequence, *p.conn, p.ipaddrServer, targetAddr, clientConn.id, (uint32)(MyMsg_DATA), mb, SEND_PROTO, RECV_PROTO, p.key, p.tcpmode, p.tcpmode_buffersize, p.tcpmode_maxwin, p.tcpmode_resend_timems, p.tcpmode_compress, p.tcpmode_stat, - p.timeout) + p.timeout, p.cryptoConfig) p.sendPacket++ p.sendPacketSize += (uint64)(len(mb)) } @@ -409,7 +411,7 @@ func (p *Client) AcceptTcpConn(conn *net.TCPConn, targetAddr string) { sendICMP(p.id, p.sequence, *p.conn, p.ipaddrServer, targetAddr, clientConn.id, (uint32)(MyMsg_DATA), mb, SEND_PROTO, RECV_PROTO, p.key, p.tcpmode, 0, 0, 0, 0, 0, - 0) + 0, p.cryptoConfig) p.sendPacket++ p.sendPacketSize += (uint64)(len(mb)) } @@ -472,7 +474,7 @@ func (p *Client) AcceptTcpConn(conn *net.TCPConn, targetAddr string) { sendICMP(p.id, p.sequence, *p.conn, p.ipaddrServer, targetAddr, clientConn.id, (uint32)(MyMsg_DATA), mb, SEND_PROTO, RECV_PROTO, p.key, p.tcpmode, 0, 0, 0, 0, 0, - 0) + 0, p.cryptoConfig) p.sendPacket++ p.sendPacketSize += (uint64)(len(mb)) } @@ -550,7 +552,7 @@ func (p *Client) Accept() error { sendICMP(p.id, p.sequence, *p.conn, p.ipaddrServer, p.targetAddr, clientConn.id, (uint32)(MyMsg_DATA), bytes[:n], SEND_PROTO, RECV_PROTO, p.key, p.tcpmode, 0, 0, 0, 0, 0, - p.timeout) + p.timeout, p.cryptoConfig) p.sequence++ @@ -675,7 +677,7 @@ func (p *Client) ping() { sendICMP(p.id, p.sequence, *p.conn, p.ipaddrServer, "", "", (uint32)(MyMsg_PING), b, SEND_PROTO, RECV_PROTO, p.key, 0, 0, 0, 0, 0, 0, - 0) + 0, p.cryptoConfig) loggo.Info("ping %s %s %d %d %d %d", p.addrServer, now.String(), p.sproto, p.rproto, p.id, p.sequence) p.sequence++ if now.Sub(p.pongTime) > time.Second*3 { @@ -775,7 +777,7 @@ func (p *Client) remoteError(uuid string) { sendICMP(p.id, p.sequence, *p.conn, p.ipaddrServer, "", uuid, (uint32)(MyMsg_KICK), []byte{}, SEND_PROTO, RECV_PROTO, p.key, 0, 0, 0, 0, 0, 0, - 0) + 0, p.cryptoConfig) } func (p *Client) AcceptDirectTcpConn(conn *net.TCPConn, targetAddr string) { diff --git a/pingtunnel/cmd/main.go b/pingtunnel/cmd/main.go index c5fc9a2f66..87dcf02120 100644 --- a/pingtunnel/cmd/main.go +++ b/pingtunnel/cmd/main.go @@ -78,6 +78,12 @@ Usage: -key 设置的密码,默认0 Set password, default 0 + -encrypt 加密模式,支持aes128, aes256, chacha20 + Encryption mode: aes128, aes256, chacha20 + + -encrypt-key 加密密钥,支持base64编码或密码短语 + Encryption key, supports base64 encoded key or passphrase + -tcp 设置是否转发tcp,默认0 Set the switch to forward tcp, the default is 0 @@ -128,6 +134,8 @@ func main() { server := flag.String("s", "", "server addr") timeout := flag.Int("timeout", 60, "conn timeout") key := flag.Int("key", 0, "key") + encryption := flag.String("encrypt", "", "encryption mode: aes128, aes256, chacha20") + encryptionKey := flag.String("encrypt-key", "", "encryption key (base64 or passphrase)") tcpmode := flag.Int("tcp", 0, "tcp mode") tcpmode_buffersize := flag.Int("tcp_bs", 1*1024*1024, "tcp mode buffer size") tcpmode_maxwin := flag.Int("tcp_mw", 20000, "tcp mode max win") @@ -173,6 +181,28 @@ func main() { return } + // Validate encryption parameters + encryptionMode, err := pingtunnel.ParseEncryptionMode(*encryption) + if err != nil { + fmt.Printf("Invalid encryption mode: %v\n", err) + return + } + + if encryptionMode != pingtunnel.NoEncryption && *encryptionKey == "" { + fmt.Println("Encryption key is required when encryption mode is specified") + return + } + + // Create crypto configuration + var cryptoConfig *pingtunnel.CryptoConfig + if encryptionMode != pingtunnel.NoEncryption { + cryptoConfig, err = pingtunnel.NewCryptoConfig(encryptionMode, *encryptionKey) + if err != nil { + fmt.Printf("Failed to create crypto config: %v\n", err) + return + } + } + level := loggo.LEVEL_INFO if loggo.NameToLevel(*loglevel) >= 0 { level = loggo.NameToLevel(*loglevel) @@ -188,7 +218,7 @@ func main() { loggo.Info("key %d", *key) if *t == "server" { - s, err := pingtunnel.NewServer(*key, *maxconn, *max_process_thread, *max_process_buffer, *conntt) + s, err := pingtunnel.NewServer(*key, *maxconn, *max_process_thread, *max_process_buffer, *conntt, cryptoConfig) if err != nil { loggo.Error("ERROR: %s", err.Error()) return @@ -243,7 +273,7 @@ func main() { c, err := pingtunnel.NewClient(*listen, *server, *target, *timeout, *key, *tcpmode, *tcpmode_buffersize, *tcpmode_maxwin, *tcpmode_resend_timems, *tcpmode_compress, - *tcpmode_stat, *open_sock5, *maxconn, &filter) + *tcpmode_stat, *open_sock5, *maxconn, &filter, cryptoConfig) if err != nil { loggo.Error("ERROR: %s", err.Error()) return diff --git a/pingtunnel/crypto.go b/pingtunnel/crypto.go new file mode 100644 index 0000000000..e6ec073702 --- /dev/null +++ b/pingtunnel/crypto.go @@ -0,0 +1,191 @@ +package pingtunnel + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "crypto/sha256" + "encoding/base64" + "errors" + "fmt" + "golang.org/x/crypto/chacha20poly1305" + "golang.org/x/crypto/pbkdf2" +) + +// EncryptionMode represents the encryption mode +type EncryptionMode int + +const ( + NoEncryption EncryptionMode = iota + AES128 + AES256 + CHACHA20 +) + +// CryptoConfig holds encryption configuration +type CryptoConfig struct { + Mode EncryptionMode + Key []byte + Cipher cipher.AEAD +} + +// NewCryptoConfig creates a new crypto configuration +func NewCryptoConfig(mode EncryptionMode, keyInput string) (*CryptoConfig, error) { + if mode == NoEncryption { + return &CryptoConfig{Mode: NoEncryption}, nil + } + + var keySize int + switch mode { + case AES128: + keySize = 16 // 128 bits + case AES256: + keySize = 32 // 256 bits + case CHACHA20: + keySize = chacha20poly1305.KeySize // 32 bytes + default: + return nil, fmt.Errorf("unsupported encryption mode: %d", mode) + } + + key, err := deriveKey(keyInput, keySize) + if err != nil { + return nil, fmt.Errorf("failed to derive key: %v", err) + } + + // Create AEAD based on mode + var aead cipher.AEAD + switch mode { + case AES128, AES256: + // AES-GCM + block, err := aes.NewCipher(key) + if err != nil { + return nil, fmt.Errorf("failed to create AES cipher: %v", err) + } + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, fmt.Errorf("failed to create GCM: %v", err) + } + aead = gcm + case CHACHA20: + // ChaCha20-Poly1305 + cc20, err := chacha20poly1305.New(key) + if err != nil { + return nil, fmt.Errorf("failed to create ChaCha20-Poly1305: %v", err) + } + aead = cc20 + default: + return nil, fmt.Errorf("unsupported encryption mode: %d", mode) + } + + return &CryptoConfig{ + Mode: mode, + Key: key, + Cipher: aead, + }, nil +} + +// deriveKey derives an encryption key from the input string +func deriveKey(keyInput string, keySize int) ([]byte, error) { + if keyInput == "" { + return nil, errors.New("encryption key cannot be empty") + } + + // First, try to decode as base64 + if decoded, err := base64.StdEncoding.DecodeString(keyInput); err == nil { + if len(decoded) == keySize { + return decoded, nil + } + } + + // If not valid base64 or wrong size, use PBKDF2 to derive key + salt := []byte("pingtunnel-salt") // Fixed salt for deterministic key derivation + iterations := 10000 // Standard iteration count + return pbkdf2.Key([]byte(keyInput), salt, iterations, keySize, sha256.New), nil +} + +// Encrypt encrypts the given data +func (c *CryptoConfig) Encrypt(data []byte) ([]byte, error) { + if c.Mode == NoEncryption { + return data, nil + } + + if c.Cipher == nil { + return nil, errors.New("cipher not initialized") + } + + // Generate a random nonce + nonce := make([]byte, c.Cipher.NonceSize()) + if _, err := rand.Read(nonce); err != nil { + return nil, fmt.Errorf("failed to generate nonce: %v", err) + } + + // Encrypt the data + ciphertext := c.Cipher.Seal(nil, nonce, data, nil) + + // Prepend nonce to ciphertext + result := make([]byte, len(nonce)+len(ciphertext)) + copy(result, nonce) + copy(result[len(nonce):], ciphertext) + + return result, nil +} + +// Decrypt decrypts the given data +func (c *CryptoConfig) Decrypt(data []byte) ([]byte, error) { + if c.Mode == NoEncryption { + return data, nil + } + + if c.Cipher == nil { + return nil, errors.New("cipher not initialized") + } + + nonceSize := c.Cipher.NonceSize() + if len(data) < nonceSize { + return nil, errors.New("ciphertext too short") + } + + // Extract nonce and ciphertext + nonce := data[:nonceSize] + ciphertext := data[nonceSize:] + + // Decrypt the data + plaintext, err := c.Cipher.Open(nil, nonce, ciphertext, nil) + if err != nil { + return nil, fmt.Errorf("decryption failed: %v", err) + } + + return plaintext, nil +} + +// String returns a string representation of the encryption mode +func (m EncryptionMode) String() string { + switch m { + case NoEncryption: + return "none" + case AES128: + return "aes128" + case AES256: + return "aes256" + case CHACHA20: + return "chacha20" + default: + return "unknown" + } +} + +// ParseEncryptionMode parses a string into an EncryptionMode +func ParseEncryptionMode(s string) (EncryptionMode, error) { + switch s { + case "", "none": + return NoEncryption, nil + case "aes128": + return AES128, nil + case "aes256": + return AES256, nil + case "chacha20", "chacha20-poly1305": + return CHACHA20, nil + default: + return NoEncryption, fmt.Errorf("invalid encryption mode: %s", s) + } +} diff --git a/pingtunnel/crypto_test.go b/pingtunnel/crypto_test.go new file mode 100644 index 0000000000..046d021831 --- /dev/null +++ b/pingtunnel/crypto_test.go @@ -0,0 +1,183 @@ +package pingtunnel + +import ( + "bytes" + "testing" +) + +func TestCryptoConfig_AES128(t *testing.T) { + // Test with a base64 encoded key + key := "MTIzNDU2Nzg5MDEyMzQ1Ng==" // "1234567890123456" in base64 + config, err := NewCryptoConfig(AES128, key) + if err != nil { + t.Fatalf("Failed to create crypto config: %v", err) + } + + testData := []byte("Hello, World! This is a test message for encryption.") + + // Test encryption + encrypted, err := config.Encrypt(testData) + if err != nil { + t.Fatalf("Failed to encrypt data: %v", err) + } + + // Encrypted data should be different from original + if bytes.Equal(testData, encrypted) { + t.Fatal("Encrypted data should be different from original") + } + + // Test decryption + decrypted, err := config.Decrypt(encrypted) + if err != nil { + t.Fatalf("Failed to decrypt data: %v", err) + } + + // Decrypted data should match original + if !bytes.Equal(testData, decrypted) { + t.Fatalf("Decrypted data doesn't match original. Got: %s, Expected: %s", string(decrypted), string(testData)) + } +} + +func TestCryptoConfig_AES256(t *testing.T) { + // Test with a passphrase (will be derived using PBKDF2) + config, err := NewCryptoConfig(AES256, "my-secret-passphrase") + if err != nil { + t.Fatalf("Failed to create crypto config: %v", err) + } + + testData := []byte("This is a longer test message to verify AES-256 encryption works correctly with derived keys.") + + // Test encryption + encrypted, err := config.Encrypt(testData) + if err != nil { + t.Fatalf("Failed to encrypt data: %v", err) + } + + // Test decryption + decrypted, err := config.Decrypt(encrypted) + if err != nil { + t.Fatalf("Failed to decrypt data: %v", err) + } + + // Decrypted data should match original + if !bytes.Equal(testData, decrypted) { + t.Fatalf("Decrypted data doesn't match original. Got: %s, Expected: %s", string(decrypted), string(testData)) + } +} + +func TestCryptoConfig_ChaCha20(t *testing.T) { + // Test with a passphrase (PBKDF2 to 32 bytes) + config, err := NewCryptoConfig(CHACHA20, "another-secret-passphrase") + if err != nil { + t.Fatalf("Failed to create crypto config: %v", err) + } + + testData := []byte("Testing ChaCha20-Poly1305 AEAD for encryption and decryption correctness.") + + encrypted, err := config.Encrypt(testData) + if err != nil { + t.Fatalf("Failed to encrypt data: %v", err) + } + + if bytes.Equal(testData, encrypted) { + t.Fatal("Encrypted data should be different from original") + } + + decrypted, err := config.Decrypt(encrypted) + if err != nil { + t.Fatalf("Failed to decrypt data: %v", err) + } + + if !bytes.Equal(testData, decrypted) { + t.Fatalf("Decrypted data doesn't match original. Got: %s, Expected: %s", string(decrypted), string(testData)) + } +} + +func TestCryptoConfig_NoEncryption(t *testing.T) { + config, err := NewCryptoConfig(NoEncryption, "") + if err != nil { + t.Fatalf("Failed to create crypto config: %v", err) + } + + testData := []byte("This should not be encrypted") + + // Test "encryption" (should return original data) + encrypted, err := config.Encrypt(testData) + if err != nil { + t.Fatalf("Failed to encrypt data: %v", err) + } + + if !bytes.Equal(testData, encrypted) { + t.Fatal("No encryption should return original data") + } + + // Test "decryption" (should return original data) + decrypted, err := config.Decrypt(encrypted) + if err != nil { + t.Fatalf("Failed to decrypt data: %v", err) + } + + if !bytes.Equal(testData, decrypted) { + t.Fatal("No encryption should return original data") + } +} + +func TestParseEncryptionMode(t *testing.T) { + tests := []struct { + input string + expected EncryptionMode + hasError bool + }{ + {"", NoEncryption, false}, + {"none", NoEncryption, false}, + {"aes128", AES128, false}, + {"aes256", AES256, false}, + {"chacha20", CHACHA20, false}, + {"chacha20-poly1305", CHACHA20, false}, + {"invalid", NoEncryption, true}, + } + + for _, test := range tests { + result, err := ParseEncryptionMode(test.input) + if test.hasError && err == nil { + t.Fatalf("Expected error for input %s, but got none", test.input) + } + if !test.hasError && err != nil { + t.Fatalf("Unexpected error for input %s: %v", test.input, err) + } + if result != test.expected { + t.Fatalf("For input %s, expected %v, got %v", test.input, test.expected, result) + } + } +} + +func TestKeyDerivation(t *testing.T) { + // Test with valid base64 key + validBase64 := "MTIzNDU2Nzg5MDEyMzQ1Ng==" // 16 bytes when decoded + key, err := deriveKey(validBase64, 16) + if err != nil { + t.Fatalf("Failed to derive key from valid base64: %v", err) + } + if len(key) != 16 { + t.Fatalf("Expected key length 16, got %d", len(key)) + } + + // Test with passphrase (should use PBKDF2) + passphrase := "my-secret-passphrase" + key2, err := deriveKey(passphrase, 32) + if err != nil { + t.Fatalf("Failed to derive key from passphrase: %v", err) + } + if len(key2) != 32 { + t.Fatalf("Expected key length 32, got %d", len(key2)) + } + + // Same passphrase should produce same key (deterministic) + key3, err := deriveKey(passphrase, 32) + if err != nil { + t.Fatalf("Failed to derive key from passphrase (second time): %v", err) + } + if !bytes.Equal(key2, key3) { + t.Fatal("Same passphrase should produce same derived key") + } +} diff --git a/pingtunnel/go.mod b/pingtunnel/go.mod index e52ed62721..ee668a71b0 100644 --- a/pingtunnel/go.mod +++ b/pingtunnel/go.mod @@ -5,6 +5,7 @@ go 1.24.1 require ( github.com/esrrhs/gohome v0.0.0-20250426022937-504d912bccf7 github.com/golang/protobuf v1.5.4 + golang.org/x/crypto v0.37.0 golang.org/x/net v0.39.0 ) @@ -27,8 +28,6 @@ require ( github.com/xtaci/smux v1.5.34 // indirect go.uber.org/automaxprocs v1.6.0 // indirect go.uber.org/mock v0.5.1 // indirect - golang.org/x/crypto v0.37.0 // indirect - golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect golang.org/x/mod v0.24.0 // indirect golang.org/x/sync v0.13.0 // indirect golang.org/x/sys v0.32.0 // indirect diff --git a/pingtunnel/go.sum b/pingtunnel/go.sum index 27a71cec93..f3db168282 100644 --- a/pingtunnel/go.sum +++ b/pingtunnel/go.sum @@ -14,7 +14,6 @@ github.com/esrrhs/gohome v0.0.0-20250426022937-504d912bccf7 h1:EpgZzfBgtv89nRgnp github.com/esrrhs/gohome v0.0.0-20250426022937-504d912bccf7/go.mod h1:4kAeSdLmMeWMOpzi/Jx82YvDnrBHcbWcLT18FwTujFA= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -34,27 +33,20 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/pprof v0.0.0-20241101162523-b92577c0c142 h1:sAGdeJj0bnMgUNVeUpp6AYlVdCt3/GdI3pGRqsNSQLs= -github.com/google/pprof v0.0.0-20241101162523-b92577c0c142/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4 h1:gD0vax+4I+mAj+jEChEf25Ia07Jq7kYOFO5PPhAxFl4= github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= -github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/klauspost/reedsolomon v1.12.4 h1:5aDr3ZGoJbgu/8+j45KtUJxzYm8k08JGtB9Wx1VQ4OA= github.com/klauspost/reedsolomon v1.12.4/go.mod h1:d3CzOMOt0JXGIFZm1StgkyF14EYr3xneR2rNWo7NcMU= -github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= -github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus= github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8= -github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8= -github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc= github.com/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU= +github.com/onsi/gomega v1.36.3/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= github.com/oschwald/geoip2-golang v1.11.0 h1:hNENhCn1Uyzhf9PTmquXENiWS6AlxAEnBII6r8krA3w= github.com/oschwald/geoip2-golang v1.11.0/go.mod h1:P9zG+54KPEFOliZ29i7SeYZ/GM6tfEL+rgSn03hYuUo= github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE= @@ -63,9 +55,9 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= +github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/quic-go/quic-go v0.48.1 h1:y/8xmfWI9qmGTc+lBr4jKRUWLGSlSigv847ULJ4hYXA= -github.com/quic-go/quic-go v0.48.1/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs= github.com/quic-go/quic-go v0.51.0 h1:K8exxe9zXxeRKxaXxi/GpUqYiTrtdiWP8bo1KFya6Wc= github.com/quic-go/quic-go v0.51.0/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP4wqrWmzQ= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= @@ -80,33 +72,21 @@ github.com/xtaci/kcp-go v5.4.20+incompatible h1:TN1uey3Raw0sTz0Fg8GkfM0uH3YwzhnZ github.com/xtaci/kcp-go v5.4.20+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE= github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37 h1:EWU6Pktpas0n8lLQwDsRyZfmkPeRbdgPtW609es+/9E= github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37/go.mod h1:HpMP7DB2CyokmAh4lp0EQnnWhmycP/TvwBGzvuie+H0= -github.com/xtaci/smux v1.5.31 h1:3ha7sHtH46h85Iv7MfQogxasuRt1KPRhoFB3S4rmHgU= -github.com/xtaci/smux v1.5.31/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY= github.com/xtaci/smux v1.5.34 h1:OUA9JaDFHJDT8ZT3ebwLWPAgEfE6sWo2LaTy3anXqwg= github.com/xtaci/smux v1.5.34/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY= go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= -go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= -go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= go.uber.org/mock v0.5.1 h1:ASgazW/qBmR+A32MYFDB6E2POoTgOwT509VP0CT/fjs= go.uber.org/mock v0.5.1/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= -golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= -golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= -golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM= -golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= -golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -115,40 +95,29 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= -golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= -golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= -golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= -golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o= -golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q= golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU= golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -166,8 +135,6 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= -google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/pingtunnel/pingtunnel b/pingtunnel/pingtunnel new file mode 100755 index 0000000000..0952c5ded3 Binary files /dev/null and b/pingtunnel/pingtunnel differ diff --git a/pingtunnel/pingtunnel.go b/pingtunnel/pingtunnel.go index 73d046daea..847ca4df20 100644 --- a/pingtunnel/pingtunnel.go +++ b/pingtunnel/pingtunnel.go @@ -15,7 +15,7 @@ import ( func sendICMP(id int, sequence int, conn icmp.PacketConn, server *net.IPAddr, target string, connId string, msgType uint32, data []byte, sproto int, rproto int, key int, tcpmode int, tcpmode_buffer_size int, tcpmode_maxwin int, tcpmode_resend_time int, tcpmode_compress int, tcpmode_stat int, - timeout int) { + timeout int, cryptoConfig *CryptoConfig) { m := &MyMsg{ Id: connId, @@ -40,6 +40,15 @@ func sendICMP(id int, sequence int, conn icmp.PacketConn, server *net.IPAddr, ta return } + // Encrypt the marshaled data if encryption is enabled + if cryptoConfig != nil { + mb, err = cryptoConfig.Encrypt(mb) + if err != nil { + loggo.Error("sendICMP Encrypt error %s %s", server.String(), err) + return + } + } + body := &icmp.Echo{ ID: id, Seq: sequence, @@ -61,7 +70,7 @@ func sendICMP(id int, sequence int, conn icmp.PacketConn, server *net.IPAddr, ta conn.WriteTo(bytes, server) } -func recvICMP(workResultLock *sync.WaitGroup, exit *bool, conn icmp.PacketConn, recv chan<- *Packet) { +func recvICMP(workResultLock *sync.WaitGroup, exit *bool, conn icmp.PacketConn, recv chan<- *Packet, cryptoConfig *CryptoConfig) { defer common.CrashLog() @@ -88,8 +97,20 @@ func recvICMP(workResultLock *sync.WaitGroup, exit *bool, conn icmp.PacketConn, echoId := int(binary.BigEndian.Uint16(bytes[4:6])) echoSeq := int(binary.BigEndian.Uint16(bytes[6:8])) + // Extract the payload data + payloadData := bytes[8:n] + + // Decrypt the data if encryption is enabled + if cryptoConfig != nil { + payloadData, err = cryptoConfig.Decrypt(payloadData) + if err != nil { + loggo.Debug("recvICMP Decrypt error: %s", err) + continue + } + } + my := &MyMsg{} - err = proto.Unmarshal(bytes[8:n], my) + err = proto.Unmarshal(payloadData, my) if err != nil { loggo.Debug("Unmarshal MyMsg error: %s", err) continue diff --git a/pingtunnel/server.go b/pingtunnel/server.go index 64cc3c4ced..7f62e2cd44 100644 --- a/pingtunnel/server.go +++ b/pingtunnel/server.go @@ -12,7 +12,7 @@ import ( "time" ) -func NewServer(key int, maxconn int, maxprocessthread int, maxprocessbuffer int, connecttmeout int) (*Server, error) { +func NewServer(key int, maxconn int, maxprocessthread int, maxprocessbuffer int, connecttmeout int, cryptoConfig *CryptoConfig) (*Server, error) { s := &Server{ exit: false, key: key, @@ -20,6 +20,7 @@ func NewServer(key int, maxconn int, maxprocessthread int, maxprocessbuffer int, maxprocessthread: maxprocessthread, maxprocessbuffer: maxprocessbuffer, connecttmeout: connecttmeout, + cryptoConfig: cryptoConfig, } if maxprocessthread > 0 { @@ -40,6 +41,7 @@ type Server struct { maxprocessthread int maxprocessbuffer int connecttmeout int + cryptoConfig *CryptoConfig conn *icmp.PacketConn @@ -85,7 +87,7 @@ func (p *Server) Run() error { recv := make(chan *Packet, 10000) p.recvcontrol = make(chan int, 1) - go recvICMP(&p.workResultLock, &p.exit, *p.conn, recv) + go recvICMP(&p.workResultLock, &p.exit, *p.conn, recv, p.cryptoConfig) go func() { defer common.CrashLog() @@ -139,9 +141,9 @@ func (p *Server) processPacket(packet *Packet) { t.UnmarshalBinary(packet.my.Data) loggo.Info("ping from %s %s %d %d %d", packet.src.String(), t.String(), packet.my.Rproto, packet.echoId, packet.echoSeq) sendICMP(packet.echoId, packet.echoSeq, *p.conn, packet.src, "", "", (uint32)(MyMsg_PING), packet.my.Data, - (int)(packet.my.Rproto), -1, p.key, - 0, 0, 0, 0, 0, 0, - 0) + (int)(packet.my.Rproto), -1, p.key, + 0, 0, 0, 0, 0, 0, + 0, p.cryptoConfig) return } @@ -297,9 +299,9 @@ func (p *Server) RecvTCP(conn *ServerConn, id string, src *net.IPAddr) { f := e.Value.(*network.Frame) mb, _ := conn.fm.MarshalFrame(f) sendICMP(conn.echoId, conn.echoSeq, *p.conn, src, "", id, (uint32)(MyMsg_DATA), mb, - conn.rproto, -1, p.key, - 0, 0, 0, 0, 0, 0, - 0) + conn.rproto, -1, p.key, 0, + 0, 0, 0, 0, 0, + 0, p.cryptoConfig) p.sendPacket++ p.sendPacketSize += (uint64)(len(mb)) } @@ -360,9 +362,9 @@ func (p *Server) RecvTCP(conn *ServerConn, id string, src *net.IPAddr) { continue } sendICMP(conn.echoId, conn.echoSeq, *p.conn, src, "", id, (uint32)(MyMsg_DATA), mb, - conn.rproto, -1, p.key, - 0, 0, 0, 0, 0, 0, - 0) + conn.rproto, -1, p.key, 0, + 0, 0, 0, 0, 0, + 0, p.cryptoConfig) p.sendPacket++ p.sendPacketSize += (uint64)(len(mb)) } @@ -422,9 +424,9 @@ func (p *Server) RecvTCP(conn *ServerConn, id string, src *net.IPAddr) { f := e.Value.(*network.Frame) mb, _ := conn.fm.MarshalFrame(f) sendICMP(conn.echoId, conn.echoSeq, *p.conn, src, "", id, (uint32)(MyMsg_DATA), mb, - conn.rproto, -1, p.key, - 0, 0, 0, 0, 0, 0, - 0) + conn.rproto, -1, p.key, 0, + 0, 0, 0, 0, 0, + 0, p.cryptoConfig) p.sendPacket++ p.sendPacketSize += (uint64)(len(mb)) } @@ -489,9 +491,9 @@ func (p *Server) Recv(conn *ServerConn, id string, src *net.IPAddr) { conn.activeSendTime = now sendICMP(conn.echoId, conn.echoSeq, *p.conn, src, "", id, (uint32)(MyMsg_DATA), bytes[:n], - conn.rproto, -1, p.key, - 0, 0, 0, 0, 0, 0, - 0) + conn.rproto, -1, p.key, 0, + 0, 0, 0, 0, 0, + 0, p.cryptoConfig) p.sendPacket++ p.sendPacketSize += (uint64)(n) @@ -576,9 +578,9 @@ func (p *Server) deleteServerConn(uuid string) { func (p *Server) remoteError(echoId int, echoSeq int, uuid string, rprpto int, src *net.IPAddr) { sendICMP(echoId, echoSeq, *p.conn, src, "", uuid, (uint32)(MyMsg_KICK), []byte{}, - rprpto, -1, p.key, - 0, 0, 0, 0, 0, 0, - 0) + rprpto, -1, p.key, + 0, 0, 0, 0, 0, 0, 0, + p.cryptoConfig) } func (p *Server) addConnError(addr string) { diff --git a/shadowsocks-rust/Cargo.lock b/shadowsocks-rust/Cargo.lock index 6f3cd06fe2..5f3bf0a7f3 100644 --- a/shadowsocks-rust/Cargo.lock +++ b/shadowsocks-rust/Cargo.lock @@ -269,38 +269,18 @@ checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" [[package]] name = "bindgen" -version = "0.69.5" +version = "0.72.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +checksum = "4f72209734318d0b619a5e0f5129918b848c416e122a3c4ce054e03cb87b726f" dependencies = [ "bitflags 2.9.1", "cexpr", "clang-sys", - "itertools 0.12.1", - "lazy_static", - "lazycell", + "itertools", "proc-macro2", "quote", "regex", - "rustc-hash 1.1.0", - "shlex", - "syn 2.0.104", -] - -[[package]] -name = "bindgen" -version = "0.71.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" -dependencies = [ - "bitflags 2.9.1", - "cexpr", - "clang-sys", - "itertools 0.13.0", - "proc-macro2", - "quote", - "regex", - "rustc-hash 2.1.1", + "rustc-hash", "shlex", "syn 2.0.104", ] @@ -1833,15 +1813,6 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.13.0" @@ -1958,17 +1929,11 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - [[package]] name = "libc" -version = "0.2.174" +version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] name = "libloading" @@ -1977,7 +1942,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.53.2", ] [[package]] @@ -2002,11 +1967,11 @@ dependencies = [ [[package]] name = "librocksdb-sys" -version = "0.17.1+9.9.3" +version = "0.17.3+10.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b7869a512ae9982f4d46ba482c2a304f1efd80c6412a3d4bf57bb79a619679f" +checksum = "cef2a00ee60fe526157c9023edab23943fae1ce2ab6f4abb2a807c1746835de9" dependencies = [ - "bindgen 0.69.5", + "bindgen", "bzip2-sys", "cc", "libc", @@ -2700,7 +2665,7 @@ dependencies = [ "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash 2.1.1", + "rustc-hash", "rustls", "socket2 0.5.10", "thiserror 2.0.12", @@ -2720,7 +2685,7 @@ dependencies = [ "lru-slab", "rand 0.9.2", "ring", - "rustc-hash 2.1.1", + "rustc-hash", "rustls", "rustls-pki-types", "slab", @@ -2976,9 +2941,9 @@ dependencies = [ [[package]] name = "rocksdb" -version = "0.23.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26ec73b20525cb235bad420f911473b69f9fe27cc856c5461bccd7e4af037f43" +checksum = "ddb7af00d2b17dbd07d82c0063e25411959748ff03e8d4f96134c2ff41fce34f" dependencies = [ "libc", "librocksdb-sys", @@ -3031,12 +2996,6 @@ version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - [[package]] name = "rustc-hash" version = "2.1.1" @@ -4384,7 +4343,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -4941,7 +4900,6 @@ version = "2.0.15+zstd.1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" dependencies = [ - "bindgen 0.71.1", "cc", "pkg-config", ] diff --git a/shadowsocks-rust/crates/shadowsocks-service/Cargo.toml b/shadowsocks-rust/crates/shadowsocks-service/Cargo.toml index c78fb24725..df795d5215 100644 --- a/shadowsocks-rust/crates/shadowsocks-service/Cargo.toml +++ b/shadowsocks-rust/crates/shadowsocks-service/Cargo.toml @@ -134,7 +134,7 @@ bytes = "1.7" byte_string = "1.0" byteorder = "1.5" rand = { version = "0.9", features = ["small_rng"] } -rocksdb = { version = "0.23", optional = true } +rocksdb = { version = "0.24", optional = true } futures = "0.3" tokio = { version = "1.38", features = [ diff --git a/shadowsocks-rust/crates/shadowsocks-service/src/acl/mod.rs b/shadowsocks-rust/crates/shadowsocks-service/src/acl/mod.rs index 92713ed253..2f120333c5 100644 --- a/shadowsocks-rust/crates/shadowsocks-service/src/acl/mod.rs +++ b/shadowsocks-rust/crates/shadowsocks-service/src/acl/mod.rs @@ -543,7 +543,7 @@ impl AccessControl { /// Returns the ASCII representation a domain name, /// if conversion fails returns original string - fn convert_to_ascii(host: &str) -> Cow { + fn convert_to_ascii(host: &str) -> Cow<'_, str> { idna::domain_to_ascii(host) .map(From::from) .unwrap_or_else(|_| host.into()) diff --git a/sing-box/.github/workflows/build.yml b/sing-box/.github/workflows/build.yml index adc51382a5..2ccc81a8d3 100644 --- a/sing-box/.github/workflows/build.yml +++ b/sing-box/.github/workflows/build.yml @@ -46,7 +46,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: ^1.24.5 + go-version: ^1.24.6 - name: Check input version if: github.event_name == 'workflow_dispatch' run: |- @@ -109,7 +109,7 @@ jobs: if: ${{ ! matrix.legacy_go }} uses: actions/setup-go@v5 with: - go-version: ^1.24.5 + go-version: ^1.24.6 - name: Cache Legacy Go if: matrix.require_legacy_go id: cache-legacy-go @@ -294,7 +294,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: ^1.24.5 + go-version: ^1.24.6 - name: Setup Android NDK id: setup-ndk uses: nttld/setup-ndk@v1 @@ -374,7 +374,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: ^1.24.5 + go-version: ^1.24.6 - name: Setup Android NDK id: setup-ndk uses: nttld/setup-ndk@v1 @@ -472,7 +472,7 @@ jobs: if: matrix.if uses: actions/setup-go@v5 with: - go-version: ^1.24.5 + go-version: ^1.24.6 - name: Setup Xcode stable if: matrix.if && github.ref == 'refs/heads/main-next' run: |- diff --git a/sing-box/.github/workflows/lint.yml b/sing-box/.github/workflows/lint.yml index 1b09ad2a70..08d5440215 100644 --- a/sing-box/.github/workflows/lint.yml +++ b/sing-box/.github/workflows/lint.yml @@ -28,7 +28,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: ^1.24.5 + go-version: ^1.24.6 - name: golangci-lint uses: golangci/golangci-lint-action@v6 with: diff --git a/sing-box/.github/workflows/linux.yml b/sing-box/.github/workflows/linux.yml index 00a3f71d55..ad0c8af594 100644 --- a/sing-box/.github/workflows/linux.yml +++ b/sing-box/.github/workflows/linux.yml @@ -7,6 +7,11 @@ on: description: "Version name" required: true type: string + forceBeta: + description: "Force beta" + required: false + type: boolean + default: false release: types: - published @@ -25,7 +30,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: ^1.24.5 + go-version: ^1.24.6 - name: Check input version if: github.event_name == 'workflow_dispatch' run: |- @@ -66,7 +71,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: ^1.24.5 + go-version: ^1.24.6 - name: Setup Android NDK if: matrix.os == 'android' uses: nttld/setup-ndk@v1 @@ -99,11 +104,11 @@ jobs: run: |- TZ=UTC touch -t '197001010000' dist/sing-box - name: Set name - if: ${{ ! contains(needs.calculate_version.outputs.version, '-') }} + if: (! contains(needs.calculate_version.outputs.version, '-')) && !inputs.forceBeta run: |- echo "NAME=sing-box" >> "$GITHUB_ENV" - name: Set beta name - if: contains(needs.calculate_version.outputs.version, '-') + if: contains(needs.calculate_version.outputs.version, '-') || inputs.forceBeta run: |- echo "NAME=sing-box-beta" >> "$GITHUB_ENV" - name: Set version diff --git a/sing-box/clients/android/app/build.gradle b/sing-box/clients/android/app/build.gradle index 159c126675..228d38ff44 100644 --- a/sing-box/clients/android/app/build.gradle +++ b/sing-box/clients/android/app/build.gradle @@ -5,12 +5,13 @@ plugins { id "kotlin-android" id "kotlin-parcelize" id "com.google.devtools.ksp" + id "org.jetbrains.kotlin.plugin.compose" id "com.github.triplet.play" } android { namespace "io.nekohasekai.sfa" - compileSdk 35 + compileSdk 36 ndkVersion "28.0.13004108" @@ -85,6 +86,7 @@ android { buildFeatures { viewBinding true aidl true + compose true } applicationVariants.configureEach { variant -> @@ -103,10 +105,10 @@ dependencies { implementation "androidx.appcompat:appcompat:1.7.1" implementation "com.google.android.material:material:1.12.0" implementation "androidx.constraintlayout:constraintlayout:2.2.1" - implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.9.1" - implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.9.1" - implementation "androidx.navigation:navigation-fragment-ktx:2.9.1" - implementation "androidx.navigation:navigation-ui-ktx:2.9.1" + implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.9.2" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.9.2" + implementation "androidx.navigation:navigation-fragment-ktx:2.9.3" + implementation "androidx.navigation:navigation-ui-ktx:2.9.3" implementation "com.google.zxing:core:3.5.3" implementation "androidx.room:room-runtime:2.7.2" implementation "androidx.coordinatorlayout:coordinatorlayout:1.3.0" @@ -115,8 +117,8 @@ dependencies { implementation "androidx.camera:camera-lifecycle:1.4.2" implementation "androidx.camera:camera-camera2:1.4.2" ksp "androidx.room:room-compiler:2.7.2" - implementation "androidx.work:work-runtime-ktx:2.10.2" - implementation "androidx.browser:browser:1.8.0" + implementation "androidx.work:work-runtime-ktx:2.10.3" + implementation "androidx.browser:browser:1.9.0" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2" // DO NOT UPDATE (minSdkVersion updated) @@ -129,6 +131,19 @@ dependencies { implementation "com.google.guava:guava:33.4.8-android" playImplementation "com.google.android.play:app-update-ktx:2.1.0" playImplementation "com.google.android.gms:play-services-mlkit-barcode-scanning:18.3.1" + + def composeBom = platform('androidx.compose:compose-bom:2025.07.00') + implementation composeBom + androidTestImplementation composeBom + implementation 'androidx.compose.material3:material3' + implementation 'androidx.compose.ui:ui-tooling-preview' + debugImplementation 'androidx.compose.ui:ui-tooling' + androidTestImplementation 'androidx.compose.ui:ui-test-junit4' + debugImplementation 'androidx.compose.ui:ui-test-manifest' + implementation 'androidx.compose.material:material-icons-extended' + implementation 'androidx.activity:activity-compose:1.10.1' + implementation 'me.zhanghai.compose.preference:library:1.1.1' + implementation "androidx.navigation:navigation-compose:2.9.3" } def playCredentialsJSON = rootProject.file("service-account-credentials.json") diff --git a/sing-box/clients/android/build.gradle b/sing-box/clients/android/build.gradle index 135472c2ac..c3c1066adb 100644 --- a/sing-box/clients/android/build.gradle +++ b/sing-box/clients/android/build.gradle @@ -5,10 +5,11 @@ buildscript { } plugins { - id 'com.android.application' version '8.11.0' apply false - id 'com.android.library' version '8.11.0' apply false - id 'org.jetbrains.kotlin.android' version '2.1.0' apply false - id 'com.google.devtools.ksp' version '2.1.0-1.0.29' apply false - id 'com.github.triplet.play' version '3.8.4' apply false + id 'com.android.application' version '8.11.1' apply false + id 'com.android.library' version '8.11.1' apply false + id 'org.jetbrains.kotlin.android' version '2.2.0' apply false + id 'com.google.devtools.ksp' version '2.2.0-2.0.2' apply false + id 'com.github.triplet.play' version '3.12.1' apply false + id 'org.jetbrains.kotlin.plugin.compose' version '2.2.0' apply false } diff --git a/sing-box/clients/android/version.properties b/sing-box/clients/android/version.properties index 31e219f2a3..dc64133669 100644 --- a/sing-box/clients/android/version.properties +++ b/sing-box/clients/android/version.properties @@ -1,3 +1,3 @@ -VERSION_CODE=549 -VERSION_NAME=1.12.0 -GO_VERSION=go1.24.5 +VERSION_CODE=550 +VERSION_NAME=1.12.1 +GO_VERSION=go1.24.6 diff --git a/sing-box/clients/apple/Library/Network/ExtensionPlatformInterface.swift b/sing-box/clients/apple/Library/Network/ExtensionPlatformInterface.swift index 77927f5054..324c3555ca 100644 --- a/sing-box/clients/apple/Library/Network/ExtensionPlatformInterface.swift +++ b/sing-box/clients/apple/Library/Network/ExtensionPlatformInterface.swift @@ -423,4 +423,12 @@ public class ExtensionPlatformInterface: NSObject, LibboxPlatformInterfaceProtoc } #endif } + + public func localDNSTransport() -> (any LibboxLocalDNSTransportProtocol)? { + nil + } + + public func systemCertificates() -> (any LibboxStringIteratorProtocol)? { + nil + } } diff --git a/sing-box/clients/apple/sing-box.xcodeproj/project.pbxproj b/sing-box/clients/apple/sing-box.xcodeproj/project.pbxproj index 1ea6f1d2b2..8cf6549586 100644 --- a/sing-box/clients/apple/sing-box.xcodeproj/project.pbxproj +++ b/sing-box/clients/apple/sing-box.xcodeproj/project.pbxproj @@ -2163,7 +2163,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.11.5; + MARKETING_VERSION = 1.12.0; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfavt; PRODUCT_NAME = "sing-box"; SDKROOT = appletvos; @@ -2198,7 +2198,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.11.5; + MARKETING_VERSION = 1.12.0; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfavt; PRODUCT_NAME = "sing-box"; SDKROOT = appletvos; @@ -2503,7 +2503,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.11.5; + MARKETING_VERSION = 1.12.10; OTHER_CODE_SIGN_FLAGS = "--deep"; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfavt; PRODUCT_NAME = "sing-box"; @@ -2545,7 +2545,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.11.5; + MARKETING_VERSION = 1.12.10; OTHER_CODE_SIGN_FLAGS = "--deep"; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfavt; PRODUCT_NAME = "sing-box"; @@ -2586,7 +2586,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 1.11.5; + MARKETING_VERSION = 1.12.0; OTHER_CODE_SIGN_FLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfavt; PRODUCT_NAME = "sing-box"; @@ -2626,7 +2626,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 1.11.5; + MARKETING_VERSION = 1.12.0; OTHER_CODE_SIGN_FLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfavt; PRODUCT_NAME = "sing-box"; @@ -2754,7 +2754,7 @@ "@executable_path/../../../../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 1.11.5; + MARKETING_VERSION = "1.12.0-beta.2"; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfavt.system; PRODUCT_NAME = "$(inherited)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -2790,7 +2790,7 @@ "@executable_path/../../../../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 1.11.5; + MARKETING_VERSION = "1.12.0-beta.2"; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfavt.system; PRODUCT_NAME = "$(inherited)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -2832,7 +2832,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 1.11.5; + MARKETING_VERSION = "1.12.0-beta.2"; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfavt.standalone; PRODUCT_NAME = SFM; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -2873,7 +2873,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 1.11.5; + MARKETING_VERSION = "1.12.0-beta.2"; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfavt.standalone; PRODUCT_NAME = SFM; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/sing-box/cmd/internal/build_libbox/main.go b/sing-box/cmd/internal/build_libbox/main.go index de470eb37a..c7bdf6cf99 100644 --- a/sing-box/cmd/internal/build_libbox/main.go +++ b/sing-box/cmd/internal/build_libbox/main.go @@ -107,13 +107,13 @@ func buildAndroid() { } if !debugEnabled { + sharedFlags[3] = sharedFlags[3] + " -checklinkname=0" args = append(args, sharedFlags...) } else { + debugFlags[1] = debugFlags[1] + " -checklinkname=0" args = append(args, debugFlags...) } - args = append(args, "-ldflags", "-checklinkname=0") - tags := append(sharedTags, memcTags...) if debugEnabled { tags = append(tags, debugTags...) diff --git a/sing-box/common/convertor/adguard/convertor.go b/sing-box/common/convertor/adguard/convertor.go index 68bc931945..3e6d0254f9 100644 --- a/sing-box/common/convertor/adguard/convertor.go +++ b/sing-box/common/convertor/adguard/convertor.go @@ -454,5 +454,5 @@ func parseADGuardIPCIDRLine(ruleLine string) (netip.Prefix, error) { for len(ruleParts) < 4 { ruleParts = append(ruleParts, 0) } - return netip.PrefixFrom(netip.AddrFrom4(*(*[4]byte)(ruleParts)), bitLen), nil + return netip.PrefixFrom(netip.AddrFrom4([4]byte(ruleParts)), bitLen), nil } diff --git a/sing-box/common/dialer/dialer.go b/sing-box/common/dialer/dialer.go index 7bf8ff02c8..bfa8af215c 100644 --- a/sing-box/common/dialer/dialer.go +++ b/sing-box/common/dialer/dialer.go @@ -111,7 +111,7 @@ func NewWithOptions(options Options) (N.Dialer, error) { dnsQueryOptions.Transport = dnsTransport.Default() } else if options.NewDialer { return nil, E.New("missing domain resolver for domain server address") - } else if !options.DirectOutbound { + } else { deprecated.Report(options.Context, deprecated.OptionMissingDomainResolver) } } diff --git a/sing-box/common/process/searcher_darwin.go b/sing-box/common/process/searcher_darwin.go index 16e2c87a53..5c1addd573 100644 --- a/sing-box/common/process/searcher_darwin.go +++ b/sing-box/common/process/searcher_darwin.go @@ -96,11 +96,11 @@ func findProcessName(network string, ip netip.Addr, port int) (string, error) { switch { case flag&0x1 > 0 && isIPv4: // ipv4 - srcIP = netip.AddrFrom4(*(*[4]byte)(buf[inp+76 : inp+80])) + srcIP = netip.AddrFrom4([4]byte(buf[inp+76 : inp+80])) srcIsIPv4 = true case flag&0x2 > 0 && !isIPv4: // ipv6 - srcIP = netip.AddrFrom16(*(*[16]byte)(buf[inp+64 : inp+80])) + srcIP = netip.AddrFrom16([16]byte(buf[inp+64 : inp+80])) default: continue } diff --git a/sing-box/common/tls/client.go b/sing-box/common/tls/client.go index 5e05c990d9..ce9cfe3d9b 100644 --- a/sing-box/common/tls/client.go +++ b/sing-box/common/tls/client.go @@ -2,6 +2,8 @@ package tls import ( "context" + "crypto/tls" + "errors" "net" "os" @@ -41,6 +43,13 @@ func ClientHandshake(ctx context.Context, conn net.Conn, config Config) (Conn, e ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout) defer cancel() tlsConn, err := aTLS.ClientHandshake(ctx, conn, config) + var echErr *tls.ECHRejectionError + if errors.As(err, &echErr) && len(echErr.RetryConfigList) > 0 { + if echConfig, isECH := config.(ECHCapableConfig); isECH { + echConfig.SetECHConfigList(echErr.RetryConfigList) + tlsConn, err = aTLS.ClientHandshake(ctx, conn, config) + } + } if err != nil { return nil, err } diff --git a/sing-box/dns/transport/local/resolv_darwin_cgo.go b/sing-box/dns/transport/local/resolv_darwin_cgo.go index e19d087f26..bbe4ccfefe 100644 --- a/sing-box/dns/transport/local/resolv_darwin_cgo.go +++ b/sing-box/dns/transport/local/resolv_darwin_cgo.go @@ -20,8 +20,8 @@ import ( ) func dnsReadConfig(_ context.Context, _ string) *dnsConfig { - var state C.res_state - if C.res_ninit(state) != 0 { + var state C.struct___res_state + if C.res_ninit(&state) != 0 { return &dnsConfig{ servers: defaultNS, search: dnsDefaultSearch(), diff --git a/sing-box/dns/transport/local/resolv_test.go b/sing-box/dns/transport/local/resolv_test.go new file mode 100644 index 0000000000..546e84082f --- /dev/null +++ b/sing-box/dns/transport/local/resolv_test.go @@ -0,0 +1,13 @@ +package local + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestDNSReadConfig(t *testing.T) { + t.Parallel() + require.NoError(t, dnsReadConfig(context.Background(), "/etc/resolv.conf").err) +} diff --git a/sing-box/docs/changelog.md b/sing-box/docs/changelog.md index 1544deb65e..e54a9d170d 100644 --- a/sing-box/docs/changelog.md +++ b/sing-box/docs/changelog.md @@ -2,6 +2,10 @@ icon: material/alert-decagram --- +#### 1.12.1 + +* Fixes and improvements + #### 1.12.0 * Refactor DNS servers **1** diff --git a/sing-box/docs/configuration/endpoint/wireguard.zh.md b/sing-box/docs/configuration/endpoint/wireguard.zh.md index f80c82b49d..cf82058001 100644 --- a/sing-box/docs/configuration/endpoint/wireguard.zh.md +++ b/sing-box/docs/configuration/endpoint/wireguard.zh.md @@ -61,7 +61,7 @@ WireGuard MTU。 ==必填== -接口的 IPv4/IPv6 地址或地址段的列表您。 +接口的 IPv4/IPv6 地址或地址段的列表。 要分配给接口的 IP(v4 或 v6)地址段列表。 diff --git a/sing-box/docs/configuration/outbound/wireguard.zh.md b/sing-box/docs/configuration/outbound/wireguard.zh.md index 4597fd9d73..46b49a94ad 100644 --- a/sing-box/docs/configuration/outbound/wireguard.zh.md +++ b/sing-box/docs/configuration/outbound/wireguard.zh.md @@ -88,7 +88,7 @@ icon: material/delete-clock ==必填== -接口的 IPv4/IPv6 地址或地址段的列表您。 +接口的 IPv4/IPv6 地址或地址段的列表。 要分配给接口的 IP(v4 或 v6)地址段列表。 diff --git a/sing-box/docs/manual/proxy/client.md b/sing-box/docs/manual/proxy/client.md index 8583a6a2ee..e8c80523d9 100644 --- a/sing-box/docs/manual/proxy/client.md +++ b/sing-box/docs/manual/proxy/client.md @@ -108,7 +108,7 @@ flowchart TB "inbounds": [ { "type": "tun", - "inet4_address": "172.19.0.1/30", + "address": ["172.19.0.1/30"], "auto_route": true, // "auto_redirect": true, // On linux "strict_route": true @@ -162,8 +162,7 @@ flowchart TB "inbounds": [ { "type": "tun", - "inet4_address": "172.19.0.1/30", - "inet6_address": "fdfe:dcba:9876::1/126", + "address": ["172.19.0.1/30", "fdfe:dcba:9876::1/126"], "auto_route": true, // "auto_redirect": true, // On linux "strict_route": true @@ -233,8 +232,7 @@ flowchart TB "inbounds": [ { "type": "tun", - "inet4_address": "172.19.0.1/30", - "inet6_address": "fdfe:dcba:9876::1/126", + "address": ["172.19.0.1/30","fdfe:dcba:9876::1/126"], "auto_route": true, // "auto_redirect": true, // On linux "strict_route": true diff --git a/sing-box/docs/migration.zh.md b/sing-box/docs/migration.zh.md index 2f027a710f..3a4dde7f69 100644 --- a/sing-box/docs/migration.zh.md +++ b/sing-box/docs/migration.zh.md @@ -8,7 +8,7 @@ icon: material/arrange-bring-forward DNS 服务器已经重构。 -!!! info "饮用" +!!! info "引用" [DNS 服务器](/configuration/dns/server/) / [旧 DNS 服务器](/configuration/dns/server/legacy/) diff --git a/sing-box/experimental/clashapi/cache.go b/sing-box/experimental/clashapi/cache.go index 9c088a82f7..4df1f89081 100644 --- a/sing-box/experimental/clashapi/cache.go +++ b/sing-box/experimental/clashapi/cache.go @@ -14,6 +14,7 @@ import ( func cacheRouter(ctx context.Context) http.Handler { r := chi.NewRouter() r.Post("/fakeip/flush", flushFakeip(ctx)) + r.Post("/dns/flush", flushDNS(ctx)) return r } @@ -31,3 +32,13 @@ func flushFakeip(ctx context.Context) func(w http.ResponseWriter, r *http.Reques render.NoContent(w, r) } } + +func flushDNS(ctx context.Context) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + dnsRouter := service.FromContext[adapter.DNSRouter](ctx) + if dnsRouter != nil { + dnsRouter.ClearCache() + } + render.NoContent(w, r) + } +} diff --git a/sing-box/go.mod b/sing-box/go.mod index dd6373eb56..fef5f03ba0 100644 --- a/sing-box/go.mod +++ b/sing-box/go.mod @@ -27,7 +27,7 @@ require ( github.com/sagernet/gomobile v0.1.7 github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb github.com/sagernet/quic-go v0.52.0-beta.1 - github.com/sagernet/sing v0.7.0-beta.2 + github.com/sagernet/sing v0.7.5 github.com/sagernet/sing-mux v0.3.2 github.com/sagernet/sing-quic v0.5.0-beta.3 github.com/sagernet/sing-shadowsocks v0.2.8 diff --git a/sing-box/go.sum b/sing-box/go.sum index e495f174cc..e83a0b05b6 100644 --- a/sing-box/go.sum +++ b/sing-box/go.sum @@ -167,8 +167,8 @@ github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/l github.com/sagernet/quic-go v0.52.0-beta.1 h1:hWkojLg64zjV+MJOvJU/kOeWndm3tiEfBLx5foisszs= github.com/sagernet/quic-go v0.52.0-beta.1/go.mod h1:OV+V5kEBb8kJS7k29MzDu6oj9GyMc7HA07sE1tedxz4= github.com/sagernet/sing v0.6.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= -github.com/sagernet/sing v0.7.0-beta.2 h1:UImAKtHGQX205lGYYXKA2qnEeVSml+hKS1oaOwvA14c= -github.com/sagernet/sing v0.7.0-beta.2/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= +github.com/sagernet/sing v0.7.5 h1:gNMwZCLPqR+4e0g6dwi0sSsrvOmoMjpZgqxKsuJZatc= +github.com/sagernet/sing v0.7.5/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/sagernet/sing-mux v0.3.2 h1:meZVFiiStvHThb/trcpAkCrmtJOuItG5Dzl1RRP5/NE= github.com/sagernet/sing-mux v0.3.2/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA= github.com/sagernet/sing-quic v0.5.0-beta.3 h1:X/acRNsqQNfDlmwE7SorHfaZiny5e67hqIzM/592ric= diff --git a/sing-box/test/clash_darwin_test.go b/sing-box/test/clash_darwin_test.go index d4eff13e88..013d8b3f1a 100644 --- a/sing-box/test/clash_darwin_test.go +++ b/sing-box/test/clash_darwin_test.go @@ -26,7 +26,7 @@ func defaultRouteIP() (netip.Addr, error) { for _, addr := range addrs { ip := addr.(*net.IPNet).IP if ip.To4() != nil { - return netip.AddrFrom4(*(*[4]byte)(ip)), nil + return netip.AddrFrom4([4]byte(ip)), nil } } diff --git a/sing-box/transport/v2raywebsocket/writer.go b/sing-box/transport/v2raywebsocket/writer.go index c08a654f00..cd5a5d6a92 100644 --- a/sing-box/transport/v2raywebsocket/writer.go +++ b/sing-box/transport/v2raywebsocket/writer.go @@ -63,7 +63,7 @@ func (w *Writer) WriteBuffer(buffer *buf.Buffer) error { if !w.isServer { maskKey := rand.Uint32() binary.BigEndian.PutUint32(header[1+payloadBitLength:], maskKey) - ws.Cipher(data, *(*[4]byte)(header[1+payloadBitLength:]), 0) + ws.Cipher(data, [4]byte(header[1+payloadBitLength:]), 0) } return wrapWsError(w.writer.WriteBuffer(buffer)) diff --git a/small/sing-box/Makefile b/small/sing-box/Makefile index 75421b9a26..3076312c37 100644 --- a/small/sing-box/Makefile +++ b/small/sing-box/Makefile @@ -6,12 +6,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=sing-box -PKG_VERSION:=1.12.0 +PKG_VERSION:=1.12.1 PKG_RELEASE:=1 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://codeload.github.com/SagerNet/sing-box/tar.gz/v$(PKG_VERSION)? -PKG_HASH:=1093254161d2dac2175a589eb0b43415b89b3e0c10bb2a09ac230f320d974c82 +PKG_HASH:=8c7de6f996c9d3ad363d60b52828dc649a579ae8a5f0b596fc8ff7ea7622908d PKG_LICENSE:=GPL-3.0-or-later PKG_LICENSE_FILES:=LICENSE diff --git a/small/v2ray-geodata/Makefile b/small/v2ray-geodata/Makefile index 7b09b37505..f7538b5d1e 100644 --- a/small/v2ray-geodata/Makefile +++ b/small/v2ray-geodata/Makefile @@ -30,13 +30,13 @@ define Download/geosite HASH:=01dae2a9c31b5c74ba7e54d8d51e0060688ed22da493eaf09f6eeeec89db395e endef -GEOSITE_IRAN_VER:=202508040050 +GEOSITE_IRAN_VER:=202508110046 GEOSITE_IRAN_FILE:=iran.dat.$(GEOSITE_IRAN_VER) define Download/geosite-ir URL:=https://github.com/bootmortis/iran-hosted-domains/releases/download/$(GEOSITE_IRAN_VER)/ URL_FILE:=iran.dat FILE:=$(GEOSITE_IRAN_FILE) - HASH:=9dc1d277be21851fbb7e4e7376f8b9cfe2d47b6ade9cf4459de35596e20782b6 + HASH:=20ee5b1bf5a10aea00aeb5b7e435ccf13cd578ef9ce55236fc7c0fdfd3f5b1f6 endef define Package/v2ray-geodata/template diff --git a/v2rayn/v2rayN/ServiceLib/Enums/EViewAction.cs b/v2rayn/v2rayN/ServiceLib/Enums/EViewAction.cs index a72e876520..bf7360e203 100644 --- a/v2rayn/v2rayN/ServiceLib/Enums/EViewAction.cs +++ b/v2rayn/v2rayN/ServiceLib/Enums/EViewAction.cs @@ -29,6 +29,7 @@ public enum EViewAction DNSSettingWindow, RoutingSettingWindow, OptionSettingWindow, + FullConfigTemplateWindow, GlobalHotkeySettingWindow, SubSettingWindow, DispatcherSpeedTest, diff --git a/v2rayn/v2rayN/ServiceLib/Handler/AppHandler.cs b/v2rayn/v2rayN/ServiceLib/Handler/AppHandler.cs index ad9a4029e7..18ba19b829 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/AppHandler.cs +++ b/v2rayn/v2rayN/ServiceLib/Handler/AppHandler.cs @@ -64,6 +64,7 @@ public sealed class AppHandler SQLiteHelper.Instance.CreateTable(); SQLiteHelper.Instance.CreateTable(); SQLiteHelper.Instance.CreateTable(); + SQLiteHelper.Instance.CreateTable(); return true; } @@ -203,6 +204,16 @@ public sealed class AppHandler return await SQLiteHelper.Instance.TableAsync().FirstOrDefaultAsync(it => it.CoreType == eCoreType); } + public async Task?> FullConfigTemplateItem() + { + return await SQLiteHelper.Instance.TableAsync().ToListAsync(); + } + + public async Task GetFullConfigTemplateItem(ECoreType eCoreType) + { + return await SQLiteHelper.Instance.TableAsync().FirstOrDefaultAsync(it => it.CoreType == eCoreType); + } + #endregion SqliteHelper #region Core Type diff --git a/v2rayn/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayn/v2rayN/ServiceLib/Handler/ConfigHandler.cs index 3825fe74dd..c05424a2dc 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/ConfigHandler.cs +++ b/v2rayn/v2rayN/ServiceLib/Handler/ConfigHandler.cs @@ -2238,6 +2238,54 @@ public class ConfigHandler #endregion Simple DNS + #region Custom Config + + public static async Task InitBuiltinFullConfigTemplate(Config config) + { + var items = await AppHandler.Instance.FullConfigTemplateItem(); + if (items.Count <= 0) + { + var item = new FullConfigTemplateItem() + { + Remarks = "V2ray", + CoreType = ECoreType.Xray, + }; + await SaveFullConfigTemplate(config, item); + + var item2 = new FullConfigTemplateItem() + { + Remarks = "sing-box", + CoreType = ECoreType.sing_box, + }; + await SaveFullConfigTemplate(config, item2); + } + + return 0; + } + public static async Task SaveFullConfigTemplate(Config config, FullConfigTemplateItem item) + { + if (item == null) + { + return -1; + } + + if (item.Id.IsNullOrEmpty()) + { + item.Id = Utils.GetGuid(false); + } + + if (await SQLiteHelper.Instance.ReplaceAsync(item) > 0) + { + return 0; + } + else + { + return -1; + } + } + + #endregion Custom Config + #region Regional Presets /// diff --git a/v2rayn/v2rayN/ServiceLib/Models/FullConfigTemplateItem.cs b/v2rayn/v2rayN/ServiceLib/Models/FullConfigTemplateItem.cs new file mode 100644 index 0000000000..f388132581 --- /dev/null +++ b/v2rayn/v2rayN/ServiceLib/Models/FullConfigTemplateItem.cs @@ -0,0 +1,18 @@ +using SQLite; + +namespace ServiceLib.Models; + +[Serializable] +public class FullConfigTemplateItem +{ + [PrimaryKey] + public string Id { get; set; } + + public string Remarks { get; set; } + public bool Enabled { get; set; } = false; + public ECoreType CoreType { get; set; } + public string? Config { get; set; } + public string? TunConfig { get; set; } + public bool? AddProxyOnly { get; set; } = false; + public string? ProxyDetour { get; set; } +} diff --git a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.Designer.cs index 6a021a2b6d..2adb4f8de9 100644 --- a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.Designer.cs +++ b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.Designer.cs @@ -186,6 +186,15 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Please fill in the correct config template 的本地化字符串。 + /// + public static string FillCorrectConfigTemplateText { + get { + return ResourceManager.GetString("FillCorrectConfigTemplateText", resourceCulture); + } + } + /// /// 查找类似 Please fill in the correct custom DNS 的本地化字符串。 /// @@ -933,6 +942,15 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Full Config Template Setting 的本地化字符串。 + /// + public static string menuFullConfigTemplate { + get { + return ResourceManager.GetString("menuFullConfigTemplate", resourceCulture); + } + } + /// /// 查找类似 Global Hotkey Setting 的本地化字符串。 /// @@ -2229,6 +2247,15 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Do Not Add Non-Proxy Protocol Outbound 的本地化字符串。 + /// + public static string TbAddProxyProtocolOutboundOnly { + get { + return ResourceManager.GetString("TbAddProxyProtocolOutboundOnly", resourceCulture); + } + } + /// /// 查找类似 Address 的本地化字符串。 /// @@ -2310,6 +2337,15 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Prevent domain-based routing rules from failing 的本地化字符串。 + /// + public static string TbBlockSVCBHTTPSQueriesTips { + get { + return ResourceManager.GetString("TbBlockSVCBHTTPSQueriesTips", resourceCulture); + } + } + /// /// 查找类似 Browse 的本地化字符串。 /// @@ -2508,6 +2544,24 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 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. 的本地化字符串。 + /// + public static string TbFullConfigTemplateDesc { + get { + return ResourceManager.GetString("TbFullConfigTemplateDesc", resourceCulture); + } + } + + /// + /// 查找类似 Enable Full Config Template 的本地化字符串。 + /// + public static string TbFullConfigTemplateEnable { + get { + return ResourceManager.GetString("TbFullConfigTemplateEnable", resourceCulture); + } + } + /// /// 查找类似 Global Hotkey Settings 的本地化字符串。 /// @@ -2688,15 +2742,6 @@ namespace ServiceLib.Resx { } } - /// - /// 查找类似 Prevent DNS Leaks 的本地化字符串。 - /// - public static string TbPreventDNSLeaks { - get { - return ResourceManager.GetString("TbPreventDNSLeaks", resourceCulture); - } - } - /// /// 查找类似 Private Key 的本地化字符串。 /// @@ -2724,6 +2769,24 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 v2ray Full Config Template 的本地化字符串。 + /// + public static string TbRayFullConfigTemplate { + get { + return ResourceManager.GetString("TbRayFullConfigTemplate", resourceCulture); + } + } + + /// + /// 查找类似 Add Outbound Config Only, routing.balancers and routing.rules.outboundTag, Click to view the document 的本地化字符串。 + /// + public static string TbRayFullConfigTemplateDesc { + get { + return ResourceManager.GetString("TbRayFullConfigTemplateDesc", resourceCulture); + } + } + /// /// 查找类似 Alias (remarks) 的本地化字符串。 /// @@ -2886,6 +2949,24 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 sing-box Full Config Template 的本地化字符串。 + /// + public static string TbSBFullConfigTemplate { + get { + return ResourceManager.GetString("TbSBFullConfigTemplate", resourceCulture); + } + } + + /// + /// 查找类似 Add Outbound and Endpoint Config Only, Click to view the document 的本地化字符串。 + /// + public static string TbSBFullConfigTemplateDesc { + get { + return ResourceManager.GetString("TbSBFullConfigTemplateDesc", resourceCulture); + } + } + /// /// 查找类似 Resolve Outbound Domains 的本地化字符串。 /// @@ -3696,6 +3777,15 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Set Upstream Proxy Tag 的本地化字符串。 + /// + public static string TbSetUpstreamProxyDetour { + get { + return ResourceManager.GetString("TbSetUpstreamProxyDetour", resourceCulture); + } + } + /// /// 查找类似 Short Id 的本地化字符串。 /// diff --git a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx index 25a3c80e9a..d35c8d20e5 100644 --- a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx +++ b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx @@ -1443,9 +1443,6 @@ Block SVCB and HTTPS Queries - - Prevent DNS Leaks - DNS Hosts: ("domain1 ip1 ip2" per line) @@ -1470,4 +1467,37 @@ Custom DNS Enabled, This Page's Settings Invalid + + Prevent domain-based routing rules from failing + + + Please fill in the correct config template + + + Full Config Template Setting + + + Enable Full Config Template + + + v2ray Full Config Template + + + Add Outbound Config Only, routing.balancers and routing.rules.outboundTag, Click to view the document + + + Do Not Add Non-Proxy Protocol Outbound + + + Set Upstream Proxy Tag + + + sing-box Full Config Template + + + Add Outbound and Endpoint Config Only, Click to view the document + + + 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. + \ No newline at end of file diff --git a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.hu.resx b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.hu.resx index 20d0ab8eaf..4bc190066d 100644 --- a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.hu.resx +++ b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.hu.resx @@ -1443,9 +1443,6 @@ Block SVCB and HTTPS Queries - - Prevent DNS Leaks - DNS Hosts: ("domain1 ip1 ip2" per line) @@ -1470,4 +1467,37 @@ Custom DNS Enabled, This Page's Settings Invalid + + Prevent domain-based routing rules from failing + + + Please fill in the correct config template + + + Full Config Template Setting + + + Enable Full Config Template + + + v2ray Full Config Template + + + Add Outbound Config Only, routing.balancers and routing.rules.outboundTag, Click to view the document + + + Do Not Add Non-Proxy Protocol Outbound + + + Set Upstream Proxy Tag + + + sing-box Full Config Template + + + Add Outbound and Endpoint Config Only, Click to view the document + + + 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. + \ No newline at end of file diff --git a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.resx index a60094344d..4cf1b91a1d 100644 --- a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.resx +++ b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.resx @@ -1443,9 +1443,6 @@ Block SVCB and HTTPS Queries - - Prevent DNS Leaks - DNS Hosts: ("domain1 ip1 ip2" per line) @@ -1470,4 +1467,37 @@ Custom DNS Enabled, This Page's Settings Invalid + + Prevent domain-based routing rules from failing + + + Please fill in the correct config template + + + Full Config Template Setting + + + Enable Full Config Template + + + v2ray Full Config Template + + + Add Outbound Config Only, routing.balancers and routing.rules.outboundTag, Click to view the document + + + Do Not Add Non-Proxy Protocol Outbound + + + Set Upstream Proxy Tag + + + sing-box Full Config Template + + + Add Outbound and Endpoint Config Only, Click to view the document + + + 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. + \ No newline at end of file diff --git a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.ru.resx b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.ru.resx index 6207b16312..fd53b82639 100644 --- a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.ru.resx +++ b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.ru.resx @@ -1443,9 +1443,6 @@ Block SVCB and HTTPS Queries - - Prevent DNS Leaks - DNS Hosts: ("domain1 ip1 ip2" per line) @@ -1470,4 +1467,37 @@ Custom DNS Enabled, This Page's Settings Invalid + + Prevent domain-based routing rules from failing + + + Please fill in the correct config template + + + Full Config Template Setting + + + Enable Full Config Template + + + v2ray Full Config Template + + + Add Outbound Config Only, routing.balancers and routing.rules.outboundTag, Click to view the document + + + Do Not Add Non-Proxy Protocol Outbound + + + Set Upstream Proxy Tag + + + sing-box Full Config Template + + + Add Outbound and Endpoint Config Only, Click to view the document + + + 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. + \ No newline at end of file diff --git a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx index 72c4260415..a2506ed8b9 100644 --- a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx +++ b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx @@ -1440,9 +1440,6 @@ 阻止 SVCB 和 HTTPS 查询 - - 避免 DNS 泄漏 - DNS Hosts:(“域名1 ip1 ip2” 一行一个) @@ -1467,4 +1464,37 @@ 自定义 DNS 已启用,此页面配置将无效 + + 避免域名分流规则失效 + + + 请填写正确的配置模板 + + + 完整配置模板设置 + + + 启用完整配置模板 + + + v2ray 完整配置模板 + + + 仅添加出站配置,routing.balancers 和 routing.rules.outboundTag,点击查看文档 + + + 不添加非代理协议出站 + + + 设置上游代理 tag + + + sing-box 完整配置模板 + + + 仅添加出站和端点配置,点击查看文档 + + + 此功能供高级用户和有特殊需求的用户使用。 启用此功能后,将忽略 Core 的基础设置,DNS 设置 ,路由设置。你需要保证系统代理的端口和流量统计等功能的配置正确,一切都由你来设置。 + \ No newline at end of file diff --git a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx index 84c8f92913..3d16e5b5fc 100644 --- a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx +++ b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx @@ -1440,9 +1440,6 @@ Block SVCB and HTTPS Queries - - Prevent DNS Leaks - DNS Hosts: ("domain1 ip1 ip2" per line) @@ -1467,4 +1464,37 @@ Custom DNS Enabled, This Page's Settings Invalid + + Prevent domain-based routing rules from failing + + + Please fill in the correct config template + + + Full Config Template Setting + + + Enable Full Config Template + + + v2ray Full Config Template + + + Add Outbound Config Only, routing.balancers and routing.rules.outboundTag, Click to view the document + + + Do Not Add Non-Proxy Protocol Outbound + + + Set Upstream Proxy Tag + + + sing-box Full Config Template + + + Add Outbound and Endpoint Config Only, Click to view the document + + + 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. + \ No newline at end of file diff --git a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs index 6ea20a2091..19f0af35f5 100644 --- a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs +++ b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs @@ -1,6 +1,7 @@ using System.Data; using System.Net; using System.Net.NetworkInformation; +using System.Text.Json.Nodes; namespace ServiceLib.Services.CoreConfig; @@ -78,7 +79,9 @@ public class CoreConfigSingboxService ret.Msg = string.Format(ResUI.SuccessfulConfiguration, ""); ret.Success = true; - ret.Data = JsonUtils.Serialize(singboxConfig); + + var fullConfigTemplate = await AppHandler.Instance.GetFullConfigTemplateItem(ECoreType.sing_box); + ret.Data = await ApplyFullConfigTemplate(fullConfigTemplate, singboxConfig); return ret; } catch (Exception ex) @@ -430,7 +433,9 @@ public class CoreConfigSingboxService await ConvertGeo2Ruleset(singboxConfig); ret.Success = true; - ret.Data = JsonUtils.Serialize(singboxConfig); + + var fullConfigTemplate = await AppHandler.Instance.GetFullConfigTemplateItem(ECoreType.sing_box); + ret.Data = await ApplyFullConfigTemplate(fullConfigTemplate, singboxConfig); return ret; } catch (Exception ex) @@ -1685,6 +1690,22 @@ public class CoreConfigSingboxService } } + if (simpleDNSItem.UseSystemHosts == true) + { + var systemHosts = Utils.GetSystemHosts(); + if (systemHosts.Count > 0) + { + foreach (var host in systemHosts) + { + if (userHostsMap[host.Key] != null) + { + continue; + } + userHostsMap[host.Key] = new List { host.Value }; + } + } + } + foreach (var host in hostsDns.predefined) { if (finalDns.server == host.Key) @@ -2197,5 +2218,61 @@ public class CoreConfigSingboxService return 0; } + private async Task ApplyFullConfigTemplate(FullConfigTemplateItem fullConfigTemplate, SingboxConfig singboxConfig) + { + var fullConfigTemplateItem = fullConfigTemplate.Config; + if (_config.TunModeItem.EnableTun) + { + fullConfigTemplateItem = fullConfigTemplate.TunConfig; + } + + if (!fullConfigTemplate.Enabled || fullConfigTemplateItem.IsNullOrEmpty()) + { + return JsonUtils.Serialize(singboxConfig); + } + + var fullConfigTemplateNode = JsonNode.Parse(fullConfigTemplateItem); + if (fullConfigTemplateNode == null) + { + return JsonUtils.Serialize(singboxConfig); + } + + // Process outbounds + var customOutboundsNode = fullConfigTemplateNode["outbounds"] is JsonArray outbounds ? outbounds : new JsonArray(); + foreach (var outbound in singboxConfig.outbounds) + { + if (outbound.type.ToLower() is "direct" or "block") + { + if (fullConfigTemplate.AddProxyOnly == true) + { + continue; + } + } + else if (outbound.detour.IsNullOrEmpty() && (!fullConfigTemplate.ProxyDetour.IsNullOrEmpty()) && !Utils.IsPrivateNetwork(outbound.server ?? string.Empty)) + { + outbound.detour = fullConfigTemplate.ProxyDetour; + } + customOutboundsNode.Add(JsonUtils.DeepCopy(outbound)); + } + fullConfigTemplateNode["outbounds"] = customOutboundsNode; + + // Process endpoints + if (singboxConfig.endpoints != null && singboxConfig.endpoints.Count > 0) + { + var customEndpointsNode = fullConfigTemplateNode["endpoints"] is JsonArray endpoints ? endpoints : new JsonArray(); + foreach (var endpoint in singboxConfig.endpoints) + { + if (endpoint.detour.IsNullOrEmpty() && (!fullConfigTemplate.ProxyDetour.IsNullOrEmpty())) + { + endpoint.detour = fullConfigTemplate.ProxyDetour; + } + customEndpointsNode.Add(JsonUtils.DeepCopy(endpoint)); + } + fullConfigTemplateNode["endpoints"] = customEndpointsNode; + } + + return await Task.FromResult(JsonUtils.Serialize(fullConfigTemplateNode)); + } + #endregion private gen function } diff --git a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs index 1986be14e0..96388ed3e1 100644 --- a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs +++ b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs @@ -3,6 +3,7 @@ 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; @@ -68,7 +69,9 @@ public class CoreConfigV2rayService ret.Msg = string.Format(ResUI.SuccessfulConfiguration, ""); ret.Success = true; - ret.Data = JsonUtils.Serialize(v2rayConfig); + + var fullConfigTemplate = await AppHandler.Instance.GetFullConfigTemplateItem(ECoreType.Xray); + ret.Data = await ApplyFullConfigTemplate(fullConfigTemplate, v2rayConfig); return ret; } catch (Exception ex) @@ -197,7 +200,9 @@ public class CoreConfigV2rayService } ret.Success = true; - ret.Data = JsonUtils.Serialize(v2rayConfig); + + var fullConfigTemplate = await AppHandler.Instance.GetFullConfigTemplateItem(ECoreType.Xray); + ret.Data = await ApplyFullConfigTemplate(fullConfigTemplate, v2rayConfig, true); return ret; } catch (Exception ex) @@ -1462,7 +1467,7 @@ public class CoreConfigV2rayService await GenDnsDomainsCompatible(node, obj, item); - v2rayConfig.dns = JsonUtils.Deserialize(obj.ToJsonString()); + v2rayConfig.dns = JsonUtils.Deserialize(JsonUtils.Serialize(obj)); } catch (Exception ex) { @@ -1839,5 +1844,83 @@ public class CoreConfigV2rayService return await Task.FromResult(0); } + private async Task ApplyFullConfigTemplate(FullConfigTemplateItem fullConfigTemplate, V2rayConfig v2rayConfig, bool handleBalancerAndRules = false) + { + if (!fullConfigTemplate.Enabled || fullConfigTemplate.Config.IsNullOrEmpty()) + { + return JsonUtils.Serialize(v2rayConfig); + } + + var fullConfigTemplateNode = JsonNode.Parse(fullConfigTemplate.Config); + if (fullConfigTemplateNode == null) + { + return JsonUtils.Serialize(v2rayConfig); + } + + // Handle balancer and rules modifications (for multiple load scenarios) + if (handleBalancerAndRules && v2rayConfig.routing?.balancers?.Count > 0) + { + var balancer = v2rayConfig.routing.balancers.First(); + + // Modify existing rules in custom config + var rulesNode = fullConfigTemplateNode["routing"]?["rules"]; + if (rulesNode != null) + { + foreach (var rule in rulesNode.AsArray()) + { + if (rule["outboundTag"]?.GetValue() == Global.ProxyTag) + { + rule.AsObject().Remove("outboundTag"); + rule["balancerTag"] = balancer.tag; + } + } + } + + // Ensure routing node exists + if (fullConfigTemplateNode["routing"] == null) + { + fullConfigTemplateNode["routing"] = new JsonObject(); + } + + // Handle balancers - append instead of override + if (fullConfigTemplateNode["routing"]["balancers"] is JsonArray customBalancersNode) + { + if (JsonNode.Parse(JsonUtils.Serialize(v2rayConfig.routing.balancers)) is JsonArray newBalancers) + { + foreach (var balancerNode in newBalancers) + { + customBalancersNode.Add(balancerNode?.DeepClone()); + } + } + } + else + { + fullConfigTemplateNode["routing"]["balancers"] = JsonNode.Parse(JsonUtils.Serialize(v2rayConfig.routing.balancers)); + } + } + + // Handle outbounds - append instead of override + var customOutboundsNode = fullConfigTemplateNode["outbounds"] is JsonArray outbounds ? outbounds : new JsonArray(); + foreach (var outbound in v2rayConfig.outbounds) + { + if (outbound.protocol.ToLower() is "blackhole" or "dns" or "freedom") + { + if (fullConfigTemplate.AddProxyOnly == true) + { + continue; + } + } + else if ((outbound.streamSettings?.sockopt?.dialerProxy.IsNullOrEmpty() == true) && (!fullConfigTemplate.ProxyDetour.IsNullOrEmpty()) && !(Utils.IsPrivateNetwork(outbound.settings?.servers?.FirstOrDefault()?.address ?? string.Empty) || Utils.IsPrivateNetwork(outbound.settings?.vnext?.FirstOrDefault()?.address ?? string.Empty))) + { + outbound.streamSettings ??= new StreamSettings4Ray(); + outbound.streamSettings.sockopt ??= new Sockopt4Ray(); + outbound.streamSettings.sockopt.dialerProxy = fullConfigTemplate.ProxyDetour; + } + customOutboundsNode.Add(JsonUtils.DeepCopy(outbound)); + } + + return await Task.FromResult(JsonUtils.Serialize(fullConfigTemplateNode)); + } + #endregion private gen function } diff --git a/v2rayn/v2rayN/ServiceLib/ViewModels/FullConfigTemplateViewModel.cs b/v2rayn/v2rayN/ServiceLib/ViewModels/FullConfigTemplateViewModel.cs new file mode 100644 index 0000000000..3aa618bf25 --- /dev/null +++ b/v2rayn/v2rayN/ServiceLib/ViewModels/FullConfigTemplateViewModel.cs @@ -0,0 +1,110 @@ +using System.Reactive; +using DynamicData.Binding; +using ReactiveUI; +using ReactiveUI.Fody.Helpers; + +namespace ServiceLib.ViewModels; +public class FullConfigTemplateViewModel : MyReactiveObject +{ + #region Reactive + [Reactive] + public bool EnableFullConfigTemplate4Ray { get; set; } + + [Reactive] + public bool EnableFullConfigTemplate4Singbox { get; set; } + + [Reactive] + public string FullConfigTemplate4Ray { get; set; } + + [Reactive] + public string FullConfigTemplate4Singbox { get; set; } + + [Reactive] + public string FullTunConfigTemplate4Singbox { get; set; } + + [Reactive] + public bool AddProxyOnly4Ray { get; set; } + + [Reactive] + public bool AddProxyOnly4Singbox { get; set; } + + [Reactive] + public string ProxyDetour4Ray { get; set; } + + [Reactive] + public string ProxyDetour4Singbox { get; set; } + + public ReactiveCommand SaveCmd { get; } + #endregion Reactive + + public FullConfigTemplateViewModel(Func>? updateView) + { + _config = AppHandler.Instance.Config; + _updateView = updateView; + SaveCmd = ReactiveCommand.CreateFromTask(async () => + { + await SaveSettingAsync(); + }); + + _ = Init(); + } + private async Task Init() + { + var item = await AppHandler.Instance.GetFullConfigTemplateItem(ECoreType.Xray); + EnableFullConfigTemplate4Ray = item?.Enabled ?? false; + FullConfigTemplate4Ray = item?.Config ?? string.Empty; + AddProxyOnly4Ray = item?.AddProxyOnly ?? false; + ProxyDetour4Ray = item?.ProxyDetour ?? string.Empty; + + var item2 = await AppHandler.Instance.GetFullConfigTemplateItem(ECoreType.sing_box); + EnableFullConfigTemplate4Singbox = item2?.Enabled ?? false; + FullConfigTemplate4Singbox = item2?.Config ?? string.Empty; + FullTunConfigTemplate4Singbox = item2?.TunConfig ?? string.Empty; + AddProxyOnly4Singbox = item2?.AddProxyOnly ?? false; + ProxyDetour4Singbox = item2?.ProxyDetour ?? string.Empty; + } + + private async Task SaveSettingAsync() + { + if (!await SaveXrayConfigAsync()) + return; + + if (!await SaveSingboxConfigAsync()) + return; + + NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess); + _ = _updateView?.Invoke(EViewAction.CloseWindow, null); + } + + private async Task SaveXrayConfigAsync() + { + var item = await AppHandler.Instance.GetFullConfigTemplateItem(ECoreType.Xray); + item.Enabled = EnableFullConfigTemplate4Ray; + item.Config = null; + + item.Config = FullConfigTemplate4Ray; + + item.AddProxyOnly = AddProxyOnly4Ray; + item.ProxyDetour = ProxyDetour4Ray; + + await ConfigHandler.SaveFullConfigTemplate(_config, item); + return true; + } + + private async Task SaveSingboxConfigAsync() + { + var item = await AppHandler.Instance.GetFullConfigTemplateItem(ECoreType.sing_box); + item.Enabled = EnableFullConfigTemplate4Singbox; + item.Config = null; + item.TunConfig = null; + + item.Config = FullConfigTemplate4Singbox; + item.TunConfig = FullTunConfigTemplate4Singbox; + + item.AddProxyOnly = AddProxyOnly4Singbox; + item.ProxyDetour = ProxyDetour4Singbox; + + await ConfigHandler.SaveFullConfigTemplate(_config, item); + return true; + } +} diff --git a/v2rayn/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs b/v2rayn/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs index 36e20a878b..8355996dca 100644 --- a/v2rayn/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs +++ b/v2rayn/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs @@ -39,6 +39,7 @@ public class MainWindowViewModel : MyReactiveObject public ReactiveCommand RoutingSettingCmd { get; } public ReactiveCommand DNSSettingCmd { get; } + public ReactiveCommand FullConfigTemplateCmd { get; } public ReactiveCommand GlobalHotkeySettingCmd { get; } public ReactiveCommand RebootAsAdminCmd { get; } public ReactiveCommand ClearServerStatisticsCmd { get; } @@ -169,6 +170,10 @@ public class MainWindowViewModel : MyReactiveObject { await DNSSettingAsync(); }); + FullConfigTemplateCmd = ReactiveCommand.CreateFromTask(async () => + { + await FullConfigTemplateAsync(); + }); GlobalHotkeySettingCmd = ReactiveCommand.CreateFromTask(async () => { if (await _updateView?.Invoke(EViewAction.GlobalHotkeySettingWindow, null) == true) @@ -220,6 +225,7 @@ public class MainWindowViewModel : MyReactiveObject await ConfigHandler.InitBuiltinRouting(_config); await ConfigHandler.InitBuiltinDNS(_config); + await ConfigHandler.InitBuiltinFullConfigTemplate(_config); await ProfileExHandler.Instance.Init(); await CoreHandler.Instance.Init(_config, UpdateHandler); TaskHandler.Instance.RegUpdateTask(_config, UpdateTaskHandler); @@ -508,6 +514,15 @@ public class MainWindowViewModel : MyReactiveObject } } + private async Task FullConfigTemplateAsync() + { + var ret = await _updateView?.Invoke(EViewAction.FullConfigTemplateWindow, null); + if (ret == true) + { + await Reload(); + } + } + public async Task RebootAsAdmin() { ProcUtils.RebootAsAdmin(); diff --git a/v2rayn/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml b/v2rayn/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml index 9ec377fff3..afb3dd9775 100644 --- a/v2rayn/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml +++ b/v2rayn/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml @@ -97,7 +97,8 @@ Grid.Column="2" Margin="{StaticResource Margin4}" VerticalAlignment="Center" - Text="{x:Static resx:ResUI.TbSBOutboundDomainResolve}" /> + Text="{x:Static resx:ResUI.TbSBOutboundDomainResolve}" + TextWrapping="Wrap" /> + Text="{x:Static resx:ResUI.TbSBFallbackDNSResolve}" + TextWrapping="Wrap" /> + Text="{x:Static resx:ResUI.TbSBDoHOverride}" + TextWrapping="Wrap" /> @@ -228,7 +231,8 @@ Grid.Column="2" Margin="{StaticResource Margin4}" VerticalAlignment="Center" - Text="{x:Static resx:ResUI.TbApplyProxyDomainsOnly}" /> + Text="{x:Static resx:ResUI.TbApplyProxyDomainsOnly}" + TextWrapping="Wrap" /> + Text="{x:Static resx:ResUI.TbBlockSVCBHTTPSQueriesTips}" + TextWrapping="Wrap" /> + Text="{x:Static resx:ResUI.TbValidateDirectExpectedIPsDesc}" + TextWrapping="Wrap" /> + + +