From c71ed5a9d3a86bea282b680cce5476c9b9683fe5 Mon Sep 17 00:00:00 2001 From: SukkaW Date: Thu, 10 Oct 2024 21:52:37 +0800 Subject: [PATCH] Feat: implement `SRC-IP`, `SRC-PORT`, `DEST-PORT` --- Build/lib/clash.ts | 29 ------------------------ Build/lib/rules/base.ts | 16 ++++++++++--- Build/lib/rules/ruleset.ts | 46 +++++++++++++++++++++++++++++++++++++- Build/lib/singbox.ts | 26 --------------------- 4 files changed, 58 insertions(+), 59 deletions(-) diff --git a/Build/lib/clash.ts b/Build/lib/clash.ts index 924a8f5d..2d407347 100644 --- a/Build/lib/clash.ts +++ b/Build/lib/clash.ts @@ -1,36 +1,7 @@ -import { domainWildCardToRegex, identity } from './misc'; -import { isProbablyIpv4, isProbablyIpv6 } from './is-fast-ip'; - const unsupported = Symbol('unsupported'); // https://dreamacro.github.io/clash/configuration/rules.html export const PROCESSOR: Record string) | typeof unsupported> = { - DOMAIN: identity, - 'DOMAIN-SUFFIX': identity, - 'DOMAIN-KEYWORD': identity, - 'DOMAIN-WILDCARD': (_raw, _type, value) => `DOMAIN-REGEX,${domainWildCardToRegex(value)}`, - GEOIP: identity, - 'IP-CIDR': identity, - 'IP-CIDR6': identity, - 'IP-ASN': identity, - 'SRC-IP': (_raw, _type, value) => { - if (value.includes('/')) { - return `SRC-IP-CIDR,${value}`; - } - if (isProbablyIpv4(value)) { - return `SRC-IP-CIDR,${value}/32`; - } - if (isProbablyIpv6(value)) { - return `SRC-IP-CIDR6,${value}/128`; - } - return ''; - }, - 'SRC-IP-CIDR': identity, - 'SRC-PORT': identity, - 'DST-PORT': identity, - 'PROCESS-NAME': (_raw, _type, value) => ((value.includes('/') || value.includes('\\')) ? `PROCESS-PATH,${value}` : `PROCESS-NAME,${value}`), - 'DEST-PORT': (_raw, _type, value) => `DST-PORT,${value}`, - 'IN-PORT': (_raw, _type, value) => `SRC-PORT,${value}`, 'URL-REGEX': unsupported, 'USER-AGENT': unsupported }; diff --git a/Build/lib/rules/base.ts b/Build/lib/rules/base.ts index 8e88bb35..ab51c3b1 100644 --- a/Build/lib/rules/base.ts +++ b/Build/lib/rules/base.ts @@ -27,9 +27,10 @@ export abstract class RuleOutput { protected ipcidr6NoResolve = new Set(); protected geoip = new Set(); protected groipNoResolve = new Set(); - // TODO: add sourceIpcidr - // TODO: add sourcePort - // TODO: add port + + protected sourceIpOrCidr = new Set(); + protected sourcePort = new Set(); + protected destPort = new Set(); protected otherRules: string[] = []; protected abstract type: 'domainset' | 'non_ip' | 'ip'; @@ -188,6 +189,15 @@ export abstract class RuleOutput { case 'GEOIP': (arg === 'no-resolve' ? this.groipNoResolve : this.geoip).add(value); break; + case 'SRC-IP': + this.sourceIpOrCidr.add(value); + break; + case 'SRC-PORT': + this.sourcePort.add(value); + break; + case 'DEST-PORT': + this.destPort.add(value); + break; default: this.otherRules.push(line); break; diff --git a/Build/lib/rules/ruleset.ts b/Build/lib/rules/ruleset.ts index ebacfde6..648eab96 100644 --- a/Build/lib/rules/ruleset.ts +++ b/Build/lib/rules/ruleset.ts @@ -8,7 +8,7 @@ import { sortDomains } from '../stable-sort-domain'; import { RuleOutput } from './base'; import picocolors from 'picocolors'; import { normalizeDomain } from '../normalize-domain'; -import { isProbablyIpv4 } from '../is-fast-ip'; +import { isProbablyIpv4, isProbablyIpv6 } from '../is-fast-ip'; type Preprocessed = [domain: string[], domainSuffix: string[], sortedDomainRules: string[]]; @@ -52,6 +52,10 @@ export class RulesetOutput extends RuleOutput { appendArrayFromSet(results, this.processName, i => `PROCESS-NAME,${i}`); appendArrayFromSet(results, this.processPath, i => `PROCESS-NAME,${i}`); + appendArrayFromSet(results, this.sourceIpOrCidr, i => `SRC-IP,${i}`); + appendArrayFromSet(results, this.sourcePort, i => `SRC-PORT,${i}`); + appendArrayFromSet(results, this.destPort, i => `DEST-PORT,${i}`); + appendArrayInPlace(results, this.otherRules); appendArrayFromSet(results, this.urlRegex, i => `URL-REGEX,${i}`); @@ -86,6 +90,21 @@ export class RulesetOutput extends RuleOutput { appendArrayFromSet(results, this.processName, i => `PROCESS-NAME,${i}`); appendArrayFromSet(results, this.processPath, i => `PROCESS-PATH,${i}`); + appendArrayFromSet(results, this.sourceIpOrCidr, value => { + if (value.includes('/')) { + return `SRC-IP-CIDR,${value}`; + } + if (isProbablyIpv4(value)) { + return `SRC-IP-CIDR,${value}/32`; + } + if (isProbablyIpv6(value)) { + return `SRC-IP-CIDR6,${value}/128`; + } + return ''; + }); + appendArrayFromSet(results, this.sourcePort, i => `SRC-PORT,${i}`); + appendArrayFromSet(results, this.destPort, i => `DST-PORT,${i}`); + // appendArrayInPlace(results, this.otherRules); appendArrayInPlace( @@ -127,6 +146,31 @@ export class RulesetOutput extends RuleOutput { domain_keyword: Array.from(this.domainKeywords), domain_regex: Array.from(this.domainWildcard).map(RuleOutput.domainWildCardToRegex), ip_cidr, + source_ip_cidr: [...this.sourceIpOrCidr].reduce((acc, cur) => { + if (cur.includes('/')) { + acc.push(cur); + } else if (isProbablyIpv4(cur)) { + acc.push(cur + '/32'); + } else if (isProbablyIpv6(cur)) { + acc.push(cur + '/128'); + } + + return acc; + }, []), + source_port: [...this.sourcePort].reduce((acc, cur) => { + const tmp = Number(cur); + if (!Number.isNaN(tmp)) { + acc.push(tmp); + } + return acc; + }, []), + port: [...this.destPort].reduce((acc, cur) => { + const tmp = Number(cur); + if (!Number.isNaN(tmp)) { + acc.push(tmp); + } + return acc; + }, []), process_name: Array.from(this.processName), process_path: Array.from(this.processPath) }] diff --git a/Build/lib/singbox.ts b/Build/lib/singbox.ts index 5b1edf22..caf48b39 100644 --- a/Build/lib/singbox.ts +++ b/Build/lib/singbox.ts @@ -1,34 +1,8 @@ -import { isProbablyIpv4, isProbablyIpv6 } from './is-fast-ip'; - const unsupported = Symbol('unsupported'); -function toNumberTuple(key: T, value: string): [T, number] | null { - const tmp = Number(value); - return Number.isNaN(tmp) ? null : [key, tmp]; -} - // https://sing-box.sagernet.org/configuration/rule-set/source-format/ export const PROCESSOR: Record [key: keyof SingboxHeadlessRule, value: Required[keyof SingboxHeadlessRule][number]] | null) | typeof unsupported> = { 'IP-ASN': unsupported, - 'SRC-IP': (_1, _2, value) => { - if (value.includes('/')) { - return ['source_ip_cidr', value]; - } - if (isProbablyIpv4(value)) { - return ['source_ip_cidr', value + '/32']; - } - if (isProbablyIpv6(value)) { - return ['source_ip_cidr', value + '/128']; - } - return null; - }, - 'SRC-IP-CIDR': (_1, _2, value) => ['source_ip_cidr', value.endsWith(',no-resolve') ? value.slice(0, -11) : value], - 'SRC-PORT': (_1, _2, value) => toNumberTuple('source_port', value), - 'DST-PORT': (_1, _2, value) => toNumberTuple('port', value), - 'PROCESS-NAME': (_1, _2, value) => ((value.includes('/') || value.includes('\\')) ? ['process_path', value] : ['process_name', value]), - // 'PROCESS-PATH': (_1, _2, value) => ['process_path', value], - 'DEST-PORT': (_1, _2, value) => toNumberTuple('port', value), - 'IN-PORT': (_1, _2, value) => toNumberTuple('source_port', value), 'URL-REGEX': unsupported, 'USER-AGENT': unsupported };