Browse Source

Feat: `sukka_local_dns_mapping.sgmodule` now uses new rule-set syntax

SukkaW 1 year ago
parent
commit
6858cd63b4

+ 58 - 18
Build/build-domestic-direct-lan-ruleset-dns-mapping-module.ts

@@ -10,7 +10,7 @@ import { SHARED_DESCRIPTION } from './constants/description';
 import { createMemoizedPromise } from './lib/memo-promise';
 import { createMemoizedPromise } from './lib/memo-promise';
 import * as yaml from 'yaml';
 import * as yaml from 'yaml';
 import { appendArrayInPlace } from './lib/append-array-in-place';
 import { appendArrayInPlace } from './lib/append-array-in-place';
-import { OUTPUT_INTERNAL_DIR, OUTPUT_MODULES_DIR, SOURCE_DIR } from './constants/dir';
+import { OUTPUT_INTERNAL_DIR, OUTPUT_MODULES_DIR, OUTPUT_MODULES_RULES_DIR, SOURCE_DIR } from './constants/dir';
 import { RulesetOutput } from './lib/create-file';
 import { RulesetOutput } from './lib/create-file';
 
 
 export function createGetDnsMappingRule(allowWildcard: boolean) {
 export function createGetDnsMappingRule(allowWildcard: boolean) {
@@ -78,7 +78,7 @@ export const getDomesticAndDirectDomainsRulesetPromise = createMemoizedPromise(a
 export const buildDomesticRuleset = task(require.main === module, __filename)(async (span) => {
 export const buildDomesticRuleset = task(require.main === module, __filename)(async (span) => {
   const [domestics, directs, lans] = await getDomesticAndDirectDomainsRulesetPromise();
   const [domestics, directs, lans] = await getDomesticAndDirectDomainsRulesetPromise();
 
 
-  const dataset: DNSMapping[] = ([DOH_BOOTSTRAP, DOMESTICS, DIRECTS] as const).flatMap(Object.values);
+  const dataset: Array<[name: string, DNSMapping]> = ([DOH_BOOTSTRAP, DOMESTICS, DIRECTS, LAN] as const).flatMap(Object.entries);
 
 
   return Promise.all([
   return Promise.all([
     new RulesetOutput(span, 'domestic', 'non_ip')
     new RulesetOutput(span, 'domestic', 'non_ip')
@@ -108,6 +108,41 @@ export const buildDomesticRuleset = task(require.main === module, __filename)(as
       ])
       ])
       .addFromRuleset(lans)
       .addFromRuleset(lans)
       .write(),
       .write(),
+
+    ...dataset.map(([name, { ruleset, domains }]) => {
+      if (!ruleset) {
+        return;
+      }
+
+      const output = new RulesetOutput(span, name.toLowerCase(), 'sukka_local_dns_mapping').withTitle(`Sukka's Ruleset - Local DNS Mapping (${name})`).withDescription([
+        ...SHARED_DESCRIPTION,
+        '',
+        'This is an internal rule that is only referenced by sukka_local_dns_mapping.sgmodule',
+        'Do not use this file in your Rule section, all rules are included in non_ip/domestic.conf already.'
+      ]);
+
+      domains.forEach((domain) => {
+        switch (domain[0]) {
+          case '$':
+            output.addDomain(domain.slice(1));
+            break;
+          case '+':
+            output.addDomainSuffix(domain.slice(1));
+            break;
+          default:
+            output.addDomainSuffix(domain);
+            break;
+        }
+      });
+
+      return output.write({
+        surge: true,
+        clash: false,
+        singbox: false,
+        surgeDir: OUTPUT_MODULES_RULES_DIR
+      });
+    }),
+
     compareAndWriteFile(
     compareAndWriteFile(
       span,
       span,
       [
       [
@@ -119,26 +154,31 @@ export const buildDomesticRuleset = task(require.main === module, __filename)(as
           // I use an object to deduplicate the domains
           // I use an object to deduplicate the domains
           // Otherwise I could just construct an array directly
           // Otherwise I could just construct an array directly
           dataset.reduce<Record<string, string>>((acc, cur) => {
           dataset.reduce<Record<string, string>>((acc, cur) => {
-            const { domains, dns, hosts } = cur;
+            const ruleset_name = cur[0].toLowerCase();
+            const { domains, dns, hosts, ruleset } = cur[1];
 
 
             Object.entries(hosts).forEach(([dns, ips]) => {
             Object.entries(hosts).forEach(([dns, ips]) => {
               acc[dns] ||= ips.join(', ');
               acc[dns] ||= ips.join(', ');
             });
             });
 
 
-            domains.forEach((domain) => {
-              switch (domain[0]) {
-                case '$':
-                  acc[domain.slice(1)] ||= `server:${dns}`;
-                  break;
-                case '+':
-                  acc[`*.${domain.slice(1)}`] ||= `server:${dns}`;
-                  break;
-                default:
-                  acc[domain] ||= `server:${dns}`;
-                  acc[`*.${domain}`] ||= `server:${dns}`;
-                  break;
-              }
-            });
+            if (ruleset) {
+              acc[`RULE-SET:https://ruleset.skk.moe/Modules/Rules/sukka_local_dns_mapping/${ruleset_name}.conf`] ||= `server:${dns}`;
+            } else {
+              domains.forEach((domain) => {
+                switch (domain[0]) {
+                  case '$':
+                    acc[domain.slice(1)] ||= `server:${dns}`;
+                    break;
+                  case '+':
+                    acc[`*.${domain.slice(1)}`] ||= `server:${dns}`;
+                    break;
+                  default:
+                    acc[domain] ||= `server:${dns}`;
+                    acc[`*.${domain}`] ||= `server:${dns}`;
+                    break;
+                }
+              });
+            }
 
 
             return acc;
             return acc;
           }, {})
           }, {})
@@ -153,7 +193,7 @@ export const buildDomesticRuleset = task(require.main === module, __filename)(as
           dns: { 'nameserver-policy': Record<string, string | string[]> },
           dns: { 'nameserver-policy': Record<string, string | string[]> },
           hosts: Record<string, string>
           hosts: Record<string, string>
         }>((acc, cur) => {
         }>((acc, cur) => {
-          const { domains, dns, ...rest } = cur;
+          const { domains, dns, ...rest } = cur[1];
           domains.forEach((domain) => {
           domains.forEach((domain) => {
             let domainWildcard = domain;
             let domainWildcard = domain;
             if (domain[0] === '$') {
             if (domain[0] === '$') {

+ 1 - 0
Build/constants/dir.ts

@@ -11,5 +11,6 @@ export const OUTPUT_SURGE_DIR = path.join(PUBLIC_DIR, 'List');
 export const OUTPUT_CLASH_DIR = path.resolve(PUBLIC_DIR, 'Clash');
 export const OUTPUT_CLASH_DIR = path.resolve(PUBLIC_DIR, 'Clash');
 export const OUTPUT_SINGBOX_DIR = path.resolve(PUBLIC_DIR, 'sing-box');
 export const OUTPUT_SINGBOX_DIR = path.resolve(PUBLIC_DIR, 'sing-box');
 export const OUTPUT_MODULES_DIR = path.resolve(PUBLIC_DIR, 'Modules');
 export const OUTPUT_MODULES_DIR = path.resolve(PUBLIC_DIR, 'Modules');
+export const OUTPUT_MODULES_RULES_DIR = path.resolve(OUTPUT_MODULES_DIR, 'Rules');
 export const OUTPUT_INTERNAL_DIR = path.resolve(PUBLIC_DIR, 'Internal');
 export const OUTPUT_INTERNAL_DIR = path.resolve(PUBLIC_DIR, 'Internal');
 export const OUTPUT_MOCK_DIR = path.resolve(PUBLIC_DIR, 'Mock');
 export const OUTPUT_MOCK_DIR = path.resolve(PUBLIC_DIR, 'Mock');

+ 33 - 13
Build/lib/rules/base.ts

@@ -34,7 +34,7 @@ export abstract class RuleOutput<TPreprocessed = unknown> {
   protected destPort = 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' | (string & {});
 
 
   private pendingPromise: Promise<any> | null = null;
   private pendingPromise: Promise<any> | null = null;
 
 
@@ -295,13 +295,29 @@ export abstract class RuleOutput<TPreprocessed = unknown> {
     );
     );
   }
   }
 
 
-  write(): Promise<void> {
+  write({
+    surge = true,
+    clash = true,
+    singbox = true,
+    surgeDir = OUTPUT_SURGE_DIR,
+    clashDir = OUTPUT_CLASH_DIR,
+    singboxDir = OUTPUT_SINGBOX_DIR
+  }: {
+    surge?: boolean,
+    clash?: boolean,
+    singbox?: boolean,
+    surgeDir?: string,
+    clashDir?: string,
+    singboxDir?: string
+  } = {}): Promise<void> {
     return this.done().then(() => this.span.traceChildAsync('write all', async () => {
     return this.done().then(() => this.span.traceChildAsync('write all', async () => {
       invariant(this.title, 'Missing title');
       invariant(this.title, 'Missing title');
       invariant(this.description, 'Missing description');
       invariant(this.description, 'Missing description');
 
 
-      const promises = [
-        compareAndWriteFile(
+      const promises: Array<Promise<void>> = [];
+
+      if (surge) {
+        promises.push(compareAndWriteFile(
           this.span,
           this.span,
           withBannerArray(
           withBannerArray(
             this.title,
             this.title,
@@ -309,9 +325,11 @@ export abstract class RuleOutput<TPreprocessed = unknown> {
             this.date,
             this.date,
             this.surge()
             this.surge()
           ),
           ),
-          path.join(OUTPUT_SURGE_DIR, this.type, this.id + '.conf')
-        ),
-        compareAndWriteFile(
+          path.join(surgeDir, this.type, this.id + '.conf')
+        ));
+      }
+      if (clash) {
+        promises.push(compareAndWriteFile(
           this.span,
           this.span,
           withBannerArray(
           withBannerArray(
             this.title,
             this.title,
@@ -319,14 +337,16 @@ export abstract class RuleOutput<TPreprocessed = unknown> {
             this.date,
             this.date,
             this.clash()
             this.clash()
           ),
           ),
-          path.join(OUTPUT_CLASH_DIR, this.type, this.id + '.txt')
-        ),
-        compareAndWriteFile(
+          path.join(clashDir, this.type, this.id + '.txt')
+        ));
+      }
+      if (singbox) {
+        promises.push(compareAndWriteFile(
           this.span,
           this.span,
           this.singbox(),
           this.singbox(),
-          path.join(OUTPUT_SINGBOX_DIR, this.type, this.id + '.json')
-        )
-      ];
+          path.join(singboxDir, this.type, this.id + '.json')
+        ));
+      }
 
 
       if (this.mitmSgmodule) {
       if (this.mitmSgmodule) {
         const sgmodule = this.mitmSgmodule();
         const sgmodule = this.mitmSgmodule();

+ 1 - 1
Build/lib/rules/ruleset.ts

@@ -12,7 +12,7 @@ import { isProbablyIpv4, isProbablyIpv6 } from 'foxts/is-probably-ip';
 type Preprocessed = [domain: string[], domainSuffix: string[], sortedDomainRules: string[]];
 type Preprocessed = [domain: string[], domainSuffix: string[], sortedDomainRules: string[]];
 
 
 export class RulesetOutput extends RuleOutput<Preprocessed> {
 export class RulesetOutput extends RuleOutput<Preprocessed> {
-  constructor(span: Span, id: string, protected type: 'non_ip' | 'ip') {
+  constructor(span: Span, id: string, protected type: 'non_ip' | 'ip' | (string & {})) {
     super(span, id);
     super(span, id);
   }
   }
 
 

+ 6 - 0
Source/non_ip/direct.ts

@@ -4,6 +4,8 @@ export interface DNSMapping {
   },
   },
   /** which also disallows wildcard */
   /** which also disallows wildcard */
   realip: boolean,
   realip: boolean,
+  /** should convert to ruleset */
+  ruleset: boolean,
   dns: string,
   dns: string,
   /**
   /**
    * domain[0]
    * domain[0]
@@ -20,6 +22,7 @@ export const DIRECTS = {
     dns: 'system',
     dns: 'system',
     hosts: {},
     hosts: {},
     realip: false,
     realip: false,
+    ruleset: false,
     domains: [
     domains: [
       'securelogin.com.cn',
       'securelogin.com.cn',
       '$captive.apple.com',
       '$captive.apple.com',
@@ -30,6 +33,7 @@ export const DIRECTS = {
     dns: 'system',
     dns: 'system',
     hosts: {},
     hosts: {},
     realip: true,
     realip: true,
+    ruleset: false,
     domains: [
     domains: [
       '+m2m',
       '+m2m',
       // '+ts.net', // TailScale Magic DNS
       // '+ts.net', // TailScale Magic DNS
@@ -47,6 +51,7 @@ export const LAN = {
     dns: 'system',
     dns: 'system',
     hosts: {},
     hosts: {},
     realip: false,
     realip: false,
+    ruleset: true,
     domains: [
     domains: [
       '+home',
       '+home',
       // 'zte.home', // ZTE CPE
       // 'zte.home', // ZTE CPE
@@ -106,6 +111,7 @@ export const LAN = {
       localhost: ['127.0.0.1']
       localhost: ['127.0.0.1']
     },
     },
     realip: true,
     realip: true,
+    ruleset: true,
     domains: [
     domains: [
       '+lan',
       '+lan',
       // 'amplifi.lan',
       // 'amplifi.lan',

+ 20 - 20
Source/non_ip/domestic.ts

@@ -5,6 +5,7 @@ export const DOMESTICS: Record<string, DNSMapping> = {
     hosts: {},
     hosts: {},
     dns: 'quic://dns.alidns.com:853',
     dns: 'quic://dns.alidns.com:853',
     realip: false,
     realip: false,
+    ruleset: true,
     domains: [
     domains: [
       'uc.cn',
       'uc.cn',
       // 'ucweb.com', // UC International
       // 'ucweb.com', // UC International
@@ -82,13 +83,18 @@ export const DOMESTICS: Record<string, DNSMapping> = {
       'tanx.com',
       'tanx.com',
       'hellobike.com',
       'hellobike.com',
       '+hichina.com',
       '+hichina.com',
-      '+yunos.com'
+      '+yunos.com',
+
+      // Bilibili Aliyun CDN
+      '$upos-sz-mirrorali.bilivideo.com',
+      '$upos-sz-estgoss.bilivideo.com'
     ]
     ]
   },
   },
   TENCENT: {
   TENCENT: {
     hosts: {},
     hosts: {},
     dns: 'https://doh.pub/dns-query',
     dns: 'https://doh.pub/dns-query',
     realip: false,
     realip: false,
+    ruleset: true,
     domains: [
     domains: [
       // 'dns.pub',
       // 'dns.pub',
       // 'doh.pub',
       // 'doh.pub',
@@ -144,28 +150,11 @@ export const DOMESTICS: Record<string, DNSMapping> = {
       '+codehub.cn'
       '+codehub.cn'
     ]
     ]
   },
   },
-  BILIBILI_ALI: {
-    dns: 'quic://dns.alidns.com:853',
-    hosts: {},
-    realip: false,
-    domains: [
-      '$upos-sz-mirrorali.bilivideo.com',
-      '$upos-sz-estgoss.bilivideo.com'
-    ]
-  },
-  BILIBILI_BD: {
-    dns: '180.76.76.76',
-    hosts: {},
-    realip: false,
-    domains: [
-      '$upos-sz-mirrorbd.bilivideo.com',
-      '$upos-sz-mirrorbos.bilivideo.com'
-    ]
-  },
   BILIBILI: {
   BILIBILI: {
     dns: 'https://doh.pub/dns-query',
     dns: 'https://doh.pub/dns-query',
     hosts: {},
     hosts: {},
     realip: false,
     realip: false,
+    ruleset: true,
     domains: [
     domains: [
       // '$upos-sz-mirrorcoso1.bilivideo.com', // already included in bilivideo.com
       // '$upos-sz-mirrorcoso1.bilivideo.com', // already included in bilivideo.com
       // '$upos-sz-estgcos.bilivideo.com', // already included in bilivideo.com, tencent cloud cdn
       // '$upos-sz-estgcos.bilivideo.com', // already included in bilivideo.com, tencent cloud cdn
@@ -196,6 +185,7 @@ export const DOMESTICS: Record<string, DNSMapping> = {
     dns: 'https://doh.pub/dns-query',
     dns: 'https://doh.pub/dns-query',
     hosts: {},
     hosts: {},
     realip: false,
     realip: false,
+    ruleset: true,
     domains: [
     domains: [
       'mi.com',
       'mi.com',
       'duokan.com',
       'duokan.com',
@@ -218,6 +208,7 @@ export const DOMESTICS: Record<string, DNSMapping> = {
     dns: '180.184.2.2',
     dns: '180.184.2.2',
     hosts: {},
     hosts: {},
     realip: false,
     realip: false,
+    ruleset: true,
     domains: [
     domains: [
       'bytedance.com',
       'bytedance.com',
       '+bytecdn.cn',
       '+bytecdn.cn',
@@ -271,6 +262,7 @@ export const DOMESTICS: Record<string, DNSMapping> = {
     dns: '180.76.76.76',
     dns: '180.76.76.76',
     hosts: {},
     hosts: {},
     realip: false,
     realip: false,
+    ruleset: true,
     domains: [
     domains: [
       '91.com',
       '91.com',
       'hao123.com',
       'hao123.com',
@@ -295,13 +287,18 @@ export const DOMESTICS: Record<string, DNSMapping> = {
       '+bdydns.com',
       '+bdydns.com',
       '+jomoxc.com', // Baidu PCDN, of sort
       '+jomoxc.com', // Baidu PCDN, of sort
       '+duapp.com',
       '+duapp.com',
-      '+antpcdn.com' // Baidu PCDN
+      '+antpcdn.com', // Baidu PCDN
+
+      // Bilibili Baidu CDN
+      '$upos-sz-mirrorbd.bilivideo.com',
+      '$upos-sz-mirrorbos.bilivideo.com'
     ]
     ]
   },
   },
   QIHOO360: {
   QIHOO360: {
     hosts: {},
     hosts: {},
     dns: 'https://doh.360.cn/dns-query',
     dns: 'https://doh.360.cn/dns-query',
     realip: false,
     realip: false,
+    ruleset: true,
     domains: [
     domains: [
       '+qhimg.com',
       '+qhimg.com',
       '+qhimgs.com',
       '+qhimgs.com',
@@ -350,6 +347,7 @@ export const DOH_BOOTSTRAP: Record<string, DNSMapping> = {
       'dns.alidns.com': ['223.5.5.5', '223.6.6.6', '2400:3200:baba::1', '2400:3200::1']
       'dns.alidns.com': ['223.5.5.5', '223.6.6.6', '2400:3200:baba::1', '2400:3200::1']
     },
     },
     realip: false,
     realip: false,
+    ruleset: false,
     dns: 'quic://223.5.5.5:853',
     dns: 'quic://223.5.5.5:853',
     domains: [
     domains: [
       '$dns.alidns.com'
       '$dns.alidns.com'
@@ -362,6 +360,7 @@ export const DOH_BOOTSTRAP: Record<string, DNSMapping> = {
       // 'dns.pub': ['120.53.53.53', '1.12.12.12']
       // 'dns.pub': ['120.53.53.53', '1.12.12.12']
     },
     },
     realip: false,
     realip: false,
+    ruleset: false,
     dns: 'https://1.12.12.12/dns-query',
     dns: 'https://1.12.12.12/dns-query',
     domains: [
     domains: [
       // '$dot.pub',
       // '$dot.pub',
@@ -382,6 +381,7 @@ export const DOH_BOOTSTRAP: Record<string, DNSMapping> = {
       // dot.360.net CNAME dns.360.net
       // dot.360.net CNAME dns.360.net
     },
     },
     realip: false,
     realip: false,
+    ruleset: false,
     // Surge only supports UDP 53 or Hosts as the bootstrap server of domain DoH
     // Surge only supports UDP 53 or Hosts as the bootstrap server of domain DoH
     dns: '101.198.198.198', // 'https://101.198.198.198/dns-query', // https://101.198.199.200/dns-query
     dns: '101.198.198.198', // 'https://101.198.198.198/dns-query', // https://101.198.199.200/dns-query
     domains: [
     domains: [