@js0.site/edns 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,557 @@
1
+ [English](#en) | [中文](#zh)
2
+
3
+ ---
4
+
5
+ <a id="en"></a>
6
+
7
+ # edns : Geo-aware CNAME Flattening via DoH + ECS
8
+
9
+ ## Table of Contents
10
+
11
+ - [Background](#background)
12
+ - [Why Root Domains Cannot Use CNAME](#why-root-domains-cannot-use-cname)
13
+ - [Limitations of Cloudflare CNAME Flattening](#limitations-of-cloudflare-cname-flattening)
14
+ - [China-Friendly Free CDN Solution](#china-friendly-free-cdn-solution)
15
+ - [Why Huawei Cloud DNS](#why-huawei-cloud-dns)
16
+ - [Introduction](#introduction)
17
+ - [Features](#features)
18
+ - [Installation](#installation)
19
+ - [Usage](#usage)
20
+ - [China Regions](#china-regions)
21
+ - [Global Regions](#global-regions)
22
+ - [Low-level API](#low-level-api)
23
+ - [Architecture](#architecture)
24
+ - [Directory Structure](#directory-structure)
25
+ - [Tech Stack](#tech-stack)
26
+ - [History](#history)
27
+
28
+ ## Background
29
+
30
+ ### Why Root Domains Cannot Use CNAME
31
+
32
+ Per RFC 1034, CNAME records cannot coexist with other records. Zone apex domains (e.g., `example.com`) must have SOA and NS records, making CNAME impossible.
33
+
34
+ More critically: **CNAME conflicts with MX records**. If root domain has CNAME, mail servers cannot query MX records, causing `user@example.com` to fail receiving emails.
35
+
36
+ This forces CDN users to use static A/AAAA records for root domains, losing the dynamic routing benefits of CNAME.
37
+
38
+ CNAME Flattening solves this: DNS providers resolve CNAME to A/AAAA records at the authoritative level, returning plain A records externally while still following CNAME targets dynamically. This enables CDN's dynamic routing without affecting MX and other records.
39
+
40
+ ### Limitations of Cloudflare CNAME Flattening
41
+
42
+ Cloudflare offers free CNAME flattening, but with issues for China:
43
+
44
+ 1. **Fixed query location**: Cloudflare queries from its data centers, which are outside China
45
+ 2. **No ECS support**: Queries lack client subnet info, so CDN cannot determine real user location
46
+ 3. **Suboptimal results**: Chinese users get IPs optimized for overseas, not domestic nodes
47
+ 4. **Single IP return**: Flattening returns only one IP, cannot leverage multi-node load balancing
48
+
49
+ Example: Tencent Cloud CDN's CNAME resolved by Cloudflare may return Hong Kong or Singapore nodes instead of Beijing or Shanghai.
50
+
51
+ **Why Anycast works overseas but not in China?**
52
+
53
+ Anycast broadcasts the same IP to multiple locations, routing users to nearest node automatically. Overseas CDNs (like Cloudflare) use Anycast extensively with great results. But China's network is different:
54
+
55
+ - **ISP interconnection bottleneck**: Limited bandwidth between Telecom/Mobile/Unicom, high cross-network latency
56
+ - **BGP routing policies**: Complex BGP policies in Chinese ISPs, Anycast routing not always optimal
57
+ - **Regulatory requirements**: IP broadcasting in China requires registration, high barrier for Anycast deployment
58
+ - **Network topology**: China's network is segmented by province; same-province cross-ISP latency can exceed cross-province same-ISP latency
59
+
60
+ Therefore, Chinese CDNs use **Unicast + Smart DNS routing**: assign unique IPs to each node, return optimal node via DNS. This requires DNS to identify user's ISP and location — exactly what this tool solves.
61
+
62
+ ### China-Friendly Free CDN Solution
63
+
64
+ Strategy: Use Cloudflare for overseas, domestic CDN for China, with GeoDNS routing.
65
+
66
+ Recommended architecture:
67
+
68
+ ```mermaid
69
+ graph TD
70
+ DNS[GeoDNS Service<br/>Huawei DNS Free]
71
+ DNS --> OVERSEAS[Overseas]
72
+ DNS --> CT[Telecom]
73
+ DNS --> CM[Mobile]
74
+ DNS --> CU[Unicom]
75
+ OVERSEAS --> CF[Cloudflare SaaS]
76
+ CT --> TENCENT[Tencent EdgeOne]
77
+ CM --> ALI[Alibaba ESA]
78
+ CU --> TENCENT
79
+ ```
80
+
81
+ Free resource combination:
82
+ - **Overseas**: Cloudflare SaaS custom hostname (free, CNAME access)
83
+ - **China**: Tencent EdgeOne (free tier) + Alibaba ESA (free tier)
84
+ - **Routing**: Huawei Cloud DNS (free, supports province/ISP-level routing)
85
+
86
+ This tool's role: Resolve CDN CNAME to region-optimal IPs, generate static records for GeoDNS import, enabling precise routing.
87
+
88
+ ### Why Huawei Cloud DNS
89
+
90
+ Comparison of major Chinese DNS providers:
91
+
92
+ | Provider | Free Geo-routing | Line Coverage | API Support |
93
+ |----------|-----------------|---------------|-------------|
94
+ | Alibaba DNS | ❌ Paid | Full | ✅ |
95
+ | Tencent DNSPod | ❌ Paid | Full | ✅ |
96
+ | Huawei Cloud DNS | ✅ Free | 31 provinces × 3 ISPs | ✅ |
97
+ | Cloudflare | ✅ Free | Country-level only | ✅ |
98
+
99
+ Huawei Cloud DNS advantages:
100
+ - **Free province + ISP routing**: Supports Telecom/Mobile/Unicom × 31 provinces = 93 lines
101
+ - **Full API**: Batch import, record updates, automation-ready
102
+ - **No ICP requirement**: Works with overseas domains
103
+
104
+ Workflow with this tool: Run scripts periodically to fetch latest CDN IPs, update DNS records via API, achieving dynamic CNAME flattening.
105
+
106
+ ## Introduction
107
+
108
+ CNAME flattening resolves CNAME records to A/AAAA records at DNS level. This tool queries DoH (DNS over HTTPS) servers with EDNS Client Subnet (ECS) to obtain region-specific IP addresses for CDN domains.
109
+
110
+ Use cases:
111
+ - Generate static DNS records for GeoDNS services (Huawei Cloud DNS, etc.)
112
+ - Test CDN node distribution across regions
113
+ - Analyze DNS resolution behavior from different locations
114
+
115
+ ## Features
116
+
117
+ - IPv4 via DNSPod DoH (`1.12.12.12`)
118
+ - IPv6 via Alibaba DoH (`dns.alidns.com`)
119
+ - Pre-configured ECS IPs: 31 Chinese provinces × 3 ISPs + 40+ global regions
120
+ - Concurrent queries with rate limiting (10 parallel)
121
+ - Auto-retry on failure (3 attempts)
122
+ - Smart aggregation: deduplicate IPs by carrier/city/continent hierarchy
123
+
124
+ ## Installation
125
+
126
+ ```bash
127
+ bun i @js0.site/edns
128
+ ```
129
+
130
+ ## Usage
131
+
132
+ ### China Regions
133
+
134
+ ```javascript
135
+ import cn from "@js0.site/edns/cn.js";
136
+
137
+ const { v4, v6 } = await cn("cdn.example.com");
138
+ // v4: { CN: [...], Dianxin: [...], Yidong: [...], Beijing: [...], ... }
139
+ // v6: { CN: [...], Dianxin: [...], Yidong: [...], Beijing: [...], ... }
140
+ ```
141
+
142
+ Results are aggregated by:
143
+ - `CN`: top 3 IPs across all lines
144
+ - `Dianxin`/`Yidong`/`Liantong`: top 3 per carrier
145
+ - City names: top 3 per city
146
+ - Individual lines only if not covered by above
147
+
148
+ ### Global Regions
149
+
150
+ ```javascript
151
+ import global from "@js0.site/edns/global.js";
152
+
153
+ const { v4, v6 } = await global("cdn.example.com");
154
+ // v4: { default_view: [...], AP: [...], EU: [...], US: [...], ... }
155
+ // v6: { default_view: [...], AP: [...], EU: [...], JP: [...], ... }
156
+ ```
157
+
158
+ Results filtered by hierarchy:
159
+ - `default_view`: global default
160
+ - Continents: `AP`, `EU`, `NA`, `LA`, `OA`, `AF`
161
+ - Countries only if different from continent
162
+
163
+ ### Low-level API
164
+
165
+ ```javascript
166
+ import flattenV4, { CN, GLOBAL } from "@js0.site/edns/v4.js";
167
+ import flattenV6 from "@js0.site/edns/v6.js";
168
+
169
+ // Query specific lines
170
+ const result = await flattenV4("cdn.example.com", {
171
+ line1: "8.8.8.8",
172
+ line2: "1.1.1.1",
173
+ });
174
+ // { line1: ["1.2.3.4"], line2: ["5.6.7.8"] }
175
+
176
+ // Use built-in line configs
177
+ const cnResult = await flattenV4("cdn.example.com", CN);
178
+ ```
179
+
180
+ Run tests:
181
+
182
+ ```bash
183
+ bun test/cn.js # China regions
184
+ bun test/global.js # Global regions
185
+ bun test/v4.js # IPv4 raw
186
+ bun test/v6.js # IPv6 raw
187
+ bun test/verify.js # IP geolocation check
188
+ ```
189
+
190
+ ## Architecture
191
+
192
+ ```mermaid
193
+ graph TD
194
+ subgraph High-Level
195
+ CN[cn.js] --> V4
196
+ CN --> V6
197
+ GL[global.js] --> V4
198
+ GL --> V6
199
+ end
200
+
201
+ subgraph Core
202
+ V4[v4.js] --> BASE[base.js]
203
+ V6[v6.js] --> BASE
204
+ BASE --> DOH[DoH Server]
205
+ end
206
+
207
+ subgraph Config
208
+ V4CN[v4/CN.js] --> V4
209
+ V4GL[v4/GLOBAL.js] --> V4
210
+ V6CN[v6/CN.js] --> V6
211
+ V6GL[v6/GLOBAL.js] --> V6
212
+ GEO[geo/GLOBAL.js] --> GL
213
+ end
214
+
215
+ DOH --> DNSPOD[DNSPod]
216
+ DOH --> ALI[Alibaba DNS]
217
+ ```
218
+
219
+ Call flow:
220
+
221
+ 1. `cn.js`/`global.js` call `v4.js` and `v6.js` with built-in line configs
222
+ 2. `v4.js`/`v6.js` call `flatten()` with DoH endpoint and record type
223
+ 3. `flatten()` iterates lines, constructs DoH queries with ECS parameter
224
+ 4. `queryDoh()` sends HTTP request, parses JSON, filters valid IPs
225
+ 5. High-level modules aggregate/filter results by hierarchy
226
+
227
+ ## Directory Structure
228
+
229
+ ```
230
+ src/
231
+ ├── base.js # Core: queryDoh, flatten, isSubset
232
+ ├── v4.js # IPv4 resolver (DNSPod DoH)
233
+ ├── v6.js # IPv6 resolver (Alibaba DoH)
234
+ ├── cn.js # China aggregator
235
+ ├── global.js # Global aggregator
236
+ ├── v4/
237
+ │ ├── CN.js # China ECS IPs (31 provinces × 3 ISPs)
238
+ │ └── GLOBAL.js # Global ECS IPs (40+ regions)
239
+ ├── v6/
240
+ │ ├── CN.js # Re-exports v4/CN.js
241
+ │ └── GLOBAL.js # IPv6 global ECS IPs
242
+ └── geo/
243
+ └── GLOBAL.js # Continent → country mapping
244
+
245
+ test/
246
+ ├── lib.js # Test utilities
247
+ ├── cn.js # China test
248
+ ├── global.js # Global test
249
+ ├── v4.js # IPv4 test
250
+ ├── v6.js # IPv6 test
251
+ └── verify.js # Geolocation verification
252
+ ```
253
+
254
+ ## Tech Stack
255
+
256
+ - Runtime: Bun / Node.js (ESM)
257
+ - HTTP: Native fetch API
258
+ - Concurrency: p-limit
259
+ - Geolocation: MaxMind GeoLite2 (dev)
260
+ - Protocol: DNS over HTTPS (RFC 8484), EDNS Client Subnet (RFC 7871)
261
+
262
+ ## History
263
+
264
+ **EDNS Client Subnet (ECS)** was proposed in 2011 by Google and Neustar to improve CDN performance. Before ECS, recursive DNS servers returned IPs optimized for resolver location, not end user location. After multiple drafts, it became RFC 7871 in 2016.
265
+
266
+ **DNS over HTTPS (DoH)** emerged from privacy concerns. RFC 8484 was published in 2018. The combination of DoH + ECS provides both privacy and geo-aware resolution.
267
+
268
+ **Fun fact**: The `/24` subnet mask commonly used in ECS queries isn't arbitrary. It provides enough geographic precision for CDN routing while preserving user privacy by not exposing exact IP addresses. Most CDN providers optimize their anycast routing based on `/24` blocks.
269
+
270
+ ---
271
+
272
+ ## About
273
+
274
+ This project is an open-source component of [js0.site ⋅ Refactoring the Internet Plan](https://js0.site).
275
+
276
+ We are redefining the development paradigm of the Internet in a componentized way. Welcome to follow us:
277
+
278
+ * [Google Group](https://groups.google.com/g/js0-site)
279
+ * [js0site.bsky.social](https://bsky.app/profile/js0site.bsky.social)
280
+
281
+ ---
282
+
283
+ <a id="zh"></a>
284
+
285
+ # edns : 基于 DoH + ECS 的地理感知 CNAME 扁平化
286
+
287
+ ## 目录
288
+
289
+ - [背景](#背景)
290
+ - [根域名为何不能用 CNAME](#根域名为何不能用-cname)
291
+ - [Cloudflare CNAME 扁平化的局限](#cloudflare-cname-扁平化的局限)
292
+ - [中国友好的免费 CDN 方案](#中国友好的免费-cdn-方案)
293
+ - [为什么选华为云 DNS](#为什么选华为云-dns)
294
+ - [简介](#简介)
295
+ - [功能](#功能)
296
+ - [安装](#安装)
297
+ - [使用](#使用)
298
+ - [国内分区](#国内分区)
299
+ - [海外分区](#海外分区)
300
+ - [底层 API](#底层-api)
301
+ - [架构](#架构)
302
+ - [目录结构](#目录结构)
303
+ - [技术栈](#技术栈)
304
+ - [历史](#历史)
305
+
306
+ ## 背景
307
+
308
+ ### 根域名为何不能用 CNAME
309
+
310
+ 根据 RFC 1034,CNAME 记录不能与其他记录共存。而根域名(Zone Apex,如 `example.com`)必须有 SOA 和 NS 记录,因此无法设置 CNAME。
311
+
312
+ 更关键的是:**CNAME 与 MX 记录冲突**。如果根域名设置 CNAME,邮件服务器无法查询 MX 记录,导致 `user@example.com` 收不到邮件。
313
+
314
+ 这导致 CDN 接入时只能用 A/AAAA 记录指向固定 IP,无法享受 CNAME 带来的动态调度能力。
315
+
316
+ CNAME 扁平化(CNAME Flattening)应运而生:DNS 服务商在权威侧将 CNAME 解析为 A/AAAA 记录返回,对外表现为普通 A 记录,实际仍跟随 CNAME 目标动态变化。这样既能使用 CDN 的动态调度,又不影响 MX 等其他记录。
317
+
318
+ ### Cloudflare CNAME 扁平化的局限
319
+
320
+ Cloudflare 提供免费的 CNAME 扁平化,但存在问题:
321
+
322
+ 1. **解析位置固定**:Cloudflare 从其数据中心发起 DNS 查询,对于中国 CDN,查询来源是海外 IP
323
+ 2. **无 ECS 支持**:查询不携带客户端子网信息,CDN 无法判断真实用户位置
324
+ 3. **结果偏差**:中国用户访问时,CDN 返回的是针对海外优化的节点 IP,而非国内最优节点
325
+ 4. **单 IP 返回**:扁平化后只返回单个 IP,无法利用多节点负载均衡
326
+
327
+ 例如:腾讯云 CDN 的 CNAME 被 Cloudflare 解析时,返回的可能是香港或新加坡节点,而非北京或上海节点。
328
+
329
+ **为什么海外 CDN 用 Anycast 没问题,国内却不行?**
330
+
331
+ Anycast 是将同一 IP 广播到多个地理位置,用户自动路由到最近节点。海外 CDN(如 Cloudflare)大量使用 Anycast,效果很好。但中国网络环境特殊:
332
+
333
+ - **运营商互联瓶颈**:电信、移动、联通三大运营商之间互联带宽有限,跨网访问延迟高
334
+ - **BGP 路由策略**:国内 ISP 的 BGP 路由策略复杂,Anycast 路由不一定最优
335
+ - **监管要求**:国内 IP 广播需要备案,Anycast 部署门槛高
336
+ - **网络拓扑**:国内网络以省为单位分割,同省不同运营商的延迟可能比跨省同运营商还高
337
+
338
+ 因此国内 CDN 普遍采用 **Unicast + 智能 DNS 调度**:为每个节点分配独立 IP,通过 DNS 返回最优节点。这要求 DNS 能识别用户的运营商和地理位置,正是本工具解决的问题。
339
+
340
+ ### 中国友好的免费 CDN 方案
341
+
342
+ 解决思路:境外用 Cloudflare,境内用国内 CDN,通过 GeoDNS 分流。
343
+
344
+ 推荐架构:
345
+
346
+ ```mermaid
347
+ graph TD
348
+ DNS[GeoDNS 服务<br/>华为云 DNS 免费]
349
+ DNS --> OVERSEAS[境外线路]
350
+ DNS --> CT[电信线路]
351
+ DNS --> CM[移动线路]
352
+ DNS --> CU[联通线路]
353
+ OVERSEAS --> CF[Cloudflare SaaS]
354
+ CT --> TENCENT[腾讯云 EdgeOne]
355
+ CM --> ALI[阿里云 ESA]
356
+ CU --> TENCENT
357
+ ```
358
+
359
+ 免费资源组合:
360
+ - **境外**:Cloudflare SaaS 自定义域名(免费,通过 CNAME 接入)
361
+ - **境内**:腾讯云 EdgeOne(免费额度)+ 阿里云 ESA(免费额度)
362
+ - **调度**:华为云 DNS(免费,支持分省分运营商解析)
363
+
364
+ 本工具的作用:将 CDN 的 CNAME 解析为各地区最优 IP,生成静态记录导入 GeoDNS,实现精准调度。
365
+
366
+ ### 为什么选华为云 DNS
367
+
368
+ 国内主流 DNS 服务商对比:
369
+
370
+ | 服务商 | 免费版分区解析 | 线路覆盖 | API 支持 |
371
+ |--------|---------------|---------|---------|
372
+ | 阿里云 DNS | ❌ 需付费 | 全 | ✅ |
373
+ | 腾讯云 DNSPod | ❌ 需付费 | 全 | ✅ |
374
+ | 华为云 DNS | ✅ 免费 | 31省×3运营商 | ✅ |
375
+ | Cloudflare | ✅ 免费 | 仅国家级 | ✅ |
376
+
377
+ 华为云 DNS 优势:
378
+ - **免费分省分运营商解析**:支持电信/移动/联通 × 31 省,共 93 条线路
379
+ - **完整 API**:支持批量导入、更新记录,可自动化
380
+ - **无 ICP 限制**:海外域名也可使用
381
+
382
+ 本工具配合华为云 DNS 使用:定时运行脚本获取 CDN 最新 IP,通过 API 更新解析记录,实现动态 CNAME 扁平化。
383
+
384
+ ## 简介
385
+
386
+ CNAME 扁平化是在 DNS 层面将 CNAME 记录解析为 A/AAAA 记录。本工具通过 DoH (DNS over HTTPS) 配合 EDNS Client Subnet (ECS) 查询 CDN 域名在不同地区的解析 IP。
387
+
388
+ 适用场景:
389
+ - 为 GeoDNS 服务(华为云 DNS 等)生成静态解析记录
390
+ - 测试 CDN 节点在各地区的分布
391
+ - 分析不同地理位置的 DNS 解析行为
392
+
393
+ ## 功能
394
+
395
+ - IPv4 解析:DNSPod DoH (`1.12.12.12`)
396
+ - IPv6 解析:阿里云 DoH (`dns.alidns.com`)
397
+ - 预置 ECS IP:国内 31 省 × 3 运营商 + 海外 40+ 地区
398
+ - 并发控制:限制 10 并发
399
+ - 自动重试:失败重试 3 次
400
+ - 智能聚合:按运营商/城市/洲级层次去重
401
+
402
+ ## 安装
403
+
404
+ ```bash
405
+ bun i @js0.site/edns
406
+ ```
407
+
408
+ ## 使用
409
+
410
+ ### 国内分区
411
+
412
+ ```javascript
413
+ import cn from "@js0.site/edns/cn.js";
414
+
415
+ const { v4, v6 } = await cn("cdn.example.com");
416
+ // v4: { CN: [...], Dianxin: [...], Yidong: [...], Beijing: [...], ... }
417
+ // v6: { CN: [...], Dianxin: [...], Yidong: [...], Beijing: [...], ... }
418
+ ```
419
+
420
+ 结果按层次聚合:
421
+ - `CN`:全国 top 3 IP
422
+ - `Dianxin`/`Yidong`/`Liantong`:各运营商 top 3
423
+ - 城市名:各城市 top 3
424
+ - 单独线路:仅当不被上级覆盖时保留
425
+
426
+ ### 海外分区
427
+
428
+ ```javascript
429
+ import global from "@js0.site/edns/global.js";
430
+
431
+ const { v4, v6 } = await global("cdn.example.com");
432
+ // v4: { default_view: [...], AP: [...], EU: [...], US: [...], ... }
433
+ // v6: { default_view: [...], AP: [...], EU: [...], JP: [...], ... }
434
+ ```
435
+
436
+ 结果按层次过滤:
437
+ - `default_view`:全球默认
438
+ - 洲:`AP`(亚太)、`EU`(欧洲)、`NA`(北美)、`LA`(南美)、`OA`(大洋洲)、`AF`(非洲)
439
+ - 国家:仅当与所属洲不同时保留
440
+
441
+ ### 底层 API
442
+
443
+ ```javascript
444
+ import flattenV4, { CN, GLOBAL } from "@js0.site/edns/v4.js";
445
+ import flattenV6 from "@js0.site/edns/v6.js";
446
+
447
+ // 查询指定线路
448
+ const result = await flattenV4("cdn.example.com", {
449
+ line1: "8.8.8.8",
450
+ line2: "1.1.1.1",
451
+ });
452
+ // { line1: ["1.2.3.4"], line2: ["5.6.7.8"] }
453
+
454
+ // 使用内置线路配置
455
+ const cnResult = await flattenV4("cdn.example.com", CN);
456
+ ```
457
+
458
+ 运行测试:
459
+
460
+ ```bash
461
+ bun test/cn.js # 国内分区
462
+ bun test/global.js # 海外分区
463
+ bun test/v4.js # IPv4 原始
464
+ bun test/v6.js # IPv6 原始
465
+ bun test/verify.js # IP 归属地校验
466
+ ```
467
+
468
+ ## 架构
469
+
470
+ ```mermaid
471
+ graph TD
472
+ subgraph 高层接口
473
+ CN[cn.js] --> V4
474
+ CN --> V6
475
+ GL[global.js] --> V4
476
+ GL --> V6
477
+ end
478
+
479
+ subgraph 核心
480
+ V4[v4.js] --> BASE[base.js]
481
+ V6[v6.js] --> BASE
482
+ BASE --> DOH[DoH 服务器]
483
+ end
484
+
485
+ subgraph 配置
486
+ V4CN[v4/CN.js] --> V4
487
+ V4GL[v4/GLOBAL.js] --> V4
488
+ V6CN[v6/CN.js] --> V6
489
+ V6GL[v6/GLOBAL.js] --> V6
490
+ GEO[geo/GLOBAL.js] --> GL
491
+ end
492
+
493
+ DOH --> DNSPOD[DNSPod]
494
+ DOH --> ALI[阿里云 DNS]
495
+ ```
496
+
497
+ 调用流程:
498
+
499
+ 1. `cn.js`/`global.js` 调用 `v4.js` 和 `v6.js`,传入内置线路配置
500
+ 2. `v4.js`/`v6.js` 调用 `flatten()`,指定 DoH 端点和记录类型
501
+ 3. `flatten()` 遍历线路,构造带 ECS 参数的 DoH 查询
502
+ 4. `queryDoh()` 发送 HTTP 请求,解析 JSON,过滤有效 IP
503
+ 5. 高层模块按层次聚合/过滤结果
504
+
505
+ ## 目录结构
506
+
507
+ ```
508
+ src/
509
+ ├── base.js # 核心:queryDoh, flatten, isSubset
510
+ ├── v4.js # IPv4 解析器 (DNSPod DoH)
511
+ ├── v6.js # IPv6 解析器 (阿里云 DoH)
512
+ ├── cn.js # 国内聚合器
513
+ ├── global.js # 海外聚合器
514
+ ├── v4/
515
+ │ ├── CN.js # 国内 ECS IP (31 省 × 3 运营商)
516
+ │ └── GLOBAL.js # 海外 ECS IP (40+ 地区)
517
+ ├── v6/
518
+ │ ├── CN.js # 复用 v4/CN.js
519
+ │ └── GLOBAL.js # IPv6 海外 ECS IP
520
+ └── geo/
521
+ └── GLOBAL.js # 洲 → 国家映射
522
+
523
+ test/
524
+ ├── lib.js # 测试工具
525
+ ├── cn.js # 国内测试
526
+ ├── global.js # 海外测试
527
+ ├── v4.js # IPv4 测试
528
+ ├── v6.js # IPv6 测试
529
+ └── verify.js # 归属地校验
530
+ ```
531
+
532
+ ## 技术栈
533
+
534
+ - 运行时:Bun / Node.js (ESM)
535
+ - HTTP:原生 fetch API
536
+ - 并发:p-limit
537
+ - 地理库:MaxMind GeoLite2 (开发依赖)
538
+ - 协议:DNS over HTTPS (RFC 8484)、EDNS Client Subnet (RFC 7871)
539
+
540
+ ## 历史
541
+
542
+ **EDNS Client Subnet (ECS)** 由 Google 和 Neustar 于 2011 年提出,旨在改善 CDN 性能。在 ECS 出现之前,递归 DNS 服务器只能返回针对解析器位置优化的 IP,而非终端用户位置。经历多次草案修订后,于 2016 年成为 RFC 7871。
543
+
544
+ **DNS over HTTPS (DoH)** 源于对 DNS 流量隐私的关注,RFC 8484 于 2018 年发布。DoH + ECS 的组合既保护隐私,又实现地理感知解析。
545
+
546
+ **趣闻**:ECS 查询中常用的 `/24` 子网掩码并非随意选择。它既能提供足够的地理精度用于 CDN 路由,又能通过不暴露精确 IP 来保护用户隐私。大多数 CDN 厂商基于 `/24` 块优化其 Anycast 路由。
547
+
548
+ ---
549
+
550
+ ## 关于
551
+
552
+ 本项目为 [js0.site ⋅ 重构互联网计划](https://js0.site) 的开源组件。
553
+
554
+ 我们正在以组件化的方式重新定义互联网的开发范式,欢迎关注:
555
+
556
+ * [谷歌邮件列表](https://groups.google.com/g/js0-site)
557
+ * [js0site.bsky.social](https://bsky.app/profile/js0site.bsky.social)
package/base.js ADDED
@@ -0,0 +1 @@
1
+ var e=class{value;next;constructor(e){this.value=e}},t=class{#head;#tail;#size;constructor(){this.clear()}enqueue(t){let n=new e(t);this.#head?(this.#tail.next=n,this.#tail=n):(this.#head=n,this.#tail=n),this.#size++}dequeue(){let e=this.#head;if(e)return this.#head=this.#head.next,this.#size--,this.#head||(this.#tail=void 0),e.value}peek(){if(this.#head)return this.#head.value}clear(){this.#head=void 0,this.#tail=void 0,this.#size=0}get size(){return this.#size}*[Symbol.iterator](){let e=this.#head;for(;e;)yield e.value,e=e.next}*drain(){for(;this.#head;)yield this.dequeue()}};function n(e){r(e);let n=new t,i=0,a=()=>{i<e&&n.size>0&&(i++,n.dequeue()())},o=()=>{i--,a()},s=async(e,t,n)=>{let r=(async()=>e(...n))();t(r);try{await r}catch{}o()},c=(t,r,o)=>{new Promise(e=>{n.enqueue(e)}).then(s.bind(void 0,t,r,o)),i<e&&a()},l=(e,...t)=>new Promise(n=>{c(e,n,t)});return Object.defineProperties(l,{activeCount:{get:()=>i},pendingCount:{get:()=>n.size},clearQueue:{value(){n.clear()}},concurrency:{get:()=>e,set(t){r(t),e=t,queueMicrotask(()=>{for(;i<e&&n.size>0;)a()})}},map:{async value(e,t){let n=Array.from(e,(e,n)=>this(t,e,n));return Promise.all(n)}}}),l}function r(e){if(!((Number.isInteger(e)||e===1/0)&&e>0))throw TypeError("Expected `concurrency` to be a number from 1 and up")}const i=n(10),a=(e,t)=>{for(let n of e)if(!t.has(n))return!1;return!0},o=async(e,t,n)=>{let r=new URL(e);for(let[e,n]of Object.entries(t))r.searchParams.set(e,n);let i;for(let e=0;e<3;e++)try{let e=await fetch(r),t=await e.json();return t.Answer?.map(e=>e.data).filter(n)||[]}catch(t){i=t,console.error(`retry ${e+1}/3: ${t}`)}throw i},s=async(e,t,n,r,a)=>{let s={},c=[];for(let[l,u]of Object.entries(n))c.push(i(async()=>{s[l]=await o(e,{name:t,type:r,edns_client_subnet:u},a)}));return await Promise.all(c),s};export{s as flatten,a as isSubset,o as queryDoh};
package/cn.js ADDED
@@ -0,0 +1 @@
1
+ var e=class{value;next;constructor(e){this.value=e}},t=class{#head;#tail;#size;constructor(){this.clear()}enqueue(t){let n=new e(t);this.#head?(this.#tail.next=n,this.#tail=n):(this.#head=n,this.#tail=n),this.#size++}dequeue(){let e=this.#head;if(e)return this.#head=this.#head.next,this.#size--,this.#head||(this.#tail=void 0),e.value}peek(){if(this.#head)return this.#head.value}clear(){this.#head=void 0,this.#tail=void 0,this.#size=0}get size(){return this.#size}*[Symbol.iterator](){let e=this.#head;for(;e;)yield e.value,e=e.next}*drain(){for(;this.#head;)yield this.dequeue()}};function n(e){r(e);let n=new t,i=0,a=()=>{i<e&&n.size>0&&(i++,n.dequeue()())},o=()=>{i--,a()},s=async(e,t,n)=>{let r=(async()=>e(...n))();t(r);try{await r}catch{}o()},c=(t,r,o)=>{new Promise(e=>{n.enqueue(e)}).then(s.bind(void 0,t,r,o)),i<e&&a()},l=(e,...t)=>new Promise(n=>{c(e,n,t)});return Object.defineProperties(l,{activeCount:{get:()=>i},pendingCount:{get:()=>n.size},clearQueue:{value(){n.clear()}},concurrency:{get:()=>e,set(t){r(t),e=t,queueMicrotask(()=>{for(;i<e&&n.size>0;)a()})}},map:{async value(e,t){let n=Array.from(e,(e,n)=>this(t,e,n));return Promise.all(n)}}}),l}function r(e){if(!((Number.isInteger(e)||e===1/0)&&e>0))throw TypeError("Expected `concurrency` to be a number from 1 and up")}const i=n(10),a=(e,t)=>{for(let n of e)if(!t.has(n))return!1;return!0},o=async(e,t,n)=>{let r=new URL(e);for(let[e,n]of Object.entries(t))r.searchParams.set(e,n);let i;for(let e=0;e<3;e++)try{let e=await fetch(r),t=await e.json();return t.Answer?.map(e=>e.data).filter(n)||[]}catch(t){i=t,console.error(`retry ${e+1}/3: ${t}`)}throw i},s=async(e,t,n,r,a)=>{let s={},c=[];for(let[l,u]of Object.entries(n))c.push(i(async()=>{s[l]=await o(e,{name:t,type:r,edns_client_subnet:u},a)}));return await Promise.all(c),s};var c={Dianxin_Liaoning:`219.148.204.66`,Yidong_Liaoning:`211.137.32.178`,Liantong_Liaoning:`202.96.64.68`,Dianxin_Jilin:`219.149.194.55`,Yidong_Jilin:`211.141.16.99`,Liantong_Jilin:`202.98.0.68`,Dianxin_Heilongjiang:`112.100.100.100`,Yidong_Heilongjiang:`211.137.241.34`,Liantong_Heilongjiang:`202.97.224.68`,Dianxin_Beijing:`219.141.136.10`,Yidong_Beijing:`221.130.33.52`,Liantong_Beijing:`202.106.196.115`,Dianxin_Tianjin:`219.150.32.132`,Yidong_Tianjin:`211.137.160.5`,Liantong_Tianjin:`202.99.96.68`,Dianxin_Hebei:`222.222.222.222`,Yidong_Hebei:`211.138.13.66`,Liantong_Hebei:`202.99.160.68`,Dianxin_Shanxi:`219.149.135.188`,Yidong_Shanxi:`211.138.106.3`,Liantong_Shanxi:`202.99.216.113`,Dianxin_Neimenggu:`219.148.162.31`,Yidong_Neimenggu:`211.138.91.1`,Liantong_Neimenggu:`202.99.224.68`,Dianxin_Hainan:`202.100.192.68`,Yidong_Hainan:`221.176.88.95`,Liantong_Hainan:`221.11.132.2`,Dianxin_Guangdong:`202.96.134.133`,Yidong_Guangdong:`211.139.163.6`,Liantong_Guangdong:`210.21.196.6`,Dianxin_Guangxi:`202.103.225.68`,Yidong_Guangxi:`211.138.245.180`,Liantong_Guangxi:`221.7.128.68`,Dianxin_Fujian:`218.85.152.99`,Yidong_Fujian:`211.138.151.161`,Liantong_Fujian:`218.104.128.106`,Dianxin_Hunan:`222.246.129.80`,Yidong_Hunan:`211.142.210.98`,Liantong_Hunan:`58.20.127.238`,Dianxin_Hubei:`202.103.24.68`,Yidong_Hubei:`211.137.58.20`,Liantong_Hubei:`218.104.111.114`,Dianxin_Henan:`222.85.85.85`,Yidong_Henan:`211.138.24.71`,Liantong_Henan:`202.102.224.68`,Dianxin_Jiangxi:`202.101.224.69`,Yidong_Jiangxi:`211.141.90.68`,Liantong_Jiangxi:`220.248.192.12`,Dianxin_Shanghai:`202.96.209.133`,Yidong_Shanghai:`211.136.112.50`,Liantong_Shanghai:`210.22.70.3`,Dianxin_Jiangsu:`218.2.2.2`,Yidong_Jiangsu:`221.131.143.69`,Liantong_Jiangsu:`221.6.4.66`,Dianxin_Zhejiang:`202.101.172.35`,Yidong_Zhejiang:`211.140.13.188`,Liantong_Zhejiang:`221.12.1.227`,Dianxin_Anhui:`61.132.163.68`,Yidong_Anhui:`211.138.180.2`,Liantong_Anhui:`218.104.78.2`,Dianxin_Shandong:`219.146.1.66`,Yidong_Shandong:`218.201.96.130`,Liantong_Shandong:`202.102.128.68`,Dianxin_Chongqing:`61.128.192.68`,Yidong_Chongqing:`218.201.4.3`,Liantong_Chongqing:`221.5.203.98`,Dianxin_Sichuan:`61.139.2.69`,Yidong_Sichuan:`211.137.82.4`,Liantong_Sichuan:`119.6.6.6`,Dianxin_Guizhou:`202.98.192.67`,Yidong_Guizhou:`211.139.5.29`,Liantong_Guizhou:`221.13.28.234`,Dianxin_Yunnan:`222.172.200.68`,Yidong_Yunnan:`211.139.29.68`,Liantong_Yunnan:`221.3.131.11`,Dianxin_Xizang:`202.98.224.68`,Yidong_Xizang:`211.139.73.34`,Liantong_Xizang:`221.13.65.34`,Dianxin_Shaanxi:`218.30.19.40`,Yidong_Shaanxi:`211.137.130.3`,Liantong_Shaanxi:`221.11.1.67`,Dianxin_Gansu:`202.100.64.68`,Yidong_Gansu:`218.203.160.194`,Liantong_Gansu:`221.7.34.10`,Dianxin_Qinghai:`202.100.128.68`,Yidong_Qinghai:`211.138.75.123`,Liantong_Qinghai:`221.207.58.58`,Dianxin_Ningxia:`222.75.152.129`,Yidong_Ningxia:`218.203.123.116`,Liantong_Ningxia:`211.93.0.81`,Dianxin_Xinjiang:`61.128.114.166`,Yidong_Xinjiang:`218.202.152.130`,Liantong_Xinjiang:`221.7.1.21`,Jiaoyuwang:`202.112.144.30`},l=(e,t)=>s(`https://1.12.12.12/resolve`,e,t,`A`,e=>/^\d+\.\d+\.\d+\.\d+$/.test(e)),u=(e,t)=>s(`https://dns.alidns.com/resolve`,e,t,`AAAA`,e=>e.includes(`:`));const d=[`Dianxin`,`Yidong`,`Liantong`],f=new Set(d),p=(e,t=3)=>Object.entries(e).sort((e,t)=>t[1]-e[1]).slice(0,t).map(([e])=>e),m=e=>{let t={};for(let[n,r]of Object.entries(e)){let e=p(r);e.length&&(t[n]=e)}return t},h=e=>{let t={};for(let[n,r]of Object.entries(e))t[n]=new Set(r);return t},g=e=>{let t={},n={},r={},i={};for(let e of d)n[e]={};for(let[a,o]of Object.entries(e)){let[e,s]=a.split(`_`);if(i[a]=[e,s],!f.has(e))continue;for(let i of o)t[i]=(t[i]||0)+1,n[e][i]=(n[e][i]||0)+1,s&&((r[s]||={})[i]=(r[s][i]||0)+1)}let o=p(t),s=m(n),c=m(r),l=h(s),u=h(c),g=o.length?{CN:o}:{};for(let e of d)s[e]&&(g[e]=s[e]);for(let[e,t]of Object.entries(c))g[e]=t;for(let[t,n]of Object.entries(e)){let[e,r]=i[t];if(!f.has(e)){g[t]=n;continue}if(l[e]&&a(n,l[e])||r&&u[r]&&a(n,u[r]))continue;g[t]=n}return g};var _=async e=>{let[t,n]=await Promise.all([l(e,c),u(e,c)]);return{v4:g(t),v6:g(n)}};export{_ as default};
package/geo/GLOBAL.js ADDED
@@ -0,0 +1,47 @@
1
+ export default {
2
+ AP: new Set([
3
+ "HK",
4
+ "MO",
5
+ "TW",
6
+ "JP",
7
+ "KR",
8
+ "SG",
9
+ "TH",
10
+ "MY",
11
+ "ID",
12
+ "PH",
13
+ "VN",
14
+ "IN",
15
+ "AE",
16
+ "TR",
17
+ "IL",
18
+ ]),
19
+ OA: new Set(["AU", "NZ", "FJ", "PG"]),
20
+ EU: new Set([
21
+ "DE",
22
+ "GB",
23
+ "FR",
24
+ "NL",
25
+ "IT",
26
+ "ES",
27
+ "RU",
28
+ "PL",
29
+ "SE",
30
+ "CH",
31
+ "AT",
32
+ "BE",
33
+ "CZ",
34
+ "DK",
35
+ "FI",
36
+ "GR",
37
+ "HU",
38
+ "IE",
39
+ "NO",
40
+ "PT",
41
+ "RO",
42
+ "UA",
43
+ ]),
44
+ NA: new Set(["US", "CA", "MX"]),
45
+ LA: new Set(["BR", "AR", "CL", "CO", "PE", "VE", "EC", "BO", "PY", "UY"]),
46
+ AF: new Set(["ZA", "EG", "NG", "KE", "MA", "GH", "TZ", "ET"]),
47
+ };
package/global.js ADDED
@@ -0,0 +1 @@
1
+ var e=class{value;next;constructor(e){this.value=e}},t=class{#head;#tail;#size;constructor(){this.clear()}enqueue(t){let n=new e(t);this.#head?(this.#tail.next=n,this.#tail=n):(this.#head=n,this.#tail=n),this.#size++}dequeue(){let e=this.#head;if(e)return this.#head=this.#head.next,this.#size--,this.#head||(this.#tail=void 0),e.value}peek(){if(this.#head)return this.#head.value}clear(){this.#head=void 0,this.#tail=void 0,this.#size=0}get size(){return this.#size}*[Symbol.iterator](){let e=this.#head;for(;e;)yield e.value,e=e.next}*drain(){for(;this.#head;)yield this.dequeue()}};function n(e){r(e);let n=new t,i=0,a=()=>{i<e&&n.size>0&&(i++,n.dequeue()())},o=()=>{i--,a()},s=async(e,t,n)=>{let r=(async()=>e(...n))();t(r);try{await r}catch{}o()},c=(t,r,o)=>{new Promise(e=>{n.enqueue(e)}).then(s.bind(void 0,t,r,o)),i<e&&a()},l=(e,...t)=>new Promise(n=>{c(e,n,t)});return Object.defineProperties(l,{activeCount:{get:()=>i},pendingCount:{get:()=>n.size},clearQueue:{value(){n.clear()}},concurrency:{get:()=>e,set(t){r(t),e=t,queueMicrotask(()=>{for(;i<e&&n.size>0;)a()})}},map:{async value(e,t){let n=Array.from(e,(e,n)=>this(t,e,n));return Promise.all(n)}}}),l}function r(e){if(!((Number.isInteger(e)||e===1/0)&&e>0))throw TypeError("Expected `concurrency` to be a number from 1 and up")}const i=n(10),a=(e,t)=>{for(let n of e)if(!t.has(n))return!1;return!0},o=async(e,t,n)=>{let r=new URL(e);for(let[e,n]of Object.entries(t))r.searchParams.set(e,n);let i;for(let e=0;e<3;e++)try{let e=await fetch(r),t=await e.json();return t.Answer?.map(e=>e.data).filter(n)||[]}catch(t){i=t,console.error(`retry ${e+1}/3: ${t}`)}throw i},s=async(e,t,n,r,a)=>{let s={},c=[];for(let[l,u]of Object.entries(n))c.push(i(async()=>{s[l]=await o(e,{name:t,type:r,edns_client_subnet:u},a)}));return await Promise.all(c),s};var c={Abroad:`8.8.8.8`,AP:`210.130.0.1`,HK:`202.181.224.2`,MO:`202.175.3.3`,TW:`168.95.1.1`,JP:`210.130.0.1`,KR:`168.126.63.1`,SG:`165.21.83.88`,TH:`203.113.5.130`,MY:`49.236.193.35`,ID:`202.134.0.155`,PH:`121.58.203.4`,VN:`203.162.4.191`,IN:`117.254.160.1`,AE:`94.200.200.200`,TR:`195.175.39.39`,IL:`147.235.225.1`,OA:`139.130.4.5`,AU:`139.130.4.5`,NZ:`202.27.184.3`,EU:`194.150.168.168`,DE:`194.150.168.168`,GB:`158.43.128.1`,FR:`80.67.169.12`,NL:`193.176.144.22`,IT:`217.141.248.10`,ES:`195.235.113.3`,RU:`77.88.8.8`,PL:`194.204.159.1`,SE:`194.17.47.1`,CH:`195.186.1.111`,NA:`64.6.64.6`,US:`64.6.64.6`,CA:`149.112.121.10`,MX:`200.94.0.10`,LA:`200.221.11.101`,BR:`200.221.11.100`,AR:`200.49.130.47`,CL:`200.1.123.46`,AF:`196.216.2.1`,ZA:`169.239.128.40`,EG:`41.128.128.128`},l=(e,t)=>s(`https://1.12.12.12/resolve`,e,t,`A`,e=>/^\d+\.\d+\.\d+\.\d+$/.test(e)),u={Abroad:`2001:4860:4860::8888`,AP:`2001:240::1`,HK:`2403:2c80::1`,TW:`2001:b000:168::1`,JP:`2001:240::1`,KR:`2001:dc5:ad::1`,SG:`2406:3003:2006:1475::1`,IN:`2405:200:1000::1`,OA:`2001:44b8:1::1`,AU:`2001:44b8:1::1`,EU:`2a01:4f8:0:a0a1::add:9999`,DE:`2a01:4f8:0:a0a1::add:9999`,GB:`2a02:c7f:7e2e:6c00::1`,FR:`2001:910:800::12`,NL:`2a02:2770::21a:4aff:fec8:b0d`,RU:`2a02:6b8::feed:0ff`,SE:`2a01:3f0:0:301::1`,CH:`2a02:a90:0:10::11`,NA:`2620:74:1b::1:1`,US:`2620:74:1b::1:1`,CA:`2620:10a:80bb::10`,LA:`2001:12ff::1`,BR:`2804:10:10::10`,AF:`2c0f:fc89:8001::1`,ZA:`2001:4200:1::1`},d=(e,t)=>s(`https://dns.alidns.com/resolve`,e,t,`AAAA`,e=>e.includes(`:`)),f={AP:new Set([`HK`,`MO`,`TW`,`JP`,`KR`,`SG`,`TH`,`MY`,`ID`,`PH`,`VN`,`IN`,`AE`,`TR`,`IL`]),OA:new Set([`AU`,`NZ`,`FJ`,`PG`]),EU:new Set([`DE`,`GB`,`FR`,`NL`,`IT`,`ES`,`RU`,`PL`,`SE`,`CH`,`AT`,`BE`,`CZ`,`DK`,`FI`,`GR`,`HU`,`IE`,`NO`,`PT`,`RO`,`UA`]),NA:new Set([`US`,`CA`,`MX`]),LA:new Set([`BR`,`AR`,`CL`,`CO`,`PE`,`VE`,`EC`,`BO`,`PY`,`UY`]),AF:new Set([`ZA`,`EG`,`NG`,`KE`,`MA`,`GH`,`TZ`,`ET`])};const p=e=>{let t=e.default_view||[];if(!t.length)return e;let n=new Set(t),r={default_view:t},i={};for(let t of Object.keys(f))e[t]&&(i[t]=new Set(e[t]));for(let[t,o]of Object.entries(e)){if(t===`default_view`||a(o,n))continue;let e=!1;for(let[n,r]of Object.entries(f))if(r.has(t)&&i[n]&&a(o,i[n])){e=!0;break}e||(r[t]=o)}return r},m=e=>{let{Abroad:t,...n}=e;return t?{default_view:t,...n}:e};var h=async e=>{let[t,n]=await Promise.all([l(e,m(c)),d(e,m(u))]);return{v4:p(t),v6:p(n)}};export{h as default};
package/package.json ADDED
@@ -0,0 +1 @@
1
+ {"name":"@js0.site/edns","version":"0.1.1","description":"Geo-aware CNAME flattening via DoH + ECS / 基于 DoH + ECS 的地理感知 CNAME 扁平化","keywords":["dns","doh","ecs","cname","geodns"],"repository":{"type":"git","url":"git+https://github.com/js0-site/js.git"},"homepage":"https://github.com/js0-site/js/tree/main/edns","author":"jssite@googlegroups.com","license":"MulanPSL-2.0","exports":{".":"./lib.js","./*":"./*"},"files":["*"],"type":"module","dependencies":{"maxmind":"^5.0.1","p-limit":"^7.2.0"}}
package/v4/CN.js ADDED
@@ -0,0 +1,104 @@
1
+ export default {
2
+ // 东北
3
+ Dianxin_Liaoning: "219.148.204.66",
4
+ Yidong_Liaoning: "211.137.32.178",
5
+ Liantong_Liaoning: "202.96.64.68",
6
+ Dianxin_Jilin: "219.149.194.55",
7
+ Yidong_Jilin: "211.141.16.99",
8
+ Liantong_Jilin: "202.98.0.68",
9
+ Dianxin_Heilongjiang: "112.100.100.100",
10
+ Yidong_Heilongjiang: "211.137.241.34",
11
+ Liantong_Heilongjiang: "202.97.224.68",
12
+ // 华北
13
+ Dianxin_Beijing: "219.141.136.10",
14
+ Yidong_Beijing: "221.130.33.52",
15
+ Liantong_Beijing: "202.106.196.115",
16
+ Dianxin_Tianjin: "219.150.32.132",
17
+ Yidong_Tianjin: "211.137.160.5",
18
+ Liantong_Tianjin: "202.99.96.68",
19
+ Dianxin_Hebei: "222.222.222.222",
20
+ Yidong_Hebei: "211.138.13.66",
21
+ Liantong_Hebei: "202.99.160.68",
22
+ Dianxin_Shanxi: "219.149.135.188",
23
+ Yidong_Shanxi: "211.138.106.3",
24
+ Liantong_Shanxi: "202.99.216.113",
25
+ Dianxin_Neimenggu: "219.148.162.31",
26
+ Yidong_Neimenggu: "211.138.91.1",
27
+ Liantong_Neimenggu: "202.99.224.68",
28
+ // 华南
29
+ Dianxin_Hainan: "202.100.192.68",
30
+ Yidong_Hainan: "221.176.88.95",
31
+ Liantong_Hainan: "221.11.132.2",
32
+ Dianxin_Guangdong: "202.96.134.133",
33
+ Yidong_Guangdong: "211.139.163.6",
34
+ Liantong_Guangdong: "210.21.196.6",
35
+ Dianxin_Guangxi: "202.103.225.68",
36
+ Yidong_Guangxi: "211.138.245.180",
37
+ Liantong_Guangxi: "221.7.128.68",
38
+ Dianxin_Fujian: "218.85.152.99",
39
+ Yidong_Fujian: "211.138.151.161",
40
+ Liantong_Fujian: "218.104.128.106",
41
+ // 华中
42
+ Dianxin_Hunan: "222.246.129.80",
43
+ Yidong_Hunan: "211.142.210.98",
44
+ Liantong_Hunan: "58.20.127.238",
45
+ Dianxin_Hubei: "202.103.24.68",
46
+ Yidong_Hubei: "211.137.58.20",
47
+ Liantong_Hubei: "218.104.111.114",
48
+ Dianxin_Henan: "222.85.85.85",
49
+ Yidong_Henan: "211.138.24.71",
50
+ Liantong_Henan: "202.102.224.68",
51
+ Dianxin_Jiangxi: "202.101.224.69",
52
+ Yidong_Jiangxi: "211.141.90.68",
53
+ Liantong_Jiangxi: "220.248.192.12",
54
+ // 华东
55
+ Dianxin_Shanghai: "202.96.209.133",
56
+ Yidong_Shanghai: "211.136.112.50",
57
+ Liantong_Shanghai: "210.22.70.3",
58
+ Dianxin_Jiangsu: "218.2.2.2",
59
+ Yidong_Jiangsu: "221.131.143.69",
60
+ Liantong_Jiangsu: "221.6.4.66",
61
+ Dianxin_Zhejiang: "202.101.172.35",
62
+ Yidong_Zhejiang: "211.140.13.188",
63
+ Liantong_Zhejiang: "221.12.1.227",
64
+ Dianxin_Anhui: "61.132.163.68",
65
+ Yidong_Anhui: "211.138.180.2",
66
+ Liantong_Anhui: "218.104.78.2",
67
+ Dianxin_Shandong: "219.146.1.66",
68
+ Yidong_Shandong: "218.201.96.130",
69
+ Liantong_Shandong: "202.102.128.68",
70
+ // 西南
71
+ Dianxin_Chongqing: "61.128.192.68",
72
+ Yidong_Chongqing: "218.201.4.3",
73
+ Liantong_Chongqing: "221.5.203.98",
74
+ Dianxin_Sichuan: "61.139.2.69",
75
+ Yidong_Sichuan: "211.137.82.4",
76
+ Liantong_Sichuan: "119.6.6.6",
77
+ Dianxin_Guizhou: "202.98.192.67",
78
+ Yidong_Guizhou: "211.139.5.29",
79
+ Liantong_Guizhou: "221.13.28.234",
80
+ Dianxin_Yunnan: "222.172.200.68",
81
+ Yidong_Yunnan: "211.139.29.68",
82
+ Liantong_Yunnan: "221.3.131.11",
83
+ Dianxin_Xizang: "202.98.224.68",
84
+ Yidong_Xizang: "211.139.73.34",
85
+ Liantong_Xizang: "221.13.65.34",
86
+ // 西北
87
+ Dianxin_Shaanxi: "218.30.19.40",
88
+ Yidong_Shaanxi: "211.137.130.3",
89
+ Liantong_Shaanxi: "221.11.1.67",
90
+ Dianxin_Gansu: "202.100.64.68",
91
+ Yidong_Gansu: "218.203.160.194",
92
+ Liantong_Gansu: "221.7.34.10",
93
+ Dianxin_Qinghai: "202.100.128.68",
94
+ Yidong_Qinghai: "211.138.75.123",
95
+ Liantong_Qinghai: "221.207.58.58",
96
+ Dianxin_Ningxia: "222.75.152.129",
97
+ Yidong_Ningxia: "218.203.123.116",
98
+ Liantong_Ningxia: "211.93.0.81",
99
+ Dianxin_Xinjiang: "61.128.114.166",
100
+ Yidong_Xinjiang: "218.202.152.130",
101
+ Liantong_Xinjiang: "221.7.1.21",
102
+ // 教育网
103
+ Jiaoyuwang: "202.112.144.30",
104
+ };
package/v4/GLOBAL.js ADDED
@@ -0,0 +1,51 @@
1
+ export default {
2
+ // 境外
3
+ Abroad: "8.8.8.8", // Google DNS
4
+ // 亚太地区
5
+ AP: "210.130.0.1", // 日本 IIJ
6
+ HK: "202.181.224.2", // 中国香港 HKBN
7
+ MO: "202.175.3.3", // 中国澳门 CTM
8
+ TW: "168.95.1.1", // 中国台湾 HiNet
9
+ JP: "210.130.0.1", // 日本 IIJ
10
+ KR: "168.126.63.1", // 韩国 KT
11
+ SG: "165.21.83.88", // 新加坡 Singtel
12
+ TH: "203.113.5.130", // 泰国 CAT Telecom
13
+ MY: "49.236.193.35", // 马来西亚 TM
14
+ ID: "202.134.0.155", // 印度尼西亚 Telkom
15
+ PH: "121.58.203.4", // 菲律宾 PLDT
16
+ VN: "203.162.4.191", // 越南 VNPT
17
+ IN: "117.254.160.1", // 印度 BSNL
18
+ AE: "94.200.200.200", // 阿联酋 du
19
+ TR: "195.175.39.39", // 土耳其 Turk Telekom
20
+ IL: "147.235.225.1", // 以色列 Bezeq
21
+ // 大洋洲
22
+ OA: "139.130.4.5", // 大洋洲
23
+ AU: "139.130.4.5", // 澳大利亚 Telstra
24
+ NZ: "202.27.184.3", // 新西兰 Spark
25
+ // 欧洲
26
+ EU: "194.150.168.168", // 德国 dns.as250.net
27
+ DE: "194.150.168.168", // 德国 dns.as250.net
28
+ GB: "158.43.128.1", // 英国 Cable&Wireless
29
+ FR: "80.67.169.12", // 法国 FDN
30
+ NL: "193.176.144.22", // 荷兰 Freedom Internet
31
+ IT: "217.141.248.10", // 意大利 Wind
32
+ ES: "195.235.113.3", // 西班牙 Telefonica
33
+ RU: "77.88.8.8", // 俄罗斯 Yandex
34
+ PL: "194.204.159.1", // 波兰 Orange
35
+ SE: "194.17.47.1", // 瑞典 Netnod
36
+ CH: "195.186.1.111", // 瑞士 Swisscom
37
+ // 北美洲
38
+ NA: "64.6.64.6", // 美国 Verisign
39
+ US: "64.6.64.6", // 美国 Verisign
40
+ CA: "149.112.121.10", // 加拿大 CIRA
41
+ MX: "200.94.0.10", // 墨西哥 Axtel
42
+ // 南美洲
43
+ LA: "200.221.11.101", // 南美洲
44
+ BR: "200.221.11.100", // 巴西 Telefonica
45
+ AR: "200.49.130.47", // 阿根廷 Telecom
46
+ CL: "200.1.123.46", // 智利 Entel
47
+ // 非洲
48
+ AF: "196.216.2.1", // 非洲 Liquid Telecom
49
+ ZA: "169.239.128.40", // 南非 TENET
50
+ EG: "41.128.128.128", // 埃及 TE Data
51
+ };
package/v4.js ADDED
@@ -0,0 +1 @@
1
+ var e=class{value;next;constructor(e){this.value=e}},t=class{#head;#tail;#size;constructor(){this.clear()}enqueue(t){let n=new e(t);this.#head?(this.#tail.next=n,this.#tail=n):(this.#head=n,this.#tail=n),this.#size++}dequeue(){let e=this.#head;if(e)return this.#head=this.#head.next,this.#size--,this.#head||(this.#tail=void 0),e.value}peek(){if(this.#head)return this.#head.value}clear(){this.#head=void 0,this.#tail=void 0,this.#size=0}get size(){return this.#size}*[Symbol.iterator](){let e=this.#head;for(;e;)yield e.value,e=e.next}*drain(){for(;this.#head;)yield this.dequeue()}};function n(e){r(e);let n=new t,i=0,a=()=>{i<e&&n.size>0&&(i++,n.dequeue()())},o=()=>{i--,a()},s=async(e,t,n)=>{let r=(async()=>e(...n))();t(r);try{await r}catch{}o()},c=(t,r,o)=>{new Promise(e=>{n.enqueue(e)}).then(s.bind(void 0,t,r,o)),i<e&&a()},l=(e,...t)=>new Promise(n=>{c(e,n,t)});return Object.defineProperties(l,{activeCount:{get:()=>i},pendingCount:{get:()=>n.size},clearQueue:{value(){n.clear()}},concurrency:{get:()=>e,set(t){r(t),e=t,queueMicrotask(()=>{for(;i<e&&n.size>0;)a()})}},map:{async value(e,t){let n=Array.from(e,(e,n)=>this(t,e,n));return Promise.all(n)}}}),l}function r(e){if(!((Number.isInteger(e)||e===1/0)&&e>0))throw TypeError("Expected `concurrency` to be a number from 1 and up")}const i=n(10),a=async(e,t,n)=>{let r=new URL(e);for(let[e,n]of Object.entries(t))r.searchParams.set(e,n);let i;for(let e=0;e<3;e++)try{let e=await fetch(r),t=await e.json();return t.Answer?.map(e=>e.data).filter(n)||[]}catch(t){i=t,console.error(`retry ${e+1}/3: ${t}`)}throw i},o=async(e,t,n,r,o)=>{let s={},c=[];for(let[l,u]of Object.entries(n))c.push(i(async()=>{s[l]=await a(e,{name:t,type:r,edns_client_subnet:u},o)}));return await Promise.all(c),s};var s={Dianxin_Liaoning:`219.148.204.66`,Yidong_Liaoning:`211.137.32.178`,Liantong_Liaoning:`202.96.64.68`,Dianxin_Jilin:`219.149.194.55`,Yidong_Jilin:`211.141.16.99`,Liantong_Jilin:`202.98.0.68`,Dianxin_Heilongjiang:`112.100.100.100`,Yidong_Heilongjiang:`211.137.241.34`,Liantong_Heilongjiang:`202.97.224.68`,Dianxin_Beijing:`219.141.136.10`,Yidong_Beijing:`221.130.33.52`,Liantong_Beijing:`202.106.196.115`,Dianxin_Tianjin:`219.150.32.132`,Yidong_Tianjin:`211.137.160.5`,Liantong_Tianjin:`202.99.96.68`,Dianxin_Hebei:`222.222.222.222`,Yidong_Hebei:`211.138.13.66`,Liantong_Hebei:`202.99.160.68`,Dianxin_Shanxi:`219.149.135.188`,Yidong_Shanxi:`211.138.106.3`,Liantong_Shanxi:`202.99.216.113`,Dianxin_Neimenggu:`219.148.162.31`,Yidong_Neimenggu:`211.138.91.1`,Liantong_Neimenggu:`202.99.224.68`,Dianxin_Hainan:`202.100.192.68`,Yidong_Hainan:`221.176.88.95`,Liantong_Hainan:`221.11.132.2`,Dianxin_Guangdong:`202.96.134.133`,Yidong_Guangdong:`211.139.163.6`,Liantong_Guangdong:`210.21.196.6`,Dianxin_Guangxi:`202.103.225.68`,Yidong_Guangxi:`211.138.245.180`,Liantong_Guangxi:`221.7.128.68`,Dianxin_Fujian:`218.85.152.99`,Yidong_Fujian:`211.138.151.161`,Liantong_Fujian:`218.104.128.106`,Dianxin_Hunan:`222.246.129.80`,Yidong_Hunan:`211.142.210.98`,Liantong_Hunan:`58.20.127.238`,Dianxin_Hubei:`202.103.24.68`,Yidong_Hubei:`211.137.58.20`,Liantong_Hubei:`218.104.111.114`,Dianxin_Henan:`222.85.85.85`,Yidong_Henan:`211.138.24.71`,Liantong_Henan:`202.102.224.68`,Dianxin_Jiangxi:`202.101.224.69`,Yidong_Jiangxi:`211.141.90.68`,Liantong_Jiangxi:`220.248.192.12`,Dianxin_Shanghai:`202.96.209.133`,Yidong_Shanghai:`211.136.112.50`,Liantong_Shanghai:`210.22.70.3`,Dianxin_Jiangsu:`218.2.2.2`,Yidong_Jiangsu:`221.131.143.69`,Liantong_Jiangsu:`221.6.4.66`,Dianxin_Zhejiang:`202.101.172.35`,Yidong_Zhejiang:`211.140.13.188`,Liantong_Zhejiang:`221.12.1.227`,Dianxin_Anhui:`61.132.163.68`,Yidong_Anhui:`211.138.180.2`,Liantong_Anhui:`218.104.78.2`,Dianxin_Shandong:`219.146.1.66`,Yidong_Shandong:`218.201.96.130`,Liantong_Shandong:`202.102.128.68`,Dianxin_Chongqing:`61.128.192.68`,Yidong_Chongqing:`218.201.4.3`,Liantong_Chongqing:`221.5.203.98`,Dianxin_Sichuan:`61.139.2.69`,Yidong_Sichuan:`211.137.82.4`,Liantong_Sichuan:`119.6.6.6`,Dianxin_Guizhou:`202.98.192.67`,Yidong_Guizhou:`211.139.5.29`,Liantong_Guizhou:`221.13.28.234`,Dianxin_Yunnan:`222.172.200.68`,Yidong_Yunnan:`211.139.29.68`,Liantong_Yunnan:`221.3.131.11`,Dianxin_Xizang:`202.98.224.68`,Yidong_Xizang:`211.139.73.34`,Liantong_Xizang:`221.13.65.34`,Dianxin_Shaanxi:`218.30.19.40`,Yidong_Shaanxi:`211.137.130.3`,Liantong_Shaanxi:`221.11.1.67`,Dianxin_Gansu:`202.100.64.68`,Yidong_Gansu:`218.203.160.194`,Liantong_Gansu:`221.7.34.10`,Dianxin_Qinghai:`202.100.128.68`,Yidong_Qinghai:`211.138.75.123`,Liantong_Qinghai:`221.207.58.58`,Dianxin_Ningxia:`222.75.152.129`,Yidong_Ningxia:`218.203.123.116`,Liantong_Ningxia:`211.93.0.81`,Dianxin_Xinjiang:`61.128.114.166`,Yidong_Xinjiang:`218.202.152.130`,Liantong_Xinjiang:`221.7.1.21`,Jiaoyuwang:`202.112.144.30`},c={Abroad:`8.8.8.8`,AP:`210.130.0.1`,HK:`202.181.224.2`,MO:`202.175.3.3`,TW:`168.95.1.1`,JP:`210.130.0.1`,KR:`168.126.63.1`,SG:`165.21.83.88`,TH:`203.113.5.130`,MY:`49.236.193.35`,ID:`202.134.0.155`,PH:`121.58.203.4`,VN:`203.162.4.191`,IN:`117.254.160.1`,AE:`94.200.200.200`,TR:`195.175.39.39`,IL:`147.235.225.1`,OA:`139.130.4.5`,AU:`139.130.4.5`,NZ:`202.27.184.3`,EU:`194.150.168.168`,DE:`194.150.168.168`,GB:`158.43.128.1`,FR:`80.67.169.12`,NL:`193.176.144.22`,IT:`217.141.248.10`,ES:`195.235.113.3`,RU:`77.88.8.8`,PL:`194.204.159.1`,SE:`194.17.47.1`,CH:`195.186.1.111`,NA:`64.6.64.6`,US:`64.6.64.6`,CA:`149.112.121.10`,MX:`200.94.0.10`,LA:`200.221.11.101`,BR:`200.221.11.100`,AR:`200.49.130.47`,CL:`200.1.123.46`,AF:`196.216.2.1`,ZA:`169.239.128.40`,EG:`41.128.128.128`},l=(e,t)=>o(`https://1.12.12.12/resolve`,e,t,`A`,e=>/^\d+\.\d+\.\d+\.\d+$/.test(e));export{s as CN,c as GLOBAL,l as default};
package/v6/CN.js ADDED
@@ -0,0 +1 @@
1
+ export { default } from "../v4/CN.js";
package/v6/GLOBAL.js ADDED
@@ -0,0 +1,34 @@
1
+ export default {
2
+ // 境外
3
+ Abroad: "2001:4860:4860::8888", // Google DNS
4
+ // 亚太地区
5
+ AP: "2001:240::1", // 日本 IIJ
6
+ HK: "2403:2c80::1", // 中国香港
7
+ TW: "2001:b000:168::1", // 中国台湾 HiNet
8
+ JP: "2001:240::1", // 日本 IIJ
9
+ KR: "2001:dc5:ad::1", // 韩国 LG U+
10
+ SG: "2406:3003:2006:1475::1", // 新加坡 Singtel
11
+ IN: "2405:200:1000::1", // 印度 Airtel
12
+ // 大洋洲
13
+ OA: "2001:44b8:1::1", // 大洋洲
14
+ AU: "2001:44b8:1::1", // 澳大利亚 Telstra
15
+ // 欧洲
16
+ EU: "2a01:4f8:0:a0a1::add:9999", // 德国
17
+ DE: "2a01:4f8:0:a0a1::add:9999", // 德国
18
+ GB: "2a02:c7f:7e2e:6c00::1", // 英国 BT
19
+ FR: "2001:910:800::12", // 法国 FDN
20
+ NL: "2a02:2770::21a:4aff:fec8:b0d", // 荷兰
21
+ RU: "2a02:6b8::feed:0ff", // 俄罗斯 Yandex
22
+ SE: "2a01:3f0:0:301::1", // 瑞典 Netnod
23
+ CH: "2a02:a90:0:10::11", // 瑞士
24
+ // 北美洲
25
+ NA: "2620:74:1b::1:1", // 美国 Verisign
26
+ US: "2620:74:1b::1:1", // 美国 Verisign
27
+ CA: "2620:10a:80bb::10", // 加拿大 CIRA
28
+ // 南美洲
29
+ LA: "2001:12ff::1", // 南美洲
30
+ BR: "2804:10:10::10", // 巴西 Registro.br
31
+ // 非洲
32
+ AF: "2c0f:fc89:8001::1", // 非洲 Liquid Telecom
33
+ ZA: "2001:4200:1::1", // 南非 TENET
34
+ };
package/v6.js ADDED
@@ -0,0 +1 @@
1
+ var e=class{value;next;constructor(e){this.value=e}},t=class{#head;#tail;#size;constructor(){this.clear()}enqueue(t){let n=new e(t);this.#head?(this.#tail.next=n,this.#tail=n):(this.#head=n,this.#tail=n),this.#size++}dequeue(){let e=this.#head;if(e)return this.#head=this.#head.next,this.#size--,this.#head||(this.#tail=void 0),e.value}peek(){if(this.#head)return this.#head.value}clear(){this.#head=void 0,this.#tail=void 0,this.#size=0}get size(){return this.#size}*[Symbol.iterator](){let e=this.#head;for(;e;)yield e.value,e=e.next}*drain(){for(;this.#head;)yield this.dequeue()}};function n(e){r(e);let n=new t,i=0,a=()=>{i<e&&n.size>0&&(i++,n.dequeue()())},o=()=>{i--,a()},s=async(e,t,n)=>{let r=(async()=>e(...n))();t(r);try{await r}catch{}o()},c=(t,r,o)=>{new Promise(e=>{n.enqueue(e)}).then(s.bind(void 0,t,r,o)),i<e&&a()},l=(e,...t)=>new Promise(n=>{c(e,n,t)});return Object.defineProperties(l,{activeCount:{get:()=>i},pendingCount:{get:()=>n.size},clearQueue:{value(){n.clear()}},concurrency:{get:()=>e,set(t){r(t),e=t,queueMicrotask(()=>{for(;i<e&&n.size>0;)a()})}},map:{async value(e,t){let n=Array.from(e,(e,n)=>this(t,e,n));return Promise.all(n)}}}),l}function r(e){if(!((Number.isInteger(e)||e===1/0)&&e>0))throw TypeError("Expected `concurrency` to be a number from 1 and up")}const i=n(10),a=async(e,t,n)=>{let r=new URL(e);for(let[e,n]of Object.entries(t))r.searchParams.set(e,n);let i;for(let e=0;e<3;e++)try{let e=await fetch(r),t=await e.json();return t.Answer?.map(e=>e.data).filter(n)||[]}catch(t){i=t,console.error(`retry ${e+1}/3: ${t}`)}throw i},o=async(e,t,n,r,o)=>{let s={},c=[];for(let[l,u]of Object.entries(n))c.push(i(async()=>{s[l]=await a(e,{name:t,type:r,edns_client_subnet:u},o)}));return await Promise.all(c),s};var s={Dianxin_Liaoning:`219.148.204.66`,Yidong_Liaoning:`211.137.32.178`,Liantong_Liaoning:`202.96.64.68`,Dianxin_Jilin:`219.149.194.55`,Yidong_Jilin:`211.141.16.99`,Liantong_Jilin:`202.98.0.68`,Dianxin_Heilongjiang:`112.100.100.100`,Yidong_Heilongjiang:`211.137.241.34`,Liantong_Heilongjiang:`202.97.224.68`,Dianxin_Beijing:`219.141.136.10`,Yidong_Beijing:`221.130.33.52`,Liantong_Beijing:`202.106.196.115`,Dianxin_Tianjin:`219.150.32.132`,Yidong_Tianjin:`211.137.160.5`,Liantong_Tianjin:`202.99.96.68`,Dianxin_Hebei:`222.222.222.222`,Yidong_Hebei:`211.138.13.66`,Liantong_Hebei:`202.99.160.68`,Dianxin_Shanxi:`219.149.135.188`,Yidong_Shanxi:`211.138.106.3`,Liantong_Shanxi:`202.99.216.113`,Dianxin_Neimenggu:`219.148.162.31`,Yidong_Neimenggu:`211.138.91.1`,Liantong_Neimenggu:`202.99.224.68`,Dianxin_Hainan:`202.100.192.68`,Yidong_Hainan:`221.176.88.95`,Liantong_Hainan:`221.11.132.2`,Dianxin_Guangdong:`202.96.134.133`,Yidong_Guangdong:`211.139.163.6`,Liantong_Guangdong:`210.21.196.6`,Dianxin_Guangxi:`202.103.225.68`,Yidong_Guangxi:`211.138.245.180`,Liantong_Guangxi:`221.7.128.68`,Dianxin_Fujian:`218.85.152.99`,Yidong_Fujian:`211.138.151.161`,Liantong_Fujian:`218.104.128.106`,Dianxin_Hunan:`222.246.129.80`,Yidong_Hunan:`211.142.210.98`,Liantong_Hunan:`58.20.127.238`,Dianxin_Hubei:`202.103.24.68`,Yidong_Hubei:`211.137.58.20`,Liantong_Hubei:`218.104.111.114`,Dianxin_Henan:`222.85.85.85`,Yidong_Henan:`211.138.24.71`,Liantong_Henan:`202.102.224.68`,Dianxin_Jiangxi:`202.101.224.69`,Yidong_Jiangxi:`211.141.90.68`,Liantong_Jiangxi:`220.248.192.12`,Dianxin_Shanghai:`202.96.209.133`,Yidong_Shanghai:`211.136.112.50`,Liantong_Shanghai:`210.22.70.3`,Dianxin_Jiangsu:`218.2.2.2`,Yidong_Jiangsu:`221.131.143.69`,Liantong_Jiangsu:`221.6.4.66`,Dianxin_Zhejiang:`202.101.172.35`,Yidong_Zhejiang:`211.140.13.188`,Liantong_Zhejiang:`221.12.1.227`,Dianxin_Anhui:`61.132.163.68`,Yidong_Anhui:`211.138.180.2`,Liantong_Anhui:`218.104.78.2`,Dianxin_Shandong:`219.146.1.66`,Yidong_Shandong:`218.201.96.130`,Liantong_Shandong:`202.102.128.68`,Dianxin_Chongqing:`61.128.192.68`,Yidong_Chongqing:`218.201.4.3`,Liantong_Chongqing:`221.5.203.98`,Dianxin_Sichuan:`61.139.2.69`,Yidong_Sichuan:`211.137.82.4`,Liantong_Sichuan:`119.6.6.6`,Dianxin_Guizhou:`202.98.192.67`,Yidong_Guizhou:`211.139.5.29`,Liantong_Guizhou:`221.13.28.234`,Dianxin_Yunnan:`222.172.200.68`,Yidong_Yunnan:`211.139.29.68`,Liantong_Yunnan:`221.3.131.11`,Dianxin_Xizang:`202.98.224.68`,Yidong_Xizang:`211.139.73.34`,Liantong_Xizang:`221.13.65.34`,Dianxin_Shaanxi:`218.30.19.40`,Yidong_Shaanxi:`211.137.130.3`,Liantong_Shaanxi:`221.11.1.67`,Dianxin_Gansu:`202.100.64.68`,Yidong_Gansu:`218.203.160.194`,Liantong_Gansu:`221.7.34.10`,Dianxin_Qinghai:`202.100.128.68`,Yidong_Qinghai:`211.138.75.123`,Liantong_Qinghai:`221.207.58.58`,Dianxin_Ningxia:`222.75.152.129`,Yidong_Ningxia:`218.203.123.116`,Liantong_Ningxia:`211.93.0.81`,Dianxin_Xinjiang:`61.128.114.166`,Yidong_Xinjiang:`218.202.152.130`,Liantong_Xinjiang:`221.7.1.21`,Jiaoyuwang:`202.112.144.30`},c={Abroad:`2001:4860:4860::8888`,AP:`2001:240::1`,HK:`2403:2c80::1`,TW:`2001:b000:168::1`,JP:`2001:240::1`,KR:`2001:dc5:ad::1`,SG:`2406:3003:2006:1475::1`,IN:`2405:200:1000::1`,OA:`2001:44b8:1::1`,AU:`2001:44b8:1::1`,EU:`2a01:4f8:0:a0a1::add:9999`,DE:`2a01:4f8:0:a0a1::add:9999`,GB:`2a02:c7f:7e2e:6c00::1`,FR:`2001:910:800::12`,NL:`2a02:2770::21a:4aff:fec8:b0d`,RU:`2a02:6b8::feed:0ff`,SE:`2a01:3f0:0:301::1`,CH:`2a02:a90:0:10::11`,NA:`2620:74:1b::1:1`,US:`2620:74:1b::1:1`,CA:`2620:10a:80bb::10`,LA:`2001:12ff::1`,BR:`2804:10:10::10`,AF:`2c0f:fc89:8001::1`,ZA:`2001:4200:1::1`},l=(e,t)=>o(`https://dns.alidns.com/resolve`,e,t,`AAAA`,e=>e.includes(`:`));export{s as CN,c as GLOBAL,l as default};