singbox.ts 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
  1. import { domainWildCardToRegex } from './misc';
  2. import { isProbablyIpv4, isProbablyIpv6 } from './is-fast-ip';
  3. const unsupported = Symbol('unsupported');
  4. const toNumberTuple = <T extends string>(key: T, value: string): [T, number] | null => {
  5. const tmp = Number(value);
  6. return Number.isNaN(tmp) ? null : [key, tmp];
  7. };
  8. // https://sing-box.sagernet.org/configuration/rule-set/source-format/
  9. export const PROCESSOR: Record<string, ((raw: string, type: string, value: string) => [key: keyof SingboxHeadlessRule, value: Required<SingboxHeadlessRule>[keyof SingboxHeadlessRule][number]] | null) | typeof unsupported> = {
  10. DOMAIN: (_1, _2, value) => ['domain', value],
  11. 'DOMAIN-SUFFIX': (_1, _2, value) => ['domain_suffix', value],
  12. 'DOMAIN-KEYWORD': (_1, _2, value) => ['domain_keyword', value],
  13. 'DOMAIN-WILDCARD': (_1, _2, value) => ['domain_regex', domainWildCardToRegex(value)],
  14. GEOIP: unsupported,
  15. 'IP-CIDR': (_1, _2, value) => ['ip_cidr', value.endsWith(',no-resolve') ? value.slice(0, -11) : value],
  16. 'IP-CIDR6': (_1, _2, value) => ['ip_cidr', value.endsWith(',no-resolve') ? value.slice(0, -11) : value],
  17. 'IP-ASN': unsupported,
  18. 'SRC-IP': (_1, _2, value) => {
  19. if (value.includes('/')) {
  20. return ['source_ip_cidr', value];
  21. }
  22. if (isProbablyIpv4(value)) {
  23. return ['source_ip_cidr', value + '/32'];
  24. }
  25. if (isProbablyIpv6(value)) {
  26. return ['source_ip_cidr', value + '/128'];
  27. }
  28. return null;
  29. },
  30. 'SRC-IP-CIDR': (_1, _2, value) => ['source_ip_cidr', value.endsWith(',no-resolve') ? value.slice(0, -11) : value],
  31. 'SRC-PORT': (_1, _2, value) => toNumberTuple('source_port', value),
  32. 'DST-PORT': (_1, _2, value) => toNumberTuple('port', value),
  33. 'PROCESS-NAME': (_1, _2, value) => ((value.includes('/') || value.includes('\\')) ? ['process_path', value] : ['process_name', value]),
  34. // 'PROCESS-PATH': (_1, _2, value) => ['process_path', value],
  35. 'DEST-PORT': (_1, _2, value) => toNumberTuple('port', value),
  36. 'IN-PORT': (_1, _2, value) => toNumberTuple('source_port', value),
  37. 'URL-REGEX': unsupported,
  38. 'USER-AGENT': unsupported
  39. };
  40. interface SingboxHeadlessRule {
  41. domain?: string[],
  42. domain_suffix?: string[],
  43. domain_keyword?: string[],
  44. domain_regex?: string[],
  45. source_ip_cidr?: string[],
  46. ip_cidr?: string[],
  47. source_port?: number[],
  48. source_port_range?: string[],
  49. port?: number[],
  50. port_range?: string[],
  51. process_name?: string[],
  52. process_path?: string[]
  53. }
  54. export interface SingboxSourceFormat {
  55. version: 2 | number & {},
  56. rules: SingboxHeadlessRule[]
  57. }