@js0.site/edns
v0.1.5
Published
Geo-aware CNAME flattening via DoH + ECS / 基于 DoH + ECS 的地理感知 CNAME 扁平化
Downloads
1,001
Readme
edns : Geo-aware CNAME Flattening via DoH + ECS
Table of Contents
Background
Why Root Domains Cannot Use CNAME
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.
More critically: CNAME conflicts with MX records. If root domain has CNAME, mail servers cannot query MX records, causing [email protected] to fail receiving emails.
This forces CDN users to use static A/AAAA records for root domains, losing the dynamic routing benefits of CNAME.
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.
Limitations of Cloudflare CNAME Flattening
Cloudflare offers free CNAME flattening, but with issues for China:
- Fixed query location: Cloudflare queries from its data centers, which are outside China
- No ECS support: Queries lack client subnet info, so CDN cannot determine real user location
- Suboptimal results: Chinese users get IPs optimized for overseas, not domestic nodes
- Single IP return: Flattening returns only one IP, cannot leverage multi-node load balancing
Example: Tencent Cloud CDN's CNAME resolved by Cloudflare may return Hong Kong or Singapore nodes instead of Beijing or Shanghai.
Why Anycast works overseas but not in China?
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:
- ISP interconnection bottleneck: Limited bandwidth between Telecom/Mobile/Unicom, high cross-network latency
- BGP routing policies: Complex BGP policies in Chinese ISPs, Anycast routing not always optimal
- Regulatory requirements: IP broadcasting in China requires registration, high barrier for Anycast deployment
- Network topology: China's network is segmented by province; same-province cross-ISP latency can exceed cross-province same-ISP latency
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.
China-Friendly Free CDN Solution
Strategy: Use Cloudflare for overseas, domestic CDN for China, with GeoDNS routing.
Recommended architecture:
graph TD
DNS[GeoDNS Service<br/>Huawei DNS Free]
DNS --> OVERSEAS[Overseas]
DNS --> CT[Telecom]
DNS --> CM[Mobile]
DNS --> CU[Unicom]
OVERSEAS --> CF[Cloudflare SaaS]
CT --> TENCENT[Tencent EdgeOne]
CM --> ALI[Alibaba ESA]
CU --> TENCENTFree resource combination:
- Overseas: Cloudflare SaaS custom hostname (free, CNAME access)
- China: Tencent EdgeOne (free tier) + Alibaba ESA (free tier)
- Routing: Huawei Cloud DNS (free, supports province/ISP-level routing)
This tool's role: Resolve CDN CNAME to region-optimal IPs, sync to Huawei Cloud DNS via API, enabling precise routing.
Why Huawei Cloud DNS
Comparison of major Chinese DNS providers:
| Provider | Free Geo-routing | Line Coverage | API Support | |----------|-----------------|---------------|-------------| | Alibaba DNS | ❌ Paid | Full | ✅ | | Tencent DNSPod | ❌ Paid | Full | ✅ | | Huawei Cloud DNS | ✅ Free | 31 provinces × 3 ISPs | ✅ | | Cloudflare | ✅ Free | Country-level only | ✅ |
Huawei Cloud DNS advantages:
- Free province + ISP routing: Supports Telecom/Mobile/Unicom × 31 provinces = 93 lines
- Full API: Batch import, record updates, automation-ready
- No ICP requirement: Works with overseas domains
Workflow with this tool: Run scripts periodically to fetch latest CDN IPs, update DNS records via API, achieving dynamic CNAME flattening.
Introduction
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.
Use cases:
- Sync DNS records to Huawei Cloud DNS for GeoDNS routing
- Test CDN node distribution across regions
- Analyze DNS resolution behavior from different locations
Features
- IPv4 via DNSPod DoH (
1.12.12.12) - IPv6 via Alibaba DoH (
dns.alidns.com) - Pre-configured ECS IPs: 31 Chinese provinces × 3 ISPs + 40+ global regions
- Concurrent queries with rate limiting (10 parallel)
- Auto-retry on failure (3 attempts)
- Smart aggregation: deduplicate IPs by carrier/city/continent hierarchy
- Huawei Cloud DNS sync: create, update, delete records via API
Installation
bun i @js0.site/ednsUsage
Huawei Cloud DNS Sync
Typical scenario: Cloudflare overseas + Tencent EdgeOne / Alibaba ESA in China, routed via Huawei Cloud DNS.
import Hw from "@js0.site/edns/Hw.js";
const hw = Hw([ak, sk, endpoint, project_id]);
const result = await hw(
"example.com",
// China CDN CNAMEs (Tencent EdgeOne + Alibaba ESA)
["example.com.cdn.dnsv1.com.cn", "example.com.chinese.cdnhwc2.com"],
// Global CDN CNAME (Cloudflare SaaS)
["cdn.example.workers.dev"],
);
console.log(`add ${result.add}, update ${result.upd}, delete ${result.rm}, unchanged ${result.same}`);Sync behavior:
- Add: target has record, DNS doesn't → create
- Update: both have record, IPs differ → update in place
- Delete: DNS has record, target doesn't → remove
- Unchanged: IPs match → skip
Free CDN combination:
- Overseas: Cloudflare SaaS custom hostname (free, CNAME access)
- China Telecom/Unicom: Tencent EdgeOne (free tier)
- China Mobile: Alibaba ESA (free tier)
- Routing: Huawei Cloud DNS (free, 31 provinces × 3 ISPs)
China CDN free tiers are limited; using two providers splits traffic and avoids quota limits.
China Regions
import cn from "@js0.site/edns/cn.js";
const { v4, v6 } = await cn("cdn.example.com");
// v4: { CN: [...], Dianxin: [...], Yidong: [...], Beijing: [...], ... }
// v6: { CN: [...], Dianxin: [...], Yidong: [...], Beijing: [...], ... }Results are aggregated by:
CN: top 3 IPs across all linesDianxin/Yidong/Liantong: top 3 per carrier- City names: top 3 per city
- Individual lines only if not covered by above
Global Regions
import global from "@js0.site/edns/global.js";
const { v4, v6 } = await global("cdn.example.com");
// v4: { default_view: [...], AP: [...], EU: [...], US: [...], ... }
// v6: { default_view: [...], AP: [...], EU: [...], JP: [...], ... }Results filtered by hierarchy:
default_view: global default- Continents:
AP,EU,NA,LA,OA,AF - Countries only if different from continent
Low-level API
import flattenV4, { CN, GLOBAL } from "@js0.site/edns/v4.js";
import flattenV6 from "@js0.site/edns/v6.js";
// Query specific lines
const result = await flattenV4("cdn.example.com", {
line1: "8.8.8.8",
line2: "1.1.1.1",
});
// { line1: ["1.2.3.4"], line2: ["5.6.7.8"] }
// Use built-in line configs
const cnResult = await flattenV4("cdn.example.com", CN);Run tests:
bun test/cn.js # China regions
bun test/global.js # Global regions
bun test/v4.js # IPv4 raw
bun test/v6.js # IPv6 raw
bun test/verify.js # IP geolocation check
bun test/hwSet.js # Huawei DNS syncArchitecture
graph TD
subgraph High-Level
CN[cn.js] --> V4
CN --> V6
GL[global.js] --> V4
GL --> V6
end
subgraph Core
V4[v4.js] --> BASE[base.js]
V6[v6.js] --> BASE
BASE --> DOH[DoH Server]
end
subgraph Config
V4CN[v4/CN.js] --> V4
V4GL[v4/GLOBAL.js] --> V4
V6CN[v6/CN.js] --> V6
V6GL[v6/GLOBAL.js] --> V6
GEO[geo/GLOBAL.js] --> GL
end
subgraph Sync
HW[Hw.js] --> CN
HW --> GL
HW --> HWAPI[Huawei Cloud DNS API]
end
DOH --> DNSPOD[DNSPod]
DOH --> ALI[Alibaba DNS]Call flow:
cn.js/global.jscallv4.jsandv6.jswith built-in line configsv4.js/v6.jscallflatten()with DoH endpoint and record typeflatten()iterates lines, constructs DoH queries with ECS parameterqueryDoh()sends HTTP request, parses JSON, filters valid IPs- High-level modules aggregate/filter results by hierarchy
Hw.jsresolves CNAMEs viacn.js/global.js, syncs to Huawei Cloud DNS
Directory Structure
src/
├── base.js # Core: queryDoh, flatten, isSubset
├── v4.js # IPv4 resolver (DNSPod DoH)
├── v6.js # IPv6 resolver (Alibaba DoH)
├── cn.js # China aggregator
├── global.js # Global aggregator
├── Hw.js # Huawei Cloud DNS sync
├── v4/
│ ├── CN.js # China ECS IPs (31 provinces × 3 ISPs)
│ └── GLOBAL.js # Global ECS IPs (40+ regions)
├── v6/
│ ├── CN.js # Re-exports v4/CN.js
│ └── GLOBAL.js # IPv6 global ECS IPs
└── geo/
└── GLOBAL.js # Continent → country mapping
test/
├── lib.js # Test utilities
├── cn.js # China test
├── global.js # Global test
├── v4.js # IPv4 test
├── v6.js # IPv6 test
├── verify.js # Geolocation verification
├── hwSet.js # Huawei DNS sync test
├── hwLines.js # List Huawei DNS lines
└── hwRateLimit.js # Rate limit testTech Stack
- Runtime: Bun / Node.js (ESM)
- HTTP: Native fetch API
- Concurrency: p-limit
- DNS Provider: Huawei Cloud SDK
- Geolocation: MaxMind GeoLite2 (dev)
- Protocol: DNS over HTTPS (RFC 8484), EDNS Client Subnet (RFC 7871)
History
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.
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.
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.
About
This project is an open-source component of js0.site ⋅ Refactoring the Internet Plan.
We are redefining the development paradigm of the Internet in a componentized way. Welcome to follow us:
edns : 基于 DoH + ECS 的地理感知 CNAME 扁平化
目录
背景
根域名为何不能用 CNAME
根据 RFC 1034,CNAME 记录不能与其他记录共存。而根域名(Zone Apex,如 example.com)必须有 SOA 和 NS 记录,因此无法设置 CNAME。
更关键的是:CNAME 与 MX 记录冲突。如果根域名设置 CNAME,邮件服务器无法查询 MX 记录,导致 [email protected] 收不到邮件。
这导致 CDN 接入时只能用 A/AAAA 记录指向固定 IP,无法享受 CNAME 带来的动态调度能力。
CNAME 扁平化(CNAME Flattening)应运而生:DNS 服务商在权威侧将 CNAME 解析为 A/AAAA 记录返回,对外表现为普通 A 记录,实际仍跟随 CNAME 目标动态变化。这样既能使用 CDN 的动态调度,又不影响 MX 等其他记录。
Cloudflare CNAME 扁平化的局限
Cloudflare 提供免费的 CNAME 扁平化,但存在问题:
- 解析位置固定:Cloudflare 从其数据中心发起 DNS 查询,对于中国 CDN,查询来源是海外 IP
- 无 ECS 支持:查询不携带客户端子网信息,CDN 无法判断真实用户位置
- 结果偏差:中国用户访问时,CDN 返回的是针对海外优化的节点 IP,而非中国大陆最优节点
- 单 IP 返回:扁平化后只返回单个 IP,无法利用多节点负载均衡
例如:腾讯云 CDN 的 CNAME 被 Cloudflare 解析时,返回的可能是香港或新加坡节点,而非北京或上海节点。
为什么海外 CDN 用 Anycast 没问题,中国大陆却不行?
Anycast 是将同一 IP 广播到多个地理位置,用户自动路由到最近节点。海外 CDN(如 Cloudflare)大量使用 Anycast,效果很好。但中国网络环境特殊:
- 运营商互联瓶颈:电信、移动、联通三大运营商之间互联带宽有限,跨网访问延迟高
- BGP 路由策略:中国大陆 ISP 的 BGP 路由策略复杂,Anycast 路由不一定最优
- 监管要求:中国大陆 IP 广播需要备案,Anycast 部署门槛高
- 网络拓扑:中国大陆网络以省为单位分割,同省不同运营商的延迟可能比跨省同运营商还高
因此中国大陆 CDN 普遍采用 Unicast + 智能 DNS 调度:为每个节点分配独立 IP,通过 DNS 返回最优节点。这要求 DNS 能识别用户的运营商和地理位置,正是本工具解决的问题。
中国友好的免费 CDN 方案
解决思路:境外用 Cloudflare,中国大陆用国产 CDN,通过 GeoDNS 分流。
推荐架构:
graph TD
DNS[GeoDNS 服务<br/>华为云 DNS 免费]
DNS --> OVERSEAS[境外线路]
DNS --> CT[电信线路]
DNS --> CM[移动线路]
DNS --> CU[联通线路]
OVERSEAS --> CF[Cloudflare SaaS]
CT --> TENCENT[腾讯云 EdgeOne]
CM --> ALI[阿里云 ESA]
CU --> TENCENT免费资源组合:
- 境外:Cloudflare SaaS 自定义域名(免费,通过 CNAME 接入)
- 境内:腾讯云 EdgeOne(免费额度)+ 阿里云 ESA(免费额度)
- 调度:华为云 DNS(免费,支持分省分运营商解析)
本工具的作用:将 CDN 的 CNAME 解析为各地区最优 IP,通过 API 同步到华为云 DNS,实现精准调度。
为什么选华为云 DNS
中国大陆主流 DNS 服务商对比:
| 服务商 | 免费版分区解析 | 线路覆盖 | API 支持 | |--------|---------------|---------|---------| | 阿里云 DNS | ❌ 需付费 | 全 | ✅ | | 腾讯云 DNSPod | ❌ 需付费 | 全 | ✅ | | 华为云 DNS | ✅ 免费 | 31省×3运营商 | ✅ | | Cloudflare | ✅ 免费 | 仅国家级 | ✅ |
华为云 DNS 优势:
- 免费分省分运营商解析:支持电信/移动/联通 × 31 省,共 93 条线路
- 完整 API:支持批量导入、更新记录,可自动化
- 无 ICP 限制:海外域名也可使用
本工具配合华为云 DNS 使用:定时运行脚本获取 CDN 最新 IP,通过 API 更新解析记录,实现动态 CNAME 扁平化。
简介
CNAME 扁平化是在 DNS 层面将 CNAME 记录解析为 A/AAAA 记录。本工具通过 DoH (DNS over HTTPS) 配合 EDNS Client Subnet (ECS) 查询 CDN 域名在不同地区的解析 IP。
适用场景:
- 同步解析记录到华为云 DNS,实现 GeoDNS 调度
- 测试 CDN 节点在各地区的分布
- 分析不同地理位置的 DNS 解析行为
功能
- IPv4 解析:DNSPod DoH (
1.12.12.12) - IPv6 解析:阿里云 DoH (
dns.alidns.com) - 预置 ECS IP:中国大陆 31 省 × 3 运营商 + 海外 40+ 地区
- 并发控制:限制 10 并发
- 自动重试:失败重试 3 次
- 智能聚合:按运营商/城市/洲级层次去重
- 华为云 DNS 同步:通过 API 创建、更新、删除记录
安装
bun i @js0.site/edns使用
华为云 DNS 同步
典型场景:境外 Cloudflare + 境内腾讯云 EdgeOne / 阿里云 ESA,通过华为云 DNS 分流。
import Hw from "@js0.site/edns/Hw.js";
const hw = Hw([ak, sk, endpoint, project_id]);
const result = await hw(
"example.com",
// 中国大陆 CDN CNAME(腾讯云 EdgeOne + 阿里云 ESA)
["example.com.cdn.dnsv1.com.cn", "example.com.chinese.cdnhwc2.com"],
// 海外 CDN CNAME(Cloudflare SaaS)
["cdn.example.workers.dev"],
);
console.log(`添加 ${result.add}, 更新 ${result.upd}, 删除 ${result.rm}, 未变 ${result.same}`);同步行为:
- 添加:目标有记录,DNS 无 → 创建
- 更新:两边都有,IP 不同 → 原地更新
- 删除:DNS 有记录,目标无 → 移除
- 未变:IP 一致 → 跳过
免费 CDN 组合:
- 境外:Cloudflare SaaS 自定义域名(免费,CNAME 接入)
- 中国大陆电信/联通:腾讯云 EdgeOne(免费额度)
- 中国大陆移动:阿里云 ESA(免费额度)
- 调度:华为云 DNS(免费,支持 31 省 × 3 运营商)
中国大陆 CDN 免费额度较小,同时使用两家可分摊流量,避免超限。
中国大陆分区
import cn from "@js0.site/edns/cn.js";
const { v4, v6 } = await cn("cdn.example.com");
// v4: { CN: [...], Dianxin: [...], Yidong: [...], Beijing: [...], ... }
// v6: { CN: [...], Dianxin: [...], Yidong: [...], Beijing: [...], ... }结果按层次聚合:
CN:全国 top 3 IPDianxin/Yidong/Liantong:各运营商 top 3- 城市名:各城市 top 3
- 单独线路:仅当不被上级覆盖时保留
海外分区
import global from "@js0.site/edns/global.js";
const { v4, v6 } = await global("cdn.example.com");
// v4: { default_view: [...], AP: [...], EU: [...], US: [...], ... }
// v6: { default_view: [...], AP: [...], EU: [...], JP: [...], ... }结果按层次过滤:
default_view:全球默认- 洲:
AP(亚太)、EU(欧洲)、NA(北美)、LA(南美)、OA(大洋洲)、AF(非洲) - 国家:仅当与所属洲不同时保留
底层 API
import flattenV4, { CN, GLOBAL } from "@js0.site/edns/v4.js";
import flattenV6 from "@js0.site/edns/v6.js";
// 查询指定线路
const result = await flattenV4("cdn.example.com", {
line1: "8.8.8.8",
line2: "1.1.1.1",
});
// { line1: ["1.2.3.4"], line2: ["5.6.7.8"] }
// 使用内置线路配置
const cnResult = await flattenV4("cdn.example.com", CN);运行测试:
bun test/cn.js # 中国大陆分区
bun test/global.js # 海外分区
bun test/v4.js # IPv4 原始
bun test/v6.js # IPv6 原始
bun test/verify.js # IP 归属地校验
bun test/hwSet.js # 华为云 DNS 同步架构
graph TD
subgraph 高层接口
CN[cn.js] --> V4
CN --> V6
GL[global.js] --> V4
GL --> V6
end
subgraph 核心
V4[v4.js] --> BASE[base.js]
V6[v6.js] --> BASE
BASE --> DOH[DoH 服务器]
end
subgraph 配置
V4CN[v4/CN.js] --> V4
V4GL[v4/GLOBAL.js] --> V4
V6CN[v6/CN.js] --> V6
V6GL[v6/GLOBAL.js] --> V6
GEO[geo/GLOBAL.js] --> GL
end
subgraph 同步
HW[Hw.js] --> CN
HW --> GL
HW --> HWAPI[华为云 DNS API]
end
DOH --> DNSPOD[DNSPod]
DOH --> ALI[阿里云 DNS]调用流程:
cn.js/global.js调用v4.js和v6.js,传入内置线路配置v4.js/v6.js调用flatten(),指定 DoH 端点和记录类型flatten()遍历线路,构造带 ECS 参数的 DoH 查询queryDoh()发送 HTTP 请求,解析 JSON,过滤有效 IP- 高层模块按层次聚合/过滤结果
Hw.js通过cn.js/global.js解析 CNAME,同步到华为云 DNS
目录结构
src/
├── base.js # 核心:queryDoh, flatten, isSubset
├── v4.js # IPv4 解析器 (DNSPod DoH)
├── v6.js # IPv6 解析器 (阿里云 DoH)
├── cn.js # 中国大陆聚合器
├── global.js # 海外聚合器
├── Hw.js # 华为云 DNS 同步
├── v4/
│ ├── CN.js # 中国大陆 ECS IP (31 省 × 3 运营商)
│ └── GLOBAL.js # 海外 ECS IP (40+ 地区)
├── v6/
│ ├── CN.js # 复用 v4/CN.js
│ └── GLOBAL.js # IPv6 海外 ECS IP
└── geo/
└── GLOBAL.js # 洲 → 国家映射
test/
├── lib.js # 测试工具
├── cn.js # 中国大陆测试
├── global.js # 海外测试
├── v4.js # IPv4 测试
├── v6.js # IPv6 测试
├── verify.js # 归属地校验
├── hwSet.js # 华为云 DNS 同步测试
├── hwLines.js # 列出华为云 DNS 线路
└── hwRateLimit.js # 限流测试技术栈
- 运行时:Bun / Node.js (ESM)
- HTTP:原生 fetch API
- 并发:p-limit
- DNS 服务商:华为云 SDK
- 地理库:MaxMind GeoLite2 (开发依赖)
- 协议:DNS over HTTPS (RFC 8484)、EDNS Client Subnet (RFC 7871)
历史
EDNS Client Subnet (ECS) 由 Google 和 Neustar 于 2011 年提出,旨在改善 CDN 性能。在 ECS 出现之前,递归 DNS 服务器只能返回针对解析器位置优化的 IP,而非终端用户位置。经历多次草案修订后,于 2016 年成为 RFC 7871。
DNS over HTTPS (DoH) 源于对 DNS 流量隐私的关注,RFC 8484 于 2018 年发布。DoH + ECS 的组合既保护隐私,又实现地理感知解析。
趣闻:ECS 查询中常用的 /24 子网掩码并非随意选择。它既能提供足够的地理精度用于 CDN 路由,又能通过不暴露精确 IP 来保护用户隐私。大多数 CDN 厂商基于 /24 块优化其 Anycast 路由。
关于
本项目为 js0.site ⋅ 重构互联网计划 的开源组件。
我们正在以组件化的方式重新定义互联网的开发范式,欢迎关注:
