Files
3x-ui/frontend/src/schemas/protocols/outbound/index.ts
T
Rouzbeh† d5ab84e8d5 feat(amneziawg): add AmneziaWG as an outbound protocol (#6320)
* feat(amneziawg): add AmneziaWG as an outbound protocol

- AmneziaWG outbound protocol end-to-end: config schema, socks bridge, netstack, panel UI
- Route amneziawg outbounds to HTTP probe in TCP mode (backend + frontend classifiers) with pinning test
- Add 2-minute idle read deadline to pumpUDPEgress to reap idle egress sessions
- Require SOCKS5 username/password auth on the egress server (reject NO-AUTH with 0xFF) with test
- Bound the egress TCP tunnel dial with portForwardDialTimeout (10s), matching portfwd.go
- Resolve UDP domain targets off the association's reader loop via deliverUDPDatagram; race-safe getOrDial starts the reply pump at session creation; client passed by value into resolver goroutines (pinned by TestEgressUDPDatagramDomainInterleavedClients)
- Reconcile early-returns on an empty desired set and closes the egress listener; EgressBasePort (64900) is reserved against local inbound port conflicts like the internal API port, with pinning tests for both the port reservation (TestCheckPortConflict_EgressPortBlockedLocal) and the Reconcile empty-desired Close/Listen lifecycle (TestOutboundManagerReconcileEmptyDesiredClosesEgress)
- Eliminate acceptLoop shutdown race by validating listener != nil and registering to tracked under s.mu before wg.Add; bound pre-auth handshake with deadline (pinned by TestEgressServerCloseDuringConcurrentAccepts)
- Support AAAA and dual-stack domain resolution in tunnel DNS resolver with v6 default fallback (DefaultTunnelDNSServerV6); add DNS field to frontend protocol form; avoid unneeded cache flushes on unchanged SetStack ticks

* fix(amneziawg): resolve IPv6-only DNS default fallback and validate required keys

- Default to IPv6 tunnel DNS on IPv6-only outbounds with blank dns
- Require non-empty secretKey and peer publicKey in ValidateAmneziaWGOutbound
- Add end-to-end IPv6 tunnel domain resolution test and test empty key rejection
- Trim comment blocks exceeding 2 lines across modified files
- Fix Storybook test execution on environments with POSIX locale

Co-Authored-By: Claude Code <noreply@anthropic.com>

---------

Co-authored-by: rqzbeh <rqzbeh@users.noreply.github.com>
Co-authored-by: Claude Code <noreply@anthropic.com>
Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com>
2026-09-10 14:50:48 +02:00

47 lines
2.4 KiB
TypeScript

import { z } from 'zod';
import { BlackholeOutboundSettingsSchema } from './blackhole';
import { AmneziaWGOutboundSettingsSchema } from './amneziawg';
import { DNSOutboundSettingsSchema } from './dns';
import { FreedomOutboundSettingsSchema } from './freedom';
import { HttpOutboundSettingsSchema } from './http';
import { HysteriaOutboundSettingsSchema } from './hysteria';
import { LoopbackOutboundSettingsSchema } from './loopback';
import { ShadowsocksOutboundSettingsSchema } from './shadowsocks';
import { SocksOutboundSettingsSchema } from './socks';
import { TrojanOutboundSettingsSchema } from './trojan';
import { VlessOutboundSettingsSchema } from './vless';
import { VmessOutboundSettingsSchema } from './vmess';
import { WireguardOutboundSettingsSchema } from './wireguard';
export * from './blackhole';
export * from './amneziawg';
export * from './dns';
export * from './freedom';
export * from './http';
export * from './hysteria';
export * from './loopback';
export * from './shadowsocks';
export * from './socks';
export * from './trojan';
export * from './vless';
export * from './vmess';
export * from './wireguard';
export const OutboundSettingsSchema = z.discriminatedUnion('protocol', [
z.object({ protocol: z.literal('vmess'), settings: VmessOutboundSettingsSchema }),
z.object({ protocol: z.literal('vless'), settings: VlessOutboundSettingsSchema }),
z.object({ protocol: z.literal('trojan'), settings: TrojanOutboundSettingsSchema }),
z.object({ protocol: z.literal('shadowsocks'), settings: ShadowsocksOutboundSettingsSchema }),
z.object({ protocol: z.literal('wireguard'), settings: WireguardOutboundSettingsSchema }),
z.object({ protocol: z.literal('amneziawg'), settings: AmneziaWGOutboundSettingsSchema }),
z.object({ protocol: z.literal('hysteria'), settings: HysteriaOutboundSettingsSchema }),
z.object({ protocol: z.literal('http'), settings: HttpOutboundSettingsSchema }),
z.object({ protocol: z.literal('socks'), settings: SocksOutboundSettingsSchema }),
z.object({ protocol: z.literal('freedom'), settings: FreedomOutboundSettingsSchema }),
z.object({ protocol: z.literal('blackhole'), settings: BlackholeOutboundSettingsSchema }),
z.object({ protocol: z.literal('dns'), settings: DNSOutboundSettingsSchema }),
z.object({ protocol: z.literal('loopback'), settings: LoopbackOutboundSettingsSchema }),
]);
export type OutboundSettings = z.infer<typeof OutboundSettingsSchema>;