本文档介绍如何在 iOS 客户端配置 DoH 地址。
参见 获取并运行 iOS 示例项目 获取包含 DoH 功能的示例项目。
警告
如果您希望移动解析 HTTPDNS 使用 DoH 解析某个域名,您必须确保在控制台添加该域名并开启 DoH接入。
HTTPDNS iOS SDK 不支持 DoH 协议。您需要通过 iOS 的原生方法接入 DoH。您可以选择以下任意一种方法。
注意
您只能在 iOS 真机上验证 DoH 功能。本文档介绍的配置方法在 iOS 模拟器中不生效。
您可以通过 PrivacyContext 为 iOS 设备在 App 级别配置 DoH。
下面的示例代码对单个 PrivacyContext
实例生效。
#import <Network/Network.h> if (@available(iOS 14.0, *)) { // 打开 DoH nw_endpoint_t dohEndpoint = nw_endpoint_create_url("https://doh-xxxxxxxxxxxxxxx.volcdns.pub/dns-query"); nw_resolver_config_t dohResolver = nw_resolver_config_create_https(dohEndpoint); nw_privacy_context_require_encrypted_name_resolution( NW_DEFAULT_PRIVACY_CONTEXT, true, dohResolver); // 关闭 DoH nw_privacy_context_require_encrypted_name_resolution( NW_DEFAULT_PRIVACY_CONTEXT, false, nil); }
下面的示例代码对单个 NWConnection 实例生效。
#import <Network/Network.h> nw_privacy_context_t connection_context = nw_privacy_context_create("connection context"); nw_endpoint_t dohEndpoint = nw_endpoint_create_url("https://doh-xxxxxxxxxxxxxxx.volcdns.pub/dns-query"); nw_resolver_config_t dohResolver = nw_resolver_config_create_https(dohEndpoint); nw_privacy_context_require_encrypted_name_resolution( connection_context, true, dohResolver); const char *hostname = "imap.gmail.com"; const char *port = "imaps"; nw_parameters_t parameters = nw_parameters_create_secure_tcp(NW_PARAMETERS_DEFAULT_CONFIGURATION, NW_PARAMETERS_DEFAULT_CONFIGURATION); nw_parameters_set_privacy_context(parameters, connection_context); nw_endpoint_t endpoint = nw_endpoint_create_host(hostname, port); nw_connection_t connection = nw_connection_create(endpoint, parameters);
您可以为您的 App 创建一个 Network Extensions,并通过 NEDNSSettingsManager 为 iOS 设备在系统级别配置 DoH。
说明
您的 Xcode 项目的 Provisioning Profile 必须开启 Network Extensions 中的 DNS Settings。
下面的代码展示了如何通过 NEDNSSettingsManager
在系统级别配置 DoH。
let manager : NEDNSSettingsManager = NEDNSSettingsManager.shared() @objc func updateSettings(disableCell: Bool, disableWiFi: Bool, completionHandler: @escaping() -> Void) { manager.loadFromPreferences { error in if error != nil { print("load failed") return } // onDemandRules settings // ... // 保存 DNS 配置,如果之前已经安装过,那么会提示因为相同配置而保存失败。 let doh = NEDNSOverHTTPSSettings() doh.serverURL = URL(string: "https://doh-xxxxxxxxxxxxxxx.volcdns.pub/dns-query") self.manager.dnsSettings = doh self.manager.saveToPreferences { saveError in if saveError != nil { print("save failed") print("saveError is %@", saveError!) return } else { print("save success") } } } }
代码运行后,DNS 配置会被安装到 iOS 系统。为了让 DNS 配置生效,用户需要在 iOS 设备的 设置 > 通用 > VPN 与设备管理 > DNS 中手动启用 DNS 配置。
生效范围是系统级别,但是您可以通过 onDemandRules 控制域名和网络环境等生效范围。
您可以通过配置描述文件为 iOS 设备在系统级别配置 DoH。
下面的 XML 代码展示了一个配置描述的文件的示例。您需要把示例中的 DoH 接入域名替换为您从控制台获取的 DoH 接入域名。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>PayloadContent</key> <array> <dict> <key>DNSSettings</key> <dict> <key>DNSProtocol</key> <string>HTTPS</string> <key>ServerURL</key> <!----把此处的域名替换为您的 DoH 接入域名---> <string>https://doh-xxxxxxxxxxxxxxx.volcdns.pub/dns-query</string> <!----把此处的域名替换为您的 DoH 接入域名---> </dict> <key>PayloadDescription</key> <string>Configures device to use Volcengine DoH</string> <key>PayloadDisplayName</key> <string>DoH</string> <key>PayloadIdentifier</key> <string>com.apple.dnsSettings.managed.C498EC0C-EF6C-44F0-BFB7-0000658B99AC</string> <key>PayloadType</key> <string>com.apple.dnsSettings.managed</string> <key>PayloadUUID</key> <string>065AB183-5E34-4794-9BEB-B5327CF61F27</string> <key>PayloadVersion</key> <integer>1</integer> <key>ProhibitDisablement</key> <false/> </dict> </array> <key>PayloadDescription</key> <string>Adds the Volcengine DoH to iOS</string> <key>PayloadDisplayName</key> <string>Volcengine DoH</string> <key>PayloadIdentifier</key> <string>com.volcengine.apple-dns</string> <key>PayloadRemovalDisallowed</key> <false/> <key>PayloadType</key> <string>Configuration</string> <key>PayloadUUID</key> <string>030E6D6F-69A2-4515-9D77-99342CB9AE76</string> <key>PayloadVersion</key> <integer>1</integer> </dict> </plist>
把内容保存为一个 .mobileconfig
文件,例如 volcengine-https.mobileconfig
,并且在您的 iOS 设备上安装该文件。参见 在 iPhone 或 iPad 上安装配置描述文件 了解安装方法。
以 PrivacyContext
为例,您可以:
URLSessionDelegate
获取 NSURLSessionTaskTransactionMetrics
,进一步获取 DNS 类型和 DNS 耗时。PrivacyContext
开启 DoH,在请求失败后关闭 DoH。#pragma mark URLSession Delegate - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics { if ([metrics.transactionMetrics count] > 0) { [metrics.transactionMetrics enumerateObjectsUsingBlock:^(NSURLSessionTaskTransactionMetrics *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) { if (obj.resourceFetchType == NSURLSessionTaskMetricsResourceFetchTypeNetworkLoad) { NSURLSessionTaskMetricsDomainResolutionProtocol dnsProtocol = obj.domainResolutionProtocol; NSLog(@"DNS类型为 %ld", (long)dnsProtocol); NSLog(@"未知,UDP,TCP,TLS,HTTPS,4为 DoH"); if (dnsProtocol == NSURLSessionTaskMetricsDomainResolutionProtocolUnknown) { NSLog(@"dns来源未知,关闭 DoH"); nw_privacy_context_require_encrypted_name_resolution(NW_DEFAULT_PRIVACY_CONTEXT, false, nil); } if (obj.domainLookupStartDate && obj.domainLookupEndDate) { int dnsLookupTime = ceil([obj.domainLookupEndDate timeIntervalSinceDate:obj.domainLookupStartDate] * 1000); NSLog(@"DNS开始时间:%@,DNS结束时间:%@", obj.domainLookupStartDate, obj.domainLookupEndDate); NSLog(@"DNS解析时长 单位ms:%d",dnsLookupTime); } } }]; } }
通过分阶段超时快速失败来控制 DoH 失效超时问题劣化,DoH 失败后,关闭 DoH。
NSURLSessionConfiguration* config = [NSURLSessionConfiguration defaultSessionConfiguration]; config.timeoutIntervalForRequest = 5; config.timeoutIntervalForResource = 10; NSURLSession* session = [NSURLSession sessionWithConfiguration: config delegate:self delegateQueue:nil];