finalmask

Co-authored-by: MHSanaei <ho3ein.sanaei@gmail.com>
This commit is contained in:
Alireza Ahmadi
2026-02-01 10:32:35 +01:00
parent 6bab6ce6c4
commit 8f5bead445
6 changed files with 276 additions and 110 deletions

View File

@@ -318,15 +318,13 @@ TcpStreamSettings.TcpResponse = class extends XrayCommonClass {
class KcpStreamSettings extends XrayCommonClass { class KcpStreamSettings extends XrayCommonClass {
constructor( constructor(
mtu = 1250, mtu = 1350,
tti = 50, tti = 20,
uplinkCapacity = 5, uplinkCapacity = 5,
downlinkCapacity = 20, downlinkCapacity = 20,
congestion = false, congestion = false,
readBufferSize = 2, readBufferSize = 1,
writeBufferSize = 2, writeBufferSize = 1,
type = 'none',
seed = RandomUtil.randomSeq(10),
) { ) {
super(); super();
this.mtu = mtu; this.mtu = mtu;
@@ -336,8 +334,6 @@ class KcpStreamSettings extends XrayCommonClass {
this.congestion = congestion; this.congestion = congestion;
this.readBuffer = readBufferSize; this.readBuffer = readBufferSize;
this.writeBuffer = writeBufferSize; this.writeBuffer = writeBufferSize;
this.type = type;
this.seed = seed;
} }
static fromJson(json = {}) { static fromJson(json = {}) {
@@ -349,8 +345,6 @@ class KcpStreamSettings extends XrayCommonClass {
json.congestion, json.congestion,
json.readBufferSize, json.readBufferSize,
json.writeBufferSize, json.writeBufferSize,
ObjectUtil.isEmpty(json.header) ? 'none' : json.header.type,
json.seed,
); );
} }
@@ -363,10 +357,6 @@ class KcpStreamSettings extends XrayCommonClass {
congestion: this.congestion, congestion: this.congestion,
readBufferSize: this.readBuffer, readBufferSize: this.readBuffer,
writeBufferSize: this.writeBuffer, writeBufferSize: this.writeBuffer,
header: {
type: this.type,
},
seed: this.seed,
}; };
} }
} }
@@ -923,6 +913,51 @@ class SockoptStreamSettings extends XrayCommonClass {
} }
} }
class FinalMask extends XrayCommonClass {
constructor(type = 'salamander', settings = {}) {
super();
this.type = type;
this.settings = this._getDefaultSettings(type, settings);
}
_getDefaultSettings(type, settings = {}) {
switch (type) {
case 'salamander':
case 'mkcp-aes128gcm':
return { password: settings.password || '' };
case 'header-dns':
case 'xdns':
return { domain: settings.domain || '' };
case 'mkcp-original':
case 'header-dtls':
case 'header-srtp':
case 'header-utp':
case 'header-wechat':
case 'header-wireguard':
return {};
default:
return settings;
}
}
static fromJson(json = {}) {
return new FinalMask(
json.type || 'salamander',
json.settings || {}
);
}
toJson() {
const result = {
type: this.type
};
if (this.settings && Object.keys(this.settings).length > 0) {
result.settings = this.settings;
}
return result;
}
}
class StreamSettings extends XrayCommonClass { class StreamSettings extends XrayCommonClass {
constructor(network = 'tcp', constructor(network = 'tcp',
security = 'none', security = 'none',
@@ -935,6 +970,7 @@ class StreamSettings extends XrayCommonClass {
grpcSettings = new GrpcStreamSettings(), grpcSettings = new GrpcStreamSettings(),
httpupgradeSettings = new HttpUpgradeStreamSettings(), httpupgradeSettings = new HttpUpgradeStreamSettings(),
xhttpSettings = new xHTTPStreamSettings(), xhttpSettings = new xHTTPStreamSettings(),
finalmask = { udp: [] },
sockopt = undefined, sockopt = undefined,
) { ) {
super(); super();
@@ -949,9 +985,23 @@ class StreamSettings extends XrayCommonClass {
this.grpc = grpcSettings; this.grpc = grpcSettings;
this.httpupgrade = httpupgradeSettings; this.httpupgrade = httpupgradeSettings;
this.xhttp = xhttpSettings; this.xhttp = xhttpSettings;
this.finalmask = finalmask;
this.sockopt = sockopt; this.sockopt = sockopt;
} }
addUdpMask(type = 'salamander') {
if (!this.finalmask.udp) {
this.finalmask.udp = [];
}
this.finalmask.udp.push(new FinalMask(type));
}
delUdpMask(index) {
if (this.finalmask.udp) {
this.finalmask.udp.splice(index, 1);
}
}
get isTls() { get isTls() {
return this.security === "tls"; return this.security === "tls";
} }
@@ -986,6 +1036,14 @@ class StreamSettings extends XrayCommonClass {
} }
static fromJson(json = {}) { static fromJson(json = {}) {
let finalmask = { udp: [] };
if (json.finalmask) {
if (Array.isArray(json.finalmask)) {
finalmask.udp = json.finalmask.map(mask => FinalMask.fromJson(mask));
} else if (json.finalmask.udp) {
finalmask.udp = json.finalmask.udp.map(mask => FinalMask.fromJson(mask));
}
}
return new StreamSettings( return new StreamSettings(
json.network, json.network,
json.security, json.security,
@@ -998,6 +1056,7 @@ class StreamSettings extends XrayCommonClass {
GrpcStreamSettings.fromJson(json.grpcSettings), GrpcStreamSettings.fromJson(json.grpcSettings),
HttpUpgradeStreamSettings.fromJson(json.httpupgradeSettings), HttpUpgradeStreamSettings.fromJson(json.httpupgradeSettings),
xHTTPStreamSettings.fromJson(json.xhttpSettings), xHTTPStreamSettings.fromJson(json.xhttpSettings),
finalmask,
SockoptStreamSettings.fromJson(json.sockopt), SockoptStreamSettings.fromJson(json.sockopt),
); );
} }
@@ -1016,6 +1075,9 @@ class StreamSettings extends XrayCommonClass {
grpcSettings: network === 'grpc' ? this.grpc.toJson() : undefined, grpcSettings: network === 'grpc' ? this.grpc.toJson() : undefined,
httpupgradeSettings: network === 'httpupgrade' ? this.httpupgrade.toJson() : undefined, httpupgradeSettings: network === 'httpupgrade' ? this.httpupgrade.toJson() : undefined,
xhttpSettings: network === 'xhttp' ? this.xhttp.toJson() : undefined, xhttpSettings: network === 'xhttp' ? this.xhttp.toJson() : undefined,
finalmask: (this.finalmask.udp && this.finalmask.udp.length > 0) ? {
udp: this.finalmask.udp.map(mask => mask.toJson())
} : undefined,
sockopt: this.sockopt != undefined ? this.sockopt.toJson() : undefined, sockopt: this.sockopt != undefined ? this.sockopt.toJson() : undefined,
}; };
} }
@@ -1927,7 +1989,8 @@ Inbound.VLESSSettings = class extends Inbound.Settings {
json.selectedAuth = this.selectedAuth; json.selectedAuth = this.selectedAuth;
} }
if (this.testseed && this.testseed.length >= 4) { const hasFlow = this.vlesses && this.vlesses.some(vless => vless.flow && vless.flow !== '');
if (hasFlow && this.testseed && this.testseed.length >= 4) {
json.testseed = this.testseed; json.testseed = this.testseed;
} }
@@ -2443,7 +2506,7 @@ Inbound.HttpSettings.HttpAccount = class extends XrayCommonClass {
Inbound.WireguardSettings = class extends XrayCommonClass { Inbound.WireguardSettings = class extends XrayCommonClass {
constructor( constructor(
protocol, protocol,
mtu = 1250, mtu = 1420,
secretKey = Wireguard.generateKeypair().privateKey, secretKey = Wireguard.generateKeypair().privateKey,
peers = [new Inbound.WireguardSettings.Peer()], peers = [new Inbound.WireguardSettings.Peer()],
kernelMode = false kernelMode = false

View File

@@ -163,15 +163,13 @@ class TcpStreamSettings extends CommonClass {
class KcpStreamSettings extends CommonClass { class KcpStreamSettings extends CommonClass {
constructor( constructor(
mtu = 1250, mtu = 1350,
tti = 50, tti = 20,
uplinkCapacity = 5, uplinkCapacity = 5,
downlinkCapacity = 20, downlinkCapacity = 20,
congestion = false, congestion = false,
readBufferSize = 2, readBufferSize = 1,
writeBufferSize = 2, writeBufferSize = 1,
type = 'none',
seed = '',
) { ) {
super(); super();
this.mtu = mtu; this.mtu = mtu;
@@ -181,8 +179,6 @@ class KcpStreamSettings extends CommonClass {
this.congestion = congestion; this.congestion = congestion;
this.readBuffer = readBufferSize; this.readBuffer = readBufferSize;
this.writeBuffer = writeBufferSize; this.writeBuffer = writeBufferSize;
this.type = type;
this.seed = seed;
} }
static fromJson(json = {}) { static fromJson(json = {}) {
@@ -194,8 +190,6 @@ class KcpStreamSettings extends CommonClass {
json.congestion, json.congestion,
json.readBufferSize, json.readBufferSize,
json.writeBufferSize, json.writeBufferSize,
ObjectUtil.isEmpty(json.header) ? 'none' : json.header.type,
json.seed,
); );
} }
@@ -208,10 +202,6 @@ class KcpStreamSettings extends CommonClass {
congestion: this.congestion, congestion: this.congestion,
readBufferSize: this.readBuffer, readBufferSize: this.readBuffer,
writeBufferSize: this.writeBuffer, writeBufferSize: this.writeBuffer,
header: {
type: this.type,
},
seed: this.seed,
}; };
} }
} }
@@ -561,27 +551,48 @@ class SockoptStreamSettings extends CommonClass {
} }
} }
class UdpMask extends CommonClass { class FinalMask extends CommonClass {
constructor(type = 'salamander', password = '') { constructor(type = 'salamander', settings = {}) {
super(); super();
this.type = type; this.type = type;
this.password = password; this.settings = this._getDefaultSettings(type, settings);
}
_getDefaultSettings(type, settings = {}) {
switch (type) {
case 'salamander':
case 'mkcp-aes128gcm':
return { password: settings.password || '' };
case 'header-dns':
case 'xdns':
return { domain: settings.domain || '' };
case 'mkcp-original':
case 'header-dtls':
case 'header-srtp':
case 'header-utp':
case 'header-wechat':
case 'header-wireguard':
return {}; // No settings needed
default:
return settings;
}
} }
static fromJson(json = {}) { static fromJson(json = {}) {
return new UdpMask( return new FinalMask(
json.type, json.type || 'salamander',
json.settings?.password || '' json.settings || {}
); );
} }
toJson() { toJson() {
return { const result = {
type: this.type, type: this.type
settings: {
password: this.password
}
}; };
if (this.settings && Object.keys(this.settings).length > 0) {
result.settings = this.settings;
}
return result;
} }
} }
@@ -598,7 +609,7 @@ class StreamSettings extends CommonClass {
httpupgradeSettings = new HttpUpgradeStreamSettings(), httpupgradeSettings = new HttpUpgradeStreamSettings(),
xhttpSettings = new xHTTPStreamSettings(), xhttpSettings = new xHTTPStreamSettings(),
hysteriaSettings = new HysteriaStreamSettings(), hysteriaSettings = new HysteriaStreamSettings(),
udpmasks = [], finalmask = { udp: [] },
sockopt = undefined, sockopt = undefined,
) { ) {
super(); super();
@@ -613,16 +624,21 @@ class StreamSettings extends CommonClass {
this.httpupgrade = httpupgradeSettings; this.httpupgrade = httpupgradeSettings;
this.xhttp = xhttpSettings; this.xhttp = xhttpSettings;
this.hysteria = hysteriaSettings; this.hysteria = hysteriaSettings;
this.udpmasks = udpmasks; this.finalmask = finalmask;
this.sockopt = sockopt; this.sockopt = sockopt;
} }
addUdpMask() { addUdpMask(type = 'salamander') {
this.udpmasks.push(new UdpMask()); if (!this.finalmask.udp) {
this.finalmask.udp = [];
}
this.finalmask.udp.push(new FinalMask(type));
} }
delUdpMask(index) { delUdpMask(index) {
this.udpmasks.splice(index, 1); if (this.finalmask.udp) {
this.finalmask.udp.splice(index, 1);
}
} }
get isTls() { get isTls() {
@@ -642,6 +658,14 @@ class StreamSettings extends CommonClass {
} }
static fromJson(json = {}) { static fromJson(json = {}) {
let finalmask = { udp: [] };
if (json.finalmask) {
if (Array.isArray(json.finalmask)) {
finalmask.udp = json.finalmask.map(mask => FinalMask.fromJson(mask));
} else if (json.finalmask.udp) {
finalmask.udp = json.finalmask.udp.map(mask => FinalMask.fromJson(mask));
}
}
return new StreamSettings( return new StreamSettings(
json.network, json.network,
json.security, json.security,
@@ -654,7 +678,7 @@ class StreamSettings extends CommonClass {
HttpUpgradeStreamSettings.fromJson(json.httpupgradeSettings), HttpUpgradeStreamSettings.fromJson(json.httpupgradeSettings),
xHTTPStreamSettings.fromJson(json.xhttpSettings), xHTTPStreamSettings.fromJson(json.xhttpSettings),
HysteriaStreamSettings.fromJson(json.hysteriaSettings), HysteriaStreamSettings.fromJson(json.hysteriaSettings),
json.udpmasks ? json.udpmasks.map(mask => UdpMask.fromJson(mask)) : [], finalmask,
SockoptStreamSettings.fromJson(json.sockopt), SockoptStreamSettings.fromJson(json.sockopt),
); );
} }
@@ -673,7 +697,9 @@ class StreamSettings extends CommonClass {
httpupgradeSettings: network === 'httpupgrade' ? this.httpupgrade.toJson() : undefined, httpupgradeSettings: network === 'httpupgrade' ? this.httpupgrade.toJson() : undefined,
xhttpSettings: network === 'xhttp' ? this.xhttp.toJson() : undefined, xhttpSettings: network === 'xhttp' ? this.xhttp.toJson() : undefined,
hysteriaSettings: network === 'hysteria' ? this.hysteria.toJson() : undefined, hysteriaSettings: network === 'hysteria' ? this.hysteria.toJson() : undefined,
udpmasks: this.udpmasks.length > 0 ? this.udpmasks.map(mask => mask.toJson()) : undefined, finalmask: (this.finalmask.udp && this.finalmask.udp.length > 0) ? {
udp: this.finalmask.udp.map(mask => mask.toJson())
} : undefined,
sockopt: this.sockopt != undefined ? this.sockopt.toJson() : undefined, sockopt: this.sockopt != undefined ? this.sockopt.toJson() : undefined,
}; };
} }
@@ -1279,11 +1305,13 @@ Outbound.VLESSSettings = class extends CommonClass {
flow: this.flow, flow: this.flow,
encryption: this.encryption, encryption: this.encryption,
}; };
if (this.testpre > 0) { if (this.flow && this.flow !== '') {
result.testpre = this.testpre; if (this.testpre > 0) {
} result.testpre = this.testpre;
if (this.testseed && this.testseed.length >= 4) { }
result.testseed = this.testseed; if (this.testseed && this.testseed.length >= 4) {
result.testseed = this.testseed;
}
} }
return result; return result;
} }
@@ -1416,7 +1444,7 @@ Outbound.HttpSettings = class extends CommonClass {
Outbound.WireguardSettings = class extends CommonClass { Outbound.WireguardSettings = class extends CommonClass {
constructor( constructor(
mtu = 1250, mtu = 1420,
secretKey = '', secretKey = '',
address = [''], address = [''],
workers = 2, workers = 2,

View File

@@ -331,20 +331,6 @@
<!-- kcp --> <!-- kcp -->
<template v-if="outbound.stream.network === 'kcp'"> <template v-if="outbound.stream.network === 'kcp'">
<a-form-item label='{{ i18n "camouflage" }}'>
<a-select v-model="outbound.stream.kcp.type" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value="none">None</a-select-option>
<a-select-option value="srtp">SRTP</a-select-option>
<a-select-option value="utp">uTP</a-select-option>
<a-select-option value="wechat-video">WeChat</a-select-option>
<a-select-option value="dtls">DTLS 1.2</a-select-option>
<a-select-option value="wireguard">WireGuard</a-select-option>
<a-select-option value="dns">DNS</a-select-option>
</a-select>
</a-form-item>
<a-form-item label='{{ i18n "password" }}'>
<a-input v-model="outbound.stream.kcp.seed"></a-input>
</a-form-item>
<a-form-item label='MTU'> <a-form-item label='MTU'>
<a-input-number v-model.number="outbound.stream.kcp.mtu"></a-input-number> <a-input-number v-model.number="outbound.stream.kcp.mtu"></a-input-number>
</a-form-item> </a-form-item>
@@ -446,8 +432,9 @@
<a-input v-model.trim="outbound.stream.hysteria.auth"></a-input> <a-input v-model.trim="outbound.stream.hysteria.auth"></a-input>
</a-form-item> </a-form-item>
<a-form-item label='Congestion'> <a-form-item label='Congestion'>
<a-select v-model="outbound.stream.hysteria.congestion" :dropdown-class-name="themeSwitcher.currentTheme"> <a-select v-model="outbound.stream.hysteria.congestion"
<a-select-option value="">BBR (Auto)</a-select-option> :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value>BBR (Auto)</a-select-option>
<a-select-option value="brutal">Brutal</a-select-option> <a-select-option value="brutal">Brutal</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
@@ -502,25 +489,57 @@
</template> </template>
</template> </template>
<!-- udpmasks settings --> <!-- finalmask settings -->
<template v-if="outbound.canEnableStream()"> <template v-if="outbound.canEnableStream()">
<a-form-item label="UDP Masks"> <a-form-item label="UDP Masks">
<a-button icon="plus" type="primary" size="small" @click="outbound.stream.addUdpMask()"></a-button> <a-button icon="plus" type="primary" size="small"
@click="outbound.stream.addUdpMask(outbound.protocol === Protocols.Hysteria ? 'salamander' : (outbound.stream.network === 'kcp' ? 'mkcp-aes128gcm' : 'xdns'))"></a-button>
</a-form-item> </a-form-item>
<template v-if="outbound.stream.udpmasks.length > 0"> <template
<a-form v-for="(mask, index) in outbound.stream.udpmasks" :key="index" :colon="false" v-if="outbound.stream.finalmask.udp && outbound.stream.finalmask.udp.length > 0">
<a-form v-for="(mask, index) in outbound.stream.finalmask.udp"
:key="index" :colon="false"
:label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }"> :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
<a-divider :style="{ margin: '0' }"> UDP Mask [[ index + 1 ]] <a-divider :style="{ margin: '0' }"> UDP Mask [[ index + 1 ]]
<a-icon type="delete" @click="() => outbound.stream.delUdpMask(index)" <a-icon type="delete"
@click="() => outbound.stream.delUdpMask(index)"
:style="{ color: 'rgb(255, 77, 79)', cursor: 'pointer' }"></a-icon> :style="{ color: 'rgb(255, 77, 79)', cursor: 'pointer' }"></a-icon>
</a-divider> </a-divider>
<a-form-item label='Type'> <a-form-item label='Type'>
<a-select v-model="mask.type" :dropdown-class-name="themeSwitcher.currentTheme"> <a-select v-model="mask.type"
<a-select-option value="salamander">Salamander</a-select-option> @change="(type) => mask.settings = mask._getDefaultSettings(type, {})"
:dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option
v-if="outbound.protocol === Protocols.Hysteria"
value="salamander">
Salamander (Hysteria2)
</a-select-option>
<template v-if="outbound.stream.network === 'kcp'">
<a-select-option value="mkcp-aes128gcm">mKCP AES-128-GCM</a-select-option>
<a-select-option value="header-dns">Header DNS</a-select-option>
<a-select-option value="header-dtls">Header DTLS 1.2</a-select-option>
<a-select-option value="header-srtp">Header SRTP</a-select-option>
<a-select-option value="header-utp">Header uTP</a-select-option>
<a-select-option value="header-wechat">Header WeChat Video</a-select-option>
<a-select-option value="header-wireguard">Header WireGuard</a-select-option>
<a-select-option value="mkcp-original">mKCP Original</a-select-option>
</template>
<a-select-option
v-if="['tcp', 'ws', 'httpupgrade', 'xhttp'].includes(outbound.stream.network)"
value="xdns">
xDNS (Experimental)
</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item label='Password'> <a-form-item label='Password'
<a-input v-model.trim="mask.password" placeholder="Obfuscation password"></a-input> v-if="['salamander', 'mkcp-aes128gcm'].includes(mask.type)">
<a-input v-model.trim="mask.settings.password"
placeholder="Obfuscation password"></a-input>
</a-form-item>
<a-form-item label='Domain'
v-if="['header-dns', 'xdns'].includes(mask.type)">
<a-input v-model.trim="mask.settings.domain"
placeholder="e.g., www.example.com"></a-input>
</a-form-item> </a-form-item>
</a-form> </a-form>
</template> </template>

View File

@@ -0,0 +1,66 @@
{{define "form/streamFinalMask"}}
<a-divider :style="{ margin: '5px 0 0' }"></a-divider>
<a-form :colon="false" :label-col="{ md: {span:8} }"
:wrapper-col="{ md: {span:14} }">
<a-form-item label="UDP Masks">
<a-button icon="plus" type="primary" size="small"
@click="inbound.stream.addUdpMask(inbound.stream.network === 'kcp' ? 'mkcp-aes128gcm' : 'xdns')"></a-button>
</a-form-item>
<template
v-if="inbound.stream.finalmask.udp && inbound.stream.finalmask.udp.length > 0">
<a-form v-for="(mask, index) in inbound.stream.finalmask.udp"
:key="index" :colon="false"
:label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
<a-divider :style="{ margin: '0' }"> UDP Mask [[ index + 1 ]]
<a-icon type="delete"
@click="() => inbound.stream.delUdpMask(index)"
:style="{ color: 'rgb(255, 77, 79)', cursor: 'pointer' }"></a-icon>
</a-divider>
<a-form-item label='Type'>
<a-select v-model="mask.type"
@change="(type) => mask.settings = mask._getDefaultSettings(type, {})"
:dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option v-if="inbound.stream.network === 'kcp'"
value="mkcp-aes128gcm">
mKCP AES-128-GCM</a-select-option>
<a-select-option v-if="inbound.stream.network === 'kcp'"
value="header-dns">
Header DNS</a-select-option>
<a-select-option v-if="inbound.stream.network === 'kcp'"
value="header-dtls">
Header DTLS 1.2</a-select-option>
<a-select-option v-if="inbound.stream.network === 'kcp'"
value="header-srtp">
Header SRTP</a-select-option>
<a-select-option v-if="inbound.stream.network === 'kcp'"
value="header-utp">
Header uTP</a-select-option>
<a-select-option v-if="inbound.stream.network === 'kcp'"
value="header-wechat">
Header WeChat Video</a-select-option>
<a-select-option v-if="inbound.stream.network === 'kcp'"
value="header-wireguard">
Header WireGuard</a-select-option>
<a-select-option v-if="inbound.stream.network === 'kcp'"
value="mkcp-original">
mKCP Original</a-select-option>
<a-select-option
v-if="['tcp', 'ws', 'httpupgrade', 'xhttp'].includes(inbound.stream.network)"
value="xdns">
xDNS (Experimental)</a-select-option>
</a-select>
</a-form-item>
<a-form-item label='Password'
v-if="['mkcp-aes128gcm'].includes(mask.type)">
<a-input v-model.trim="mask.settings.password"
placeholder="Obfuscation password"></a-input>
</a-form-item>
<a-form-item label='Domain'
v-if="['header-dns', 'xdns'].includes(mask.type)">
<a-input v-model.trim="mask.settings.domain"
placeholder="e.g., www.example.com"></a-input>
</a-form-item>
</a-form>
</template>
</a-form>
{{end}}

View File

@@ -1,48 +1,32 @@
{{define "form/streamKCP"}} {{define "form/streamKCP"}}
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }"> <a-form :colon="false" :label-col="{ md: {span:8} }"
<a-form-item label='{{ i18n "camouflage" }}'> :wrapper-col="{ md: {span:14} }">
<a-select v-model="inbound.stream.kcp.type" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value="none">None</a-select-option>
<a-select-option value="srtp">SRTP</a-select-option>
<a-select-option value="utp">uTP</a-select-option>
<a-select-option value="wechat-video">WeChat</a-select-option>
<a-select-option value="dtls">DTLS 1.2</a-select-option>
<a-select-option value="wireguard">WireGuard</a-select-option>
<a-select-option value="dns">DNS</a-select-option>
</a-select>
</a-form-item>
<a-form-item>
<template slot="label">
<a-tooltip>
<template slot="title">
<span>{{ i18n "reset" }}</span>
</template>
{{ i18n "password" }}
<a-icon @click="inbound.stream.kcp.seed = RandomUtil.randomSeq(10)"type="sync"> </a-icon>
</a-tooltip>
</template>
<a-input v-model.trim="inbound.stream.kcp.seed"></a-input>
</a-form-item>
<a-form-item label='MTU'> <a-form-item label='MTU'>
<a-input-number v-model.number="inbound.stream.kcp.mtu"></a-input-number> <a-input-number v-model.number="inbound.stream.kcp.mtu" :min="576"
:max="1460"></a-input-number>
</a-form-item> </a-form-item>
<a-form-item label='TTI (ms)'> <a-form-item label='TTI (ms)'>
<a-input-number v-model.number="inbound.stream.kcp.tti"></a-input-number> <a-input-number v-model.number="inbound.stream.kcp.tti" :min="10"
:max="100"></a-input-number>
</a-form-item> </a-form-item>
<a-form-item label='Uplink (MB/s)'> <a-form-item label='Uplink (MB/s)'>
<a-input-number v-model.number="inbound.stream.kcp.upCap"></a-input-number> <a-input-number v-model.number="inbound.stream.kcp.upCap"
</a-form-item> :min="0"></a-input-number>
</a-form-item>
<a-form-item label='Downlink (MB/s)'> <a-form-item label='Downlink (MB/s)'>
<a-input-number v-model.number="inbound.stream.kcp.downCap"></a-input-number> <a-input-number v-model.number="inbound.stream.kcp.downCap"
:min="0"></a-input-number>
</a-form-item> </a-form-item>
<a-form-item label='Congestion'> <a-form-item label='Congestion'>
<a-switch v-model="inbound.stream.kcp.congestion"></a-switch> <a-switch v-model="inbound.stream.kcp.congestion"></a-switch>
</a-form-item> </a-form-item>
<a-form-item label='Read Buffer (MB)'> <a-form-item label='Read Buffer (MB)'>
<a-input-number v-model.number="inbound.stream.kcp.readBuffer"></a-input-number> <a-input-number v-model.number="inbound.stream.kcp.readBuffer"
:min="0"></a-input-number>
</a-form-item> </a-form-item>
<a-form-item label='Write Buffer (MB)'> <a-form-item label='Write Buffer (MB)'>
<a-input-number v-model.number="inbound.stream.kcp.writeBuffer"></a-input-number> <a-input-number v-model.number="inbound.stream.kcp.writeBuffer"
:min="0"></a-input-number>
</a-form-item> </a-form-item>
</a-form> </a-form>
{{end}} {{end}}

View File

@@ -44,6 +44,12 @@
{{template "form/streamXHTTP"}} {{template "form/streamXHTTP"}}
</template> </template>
<!-- finalmask - only for TCP, WS, HTTPUpgrade, XHTTP, mKCP -->
<template
v-if="['tcp', 'ws', 'httpupgrade', 'xhttp', 'kcp'].includes(inbound.stream.network)">
{{template "form/streamFinalMask"}}
</template>
<!-- sockopt --> <!-- sockopt -->
<template> <template>
{{template "form/streamSockopt"}} {{template "form/streamSockopt"}}