build-sgmodule-always-realip.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. import path from 'node:path';
  2. import { task } from './trace';
  3. import { compareAndWriteFile } from './lib/create-file';
  4. import { DIRECTS, LAN } from '../Source/non_ip/direct';
  5. import type { DNSMapping } from '../Source/non_ip/direct';
  6. import { DOMESTICS, DOH_BOOTSTRAP } from '../Source/non_ip/domestic';
  7. import * as yaml from 'yaml';
  8. import { OUTPUT_INTERNAL_DIR, OUTPUT_MODULES_DIR } from './constants/dir';
  9. import { appendArrayInPlace } from 'foxts/append-array-in-place';
  10. import { SHARED_DESCRIPTION } from './constants/description';
  11. import { createGetDnsMappingRule } from './build-domestic-direct-lan-ruleset-dns-mapping-module';
  12. import { ClashDomainSet } from './lib/writing-strategy/clash';
  13. import { FileOutput } from './lib/rules/base';
  14. const HOSTNAMES = [
  15. // Network Detection, Captive Portal
  16. 'dns.msftncsi.com',
  17. // '*.msftconnecttest.com',
  18. // 'network-test.debian.org',
  19. // 'detectportal.firefox.com',
  20. // Handle SNAT conversation properly
  21. '*.srv.nintendo.net',
  22. '*.stun.playstation.net',
  23. 'xbox.*.microsoft.com',
  24. '*.xboxlive.com',
  25. '*.turn.twilio.com',
  26. '*.stun.twilio.com',
  27. 'stun.syncthing.net',
  28. 'stun.*',
  29. // Steam LAN Cache
  30. //
  31. // Steam will DNS lookup this domain, trying to find Local LAN Cache server
  32. // If one is found, Steam client will try to connect with this IP with original CDN domain in
  33. // HTTP Host header, while in HTTP plain HTTP/1.1. It is up to the HTTP server to handle this.
  34. //
  35. // By having lancache.steamcontent.com in Real IP, we can avoid Steam client accidentally mistaking
  36. // the Fake IP as a local LAN cache. This also helps real LAN cache to work properly.
  37. 'lancache.steamcontent.com',
  38. // 'controlplane.tailscale.com',
  39. // NTP
  40. // 'time.*.com', 'time.*.gov', 'time.*.edu.cn', 'time.*.apple.com', 'time?.*.com', 'ntp.*.com', 'ntp?.*.com', '*.time.edu.cn', '*.ntp.org.cn', '*.pool.ntp.org'
  41. // 'time*.cloud.tencent.com', 'ntp?.aliyun.com',
  42. // QQ Login
  43. // 'localhost.*.qq.com'
  44. // 'localhost.ptlogin2.qq.com
  45. // 'localhost.sec.qq.com',
  46. // 'localhost.work.weixin.qq.com',
  47. '127.*.*.*.sslip.io',
  48. '127-*-*-*.sslip.io',
  49. '*.127.*.*.*.sslip.io',
  50. '*-127-*-*-*.sslip.io',
  51. '127.*.*.*.nip.io',
  52. '127-*-*-*.nip.io',
  53. '*.127.*.*.*.nip.io',
  54. '*-127-*-*-*.nip.io',
  55. '127.atlas.skk.moe'
  56. ];
  57. export const buildAlwaysRealIPModule = task(require.main === module, __filename)(async (span) => {
  58. const surge: string[] = [];
  59. const clashFakeIpFilter = new FileOutput(span, 'clash_fake_ip_filter')
  60. .withTitle('Sukka\'s Ruleset - Always Real IP Plus')
  61. .withDescription([
  62. ...SHARED_DESCRIPTION,
  63. '',
  64. 'Clash.Meta fake-ip-filter as ruleset'
  65. ])
  66. .withStrategies([
  67. new ClashDomainSet('domainset')
  68. ]);
  69. // Intranet, Router Setup, and mant more
  70. const dataset = [DIRECTS, LAN, DOMESTICS, DOH_BOOTSTRAP].reduce<DNSMapping[]>((acc, item) => {
  71. Object.values(item).forEach((i: DNSMapping) => {
  72. if (i.realip) {
  73. acc.push(i);
  74. }
  75. });
  76. return acc;
  77. }, []);
  78. const getDnsMappingRuleWithoutWildcard = createGetDnsMappingRule(false);
  79. for (const { domains } of dataset) {
  80. clashFakeIpFilter.addFromRuleset(domains.flatMap(getDnsMappingRuleWithoutWildcard));
  81. }
  82. return Promise.all([
  83. compareAndWriteFile(
  84. span,
  85. [
  86. '#!name=[Sukka] Always Real IP Plus',
  87. `#!desc=Last Updated: ${new Date().toISOString()}`,
  88. '',
  89. '[General]',
  90. `always-real-ip = %APPEND% ${HOSTNAMES.concat(surge).join(', ')}`
  91. ],
  92. path.resolve(OUTPUT_MODULES_DIR, 'sukka_common_always_realip.sgmodule')
  93. ),
  94. compareAndWriteFile(
  95. span,
  96. yaml.stringify(
  97. {
  98. dns: {
  99. 'fake-ip-filter': appendArrayInPlace(
  100. /** clash */
  101. dataset.flatMap(({ domains }) => domains.map((domain) => {
  102. switch (domain[0]) {
  103. case '$':
  104. return domain.slice(1);
  105. case '+':
  106. return '+.' + domain.slice(1);
  107. default:
  108. return domain;
  109. }
  110. })),
  111. HOSTNAMES
  112. )
  113. }
  114. },
  115. { version: '1.1' }
  116. ).split('\n'),
  117. path.join(OUTPUT_INTERNAL_DIR, 'clash_fake_ip_filter.yaml')
  118. )
  119. ]);
  120. });