diff options
| author | MHSanaei <ho3ein.sanaei@gmail.com> | 2026-02-01 03:56:23 +0300 |
|---|---|---|
| committer | MHSanaei <ho3ein.sanaei@gmail.com> | 2026-02-01 03:56:23 +0300 |
| commit | c59f54bb0ef9ef06ade11aa75fba61224830be7e (patch) | |
| tree | 3f59e029a212bf2452648fa73fe329458b49c66b | |
| parent | 6b3da4fe5ea072cdadd46f5d1ba8de514c45d206 (diff) | |
outbound: finalmask
| -rw-r--r-- | web/assets/js/model/inbound.js | 4 | ||||
| -rw-r--r-- | web/assets/js/model/outbound.js | 93 | ||||
| -rw-r--r-- | web/html/form/outbound.html | 57 |
3 files changed, 115 insertions, 39 deletions
diff --git a/web/assets/js/model/inbound.js b/web/assets/js/model/inbound.js index 58cc7421..9077ea53 100644 --- a/web/assets/js/model/inbound.js +++ b/web/assets/js/model/inbound.js @@ -318,7 +318,7 @@ TcpStreamSettings.TcpResponse = class extends XrayCommonClass { class KcpStreamSettings extends XrayCommonClass { constructor( - mtu = 1250, + mtu = 1350, tti = 50, uplinkCapacity = 5, downlinkCapacity = 20, @@ -2509,7 +2509,7 @@ Inbound.HttpSettings.HttpAccount = class extends XrayCommonClass { Inbound.WireguardSettings = class extends XrayCommonClass { constructor( protocol, - mtu = 1250, + mtu = 1420, secretKey = Wireguard.generateKeypair().privateKey, peers = [new Inbound.WireguardSettings.Peer()], noKernelTun = false diff --git a/web/assets/js/model/outbound.js b/web/assets/js/model/outbound.js index c6529560..3e5374e3 100644 --- a/web/assets/js/model/outbound.js +++ b/web/assets/js/model/outbound.js @@ -165,7 +165,7 @@ class TcpStreamSettings extends CommonClass { class KcpStreamSettings extends CommonClass { constructor( - mtu = 1250, + mtu = 1350, tti = 50, uplinkCapacity = 5, downlinkCapacity = 20, @@ -558,27 +558,49 @@ class SockoptStreamSettings extends CommonClass { } } -class UdpMask extends CommonClass { - constructor(type = 'salamander', password = '') { +class FinalMask extends CommonClass { + constructor(type = 'salamander', settings = {}) { super(); 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 = {}) { - return new UdpMask( - json.type, - json.settings?.password || '' + return new FinalMask( + json.type || 'salamander', + json.settings || {} ); } toJson() { - return { - type: this.type, - settings: { - password: this.password - } + const result = { + type: this.type }; + // Only include settings if they exist and are not empty + if (this.settings && Object.keys(this.settings).length > 0) { + result.settings = this.settings; + } + return result; } } @@ -595,7 +617,7 @@ class StreamSettings extends CommonClass { httpupgradeSettings = new HttpUpgradeStreamSettings(), xhttpSettings = new xHTTPStreamSettings(), hysteriaSettings = new HysteriaStreamSettings(), - udpmasks = [], + finalmask = { udp: [] }, sockopt = undefined, ) { super(); @@ -610,16 +632,21 @@ class StreamSettings extends CommonClass { this.httpupgrade = httpupgradeSettings; this.xhttp = xhttpSettings; this.hysteria = hysteriaSettings; - this.udpmasks = udpmasks; + this.finalmask = finalmask; this.sockopt = sockopt; } - addUdpMask() { - this.udpmasks.push(new UdpMask()); + addUdpMask(type = 'salamander') { + if (!this.finalmask.udp) { + this.finalmask.udp = []; + } + this.finalmask.udp.push(new FinalMask(type)); } delUdpMask(index) { - this.udpmasks.splice(index, 1); + if (this.finalmask.udp) { + this.finalmask.udp.splice(index, 1); + } } get isTls() { @@ -639,7 +666,16 @@ class StreamSettings extends CommonClass { } static fromJson(json = {}) { - const udpmasks = json.udpmasks ? json.udpmasks.map(mask => UdpMask.fromJson(mask)) : []; + let finalmask = { udp: [] }; + if (json.finalmask) { + if (Array.isArray(json.finalmask)) { + // Legacy format: direct array (backward compatibility) + finalmask.udp = json.finalmask.map(mask => FinalMask.fromJson(mask)); + } else if (json.finalmask.udp) { + // New format: object with udp array + finalmask.udp = json.finalmask.udp.map(mask => FinalMask.fromJson(mask)); + } + } return new StreamSettings( json.network, json.security, @@ -652,7 +688,7 @@ class StreamSettings extends CommonClass { HttpUpgradeStreamSettings.fromJson(json.httpupgradeSettings), xHTTPStreamSettings.fromJson(json.xhttpSettings), HysteriaStreamSettings.fromJson(json.hysteriaSettings), - udpmasks, + finalmask, SockoptStreamSettings.fromJson(json.sockopt), ); } @@ -671,7 +707,9 @@ class StreamSettings extends CommonClass { httpupgradeSettings: network === 'httpupgrade' ? this.httpupgrade.toJson() : undefined, xhttpSettings: network === 'xhttp' ? this.xhttp.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, }; } @@ -1285,11 +1323,14 @@ Outbound.VLESSSettings = class extends CommonClass { flow: this.flow, encryption: this.encryption, }; - if (this.testpre > 0) { - result.testpre = this.testpre; - } - if (this.testseed && this.testseed.length >= 4) { - result.testseed = this.testseed; + // Only include Vision settings when flow is set + if (this.flow && this.flow !== '') { + if (this.testpre > 0) { + result.testpre = this.testpre; + } + if (this.testseed && this.testseed.length >= 4) { + result.testseed = this.testseed; + } } return result; } @@ -1422,7 +1463,7 @@ Outbound.HttpSettings = class extends CommonClass { Outbound.WireguardSettings = class extends CommonClass { constructor( - mtu = 1250, + mtu = 1420, secretKey = '', address = [''], workers = 2, diff --git a/web/html/form/outbound.html b/web/html/form/outbound.html index 688a3564..074137f7 100644 --- a/web/html/form/outbound.html +++ b/web/html/form/outbound.html @@ -546,8 +546,9 @@ <a-input v-model.trim="outbound.stream.hysteria.auth"></a-input> </a-form-item> <a-form-item label='Congestion'> - <a-select v-model="outbound.stream.hysteria.congestion" :dropdown-class-name="themeSwitcher.currentTheme"> - <a-select-option value="">BBR (Auto)</a-select-option> + <a-select v-model="outbound.stream.hysteria.congestion" + :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> </a-form-item> @@ -602,25 +603,59 @@ </template> </template> - <!-- udpmasks settings --> + <!-- finalmask settings --> <template v-if="outbound.canEnableStream()"> <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' : 'mkcp-aes128gcm')"></a-button> </a-form-item> - <template v-if="outbound.stream.udpmasks.length > 0"> - <a-form v-for="(mask, index) in outbound.stream.udpmasks" :key="index" :colon="false" + <template + 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} }"> <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> </a-divider> <a-form-item label='Type'> - <a-select v-model="mask.type" :dropdown-class-name="themeSwitcher.currentTheme"> - <a-select-option value="salamander">Salamander</a-select-option> + <a-select v-model="mask.type" + @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> + <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> + <a-select-option value="xdns"> + xDNS (Experimental)</a-select-option> </a-select> </a-form-item> - <a-form-item label='Password'> - <a-input v-model.trim="mask.password" placeholder="Obfuscation password"></a-input> + <!-- Settings for password-based masks --> + <a-form-item label='Password' + v-if="['salamander', 'mkcp-aes128gcm'].includes(mask.type)"> + <a-input v-model.trim="mask.settings.password" + placeholder="Obfuscation password"></a-input> + </a-form-item> + <!-- Settings for domain-based masks --> + <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> |
