客户端接入HttpDns原理及测试方法
HttpDns原理及对比
HTTPDNS 原理简述
HTTPDNS 是一种利用 HTTPS 协议(而非传统的 UDP/53)向特定的 DNS 服务器发起域名解析请求的技术。
其核心机制是 :
“绕过 LocalDNS”:客户端直接通过 IP 地址向 HTTPDNS 服务器请求域名的解析结果(通常为 JSON 格式)。这一做法彻底规避了运营商 LocalDNS 可能存在的域名劫持、缓存污染以及跨网调度不准等问题,确保 App 能够获取最精准、最快的业务服务器 IP。
与 DoH / DoT 的区别
虽然三者都利用加密通道传输 DNS 数据,但设计目标与应用场景截然不同:
- HTTPDNS (App 专用):
- 核心目标:“高可用与精准调度”。主要由云服务商提供,由 App 业务层主动接入。它不仅可以防劫持,还可以用户引导至最优节点。
- 协议形态:通常是 RESTful API,返回 JSON 等自定义格式。
- DoH (DNS over HTTPS) / DoT (DNS over TLS) (标准协议):
- 核心目标:“隐私与安全”。属于 IETF 标准,主要为了防止 DNS 查询被监听或篡改。通常由 操作系统(Android/iOS)或 浏览器 底层支持。
- 协议形态:传输的是标准 DNS 协议报文,只是给它套了一层 HTTPS 或 TLS 的“外壳”。
与LocalDNS的区别
--- config: look: neo theme: neutral --- sequenceDiagram participant App as 手机App participant LocalDNS as 运营商LocalDNS/系统DNS participant HTTPDNS as HTTPDNS服务器 participant Server as 业务服务器(Target) note over App, Server: 场景:LocalDNS 被劫持/污染 rect rgb(255, 230, 230) note right of App: 传统 LocalDNS 路径 App->>LocalDNS: 1. 查询 offertoday.com (UDP/53) LocalDNS-->>App: 2. 返回假IP (2.2.2.2) App->>App: 3. 尝试连接 2.2.2.2 App--xServer: 4. 连接超时/失败 end rect rgb(230, 255, 230) note right of App: HTTPDNS 路径 App->>HTTPDNS: 1. HTTP请求: /resolve?host=offertoday.com note right of HTTPDNS: 此请求走 TCP/80 或 443<br/>绕过 UDP 劫持 HTTPDNS-->>App: 2. JSON返回真IP (8.212.x.x) App->>Server: 3. 直连真IP (Host头保持域名) Server-->>App: 4. 业务数据交互成功 end
具体实现:
客户端差异
--- config: look: neo theme: neutral --- graph LR %% ========================================== %% 统一入口 %% ========================================== Start(App 发起请求<br/>https://api.domain.com):::start --> Split{操作系统?} %% ========================================== %% iOS 路径:侵入式 URL 替换 %% ========================================== subgraph iOS_Path [iOS: 上层 URL 替换模式] direction TB iOS_Step1[业务层代码]:::blue iOS_Action1[HTTPDNS 获取 IP]:::blue_light iOS_Action2[<B>URL 替换</B><br/>域名被替换为 IP<br/>https://1.2.3.4/...]:::orange iOS_Fix1[<B>SNI 修复</B><br/>需处理 HTTPS 握手]:::red iOS_Fix2[<B>Host 修正</B><br/>Header 补填域名]:::red iOS_Result[<B>建立连接</B>]:::blue_solid iOS_Step1 --> iOS_Action1 iOS_Action1 --> iOS_Action2 iOS_Action2 --> iOS_Fix1 iOS_Fix1 --> iOS_Fix2 iOS_Fix2 --> iOS_Result end Split -->|Android| And_Step1 Split -->|iOS| iOS_Step1 %% ========================================== %% Android 路径:优雅的原生注入 %% ========================================== subgraph Android_Path [Android: 底层 DNS 注入模式] direction TB And_Step1[OkHttp 网络库]:::green And_Action1[<B>DNS 注入</B><br/>拦截 DNS 解析过程]:::green_light And_Action2[HTTPDNS 获取 IP]:::green_light And_Result[<B>建立连接</B><br/>URL 保持域名不变<br/>SNI/Host 自动正确]:::green_solid And_Step1 --> And_Action1 And_Action1 --> And_Action2 And_Action2 --> And_Result end %% ========================================== %% 样式定义 (追求简洁美观) %% ========================================== classDef start fill:#333,stroke:#333,stroke-width:2px,color:#fff,rx:5,ry:5; %% Android 绿色系 (代表顺畅、自动) classDef green fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px; classDef green_light fill:#c8e6c9,stroke:#2e7d32,stroke-width:1px,stroke-dasharray: 5 5; classDef green_solid fill:#4caf50,stroke:#2e7d32,stroke-width:2px,color:#fff; %% iOS 蓝色/橙红色系 (代表手动、风险) classDef blue fill:#e3f2fd,stroke:#1565c0,stroke-width:2px; classDef blue_light fill:#bbdefb,stroke:#1565c0,stroke-width:1px,stroke-dasharray: 5 5; classDef orange fill:#fff3e0,stroke:#ef6c00,stroke-width:2px; classDef red fill:#ffebee,stroke:#c62828,stroke-width:2px,color:#b71c1c; classDef blue_solid fill:#2196f3,stroke:#1565c0,stroke-width:2px,color:#fff; %% 连接线 linkStyle default stroke:#666,stroke-width:2px;
差异主要源于操作系统底层网络栈的开放程度。
简单来说:Android 允许你“修改 DNS 解析过程”,而 iOS 强迫你“修改请求 URL”。
Android 的优势:OkHttp Dns 方法
在您提供的 Android 实现中,核心在于 OkHttp 允许开发者通过 .dns(new MyHttpDns()) 注入自己的解析逻辑。
- 非侵入性:上层业务代码只管请求 https://api.domain.com,完全不需要知道 HTTPDNS 的存在。
- 原生支持 HTTPS:因为 OkHttp 知道原始域名是 domain.com,虽然它底层连接的是 HTTPDNS 返回的 IP,但它在 TLS 握手时,会自动把原始域名填入 SNI 字段。这彻底消除了 iOS 上最头疼的 SNI/证书 兼容性问题。
- Happy Eyeballs (RFC 8305):这是一个工业级的连接尝试算法。当 HTTPDNS 返回了 IPv6 和 IPv4 两个地址时,OkHttp 不会死等,而是优先尝试 IPv6,如果稍有延迟立即并发尝试 IPv4,谁先通就用谁。这保证了在“双栈网络”下的极速体验。
iOS 的的困境:URL 替换
相比之下,iOS 的 NSURLSession 没有开放 DNS 注入。
- 后果:开发者必须在发起请求前,自己手动把 URL 里的域名抠掉换成 IP。
- 副作用:一旦 URL 变成了 IP,系统网络库就“失忆”了,它不知道这个 IP 对应的原本域名是谁,导致 Host 头缺失、SNI 字段错误。开发者必须手动把这些信息补回去,就像在给系统打补丁。
测试方法
见后文
参考文档:

浙公网安备 33010602011771号