This document describes how to integrate and develop the native encrypted DNS solution in iOS 14.
Overview
DNS resolution is the first hop for accessing network resources. Beginning with iOS 14, the system natively supports two standard encrypted DNS protocols: DNS over TLS (DoT) and DNS over HTTPS (DoH). These protocols can address the following two issues:
First, traditional Local DNS queries and responses are based on unencrypted UDP, which may lead to common DNS hijacks.
Second, the Local DNS server itself may be untrustworthy, or the local DNS service may be unavailable.
To address these issues, HTTPDNS provides a solution that uses the HTTPDNS software development kit (SDK). However, using the SDK presents technical challenges, such as handling direct IP connections in 302 redirect scenarios and Server Name Indication (SNI) issues on iOS. The Encrypted DNS feature in iOS 14 effectively resolves the issues associated with the SDK integration solution. The following examples show you how to set HTTPDNS as the default encrypted DNS resolver.
The iOS 14 native encrypted DNS solution is supported only on physical devices running iOS 14 or later.
To ensure that your app runs as expected, you must implement a fallback mechanism to Local DNS to prevent domain name resolution failures caused by DoH or DoT service exceptions.
Compared with the SDK integration solution, the native solution does not support automatic service failover or provide a Service-Level Agreement (SLA). If you use the native solution for your business, you must implement code for service exception monitoring and automatic failover to the device's local DNS for domain name resolution.
How to integrate HTTPDNS with the iOS 14 native encrypted DNS solution
iOS 14 provides two methods for setting up encrypted DNS.
Enable encrypted DNS for all connections of a single app.
To use encrypted DNS for your app only, rather than for the entire device, you can adapt the nw_privacy_context_t of NetworkExtension to enable encrypted DNS for your app. Every DNS resolution initiated within the app uses this configuration. For your reference, we provide a demo that enables DoH for all connections of a single app.
Sample code for using encrypted DNS at the app level with the DoH protocol:
#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 encrypted address
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);
}Sample code for using encrypted DNS at the app level with the DoT protocol:
#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 encrypted address
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);
}The following code shows how to implement a fallback mechanism to Local DNS. You can obtain NSURLSessionTaskTransactionMetrics through URLSessionDelegate and disable DoH or DoT after a request fails.
#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 type is %ld", (long)dnsProtocol]);
NSLog(@"%@",[NSString stringWithFormat:@"0:Unknown,1:UDP,2:TCP,3:TLS,4:HTTPS"]);
if (dnsProtocol == NSURLSessionTaskMetricsDomainResolutionProtocolUnknown) {
NSLog(@"%@",[NSString stringWithFormat:@"DNS source unknown, disabling DoH"]);
nw_privacy_context_require_encrypted_name_resolution(NW_DEFAULT_PRIVACY_CONTEXT, false, nil);
}
}
}
}];
}Enable encrypted DNS for all applications on the device.
If you want to use encrypted DNS system-wide, you can write a NetworkExtension app that uses the NEDNSSettingsManager API to configure the system-wide encrypted DNS settings.
Example code for setting system-wide DNS servers through NetworkExtension using the DoH protocol:
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 encrypted address
NEDNSSettingsManager.shared().dnsSettings = dohSettings
NEDNSSettingsManager.shared().saveToPreferences { saveError in
if let saveError = saveError {
// ...handle error...
return
}
}
}
Example code for setting system-wide DNS servers through NetworkExtension using the DoT protocol:
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 encrypted address
NEDNSSettingsManager.shared().dnsSettings = dotSettings
NEDNSSettingsManager.shared().saveToPreferences { saveError in
if let saveError = saveError {
// ...handle error...
return
}
}
}A DNS configuration includes the HTTPDNS server addresses, a DoT or DoH protocol, and a set of network rules. The network rules ensure that the DNS settings are compatible with different networks.
Example code for network rule configuration:
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
]The code above sets three network rules. The first rule specifies that the DNS configuration takes effect on Wi-Fi networks with the SSID "MyWorkWiFi", but is not enabled for the private enterprise domain name enterprise.example.net. The second rule specifies that the configuration is disabled on cellular networks. The third rule, NEOnDemandRuleConnect, specifies that the DNS configuration is enabled by default. Because the DNS configuration is supported by the system, you do not need to implement an extension program when you write a NetworkExtension app. You only need to select the DNS Settings option in Network Extensions.

After you run the NetworkExtension app, the DNS configuration is installed on the system. To activate the configuration, you must navigate to Settings -> General -> VPN & Network -> DNS and manually enable it.
