feat: support latest Wireguard features from Xray-core (PRs #5643, #5833, #5850) (#5131)

* feat: support latest Wireguard features from Xray-core

Implements support for Xray-core PRs #5833, #5643, and #5850 for Wireguard Inbounds:
- Adds 'domainStrategy' and 'workers' to Wireguard inbound configuration.
- Enables the Stream Settings tab for Wireguard inbounds to configure 'sockopt' and 'finalmask', hiding the irrelevant 'network' transmission dropdown.
- Adds the 'randRange' field to the 'noise' UDP Finalmask obfuscation settings.

* fix

---------

Co-authored-by: Rqzbeh <Rqzbeh@example.com>
Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com>
This commit is contained in:
Rouzbeh†
2026-06-10 17:02:41 +02:00
committed by GitHub
parent f9b275dd23
commit 4002be4ade
8 changed files with 120 additions and 33 deletions
@@ -372,9 +372,15 @@ export default function InboundFormModal({
}],
},
});
} else if (next === Protocols.WIREGUARD) {
// Wireguard has no user-selectable transport: the listener is always
// UDP and only finalmask/sockopt from streamSettings apply. Drop the
// leftover network/transport slices so the stream tab doesn't render
// a TCP sub-form and the wire payload carries no dead tcpSettings.
form.setFieldValue('streamSettings', { security: 'none' });
} else {
const current = form.getFieldValue('streamSettings') as { network?: string } | undefined;
if (current?.network === 'hysteria') {
if (current?.network === 'hysteria' || !current?.network) {
form.setFieldValue('streamSettings', { network: 'tcp', security: 'none', tcpSettings: {} });
}
}
@@ -645,7 +651,7 @@ export default function InboundFormModal({
const streamTab = (
<>
{protocol !== Protocols.HYSTERIA && (
{protocol !== Protocols.HYSTERIA && protocol !== Protocols.WIREGUARD && (
<Form.Item label={t('transmission')} name={['streamSettings', 'network']}>
<Select
style={{ width: '75%' }}
@@ -683,7 +689,10 @@ export default function InboundFormModal({
{network === 'kcp' && <KcpForm />}
<ExternalProxyForm toggleExternalProxy={toggleExternalProxy} />
{/* externalProxy only feeds client share links, and wireguard's
per-peer .conf fanout resolves its host elsewhere — the section
would be dead weight on a wireguard inbound. */}
{protocol !== Protocols.WIREGUARD && <ExternalProxyForm toggleExternalProxy={toggleExternalProxy} />}
<SockoptForm toggleSockopt={toggleSockopt} />
@@ -897,7 +906,11 @@ export default function InboundFormModal({
...(streamEnabled
? [
{ key: 'stream', label: t('pages.inbounds.streamTab'), children: streamTab, forceRender: true },
{ key: 'security', label: t('pages.inbounds.securityTab'), children: securityTab, forceRender: true },
// Wireguard can't do TLS/Reality (canEnableTls is false), so
// the security tab would only show a fully disabled radio.
...(protocol !== Protocols.WIREGUARD
? [{ key: 'security', label: t('pages.inbounds.securityTab'), children: securityTab, forceRender: true }]
: []),
]
: []),
...(sniffingSupported
@@ -1,5 +1,5 @@
import { useTranslation } from 'react-i18next';
import { Button, Divider, Form, Input, InputNumber, Space, Switch } from 'antd';
import { Button, Divider, Form, Input, InputNumber, Select, Space, Switch } from 'antd';
import { MinusOutlined, PlusOutlined, ReloadOutlined } from '@ant-design/icons';
import { Wireguard } from '@/utils';
@@ -62,6 +62,21 @@ export default function WireguardFields({ wgPubKey, regenInboundWg, regenWgPeerK
>
<Switch />
</Form.Item>
<Form.Item name={['settings', 'workers']} label='Workers'>
<InputNumber min={1} />
</Form.Item>
<Form.Item name={['settings', 'domainStrategy']} label={t('pages.xray.wireguard.domainStrategy')}>
<Select
allowClear
options={[
{ value: 'ForceIP', label: 'ForceIP' },
{ value: 'ForceIPv4', label: 'ForceIPv4' },
{ value: 'ForceIPv4v6', label: 'ForceIPv4v6' },
{ value: 'ForceIPv6', label: 'ForceIPv6' },
{ value: 'ForceIPv6v4', label: 'ForceIPv6v4' },
]}
/>
</Form.Item>
<Form.List name={['settings', 'peers']}>
{(fields, { add, remove }) => (
<>
@@ -156,10 +156,17 @@ export default function OutboundFormModal({
useEffect(() => {
if (!streamAllowed) return;
// Wireguard dials its own UDP — only finalmask/sockopt apply, never a
// transport. Don't seed network 'tcp'; clear a leftover one (from a
// protocol switch) so the transmission/security blocks stay hidden.
if (protocol === 'wireguard') {
if (network) form.setFieldValue('streamSettings', { security: 'none' });
return;
}
if (network) return;
form.setFieldValue('streamSettings', { ...newStreamSlice('tcp'), security: 'none' });
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [streamAllowed, network]);
}, [streamAllowed, network, protocol]);
useEffect(() => {
if (protocol !== 'hysteria') return;
@@ -565,7 +572,7 @@ export default function OutboundFormModal({
{security === 'reality' && realityAllowed && <RealityForm />}
{((streamAllowed && network) || !streamAllowed) && (
{((streamAllowed && network) || !streamAllowed || protocol === 'wireguard') && (
<SockoptForm form={form} outboundTags={existingTags} />
)}