mirror of
https://github.com/SukkaW/Surge.git
synced 2025-12-12 17:20:35 +08:00
Feat: implement SRC-IP, SRC-PORT, DEST-PORT
This commit is contained in:
parent
e40979e50e
commit
c71ed5a9d3
@ -1,36 +1,7 @@
|
|||||||
import { domainWildCardToRegex, identity } from './misc';
|
|
||||||
import { isProbablyIpv4, isProbablyIpv6 } from './is-fast-ip';
|
|
||||||
|
|
||||||
const unsupported = Symbol('unsupported');
|
const unsupported = Symbol('unsupported');
|
||||||
|
|
||||||
// https://dreamacro.github.io/clash/configuration/rules.html
|
// https://dreamacro.github.io/clash/configuration/rules.html
|
||||||
export const PROCESSOR: Record<string, ((raw: string, type: string, value: string) => string) | typeof unsupported> = {
|
export const PROCESSOR: Record<string, ((raw: string, type: string, value: string) => 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,
|
'URL-REGEX': unsupported,
|
||||||
'USER-AGENT': unsupported
|
'USER-AGENT': unsupported
|
||||||
};
|
};
|
||||||
|
|||||||
@ -27,9 +27,10 @@ export abstract class RuleOutput<TPreprocessed = unknown> {
|
|||||||
protected ipcidr6NoResolve = new Set<string>();
|
protected ipcidr6NoResolve = new Set<string>();
|
||||||
protected geoip = new Set<string>();
|
protected geoip = new Set<string>();
|
||||||
protected groipNoResolve = new Set<string>();
|
protected groipNoResolve = new Set<string>();
|
||||||
// TODO: add sourceIpcidr
|
|
||||||
// TODO: add sourcePort
|
protected sourceIpOrCidr = new Set<string>();
|
||||||
// TODO: add port
|
protected sourcePort = new Set<string>();
|
||||||
|
protected destPort = new Set<string>();
|
||||||
|
|
||||||
protected otherRules: string[] = [];
|
protected otherRules: string[] = [];
|
||||||
protected abstract type: 'domainset' | 'non_ip' | 'ip';
|
protected abstract type: 'domainset' | 'non_ip' | 'ip';
|
||||||
@ -188,6 +189,15 @@ export abstract class RuleOutput<TPreprocessed = unknown> {
|
|||||||
case 'GEOIP':
|
case 'GEOIP':
|
||||||
(arg === 'no-resolve' ? this.groipNoResolve : this.geoip).add(value);
|
(arg === 'no-resolve' ? this.groipNoResolve : this.geoip).add(value);
|
||||||
break;
|
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:
|
default:
|
||||||
this.otherRules.push(line);
|
this.otherRules.push(line);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import { sortDomains } from '../stable-sort-domain';
|
|||||||
import { RuleOutput } from './base';
|
import { RuleOutput } from './base';
|
||||||
import picocolors from 'picocolors';
|
import picocolors from 'picocolors';
|
||||||
import { normalizeDomain } from '../normalize-domain';
|
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[]];
|
type Preprocessed = [domain: string[], domainSuffix: string[], sortedDomainRules: string[]];
|
||||||
|
|
||||||
@ -52,6 +52,10 @@ export class RulesetOutput extends RuleOutput<Preprocessed> {
|
|||||||
appendArrayFromSet(results, this.processName, i => `PROCESS-NAME,${i}`);
|
appendArrayFromSet(results, this.processName, i => `PROCESS-NAME,${i}`);
|
||||||
appendArrayFromSet(results, this.processPath, 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);
|
appendArrayInPlace(results, this.otherRules);
|
||||||
|
|
||||||
appendArrayFromSet(results, this.urlRegex, i => `URL-REGEX,${i}`);
|
appendArrayFromSet(results, this.urlRegex, i => `URL-REGEX,${i}`);
|
||||||
@ -86,6 +90,21 @@ export class RulesetOutput extends RuleOutput<Preprocessed> {
|
|||||||
appendArrayFromSet(results, this.processName, i => `PROCESS-NAME,${i}`);
|
appendArrayFromSet(results, this.processName, i => `PROCESS-NAME,${i}`);
|
||||||
appendArrayFromSet(results, this.processPath, i => `PROCESS-PATH,${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(results, this.otherRules);
|
||||||
|
|
||||||
appendArrayInPlace(
|
appendArrayInPlace(
|
||||||
@ -127,6 +146,31 @@ export class RulesetOutput extends RuleOutput<Preprocessed> {
|
|||||||
domain_keyword: Array.from(this.domainKeywords),
|
domain_keyword: Array.from(this.domainKeywords),
|
||||||
domain_regex: Array.from(this.domainWildcard).map(RuleOutput.domainWildCardToRegex),
|
domain_regex: Array.from(this.domainWildcard).map(RuleOutput.domainWildCardToRegex),
|
||||||
ip_cidr,
|
ip_cidr,
|
||||||
|
source_ip_cidr: [...this.sourceIpOrCidr].reduce<string[]>((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<number[]>((acc, cur) => {
|
||||||
|
const tmp = Number(cur);
|
||||||
|
if (!Number.isNaN(tmp)) {
|
||||||
|
acc.push(tmp);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, []),
|
||||||
|
port: [...this.destPort].reduce<number[]>((acc, cur) => {
|
||||||
|
const tmp = Number(cur);
|
||||||
|
if (!Number.isNaN(tmp)) {
|
||||||
|
acc.push(tmp);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, []),
|
||||||
process_name: Array.from(this.processName),
|
process_name: Array.from(this.processName),
|
||||||
process_path: Array.from(this.processPath)
|
process_path: Array.from(this.processPath)
|
||||||
}]
|
}]
|
||||||
|
|||||||
@ -1,34 +1,8 @@
|
|||||||
import { isProbablyIpv4, isProbablyIpv6 } from './is-fast-ip';
|
|
||||||
|
|
||||||
const unsupported = Symbol('unsupported');
|
const unsupported = Symbol('unsupported');
|
||||||
|
|
||||||
function toNumberTuple<T extends string>(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/
|
// https://sing-box.sagernet.org/configuration/rule-set/source-format/
|
||||||
export const PROCESSOR: Record<string, ((raw: string, type: string, value: string) => [key: keyof SingboxHeadlessRule, value: Required<SingboxHeadlessRule>[keyof SingboxHeadlessRule][number]] | null) | typeof unsupported> = {
|
export const PROCESSOR: Record<string, ((raw: string, type: string, value: string) => [key: keyof SingboxHeadlessRule, value: Required<SingboxHeadlessRule>[keyof SingboxHeadlessRule][number]] | null) | typeof unsupported> = {
|
||||||
'IP-ASN': 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,
|
'URL-REGEX': unsupported,
|
||||||
'USER-AGENT': unsupported
|
'USER-AGENT': unsupported
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user