feat(xray): add loopback sniffing and per-segment fragment masks

- Loopback outbound: add sniffing support (xray-core #6320)

- FinalMask fragment: support per-segment lengths/delays arrays with legacy length/delay migration (xray-core #6334)

- Consolidate sniffing into a shared SniffingFields component and the canonical SniffingSchema across inbound, VLESS reverse, and loopback
This commit is contained in:
MHSanaei
2026-06-23 13:24:16 +02:00
parent 42cd351e4e
commit 852b53db79
18 changed files with 343 additions and 255 deletions
@@ -8,11 +8,11 @@ import {
Radio,
Select,
Space,
Switch,
Tabs,
message,
} from 'antd';
import { FinalMaskForm } from '@/lib/xray/forms/transport';
import SniffingFields from '@/lib/xray/forms/SniffingFields';
import { JsonEditor } from '@/components/form';
import { Wireguard } from '@/utils';
import {
@@ -25,7 +25,6 @@ import {
OutboundFormBaseSchema,
type OutboundFormValues,
} from '@/schemas/forms/outbound-form';
import { SNIFFING_OPTION } from '@/schemas/primitives';
import {
canEnableReality,
canEnableStream,
@@ -412,70 +411,12 @@ export default function OutboundFormModal({
{() => {
const reverseTag = form.getFieldValue(['settings', 'reverseTag']);
if (!reverseTag) return null;
const sniff = (form.getFieldValue(['settings', 'reverseSniffing']) ?? {}) as {
enabled?: boolean;
};
return (
<>
<Form.Item
label={t('pages.xray.outboundForm.reverseSniffing')}
name={['settings', 'reverseSniffing', 'enabled']}
valuePropName="checked"
>
<Switch />
</Form.Item>
{sniff.enabled && (
<>
<Form.Item
wrapperCol={{ md: { span: 14, offset: 8 } }}
name={['settings', 'reverseSniffing', 'destOverride']}
>
<Select
mode="multiple"
className="sniffing-options"
options={Object.entries(SNIFFING_OPTION).map(([k, v]) => ({
value: v,
label: k,
}))}
/>
</Form.Item>
<Form.Item
label={t('pages.inbounds.sniffingMetadataOnly')}
name={['settings', 'reverseSniffing', 'metadataOnly']}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t('pages.inbounds.sniffingRouteOnly')}
name={['settings', 'reverseSniffing', 'routeOnly']}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t('pages.inbounds.sniffingIpsExcluded')}
name={['settings', 'reverseSniffing', 'ipsExcluded']}
>
<Select
mode="tags"
tokenSeparators={[',']}
placeholder="IP/CIDR/geoip:*"
/>
</Form.Item>
<Form.Item
label={t('pages.inbounds.sniffingDomainsExcluded')}
name={['settings', 'reverseSniffing', 'domainsExcluded']}
>
<Select
mode="tags"
tokenSeparators={[',']}
placeholder="domain:*"
/>
</Form.Item>
</>
)}
</>
<SniffingFields
name={['settings', 'reverseSniffing']}
form={form}
enableLabel={t('pages.xray.outboundForm.reverseSniffing')}
/>
);
}}
</Form.Item>
@@ -1,11 +1,23 @@
import { useTranslation } from 'react-i18next';
import { Form, Input } from 'antd';
import SniffingFields from '@/lib/xray/forms/SniffingFields';
export default function LoopbackFields() {
const { t } = useTranslation();
const form = Form.useFormInstance();
return (
<Form.Item label={t('pages.xray.outboundForm.inboundTag')} name={['settings', 'inboundTag']}>
<Input placeholder={t('pages.xray.outboundForm.inboundTagPlaceholder')} />
</Form.Item>
<>
<Form.Item label={t('pages.xray.outboundForm.inboundTag')} name={['settings', 'inboundTag']}>
<Input placeholder={t('pages.xray.outboundForm.inboundTagPlaceholder')} />
</Form.Item>
<SniffingFields
name={['settings', 'sniffing']}
form={form}
enableLabel={t('pages.inbounds.sniffingTab')}
/>
</>
);
}