节点域名、Hosts 与私有 DNS 联动模型
某些一线机场配置使用 Hosts 真假映射与私有 DoH 解析;下面分别对仅有 Hosts 真假映射与仅有私有 DoH 解析展开。
一、两个主题的分述
这一节把“仅有 Hosts 真假映射”与“仅有私有 DoH 解析”作为两个独立主题,分别说明某一侧在配置中的角色与作用;两者在真实配置中往往同时出现,组合逻辑在下一节(两个主题的关联)展开。
1. 仅有 Hosts 真假映射
某些一线机场原始 YAML 中的 hosts 在形式上与广告屏蔽表、局域网解析表相同,但在节点语境下应理解为一组真假节点域名映射:左侧是可能被公共解析路径导向的“假节点域名”或低质量结果,右侧是配置希望正常用户连接的真实服务入口。本节聚焦 hosts 真假映射这一侧;私有 DoH 在下一节单独展开。
# 简化模型:仅有 Hosts 真假映射的整体配置;以下为概念占位,不使用真实节点域名
dns:
enable: true
# hosts 要参与节点解析,先启用 use-hosts
use-hosts: true
# 本机 DNS 监听端点,节点解析经此进入
listen: 127.0.0.1:7846
# 节点专用解析入口;必须与本机监听端点对应,才能进入 hosts 映射真假域名流程
proxy-server-nameserver:
- udp://127.0.0.1:7846
# 假节点域名的真假映射:左侧为假候选,右侧为真服务入口
hosts:
POSSIBLE_BAD_RESULT_A: REAL_SERVICE_ENTRY_A
POSSIBLE_BAD_RESULT_B: REAL_SERVICE_ENTRY_B
POSSIBLE_BAD_RESULT_C: REAL_SERVICE_ENTRY_C
# 订阅中的原始节点仍引用左侧假域名
proxies:
- name: Example Node
type: anytls
server: POSSIBLE_BAD_RESULT_A
port: 443
password: REDACTED_EXAMPLE_ONLY
sni: tls.example.com
udp: true
在这类 YAML 中,hosts 左侧是“假”起点的候选,右侧是“真”目标;前者是需要被修正的解析对象,后者是配置希望正常用户连接的服务入口。
| Hosts 映射情形 | 真假判定 | 仅凭 Hosts 能确认的结果 |
|---|---|---|
| 假域名 → 真域名 | 左假右真 | 解析对象修正为右侧真域名,后续仍需 DNS |
(1)Hosts 真假映射的生效条件
在只分析 YAML 的前提下,至少要同时满足以下条件,才能说这组真假映射具备生效基础:
| 条件 | 需要从 YAML 确认的内容 | 不满足时的含义 |
|---|---|---|
| Hosts 开关开启 | dns.use-hosts: true,或目标客户端明确默认启用 hosts | 映射表可能只存在于配置中,不参与解析 |
| 左侧域名确实被使用 | 节点或相关解析请求使用 hosts 左侧的域名 | 该条映射不会影响没有使用它的对象 |
| 右侧值合法 | 右侧是可解析域名或合法 IP | 无法形成确定的服务入口 |
| 本地 DNS 监听覆盖节点解析入口的同端口回环 | dns.listen 填写 IPv4 通配监听地址,proxy-server-nameserver 填写同一端口上的 IPv4 回环地址;通配监听包含该回环地址时,Mihomo 判定节点解析回到自身本地 DNS,产生回环,hosts 参与节点解析 | 监听地址不覆盖该回环(端口不一致,或只绑定特定接口而非通配)则不产生回环,节点域名由外部解析器处理,hosts 不参与 |
| 没有被更高优先级规则抵消 | 没有其他同名 hosts、policy 或客户端规则覆盖它 | 最终结果可能不是右侧值 |
因此,“左侧假目标 → 右侧真入口”描述的是 YAML 设计意图;“什么时候真正生效”取决于 hosts 开关、左侧域名是否实际被使用、右侧值是否有效、客户端是否让 hosts 参与解析,以及是否存在覆盖规则。
Mihomo 原生节点解析行为(补充)
| 映射情况 | 节点解析行为 |
|---|---|
| 节点域名 → IP,或旧域名 → 新域名 → IP。 | hosts 映射最终指向 IP,节点直接使用该 IP。 |
| 旧域名 → 新域名,且最终仍是域名。 (约束:不依赖 DNS 模块。) | hosts 不会把查询名切换为新域名;仍需进行 DNS 查询,查询名为旧域名。 |
| 旧域名 → 新域名,且最终仍是域名。 (约束:依赖 DNS 模块。) | 查询名按 hosts 映射切换为新域名,继续 DNS 查询,响应中包含 CNAME。 |
2. 仅有私有 DoH 解析
私有 DoH 是机场提供的一个以 HTTPS 传输的 DNS 端点,用于为节点域名提供解析。它与 hosts 在配置中同时存在,但作为解析来源独立于 hosts 的映射关系:无论节点域名是否发生 hosts 替换,私有 DoH 都只是被选中的一条解析来源。本节只看私有 DoH 这一侧,下表列出它可能落入的典型配置字段。
下面给出两种模型:全部节点都走私有 DoH,用 proxy-server-nameserver;只有部分节点走私有 DoH,用 proxy-server-nameserver-policy 针对指定域名单独指定解析来源。以下均为概念占位,不使用真实端点。
# 简化模型:全部节点都走私有 DoH;以下为概念占位,不使用真实 DoH 端点
dns:
enable: true
# 本机 DNS 监听地址;保留该字段,避免 dns 配置缺失
listen: 127.0.0.1:53
# 所有节点域名统一由私有 DoH 解析
proxy-server-nameserver:
- https://private-dns.example/dns-query
# 普通业务走公共解析,不承担私有 DoH
nameserver:
- https://doh.pub/dns-query
# 本主题 hosts 不承载真假映射(空表)
hosts: {}
# 订阅中的原始节点使用真实域名,最终地址由私有 DoH 决定
proxies:
- name: Example Node
type: anytls
server: node.example.com
port: 443
password: REDACTED_EXAMPLE_ONLY
sni: tls.example.com
udp: true
# 简化模型:部分节点走私有 DoH;以下为概念占位,不使用真实 DoH 端点
dns:
enable: true
# 本机 DNS 监听地址;保留该字段,避免 dns 配置缺失
listen: 127.0.0.1:53
# 未命中 policy 的节点走公共解析,不使用本机回环作为节点解析入口
proxy-server-nameserver:
- https://doh.pub/dns-query
# 仅对指定节点域名启用私有 DoH
proxy-server-nameserver-policy:
"*.private-doh.example":
- https://private-dns.example/dns-query
# 普通业务也走公共解析
nameserver:
- https://doh.pub/dns-query
# 本主题 hosts 不承载真假映射(空表)
hosts: {}
# 订阅中的原始节点:命中 policy 的走私有 DoH,其余走常规解析
proxies:
- name: Plain Node
type: anytls
server: plain.example.com
port: 443
password: REDACTED_EXAMPLE_ONLY
sni: tls.example.com
udp: true
- name: DoH Node
type: anytls
server: node.private-doh.example
port: 443
password: REDACTED_EXAMPLE_ONLY
sni: tls.example.com
udp: true
| 配置角色 | 典型字段 | 作用 |
|---|---|---|
| 节点专用解析入口 | proxy-server-nameserver、proxy-server-nameserver-policy | 为代理节点的 server 域名提供解析,私有 DoH 常作为其中一项来源 |
| 普通解析入口 | nameserver、nameserver-policy | 为普通业务域名提供解析 |
| 引导解析入口 | default-nameserver | 帮助解析 DoH 服务域名或其他初始域名 |
| 本机 DNS 监听 | dns.listen | 当前 Mihomo 实例自己提供的本地 DNS 端点,可能被其他解析入口引用 |
对节点域名而言,解析来源可遵循如下概念性优先级:
节点专用 policy
> 节点专用解析入口(可能含私有 DoH)
> 普通 policy
> 普通解析入口
这里的“优先级”不是要求脚本必须重写所有字段,而是帮助判断:一个节点域名最终应沿哪条解析来源获得地址。域名级 policy 通常比全局列表更具体;节点专用来源通常比普通业务来源更贴近节点连接需求。
(1)减少公共 DNS 污染或不稳定结果
公共 DNS 可能受到地域调度、缓存差异、污染、拦截、过期记录或运营商策略影响。对于普通网页域名,这些差异未必会立即暴露;但对于机场节点域名,错误或过期的地址可能直接造成连接失败。因此,机场可能提供私有 DoH,使节点域名使用更稳定、更接近其服务端配置的解析结果。
(2)为节点设计专用的解析结果
另一种可能是:公共 DNS 得到的结果并不是服务希望普通用户使用的入口。例如,公共 DNS 可能返回一个低质量、受限、失效或面向异常流量的地址,而私有 DoH 可能返回更适合正常用户连接的高质量中转入口。这里的“内鬼组结果”“危险分子低质量 IP”只能作为对配置意图的假设性解释,不能仅凭 YAML 证实机场一定在主动区分用户。
二、两个主题的关联:Hosts-DNS 联动流程
把“仅 Hosts 真假映射”与“仅私有 DoH 解析”这两个独立主题组合起来,可以得到一个不依赖具体机场名称的联动流程:hosts 决定解析对象的名字,私有 DoH 或节点解析入口决定这个名字从哪里获得地址。
在这个流程中,hosts 不是 DNS policy,私有 DoH 也不是 hosts。二者的联动只是:hosts 改变了需要解析的域名,DNS policy 决定这个最终域名应使用哪条解析路径。
结合后的“入口”对应同一个目标:hosts 右侧的真域名(“真实服务入口”),与私有 DoH 所提供的高质量中转入口(“为节点设计专用的解析结果”),指向的是配置希望正常用户连接的那一个服务入口。二者分工是:hosts 先把假域名修正成这个真域名,真正的入口地址仍由节点 DNS 优先级在解析阶段决定;若该路径使用了私有 DoH,那么最终解析到的就是私有 DoH 设计给正常用户的中转入口。
三、抽象模型:从具体机场配置到通用概念
OriginalDomains = 订阅中的原始节点域名 HostRelations = 原始域名到候选新域名或 IP 的关系 ResolvedServers = 经过允许的 hosts 关系后得到的最终节点目标 NodeDnsSources = 节点专用 policy、节点专用 DNS、普通 policy、普通 DNS EffectiveDnsDomains = 最终目标中仍然属于域名的部分
| 模型对象 | 回答的问题 |
|---|---|
OriginalDomains | 订阅最初希望连接哪个域名? |
HostRelations | 配置是否声明了入口迁移或地址替换? |
ResolvedServers | 在允许 hosts 后,节点最终可能连接什么? |
NodeDnsSources | 最终节点域名由谁解析、按什么优先级解析? |
EffectiveDnsDomains | 哪些最终节点目标仍需要 DNS policy? |
该模型可以解释不同机场的类似配置,也允许脚本作者选择不同的 policy 迁移策略:只保留最终键、旧新双端保留,或完全独立处理。
四、带注释的 YAML 模型示例
下面示例只表达配置语义,不代表任何具体机场的实际返回结果。示例不含真实节点凭据。
# 节点与普通业务都由同一个 Mihomo DNS 实例提供解析服务
dns:
enable: true
use-hosts: true
# 本机 DNS 服务监听地址
listen: 127.0.0.1:7846
# 节点专用解析入口;必须与 dns 监听端点对应,才能正确进入 hosts 映射真假域名流程
proxy-server-nameserver:
- udp://127.0.0.1:7846
# 节点或普通域名可能使用的私有 DoH
# 真实项目中应由配置作者确认其用途和优先级
nameserver:
- https://private-dns.example/dns-query
# 普通业务域名的更具体规则
nameserver-policy:
"+.example.net":
- https://dns.example.net/dns-query
# 这是附加兼容项,不是 hosts 生效条件
enhanced-mode: fake-ip
fake-ip-filter:
- old-node.example.com
- new-node.example.com
# 旧节点入口到新节点入口的关系
hosts:
old-node.example.com: new-node.example.com
# 订阅原始节点仍然使用旧域名
proxies:
- name: Example Node
type: anytls
server: old-node.example.com
port: 443
password: REDACTED_EXAMPLE_ONLY
sni: tls.example.com
udp: true
这个示例表达的是一种设计语义:节点订阅入口是旧域名,hosts 提供可能的新入口,DNS 提供解析来源,而 fake-IP 过滤只作为附加保护。具体脚本是否接受该 hosts 关系、是否迁移 DNS policy,应由脚本作者的明确设计决定。
五、Fake-IP 过滤:附加的防御性冗余
fake-IP 过滤不属于 hosts 生效的前提,也不是 DNS 优先级的一部分。它只解决一个附加问题:某些基础设施域名是否应该避免返回 198.18.x.x 一类虚假地址。
旧、新节点域名同时出现在 fake-ip-filter 中,可能是为了兼容 hosts 未生效、订阅更新、旧客户端或缓存残留。它可以降低兼容风险,但不能据此证明 hosts 一定生效,也不能据此证明某个域名一定需要私有 DoH。
六、最后:给 JS 编写者的通用指导
本节面向准备编写类似覆写脚本的人,而不是对任何现有仓库实现进行逐行解释。实现者可以依据自己的覆写入口、目标客户端和兼容目标进行取舍。
1. 先定义中间状态
建议分别保存原始节点目标、候选 hosts 结果、最终节点目标、节点类型、DNS policy 命中状态和异常状态。不要边读取配置边覆盖同一个 server 字段,否则很难判断一条 policy 是针对原始域名还是最终域名。
2. 明确四个独立决策
| 决策对象 | 需要定义的内容 |
|---|---|
| Hosts 是否可用 | 开关、DNS 端点关系、节点类型和配置合法性的判定标准 |
| Hosts 关系如何解释 | 单跳、多跳、成环、IP 目标和无效目标的处理方式 |
| DNS policy 如何关联 | 独立、关系迁移或旧新双端兼容 |
| Fake-IP 是否保护 | 哪些域名属于基础设施例外,以及是否仅作兼容性选项 |
3. 明确优先级和失败策略
脚本应明确“第一个非空 DNS 来源组”还是“多个来源的 fallback”语义;应明确高优先级 policy 与低优先级 policy 冲突时谁胜出;应明确 hosts 不能确认时是保留原值、跳过节点还是报错。隐式 fallback 往往是复杂覆写中最难排查的来源。
4. 保证幂等性与可测试性
重复执行脚本不应不断新增重复 policy,也不应把已迁移的最终域名再次当成旧域名。测试至少应覆盖单跳、多跳、IP 映射、成环、hosts 关闭、DNS 来源不匹配、旧新 policy 冲突以及无 fake-IP-filter 的情况。
5. 保持安全边界
- 测试与日志中不得写入真实密码、UUID、订阅令牌或完整 DoH 路径。
- 不要将
server与sni视为同一个字段。 - 不要以 fake-IP-filter 的存在证明 hosts 已生效。
- 不要为了兼容监听地址而把实际连接目标改写成
0.0.0.0。 - 不确定时宁可保留原节点和原 policy,也不要生成无法解释的猜测性规则。