mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-09-23 18:26:37 +08:00
fix(link): restore mKCP seed and headerType on share-link import (#6480)
* fix(link): restore mKCP seed and headerType on share-link import applyTransport / applyTransportParams ignored kcp query params that applyKcpShareParams emits, so re-imported outbounds lost seed and header and could not talk to the inbound. Mirror those fields (plus mtu/tti) into kcpSettings in both Go and TS importers. Fixes #6476 * fix(link): restore mKCP header/seed via finalmask mkcp-legacy * fix(link): split mKCP header and seed into separate masks on import Both importers folded a share link's headerType and seed into one mkcp-legacy mask {header, value}. xray-core's MkcpLegacy.Build ignores value once header is set (and reads it as the fake DNS domain for header=dns), so an imported outbound carried the header mask but no AES-128-GCM seed while the emitting inbound has both, and could not connect — the failure #6476 reports, now for every link carrying both params. Emit one mask per field, seed first: the finalmask array's first item is the innermost layer, which puts the header around the cipher as legacy mKCP did. Also bound mtu/tti to KCPConfig.Build's accepted ranges (mtu >= 21, tti 10..1000, decimal digits only on both importers) so a pasted link cannot fail the whole Xray config load, and look header types up as own properties so a prototype key such as "constructor" is not mapped. --------- Co-authored-by: mrchatam <287639636+mrchatam@users.noreply.github.com> Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/base64"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"slices"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -244,3 +245,76 @@ func TestParseTrojanAndSS_CoreFields(t *testing.T) {
|
||||
t.Errorf("ss server = %#v", ssrv)
|
||||
}
|
||||
}
|
||||
|
||||
type mkcpMask struct{ header, value string }
|
||||
|
||||
func mkcpLegacyMasks(t *testing.T, res *ParseResult) []mkcpMask {
|
||||
t.Helper()
|
||||
var out []mkcpMask
|
||||
for _, raw := range finalmaskUDP(t, res) {
|
||||
mask, _ := raw.(map[string]any)
|
||||
if mask["type"] != "mkcp-legacy" {
|
||||
t.Fatalf("unexpected udp mask %#v", mask)
|
||||
}
|
||||
settings, _ := mask["settings"].(map[string]any)
|
||||
header, _ := settings["header"].(string)
|
||||
value, _ := settings["value"].(string)
|
||||
out = append(out, mkcpMask{header, value})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func TestParse_KcpShareParams(t *testing.T) {
|
||||
// The emitter flattens one mkcp-legacy mask per field into headerType/seed; a merged
|
||||
// mask drops the seed in xray-core (MkcpLegacy.Build), so import rebuilds them separately.
|
||||
cases := []struct {
|
||||
name string
|
||||
link string
|
||||
wantMTU int
|
||||
wantTTI int
|
||||
wantMasks []mkcpMask
|
||||
}{
|
||||
{
|
||||
name: "vless header and seed become two masks, seed first",
|
||||
link: "vless://uuid@h.com:443?type=kcp&headerType=wechat-video&seed=secret-seed&mtu=1400&tti=50&security=none#kcp1",
|
||||
wantMTU: 1400,
|
||||
wantTTI: 50,
|
||||
wantMasks: []mkcpMask{{"", "secret-seed"}, {"wechat", ""}},
|
||||
},
|
||||
{
|
||||
name: "trojan header only adds no seed mask",
|
||||
link: "trojan://pw@h.com:443?type=kcp&headerType=srtp&security=none#kcp-tj",
|
||||
wantMTU: 1350,
|
||||
wantTTI: 20,
|
||||
wantMasks: []mkcpMask{{"srtp", ""}},
|
||||
},
|
||||
{
|
||||
name: "seed only adds no header mask",
|
||||
link: "vless://uuid@h.com:443?type=kcp&headerType=none&seed=abc123&security=none",
|
||||
wantMTU: 1350,
|
||||
wantTTI: 20,
|
||||
wantMasks: []mkcpMask{{"", "abc123"}},
|
||||
},
|
||||
{
|
||||
name: "mtu/tti outside KCPConfig.Build bounds keep the defaults",
|
||||
link: "vless://uuid@h.com:443?type=kcp&mtu=10&tti=5000&security=none",
|
||||
wantMTU: 1350,
|
||||
wantTTI: 20,
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
res, err := ParseLink(c.link)
|
||||
if err != nil {
|
||||
t.Fatalf("parse: %v", err)
|
||||
}
|
||||
kcp := streamSub(t, res, "kcpSettings")
|
||||
if kcp["mtu"] != c.wantMTU || kcp["tti"] != c.wantTTI {
|
||||
t.Fatalf("kcpSettings mtu/tti = %v/%v, want %d/%d", kcp["mtu"], kcp["tti"], c.wantMTU, c.wantTTI)
|
||||
}
|
||||
if got := mkcpLegacyMasks(t, res); !slices.Equal(got, c.wantMasks) {
|
||||
t.Fatalf("mkcp-legacy masks = %v, want %v", got, c.wantMasks)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user