| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121 |
- import picocolors from 'picocolors';
- import { domainWildCardToRegex } from './misc';
- import { isProbablyIpv4, isProbablyIpv6 } from './is-fast-ip';
- const unsupported = Symbol('unsupported');
- // https://sing-box.sagernet.org/configuration/rule-set/source-format/
- const PROCESSOR: Record<string, ((raw: string, type: string, value: string) => [key: keyof SingboxHeadlessRule, value: string] | null) | typeof unsupported> = {
- DOMAIN: (_1, _2, value) => ['domain', value],
- 'DOMAIN-SUFFIX': (_1, _2, value) => ['domain_suffix', value],
- 'DOMAIN-KEYWORD': (_1, _2, value) => ['domain_keyword', value],
- 'DOMAIN-WILDCARD': (_1, _2, value) => ['domain_regex', domainWildCardToRegex(value)],
- GEOIP: unsupported,
- 'IP-CIDR': (_1, _2, value) => ['ip_cidr', value.endsWith(',no-resolve') ? value.slice(0, -11) : value],
- 'IP-CIDR6': (_1, _2, value) => ['ip_cidr', value.endsWith(',no-resolve') ? value.slice(0, -11) : value],
- '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) => ['source_port', value],
- 'DST-PORT': (_1, _2, value) => ['port', value],
- 'PROCESS-NAME': (_1, _2, value) => ['process_name', value],
- 'PROCESS-PATH': (_1, _2, value) => ['process_path', value],
- 'DEST-PORT': (_1, _2, value) => ['port', value],
- 'IN-PORT': (_1, _2, value) => ['source_port', value],
- 'URL-REGEX': unsupported,
- 'USER-AGENT': unsupported
- };
- interface SingboxHeadlessRule {
- domain?: string[],
- domain_suffix?: string[],
- domain_keyword?: string[],
- domain_regex?: string[],
- source_ip_cidr?: string[],
- ip_cidr?: string[],
- source_port?: string[],
- source_port_range?: string[],
- port?: string[],
- port_range?: string[],
- process_name?: string[],
- process_path?: string[]
- }
- interface SingboxSourceFormat {
- version: 2 | number & {},
- rules: SingboxHeadlessRule[]
- }
- export const surgeRulesetToSingbox = (rules: string[] | Set<string>): SingboxSourceFormat => {
- const rule: SingboxHeadlessRule = Array.from(rules).reduce<SingboxHeadlessRule>((acc, cur) => {
- let buf = '';
- let type = '';
- let i = 0;
- for (const len = cur.length; i < len; i++) {
- if (cur[i] === ',') {
- type = buf;
- break;
- }
- buf += cur[i];
- }
- if (type === '') {
- return acc;
- }
- const value = cur.slice(i + 1);
- if (type in PROCESSOR) {
- const proc = PROCESSOR[type];
- if (proc !== unsupported) {
- const r = proc(cur, type, value);
- if (r) {
- const [k, v] = r;
- acc[k] ||= [];
- acc[k].push(v);
- }
- }
- } else {
- console.log(picocolors.yellow(`[sing-box] unknown rule type: ${type}`), cur);
- }
- return acc;
- }, {});
- return {
- version: 2,
- rules: [rule]
- };
- };
- export const surgeDomainsetToSingbox = (domainset: string[]) => {
- const rule = domainset.reduce((acc, cur) => {
- if (cur[0] === '.') {
- acc.domain_suffix.push(cur.slice(1));
- } else {
- acc.domain.push(cur);
- }
- return acc;
- }, { domain: [] as string[], domain_suffix: [] as string[] } satisfies SingboxHeadlessRule);
- return {
- version: 2,
- rules: [rule]
- };
- };
- export const ipCidrListToSingbox = (ipCidrList: string[]): SingboxSourceFormat => {
- return {
- version: 2,
- rules: [{
- ip_cidr: ipCidrList
- }]
- };
- };
|