@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 +557 -0
- package/base.js +1 -0
- package/cn.js +1 -0
- package/geo/GLOBAL.js +47 -0
- package/global.js +1 -0
- package/package.json +1 -0
- package/v4/CN.js +104 -0
- package/v4/GLOBAL.js +51 -0
- package/v4.js +1 -0
- package/v6/CN.js +1 -0
- package/v6/GLOBAL.js +34 -0
- package/v6.js +1 -0
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};
|