mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-08-12 22:31:00 +00:00
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:
@@ -193,7 +193,6 @@ export default function InboundFormModal({
|
||||
// actually live on a node — otherwise the node address it would resolve to is
|
||||
// always empty. Offer it only then; `listen`/`custom` work for local inbounds.
|
||||
const nodeShareOptionAvailable = selectableNodes.length > 0 && isNodeEligible;
|
||||
const sniffingEnabled = Form.useWatch(['sniffing', 'enabled'], form) ?? false;
|
||||
const vlessEncryption = Form.useWatch(['settings', 'encryption'], form) ?? '';
|
||||
const ssMethod = Form.useWatch(['settings', 'method'], form);
|
||||
const isSSWith2022 = isSS2022({
|
||||
@@ -977,7 +976,7 @@ export default function InboundFormModal({
|
||||
</div>
|
||||
);
|
||||
|
||||
const sniffingTab = <SniffingTab sniffingEnabled={sniffingEnabled} />;
|
||||
const sniffingTab = <SniffingTab />;
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -1,67 +1,16 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Checkbox, Form, Select, Switch } from 'antd';
|
||||
import { Form } from 'antd';
|
||||
|
||||
import { SNIFFING_OPTION } from '@/schemas/primitives';
|
||||
import SniffingFields from '@/lib/xray/forms/SniffingFields';
|
||||
|
||||
export default function SniffingTab({ sniffingEnabled }: { sniffingEnabled: boolean }) {
|
||||
export default function SniffingTab() {
|
||||
const { t } = useTranslation();
|
||||
const form = Form.useFormInstance();
|
||||
return (
|
||||
<>
|
||||
<Form.Item name={['sniffing', 'enabled']} label={t('enable')} valuePropName="checked">
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
|
||||
{sniffingEnabled && (
|
||||
<>
|
||||
<Form.Item name={['sniffing', 'destOverride']} wrapperCol={{ span: 24 }}>
|
||||
<Checkbox.Group>
|
||||
{Object.entries(SNIFFING_OPTION).map(([key, value]) => (
|
||||
<Checkbox key={key} value={value}>{key}</Checkbox>
|
||||
))}
|
||||
</Checkbox.Group>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name={['sniffing', 'metadataOnly']}
|
||||
label={t('pages.inbounds.sniffingMetadataOnly')}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name={['sniffing', 'routeOnly']}
|
||||
label={t('pages.inbounds.sniffingRouteOnly')}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name={['sniffing', 'ipsExcluded']}
|
||||
label={t('pages.inbounds.sniffingIpsExcluded')}
|
||||
>
|
||||
<Select
|
||||
mode="tags"
|
||||
tokenSeparators={[',']}
|
||||
placeholder="IP/CIDR/geoip:*/ext:*"
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name={['sniffing', 'domainsExcluded']}
|
||||
label={t('pages.inbounds.sniffingDomainsExcluded')}
|
||||
>
|
||||
<Select
|
||||
mode="tags"
|
||||
tokenSeparators={[',']}
|
||||
placeholder="domain:*/ext:*"
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
<SniffingFields
|
||||
name={['sniffing']}
|
||||
form={form}
|
||||
enableLabel={t('enable')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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')}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user