本文檔介紹iOS 14原生加密DNS方案中的接入和開發方式。
概述
DNS解析是網路資源訪問的第一跳,iOS 14 開始系統原生支援兩種標準規範的 Encrypted DNS, 分別是 DNS over TLS 與 DNS over HTTPS,可以解決以下兩個問題:
一、傳統Local DNS的查詢與回複均基於非加密UDP,發生我們常見的DNS劫持問題。
二、Local DNS Server本身不可信,或者本地Local DNS 服務不可用問題。
針對DNS解析過程中以上兩個問題,移動解析HTTPDNS已經有瞭解決方案:使用移動解析HTTPDNS SDK,但使用SDK會面臨一些難題,比如302情境的IP直連處理、以及iOS上的SNI問題等,而iOS 14 上的 Encrypted DNS 功能很好的解決了整合SDK的方案存在的問題,您可以參考以下樣本瞭解如何設定移動解析HTTPDNS為加密DNS預設解析器。
iOS14原生加密DNS方案僅支援真機運行且僅支援iOS14及其以上系統版本。
為確保app正常運行,請開發人員自實現降級Local DNS 兜底機制,避免因DoH/DoT服務異常導致網域名稱解析失敗。
原生方案相對比SDK接入方案,服務無法自動故障降級,不承諾服務SLA。若您業務確實需要通過原生方案接入,請代碼實現服務異常監測和故障自動降級使用裝置本地DNS解析。
iOS 14原生加密DNS方案如何接入移動解析HTTPDNS
iOS 14 提供了兩種設定加密DNS的方法。
第一種方式是針對單個App的所有串連啟用加密DNS。
如果您只想為您的App使用加密DNS,而非整個終端系統全部接入使用加密DNS。您可以適配NetworkExtension的nw_privacy_context_t,只對您的整個App開啟加密DNS,App內發起的每個DNS解析都會使用這個配置。我們為您提供了針對單個App的所有串連啟用DoH的demo樣本供您參考。
在App範圍內使用加密DNS,使用DoH協議範例程式碼:
#import <NetworkExtension/NetworkExtension.h>
if (@available(iOS 14.0, *)){
nw_privacy_context_t defaultPrivacyContext = NW_DEFAULT_PRIVACY_CONTEXT;
nw_endpoint_t dohResolverEndpoint = nw_endpoint_create_url("https://*****-************.alidns.com/dns-query");//DoH加密地址
nw_endpoint_t v4ResolverEndpoint1 = nw_endpoint_create_host("223.5.5.5", "443");
nw_endpoint_t v4ResolverEndpoint2 = nw_endpoint_create_host("223.6.6.6", "443");
nw_endpoint_t v6ResolverEndpoint1 = nw_endpoint_create_host("2400:3200::1", "443");
nw_endpoint_t v6ResolverEndpoint2 = nw_endpoint_create_host("2400:3200:baba::1", "443");
nw_resolver_config_t fallbackResolvers = nw_resolver_config_create_https(dohResolverEndpoint);
nw_resolver_config_add_server_address(fallbackResolvers, v4ResolverEndpoint1);
nw_resolver_config_add_server_address(fallbackResolvers, v4ResolverEndpoint2);
nw_resolver_config_add_server_address(fallbackResolvers, v6ResolverEndpoint1);
nw_resolver_config_add_server_address(fallbackResolvers, v6ResolverEndpoint2);
nw_privacy_context_require_encrypted_name_resolution(defaultPrivacyContext, true, fallbackResolvers);
}在App範圍內使用加密DNS,使用DoT協議範例程式碼:
#import <NetworkExtension/NetworkExtension.h>
if (@available(iOS 14.0, *)){
nw_privacy_context_t defaultPrivacyContext = NW_DEFAULT_PRIVACY_CONTEXT;
nw_endpoint_t dotResolverEndpoint = nw_endpoint_create_host("******-************.alidns.com", "853");//DoT加密地址
nw_endpoint_t v4ResolverEndpoint1 = nw_endpoint_create_host("223.5.5.5", "853");
nw_endpoint_t v4ResolverEndpoint2 = nw_endpoint_create_host("223.6.6.6", "853");
nw_endpoint_t v6ResolverEndpoint1 = nw_endpoint_create_host("2400:3200::1", "853");
nw_endpoint_t v6ResolverEndpoint2 = nw_endpoint_create_host("2400:3200:baba::1", "853");
nw_resolver_config_t fallbackResolvers = nw_resolver_config_create_tls(dotResolverEndpoint);
nw_resolver_config_add_server_address(fallbackResolvers, v4ResolverEndpoint1);
nw_resolver_config_add_server_address(fallbackResolvers, v4ResolverEndpoint2);
nw_resolver_config_add_server_address(fallbackResolvers, v6ResolverEndpoint1);
nw_resolver_config_add_server_address(fallbackResolvers, v6ResolverEndpoint2);
nw_privacy_context_require_encrypted_name_resolution(defaultPrivacyContext, true, fallbackResolvers);
}⽤戶可以通過以下代碼實現降級Local DNS 兜底機制,通過 URLSessionDelegate 擷取 NSURLSessionTaskTransactionMetrics ,在請求失敗後關閉 DoH/DoT。
#pragma mark URLSession Delegate
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics {
if ([metrics.transactionMetrics count] <= 0) return;
[metrics.transactionMetrics enumerateObjectsUsingBlock:^(NSURLSessionTaskTransactionMetrics *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) {
if (obj.resourceFetchType == NSURLSessionTaskMetricsResourceFetchTypeNetworkLoad) {
if (@available(iOS 14.0, *)) {
NSURLSessionTaskMetricsDomainResolutionProtocol dnsProtocol = obj.domainResolutionProtocol;
NSLog(@"%@",[NSString stringWithFormat:@"DNS類型為 %ld", (long)dnsProtocol]);
NSLog(@"%@",[NSString stringWithFormat:@"0:未知,1:UDP,2:TCP,3:TLS,4:HTTPS"]);
if (dnsProtocol == NSURLSessionTaskMetricsDomainResolutionProtocolUnknown) {
NSLog(@"%@",[NSString stringWithFormat:@"dns來源未知,關閉Doh"]);
nw_privacy_context_require_encrypted_name_resolution(NW_DEFAULT_PRIVACY_CONTEXT, false, nil);
}
}
}
}];
}第二種方式是針對整個終端內所有應用的串連啟用加密DNS。
如果您想為整個系統域使用加密DNS,您可以使用NEDNSSettingsManager API編寫一個NetworkExtension App完成系統全域加密DNS設定。
通過NetworkExtension設定系統域全域DNS伺服器,使用DoH協議範例程式碼:
import NetworkExtension
NEDNSSettingsManager.shared().loadFromPreferences { loadError in
if let loadError = loadError {
// ...handle error...
return
}
let dohSettings = NEDNSOverHTTPSSettings(servers: ["223.5.5.5","223.6.6.6","2400:3200:baba::1","2400:3200::1"])
dohSettings.serverURL = URL(string: "https://*****-************.alidns.com/dns-query")//DoH加密地址
NEDNSSettingsManager.shared().dnsSettings = dohSettings
NEDNSSettingsManager.shared().saveToPreferences { saveError in
if let saveError = saveError {
// ...handle error...
return
}
}
}
通過NetworkExtension設定系統域全域DNS伺服器,使用DoT協議範例程式碼:
import NetworkExtension
NEDNSSettingsManager.shared().loadFromPreferences { loadError in
if let loadError = loadError {
// ...handle error...
return
}
let dotSettings = NEDNSOverTLSSettings(servers: ["223.5.5.5","223.6.6.6","2400:3200:baba::1","2400:3200::1"])
dotSettings.serverName = "******-************.alidns.com"//DoT加密地址
NEDNSSettingsManager.shared().dnsSettings = dotSettings
NEDNSSettingsManager.shared().saveToPreferences { saveError in
if let saveError = saveError {
// ...handle error...
return
}
}
}一條DNS配置包括移動解析HTTPDNS伺服器位址、DoT/DoH協議、一組網路規則。網路規則確保DNS設定相容不同的網路。
網路規則設定範例程式碼:
let workWiFi = NEOnDemandRuleEvaluateConnection()
workWiFi.interfaceTypeMatch = .wiFi
workWiFi.ssidMatch = ["MyWorkWiFi"]
workWiFi.connectionRules = [NEEvaluateConnectionRule(matchDomains: ["enterprise.example"], andAction: .neverConnect)]
let disableOnCell = NEOnDemandRuleDisconnect()
disableOnCell.interfaceTypeMatch = .cellular
let enableByDefault = NEOnDemandRuleConnect()
NEDNSSettingsManager.shared().onDemandRules = [
workWiFi,
disableOnCell,
enableByDefault
]上述代碼設定了三個網路規則,第一個規則表示DNS配置應該在SSID=“MyWorkWiFi”的WiFi網路生效,但對私人企業網域名稱enterprise.example.net不開啟;第二個規則表示規則在蜂窩網下應該被禁止使用;第三個NEOnDemandRuleConnect表示DNS配置應該預設開啟;因為配置DNS是系統支援的,所以在編寫NetworkExtension App時不需要實現Extension程式,只需要在Network Extensions中勾選DNS Settings選項。

運行NetworkExtension App,DNS配置將會被安裝到系統,為了讓DNS配置生效,需要前往設定->通用->VPN & Network->DNS手動啟用。
