Finalmask: noise "exp" type and valid noise items, new quicParams flags, Hysteria masquerade xForwarded

This commit is contained in:
Alireza Ahmadi 2026-10-09 22:51:27 +02:00
parent 56639e2dbc
commit 81f38059e3
11 changed files with 210 additions and 44 deletions

View file

@ -774,6 +774,7 @@ class HysteriaMasquerade extends XrayCommonClass {
dir = '',
url = '',
rewriteHost = false,
xForwarded = false,
insecure = false,
content = '',
headers = [],
@ -784,6 +785,7 @@ class HysteriaMasquerade extends XrayCommonClass {
this.dir = dir;
this.url = url;
this.rewriteHost = rewriteHost;
this.xForwarded = xForwarded;
this.insecure = insecure;
this.content = content;
this.headers = headers;
@ -804,6 +806,7 @@ class HysteriaMasquerade extends XrayCommonClass {
json.dir,
json.url,
json.rewriteHost,
json.xForwarded,
json.insecure,
json.content,
XrayCommonClass.toHeaders(json.headers),
@ -817,6 +820,7 @@ class HysteriaMasquerade extends XrayCommonClass {
dir: XrayCommonClass.shrinkObject(this.dir),
url: XrayCommonClass.shrinkObject(this.url),
rewriteHost: this.rewriteHost ? true : undefined,
xForwarded: this.xForwarded ? true : undefined,
insecure: this.insecure ? true : undefined,
content: XrayCommonClass.shrinkObject(this.content),
headers: XrayCommonClass.shrinkObject(XrayCommonClass.toV2Headers(this.headers, false)),
@ -1323,6 +1327,31 @@ class UdpMask extends XrayCommonClass {
.filter(d => d && d.name !== undefined);
}
// Xray-core takes either random bytes ("rand") or a fixed "packet" per noise
// item, never both, and an "array" packet must be a real JSON array. "exp"
// builds the packet from tags such as <b 0d0a><t><rc 20-40>.
static noiseItemToJson(item = {}) {
const out = {};
const packet = item.packet ?? '';
if (item.type === 'exp') {
out.type = 'exp';
out.packet = String(packet);
} else if (Array.isArray(packet) ? packet.length > 0 : String(packet) !== '') {
if (!item.type || item.type === 'array') {
out.packet = Array.isArray(packet) ? packet
: String(packet).split(/[\s,]+/).filter(Boolean).map(Number);
} else {
out.type = item.type;
out.packet = packet;
}
} else {
if (item.rand !== '' && item.rand != null) out.rand = item.rand;
if (item.randRange) out.randRange = item.randRange;
}
if (item.delay !== '' && item.delay != null && item.delay !== 0) out.delay = item.delay;
return out;
}
// Masks that open their own sockets, so Xray-core only accepts them first.
get dialsItself() {
return this.type === 'xicmp';
@ -1337,6 +1366,9 @@ class UdpMask extends XrayCommonClass {
toJson() {
let settings = this.settings;
if (this.type === 'noise') {
settings = { reset: settings.reset || undefined, noise: (settings.noise || []).map(n => UdpMask.noiseItemToJson(n)) };
}
if (this.type === 'xicmp') {
settings = { dgram: settings.dgram || undefined, ips: settings.ips?.length ? settings.ips : undefined };
}
@ -1415,6 +1447,10 @@ class QuicParams extends XrayCommonClass {
maxIdleTimeout = 30,
keepAlivePeriod = 0,
disablePathMTUDiscovery = false,
brutalDisableLossCompensation = false,
disableChromeParrot = false,
disableGSO = false,
disableStatelessReset = false,
maxIncomingStreams = 1024,
} = {}) {
super();
@ -1431,6 +1467,10 @@ class QuicParams extends XrayCommonClass {
this.maxIdleTimeout = maxIdleTimeout;
this.keepAlivePeriod = keepAlivePeriod;
this.disablePathMTUDiscovery = disablePathMTUDiscovery;
this.brutalDisableLossCompensation = brutalDisableLossCompensation;
this.disableChromeParrot = disableChromeParrot;
this.disableGSO = disableGSO;
this.disableStatelessReset = disableStatelessReset;
this.maxIncomingStreams = maxIncomingStreams;
}
@ -1440,7 +1480,7 @@ class QuicParams extends XrayCommonClass {
'initStreamReceiveWindow', 'maxStreamReceiveWindow',
'initConnectionReceiveWindow', 'maxConnectionReceiveWindow',
'maxIdleTimeout', 'keepAlivePeriod', 'disablePathMTUDiscovery',
'maxIncomingStreams'];
'maxIncomingStreams', 'brutalDisableLossCompensation', 'disableChromeParrot', 'disableGSO', 'disableStatelessReset'];
return keys.some(k => json[k] !== undefined && json[k] !== '' && json[k] !== 0 && json[k] !== false);
}
@ -1472,6 +1512,10 @@ class QuicParams extends XrayCommonClass {
maxIdleTimeout: json.maxIdleTimeout || 0,
keepAlivePeriod: json.keepAlivePeriod || 0,
disablePathMTUDiscovery: !!json.disablePathMTUDiscovery,
brutalDisableLossCompensation: !!json.brutalDisableLossCompensation,
disableChromeParrot: !!json.disableChromeParrot,
disableGSO: !!json.disableGSO,
disableStatelessReset: !!json.disableStatelessReset,
maxIncomingStreams: json.maxIncomingStreams || 0,
});
}
@ -1493,6 +1537,10 @@ class QuicParams extends XrayCommonClass {
if (this.maxIdleTimeout) result.maxIdleTimeout = this.maxIdleTimeout;
if (this.keepAlivePeriod) result.keepAlivePeriod = this.keepAlivePeriod;
if (this.disablePathMTUDiscovery) result.disablePathMTUDiscovery = this.disablePathMTUDiscovery;
if (this.brutalDisableLossCompensation) result.brutalDisableLossCompensation = true;
if (this.disableChromeParrot) result.disableChromeParrot = true;
if (this.disableGSO) result.disableGSO = true;
if (this.disableStatelessReset) result.disableStatelessReset = true;
if (this.maxIncomingStreams) result.maxIncomingStreams = this.maxIncomingStreams;
return Object.keys(result).length > 0 ? result : undefined;
}

View file

@ -924,6 +924,31 @@ class UdpMask extends CommonClass {
return { domains, resolvers, extraPoll: settings.extraPoll ?? 0 };
}
// Xray-core takes either random bytes ("rand") or a fixed "packet" per noise
// item, never both, and an "array" packet must be a real JSON array. "exp"
// builds the packet from tags such as <b 0d0a><t><rc 20-40>.
static noiseItemToJson(item = {}) {
const out = {};
const packet = item.packet ?? '';
if (item.type === 'exp') {
out.type = 'exp';
out.packet = String(packet);
} else if (Array.isArray(packet) ? packet.length > 0 : String(packet) !== '') {
if (!item.type || item.type === 'array') {
out.packet = Array.isArray(packet) ? packet
: String(packet).split(/[\s,]+/).filter(Boolean).map(Number);
} else {
out.type = item.type;
out.packet = packet;
}
} else {
if (item.rand !== '' && item.rand != null) out.rand = item.rand;
if (item.randRange) out.randRange = item.randRange;
}
if (item.delay !== '' && item.delay != null && item.delay !== 0) out.delay = item.delay;
return out;
}
// Masks that open their own sockets, so Xray-core only accepts them first.
get dialsItself() {
return ['udphop', 'xicmp'].includes(this.type);
@ -938,6 +963,10 @@ class UdpMask extends CommonClass {
toJson() {
let settings = this.settings;
if (this.type === 'noise') {
settings = { ...settings, noise: (settings.noise || []).map(n => UdpMask.noiseItemToJson(n)) };
if (!settings.reset) delete settings.reset;
}
if (['xdns', 'xicmp', 'udphop'].includes(this.type)) {
settings = Object.fromEntries(Object.entries(settings).filter(([, v]) =>
!(v === '' || v === 0 || v === false || (Array.isArray(v) && v.length === 0))));
@ -1015,6 +1044,10 @@ class QuicParams extends CommonClass {
maxIdleTimeout = 0,
keepAlivePeriod = 0,
disablePathMTUDiscovery = false,
brutalDisableLossCompensation = false,
disableChromeParrot = false,
disableGSO = false,
disableStatelessReset = false,
maxIncomingStreams = 0,
} = {}) {
super();
@ -1029,6 +1062,10 @@ class QuicParams extends CommonClass {
this.maxIdleTimeout = maxIdleTimeout;
this.keepAlivePeriod = keepAlivePeriod;
this.disablePathMTUDiscovery = disablePathMTUDiscovery;
this.brutalDisableLossCompensation = brutalDisableLossCompensation;
this.disableChromeParrot = disableChromeParrot;
this.disableGSO = disableGSO;
this.disableStatelessReset = disableStatelessReset;
this.maxIncomingStreams = maxIncomingStreams;
}
@ -1038,7 +1075,7 @@ class QuicParams extends CommonClass {
'initStreamReceiveWindow', 'maxStreamReceiveWindow',
'initConnectionReceiveWindow', 'maxConnectionReceiveWindow',
'maxIdleTimeout', 'keepAlivePeriod', 'disablePathMTUDiscovery',
'maxIncomingStreams'];
'maxIncomingStreams', 'brutalDisableLossCompensation', 'disableChromeParrot', 'disableGSO', 'disableStatelessReset'];
return keys.some(k => json[k] !== undefined && json[k] !== '' && json[k] !== 0 && json[k] !== false);
}
@ -1067,6 +1104,10 @@ class QuicParams extends CommonClass {
maxIdleTimeout: json.maxIdleTimeout || 0,
keepAlivePeriod: json.keepAlivePeriod || 0,
disablePathMTUDiscovery: !!json.disablePathMTUDiscovery,
brutalDisableLossCompensation: !!json.brutalDisableLossCompensation,
disableChromeParrot: !!json.disableChromeParrot,
disableGSO: !!json.disableGSO,
disableStatelessReset: !!json.disableStatelessReset,
maxIncomingStreams: json.maxIncomingStreams || 0,
});
}
@ -1099,6 +1140,10 @@ class QuicParams extends CommonClass {
if (this.maxIdleTimeout) result.maxIdleTimeout = this.maxIdleTimeout;
if (this.keepAlivePeriod) result.keepAlivePeriod = this.keepAlivePeriod;
if (this.disablePathMTUDiscovery) result.disablePathMTUDiscovery = this.disablePathMTUDiscovery;
if (this.brutalDisableLossCompensation) result.brutalDisableLossCompensation = true;
if (this.disableChromeParrot) result.disableChromeParrot = true;
if (this.disableGSO) result.disableGSO = true;
if (this.disableStatelessReset) result.disableStatelessReset = true;
if (this.maxIncomingStreams) result.maxIncomingStreams = this.maxIncomingStreams;
return Object.keys(result).length > 0 ? result : undefined;
}

View file

@ -977,7 +977,7 @@
</template>
<template v-if="mask.type === 'noise'">
<a-form-item label='{{ i18n "reset" }}'>
<a-input-number v-model.number="mask.settings.reset" :min="0" />
<a-input v-model.trim="mask.settings.reset" placeholder="60-120" />
</a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.stream.finalmask.noise" }}'>
<a-icon type="plus" type="primary" size="small" @click="mask.settings.noise.push({rand: '1-8192', randRange: '0-255', type: 'array', packet: '', delay: ''})" />
@ -988,26 +988,7 @@
@click="() => mask.settings.noise.splice(index, 1)"
:style="{ color: 'rgb(255, 77, 79)', cursor: 'pointer' }"></a-icon>
</a-divider>
<a-form-item label='{{ i18n "pages.inbounds.stream.finalmask.rand" }}'>
<a-input-number v-model.number="n.rand" :min="0"></a-input-number>
</a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.stream.finalmask.randRange" }}'>
<a-input v-model.trim="n.randRange" placeholder="0-255"></a-input>
</a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.stream.hysteria.type" }}'>
<a-select v-model="n.type" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value="array">Array</a-select-option>
<a-select-option value="str">String</a-select-option>
<a-select-option value="hex">Hex</a-select-option>
<a-select-option value="base64">Base64</a-select-option>
</a-select>
</a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.stream.finalmask.packet" }}'>
<a-input v-model.trim="n.packet" placeholder='{{ i18n "pages.inbounds.stream.finalmask.binaryData" }}' />
</a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.stream.finalmask.delay" }}'>
<a-input v-model.trim="n.delay" placeholder="10-20" />
</a-form-item>
{{template "form/noiseItem" "n"}}
</template>
</template>
<template v-if="mask.type === 'xicmp'">
@ -1067,6 +1048,18 @@
<a-form-item label='{{ i18n "pages.inbounds.stream.finalmask.maxIncomingStreams" }}'>
<a-input-number v-model.number="outbound.stream.finalmask.quicParams.maxIncomingStreams" :min="0" />
</a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.stream.finalmask.brutalDisableLossCompensation" }}'>
<a-switch v-model="outbound.stream.finalmask.quicParams.brutalDisableLossCompensation"></a-switch>
</a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.stream.finalmask.disableChromeParrot" }}'>
<a-switch v-model="outbound.stream.finalmask.quicParams.disableChromeParrot"></a-switch>
</a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.stream.finalmask.disableGSO" }}'>
<a-switch v-model="outbound.stream.finalmask.quicParams.disableGSO"></a-switch>
</a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.stream.finalmask.disableStatelessReset" }}'>
<a-switch v-model="outbound.stream.finalmask.quicParams.disableStatelessReset"></a-switch>
</a-form-item>
</template>
</template>
</template>

View file

@ -234,7 +234,7 @@
</template>
<template v-if="mask.type === 'noise'">
<a-form-item label='{{ i18n "reset" }}'>
<a-input-number v-model.number="mask.settings.reset" :min="0" />
<a-input v-model.trim="mask.settings.reset" placeholder="60-120" />
</a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.stream.finalmask.noise" }}'>
<a-icon type="plus" type="primary" size="small" @click="mask.settings.noise.push({rand: '1-8192', randRange: '0-255', type: 'array', packet: '', delay: ''})" />
@ -245,26 +245,7 @@
@click="() => mask.settings.noise.splice(index, 1)"
:style="{ color: 'rgb(255, 77, 79)', cursor: 'pointer' }"></a-icon>
</a-divider>
<a-form-item label='{{ i18n "pages.inbounds.stream.finalmask.rand" }}'>
<a-input v-model.trim="n.rand" placeholder="1-8192" />
</a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.stream.finalmask.randRange" }}'>
<a-input v-model.trim="n.randRange" placeholder="0-255" />
</a-form-item>
<a-form-item label='{{ i18n "pages.xray.outbound.type" }}'>
<a-select v-model="n.type" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value="array">Array</a-select-option>
<a-select-option value="str">String</a-select-option>
<a-select-option value="hex">Hex</a-select-option>
<a-select-option value="base64">Base64</a-select-option>
</a-select>
</a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.stream.finalmask.packet" }}'>
<a-input v-model.trim="n.packet" placeholder='{{ i18n "pages.inbounds.stream.finalmask.binaryData" }}' />
</a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.stream.finalmask.delay" }}'>
<a-input v-model.trim="n.delay" placeholder="10-20" />
</a-form-item>
{{template "form/noiseItem" "n"}}
</template>
</template>
<template v-if="mask.type === 'sudoku'">
@ -355,6 +336,18 @@
<a-form-item label='{{ i18n "pages.inbounds.stream.finalmask.maxIncomingStreams" }}'>
<a-input-number v-model.number="inbound.stream.finalmask.quicParams.maxIncomingStreams" :min="0" />
</a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.stream.finalmask.brutalDisableLossCompensation" }}'>
<a-switch v-model="inbound.stream.finalmask.quicParams.brutalDisableLossCompensation"></a-switch>
</a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.stream.finalmask.disableChromeParrot" }}'>
<a-switch v-model="inbound.stream.finalmask.quicParams.disableChromeParrot"></a-switch>
</a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.stream.finalmask.disableGSO" }}'>
<a-switch v-model="inbound.stream.finalmask.quicParams.disableGSO"></a-switch>
</a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.stream.finalmask.disableStatelessReset" }}'>
<a-switch v-model="inbound.stream.finalmask.quicParams.disableStatelessReset"></a-switch>
</a-form-item>
</template>
</template>
</a-form>

View file

@ -32,3 +32,52 @@
:dropdown-class-name="themeSwitcher.currentTheme"></a-select>
</a-form-item>
{{end}}
{{define "form/noiseItem"}}
<a-form-item label='{{ i18n "pages.xray.outbound.type" }}'>
<a-select v-model="{{.}}.type" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value="array">Array</a-select-option>
<a-select-option value="str">String</a-select-option>
<a-select-option value="hex">Hex</a-select-option>
<a-select-option value="base64">Base64</a-select-option>
<a-select-option value="exp">Exp</a-select-option>
</a-select>
</a-form-item>
<template v-if="{{.}}.type === 'exp'">
<a-form-item>
<template slot="label">
<a-tooltip>
<template slot="title">
<span>{{ i18n "pages.inbounds.stream.finalmask.noiseExpDesc" }}</span>
</template>
{{ i18n "pages.inbounds.stream.finalmask.packet" }} <a-icon type="question-circle"></a-icon>
</a-tooltip>
</template>
<a-input v-model.trim="{{.}}.packet" placeholder="<b 0d0a0d0a><t><rc 20-40>"></a-input>
</a-form-item>
</template>
<template v-else>
<a-form-item>
<template slot="label">
<a-tooltip>
<template slot="title">
<span>{{ i18n "pages.inbounds.stream.finalmask.noisePacketDesc" }}</span>
</template>
{{ i18n "pages.inbounds.stream.finalmask.packet" }} <a-icon type="question-circle"></a-icon>
</a-tooltip>
</template>
<a-input v-model.trim="{{.}}.packet" placeholder='{{ i18n "pages.inbounds.stream.finalmask.binaryData" }}'></a-input>
</a-form-item>
<template v-if="!({{.}}.packet && {{.}}.packet.length)">
<a-form-item label='{{ i18n "pages.inbounds.stream.finalmask.rand" }}'>
<a-input v-model.trim="{{.}}.rand" placeholder="1-8192"></a-input>
</a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.stream.finalmask.randRange" }}'>
<a-input v-model.trim="{{.}}.randRange" placeholder="0-255"></a-input>
</a-form-item>
</template>
</template>
<a-form-item label='{{ i18n "pages.inbounds.stream.finalmask.delay" }}'>
<a-input v-model.trim="{{.}}.delay" placeholder="10-20"></a-input>
</a-form-item>
{{end}}

View file

@ -25,6 +25,9 @@
<a-form-item label='{{ i18n "pages.inbounds.stream.hysteria.rewriteHost" }}'>
<a-switch v-model="inbound.stream.hysteria.masquerade.rewriteHost"></a-switch>
</a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.stream.hysteria.xForwarded" }}'>
<a-switch v-model="inbound.stream.hysteria.masquerade.xForwarded"></a-switch>
</a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.stream.hysteria.insecure" }}'>
<a-switch v-model="inbound.stream.hysteria.masquerade.insecure"></a-switch>
</a-form-item>

View file

@ -454,6 +454,12 @@
"remoteIPs" = "Remote IPs"
"remotePorts" = "Remote Ports"
"udpHopLinkDesc" = "Only written into share links and subscriptions. On the server, forward this port range to the inbound port with iptables"
"brutalDisableLossCompensation" = "Brutal: Disable Loss Compensation"
"disableChromeParrot" = "Disable Chrome Parrot"
"disableGSO" = "Disable GSO"
"disableStatelessReset" = "Disable Stateless Reset"
"noiseExpDesc" = "Builds a fresh packet on every send from tags: <b hex>, <r N-M>, <rc N-M>, <rd N-M>, <t>, <c>, <n> (bytes, random, random letters, random digits, timestamp, counter, nonce)"
"noisePacketDesc" = "A fixed packet replaces the random bytes; leave it empty to use Rand. Array: comma-separated byte values"
[pages.inbounds.stream.hysteria]
"authPassword" = "Auth Password"
@ -466,6 +472,7 @@
"insecure" = "Insecure"
"content" = "Content"
"statusCode" = "Status Code"
"xForwarded" = "X-Forwarded Headers"
[pages.inbounds.protocols]
"flow" = "Flow"

View file

@ -454,6 +454,12 @@
"remoteIPs" = "آی‌پی‌های مقصد"
"remotePorts" = "پورت‌های مقصد"
"udpHopLinkDesc" = "فقط در لینک‌های اشتراک و سابسکریپشن قرار می‌گیرد. روی سرور این بازه پورت را با iptables به پورت اینباند هدایت کنید"
"brutalDisableLossCompensation" = "Brutal: غیرفعال‌سازی جبران Loss"
"disableChromeParrot" = "غیرفعال‌سازی تقلید Chrome"
"disableGSO" = "غیرفعال‌سازی GSO"
"disableStatelessReset" = "غیرفعال‌سازی Stateless Reset"
"noiseExpDesc" = "در هر ارسال یک پکت تازه از این تگ‌ها می‌سازد: <b hex>, <r N-M>, <rc N-M>, <rd N-M>, <t>, <c>, <n> (بایت، تصادفی، حروف تصادفی، ارقام تصادفی، زمان، شمارنده، nonce)"
"noisePacketDesc" = "پکت ثابت جای بایت‌های تصادفی را می‌گیرد؛ برای استفاده از Rand خالی بگذارید. Array: مقادیر بایت جداشده با کاما"
[pages.inbounds.stream.hysteria]
"authPassword" = "رمز احراز هویت"
@ -466,6 +472,7 @@
"insecure" = "ناامن"
"content" = "محتوا"
"statusCode" = "Status Code"
"xForwarded" = "هدرهای X-Forwarded"
[pages.inbounds.protocols]
"flow" = "Flow"

View file

@ -454,6 +454,12 @@
"remoteIPs" = "Удалённые IP"
"remotePorts" = "Удалённые порты"
"udpHopLinkDesc" = "Используется только в ссылках и подписках. На сервере перенаправьте этот диапазон портов на порт входящего через iptables"
"brutalDisableLossCompensation" = "Brutal: отключить компенсацию потерь"
"disableChromeParrot" = "Отключить имитацию Chrome"
"disableGSO" = "Отключить GSO"
"disableStatelessReset" = "Отключить Stateless Reset"
"noiseExpDesc" = "При каждой отправке собирает новый пакет из тегов: <b hex>, <r N-M>, <rc N-M>, <rd N-M>, <t>, <c>, <n> (байты, случайные, случайные буквы, случайные цифры, метка времени, счётчик, nonce)"
"noisePacketDesc" = "Фиксированный пакет заменяет случайные байты; оставьте пустым, чтобы использовать Rand. Array: байты через запятую"
[pages.inbounds.stream.hysteria]
"authPassword" = "Пароль аутентификации"
@ -466,6 +472,7 @@
"insecure" = "Небезопасный"
"content" = "Контент"
"statusCode" = "Код статуса"
"xForwarded" = "Заголовки X-Forwarded"
[pages.inbounds.protocols]
"flow" = "Flow"

View file

@ -454,6 +454,12 @@
"remoteIPs" = "IP từ xa"
"remotePorts" = "Cổng từ xa"
"udpHopLinkDesc" = "Chỉ dùng trong liên kết chia sẻ và gói đăng ký. Trên máy chủ, chuyển tiếp dải cổng này tới cổng inbound bằng iptables"
"brutalDisableLossCompensation" = "Brutal: Tắt bù mất gói"
"disableChromeParrot" = "Tắt giả lập Chrome"
"disableGSO" = "Tắt GSO"
"disableStatelessReset" = "Tắt Stateless Reset"
"noiseExpDesc" = "Tạo gói mới mỗi lần gửi từ các thẻ: <b hex>, <r N-M>, <rc N-M>, <rd N-M>, <t>, <c>, <n> (byte, ngẫu nhiên, chữ ngẫu nhiên, số ngẫu nhiên, dấu thời gian, bộ đếm, nonce)"
"noisePacketDesc" = "Gói cố định thay cho byte ngẫu nhiên; để trống để dùng Rand. Array: các giá trị byte phân tách bằng dấu phẩy"
[pages.inbounds.stream.hysteria]
"authPassword" = "Mật khẩu xác thực"
@ -466,6 +472,7 @@
"insecure" = "Không bảo mật"
"content" = "Nội dung"
"statusCode" = "Status Code"
"xForwarded" = "Header X-Forwarded"
[pages.inbounds.protocols]
"flow" = "Flow"

View file

@ -454,6 +454,12 @@
"remoteIPs" = "远程 IP"
"remotePorts" = "远程端口"
"udpHopLinkDesc" = "仅写入分享链接和订阅。服务端需用 iptables 将该端口范围转发到入站端口"
"brutalDisableLossCompensation" = "Brutal:禁用丢包补偿"
"disableChromeParrot" = "禁用 Chrome 模拟"
"disableGSO" = "禁用 GSO"
"disableStatelessReset" = "禁用无状态重置"
"noiseExpDesc" = "每次发送时用以下标签生成新包:<b hex>, <r N-M>, <rc N-M>, <rd N-M>, <t>, <c>, <n>(字节、随机、随机字母、随机数字、时间戳、计数器、nonce)"
"noisePacketDesc" = "固定包会替代随机字节;留空则使用 Rand。Array:以逗号分隔的字节值"
[pages.inbounds.stream.hysteria]
"authPassword" = "认证密码"
@ -466,6 +472,7 @@
"insecure" = "不安全"
"content" = "内容"
"statusCode" = "状态码"
"xForwarded" = "X-Forwarded 头"
[pages.inbounds.protocols]
"flow" = "Flow"