finalmask.ts 3.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. import { z } from 'zod';
  2. // FinalMask is xray-core's late-layer obfuscation wrapper applied AFTER
  3. // the network/security layers. It models per-type masks on TCP and UDP
  4. // plus optional QUIC tuning. The `settings` sub-object is polymorphic on
  5. // `type`; we model the wire-faithful shape with a permissive
  6. // record-of-unknown for `settings` and leave per-type tightening to
  7. // Step 6 — there are 8 UDP mask types plus 3 TCP mask types, each with
  8. // distinct setting fields, and modeling them all as discriminated unions
  9. // here would dwarf the rest of the stream module without buying anything
  10. // the safety net doesn't already cover.
  11. export const TcpMaskTypeSchema = z.enum(['fragment', 'sudoku', 'header-custom']);
  12. export type TcpMaskType = z.infer<typeof TcpMaskTypeSchema>;
  13. export const TcpMaskSchema = z.object({
  14. type: TcpMaskTypeSchema,
  15. settings: z.record(z.string(), z.unknown()).optional(),
  16. });
  17. export type TcpMask = z.infer<typeof TcpMaskSchema>;
  18. export const UdpMaskTypeSchema = z.enum([
  19. 'salamander',
  20. 'mkcp-legacy',
  21. 'header-custom',
  22. 'xdns',
  23. 'xicmp',
  24. 'noise',
  25. 'sudoku',
  26. 'realm',
  27. ]);
  28. export type UdpMaskType = z.infer<typeof UdpMaskTypeSchema>;
  29. export const UdpMaskSchema = z.object({
  30. type: UdpMaskTypeSchema,
  31. settings: z.record(z.string(), z.unknown()).optional(),
  32. });
  33. export type UdpMask = z.infer<typeof UdpMaskSchema>;
  34. export const QuicCongestionSchema = z.enum(['reno', 'bbr', 'brutal', 'force-brutal']);
  35. export type QuicCongestion = z.infer<typeof QuicCongestionSchema>;
  36. export const BbrProfileSchema = z.enum(['conservative', 'standard', 'aggressive']);
  37. export type BbrProfile = z.infer<typeof BbrProfileSchema>;
  38. // udpHop randomizes the QUIC port between a range every `interval` seconds
  39. // to dodge port-based blocking. Both fields are dash-range strings on the
  40. // wire (e.g. '20000-50000', '5-10'). preprocess coerces legacy DB rows
  41. // where interval was stored as a number (UI bug — see B19 in commit history).
  42. const StringRangeSchema = z.preprocess(
  43. (v) => (typeof v === 'number' ? String(v) : v),
  44. z.string(),
  45. );
  46. export const QuicUdpHopSchema = z.object({
  47. ports: StringRangeSchema.default('20000-50000'),
  48. interval: StringRangeSchema.default('5-10'),
  49. });
  50. export type QuicUdpHop = z.infer<typeof QuicUdpHopSchema>;
  51. export const QuicParamsSchema = z.object({
  52. congestion: QuicCongestionSchema.default('bbr'),
  53. bbrProfile: BbrProfileSchema.optional(),
  54. debug: z.boolean().optional(),
  55. brutalUp: z.string().optional(),
  56. brutalDown: z.string().optional(),
  57. udpHop: QuicUdpHopSchema.optional(),
  58. initStreamReceiveWindow: z.number().int().min(0).optional(),
  59. maxStreamReceiveWindow: z.number().int().min(0).optional(),
  60. initConnectionReceiveWindow: z.number().int().min(0).optional(),
  61. maxConnectionReceiveWindow: z.number().int().min(0).optional(),
  62. maxIdleTimeout: z.number().int().min(4).max(120).optional(),
  63. keepAlivePeriod: z.number().int().min(2).max(60).optional(),
  64. disablePathMTUDiscovery: z.boolean().optional(),
  65. maxIncomingStreams: z.number().int().min(8).optional(),
  66. });
  67. export type QuicParams = z.infer<typeof QuicParamsSchema>;
  68. // `tcp` and `udp` are omitted from the wire entirely when their arrays
  69. // are empty (legacy toJson() drops them). Our default([]) here mirrors
  70. // the parsed-in shape; the shadow harness already treats empty arrays as
  71. // equivalent to absence so both pipelines converge.
  72. export const FinalMaskStreamSettingsSchema = z.object({
  73. tcp: z.array(TcpMaskSchema).default([]),
  74. udp: z.array(UdpMaskSchema).default([]),
  75. quicParams: QuicParamsSchema.optional(),
  76. });
  77. export type FinalMaskStreamSettings = z.infer<typeof FinalMaskStreamSettingsSchema>;