Using HTTPDNS in iOS native scenarios

Updated at:

This topic describes how to implement a direct IP connection when you integrate HTTPDNS into an iOS app. For more information about how to integrate HTTPDNS on iOS, see iOS SDK integration.

1. Introduction

In mobile network environments, issues such as DNS hijacking and local DNS cache pollution often prevent domain names from resolving correctly, which leads to network request failures. For these scenarios, Alibaba Cloud HTTPDNS provides a reliable recursive domain name resolution service. This service helps mobile apps bypass the potential risks of local DNS and improves the success rate and stability of network requests.

However, using HTTPDNS on the iOS platform requires you to replace the original domain name in a request with the resolved IP address before you initiate the request. This can cause additional issues, especially in complex scenarios such as HTTPS and Server Name Indication (SNI). Therefore, before you integrate HTTPDNS, you must have a comprehensive understanding of the potential issues and viable solutions. This ensures that you can use HTTPDNS safely and correctly in your business.

image.png

This topic describes the main issues that you may encounter when using HTTPDNS on iOS. It also provides integration solutions for different scenarios, along with their pros and cons, to help developers quickly complete HTTPDNS integration.

2. Issues when using HTTPDNS on iOS

In mobile applications, if you replace the domain name in the original URL, such as example.com, with the IP address resolved by HTTPDNS, you often encounter the following issues. These problems are closely related to how the HTTPS protocol uses the Host field at different layers: the TLS/SSL layer and the HTTP layer.

  • TLS/SSL layer: In an HTTPS scenario, the client first performs a TLS/SSL handshake. The client uses the Host from the URL to complete the following tasks:

    1. Certificate verification: Verifies that the domain name of the server certificate (Common Name or Subject Alternative Name) matches the requested Host.

    2. SNI: When establishing a TLS connection, the client sends the requested domain name information (the Host in the URL) to the server. This allows the server to return the corresponding certificate.

  • HTTP layer: After the TLS handshake is complete, the client includes the Host field in the HTTP request header. This tells the server the specific site or resource for the request. If you replace the domain name in the URL with an IP address and do not manually set the Host in the HTTP header, the server may not be able to identify the actual domain name being accessed. This can cause the request to fail or return abnormal content.

Based on the role of the Host at various layers of the HTTPS protocol stack, replacing the domain name in the URL with the IP address resolved by HTTPDNS causes the following technical issues:

  1. Domain name and certificate mismatch For an HTTPS request, if you directly use the IP address resolved by HTTPDNS as the Host in the URL, the TLS layer cannot match the correct certificate domain name (Common Name or SAN additional domain name). This causes the SSL handshake to fail.

  2. SNI issues In an SNI scenario, a single server IP address may correspond to certificates for multiple domain names. If the client does not pass the correct domain name information during the SSL handshake phase and sends only the IP address, the server cannot return a certificate that matches the domain name. This causes the SSL handshake to fail. Because high-level network APIs in iOS, such as NSURLSession, do not expose an interface to directly configure SNI, SNI issues are often difficult to solve with simple methods.

  3. Host header and service addressing If you replace the domain name in the URL with an IP address but forget to explicitly set the Host in the HTTP request header to the original domain name, the server-side may not be able to identify the specific site or resource at the HTTP layer. For example, in a CDN scenario, the server relies on the Host field to distribute the correct content. If the Host is an IP address, the service becomes abnormal.

  4. Choice of underlying network library The built-in high-level APIs in iOS, such as NSURLSession, have limited extensibility for customizing SNI or manual certificate verification. If a developer wants to handle SNI or modify the TLS handshake logic, they need to use lower-level interfaces, such as CFNetwork or libcurl. However, this increases development and maintenance costs.

In summary, directly replacing the domain name in a URL with the IP address resolved by HTTPDNS affects certificate verification and SNI transmission at the TLS layer in HTTPS scenarios. It may also cause Host header information to be abnormal at the HTTP layer. Therefore, when you integrate HTTPDNS on iOS, you must solve these issues in a targeted manner to ensure the reliability and security of network requests.

3. Recommended solution: Integrate with EMASCurl

In real-world iOS applications, HTTPS is standard, and scenarios involving CDNs, single IPs for multiple domains, and SNI are common. To address these challenges, the Alibaba Cloud EMAS team provides EMASCurl, an iOS network library built on libcurl. It works directly with HTTPDNS and is the recommended solution for implementing a direct IP connection on iOS. The code is open-source and available on GitHub.

This section describes the use cases, integration approach, and usage notes for the EMASCurl solution. For specific installation instructions, interception configurations, and API usage, see the README file on GitHub.

  1. Installation and interception

    • It can be used in two main ways:

      1) Intercept requests from an NSURLSession created with a specific NSURLSessionConfiguration.

      2) Intercept requests from the system's global [NSURLSession sharedSession].

  2. Integration with HTTPDNS

    • Implement the EMASCurlProtocolDNSResolver protocol to pass the HTTPDNS resolution results to EMASCurl.

    • In the resolveDomain: method, call [HttpDnsService resolveHostSyncNonBlocking:] to get the IP address. Then, return it to EMASCurl to handle the SNI setup and send the request.

  3. Certificate verification

    • EMASCurl relies on the certificate verification mechanism of libcurl. You can also extend or customize certificate verification through the provided interfaces to meet your service requirements.

  4. HTTP/3 support

    • EMASCurl provides EMASCurl/HTTP3, an encapsulated version based on a QUIC-enabled libcurl. You can integrate it as needed to use HTTP/3 capabilities without extra adaptation.

The following example shows the basic method for integrating EMASCurl with HTTPDNS:

@interface MyDNSResolver : NSObject <EMASCurlProtocolDNSResolver>
@end

@implementation MyDNSResolver

+ (nullable NSString *)resolveDomain:(nonnull NSString *)domain {
    HttpDnsService *httpdns = [HttpDnsService sharedInstance];
    HttpdnsResult *result = [httpdns resolveHostSyncNonBlocking:domain
                                                      byIpType:HttpdnsQueryIPTypeBoth];
    if (!result || (!result.hasIpv4Address && !result.hasIpv6Address)) {
        return nil;
    }

    NSMutableArray<NSString *> *allIPs = [NSMutableArray array];
    if (result.hasIpv4Address) {
        [allIPs addObjectsFromArray:result.ips];
    }
    if (result.hasIpv6Address) {
        [allIPs addObjectsFromArray:result.ipv6s];
    }
    return [allIPs componentsJoinedByString:@","];
}

@end

- (void)setupEMASCurl {
    EMASCurlConfiguration *config = [EMASCurlConfiguration defaultConfiguration];

    // Configure the HTTPDNS resolver
    config.dnsResolver = [MyDNSResolver class];

    NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];

    // Install EMASCurl
    [EMASCurlProtocol installIntoSessionConfiguration:sessionConfig
                                    withConfiguration:config];

    // Create a specific session
    self.session = [NSURLSession sessionWithConfiguration:sessionConfig];
    
    // Use this session for subsequent requests
}
Note

Pros:

  • It encapsulates the underlying capabilities of libcurl with an API that is familiar to iOS developers.

  • It easily integrates with HTTPDNS through a DNS hook mechanism.

  • It solves both the domain transmission and certificate verification issues in SNI scenarios, which reduces integration cost.

  • It supports HTTP/3 out of the box and does not require custom compilation or adaptation.

Cons:

  • It depends on a third-party library (EMASCurl and libcurl). You must consider compatibility and version upgrades.

  • For complex HTTP features or custom requirements, you may need to understand EMASCurl's internal implementation to ensure service availability.

Important
  • When you integrate EMASCurl, test common HTTP/HTTPS features, such as redirection, cookies, and concurrent requests, to ensure that they meet your service requirements.

  • If your business has strict security or network performance requirements, evaluate the performance of EMASCurl on your current iOS version.

  • Ensure that requests and handshakes complete normally in different network environments, such as Wi-Fi, cellular networks, and proxies.

4. Integration solutions for simplified scenarios

For plain HTTP or HTTPS without SNI scenarios, you can typically continue to use the system's built-in NSURLSession and regular network request logic to integrate HTTPDNS with simple adjustments. These solutions are simpler to implement, suitable for specific scenarios, and have limited applicability.

Note that plain HTTP scenarios do not involve a TLS handshake or certificate verification. However, HTTPS without SNI scenarios still require you to handle certificate verification, which you can do by hooking the verification process in NSURLSession.

4.1 Plain HTTP scenarios

For plain HTTP requests, there is no TLS/SSL handshake or certificate verification. Therefore, the core operations for integrating HTTPDNS occur only at the HTTP layer:

  1. Replace the Host in the request URL with the IP address from HTTPDNS

    • For example, if the original request URL is http://example.com/api and HTTPDNS resolves it to the IP address 1.2.3.4, change the URL to http://1.2.3.4/api.

  2. Explicitly set the Host header to the original domain

    • If you use NSMutableURLRequest, you can add request.allHTTPHeaderFields[@"Host"] = @"example.com"; to the request header. This ensures that the server can identify the correct domain at the application layer.

Note

Pros: Simple to implement. It only requires replacing the Host and setting the header in the existing HTTP request, with minimal development effort.

Cons: Only applicable to plain HTTP. It cannot solve certificate verification and SNI-related issues in HTTPS scenarios.

4.2 HTTPS scenarios without SNI

For HTTPS sites that do not use SNI or that have only a few fixed domains covered by their certificate, you can integrate HTTPDNS and perform certificate verification at the NSURLSession layer as follows:

  1. Replace the Host in the request URL with the IP address from HTTPDNS

    • For example, if the original request URL is https://example.com/api and HTTPDNS resolves it to the IP address 1.2.3.4, change the URL to https://1.2.3.4/api.

  2. Explicitly set the Host header to the original domain

    • Similarly, you can set request.allHTTPHeaderFields[@"Host"] = @"example.com"; in NSMutableURLRequest.

  3. Hook the certificate verification process

    • Because this is an HTTPS request, certificate verification is required during the TLS handshake. At this point, using the IP address as the Host for verification will cause a domain and certificate mismatch.

    • In the NSURLSessionDelegate callback method URLSession:didReceiveChallenge:completionHandler:, you can replace the IP address with the original domain (example.com) when you verify the serverTrust obtained from the system. This allows the certificate verification to pass.

    • Code example:

      - (void)URLSession:(NSURLSession *)session
                    task:(NSURLSessionTask *)task
      didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
        completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition,
                                    NSURLCredential *credential))completionHandler {
          if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
              NSString *originalHost = [self getOriginalHostFromRequest:task.originalRequest];
              SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
              if ([self evaluateServerTrust:serverTrust forDomain:originalHost]) {
                  // The certificate is verified.
                  NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust];
                  completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
              } else {
                  // Certificate verification failed. Use the default handler.
                  completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
              }
          } else {
              completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
          }
      }
      
      - (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain {
          // Create a certificate verification policy.
          NSMutableArray *policies = [NSMutableArray array];
          if (domain) {
              [policies addObject:(__bridge_transfer id) SecPolicyCreateSSL(true, (__bridge CFStringRef) domain)];
          } else {
              [policies addObject:(__bridge_transfer id) SecPolicyCreateBasicX509()];
          }
      
          // Bind the verification policy to the server certificate.
          SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef) policies);
          // Evaluate whether the current serverTrust is trusted. Apple recommends that the serverTrust can be verified if the result is kSecTrustResultUnspecified or kSecTrustResultProceed. For more information, see https://developer.apple.com/library/ios/technotes/tn2232/_index.html.
          // For more information about SecTrustResultType, see SecTrust.h.
          SecTrustResultType result;
          SecTrustEvaluate(serverTrust, &result);
          return (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);
      }
Note

Pros:

  • You can use the native NSURLSession and its certificate verification logic without adding a third-party library.

  • The implementation cost is manageable, which makes it suitable for scenarios that do not involve SNI or require only a few domain certificates.

Cons:

  • This approach does not handle SNI scenarios. If multiple domains are deployed on the same IP address, such as in a CDN scenario, the handshake still fails because the server returns the wrong certificate.

5. Other solutions for HTTPS + SNI scenarios

For HTTPS + SNI scenarios, besides EMASCurl, you can also implement HTTPDNS integration by using lower-level capabilities. These solutions typically offer greater flexibility but come with higher development and maintenance costs. They are better suited for scenarios with existing custom network stacks, a need for stronger low-level control, or cross-platform unification requirements. If you do not have special requirements, we recommend using the EMASCurl solution.

5.1 Custom NSURLProtocol

iOS allows you to intercept network requests initiated by the system by subclassing NSURLProtocol and then implementing the HTTP/HTTPS request logic yourself at a lower level. You can manually complete all network operations by using interfaces such as CFNetwork or NSInputStream/NSOutputStream:

  1. Intercept requests

    • In the canInitWithRequest: method, determine whether to intercept the current request.

    • In the startLoading method, replace the domain in the original request URL with the IP address. You must retain the original domain for subsequent certificate verification and SNI settings.

  2. Set SNI

    • Use CFStream-related APIs or the SecureTransport interface to specify kCFStreamSSLPeerName as the original domain. This ensures that the underlying layer includes the correct domain information during the SSL handshake.

  3. Verify certificates

    • Manually execute the certificate verification process to ensure that the domain included in the certificate matches the original domain.

Note

Pros: Does not depend on a third-party library. It is based entirely on low-level system APIs and offers high flexibility.

Cons: High implementation cost. You must manually handle redirection, cookies, caching, encoding, and traffic statistics. It does not support connection reuse, which results in average performance. It also carries a high maintenance risk and requires extra adaptation when the system or network environment is upgraded.

For a reference example, Alibaba Cloud EMAS provides a sample implementation named HttpDnsNSURLProtocolImpl.m in the httpdns_ios_demo. You can modify or reuse it based on your service requirements.

5.2 Using libcurl

libcurl is a cross-platform network library implemented in C. It supports manually setting the SNI field to pass the correct domain information during the SSL handshake. This is required for certificate verification in scenarios where multiple domains share the same IP address. The general process is as follows:

  1. Resolve the domain to get the corresponding IP address

    • For example, use an HTTPDNS API method such as resolveHostSyncNonBlocking: to get the IP address of the target domain.

  2. Set the SNI and IP mapping

    • Use CURLOPT_RESOLVE or another API to write the "domain:port:resolved_IP" mapping to curl's internal DNS cache.

    • Continue to use the original domain for CURLOPT_URL. This ensures that the correct domain information is included in the TLS handshake.

  3. Verify certificates

    • libcurl enables certificate verification by default. You can also use corresponding callbacks for more fine-grained checks on certificates as needed.

The following code snippet (pseudocode) demonstrates how to use the HTTPDNS resolution result with libcurl to complete a request in iOS:

CURL *curl_handle = curl_easy_init();
if (curl_handle) {
    // For example, get IP = 1.2.3.4 from HTTPDNS, target domain = example.com, and port = 443
    struct curl_slist *dnsResolve = NULL;
    dnsResolve = curl_slist_append(dnsResolve, "example.com:443:1.2.3.4");
    // Set the domain-to-IP mapping
    curl_easy_setopt(curl_handle, CURLOPT_RESOLVE, dnsResolve);

    // Continue to use the original domain as the URL
    curl_easy_setopt(curl_handle, CURLOPT_URL, "https://example.com");

    // Enable SSL verification
    curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 1L);
    curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 2L);

    // Initiate the request
    CURLcode res = curl_easy_perform(curl_handle);

    // Check the result
    if (res != CURLE_OK) {
        fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
    }

    // Clean up
    curl_easy_cleanup(curl_handle);
    curl_slist_free_all(dnsResolve);
}
Note

Pros:

  • Mature and stable, supports a rich set of protocols, and can adapt to complex network environments. It has built-in support for SNI scenarios.

Cons:

  • Requires compiling libcurl into the iOS project and using a pure C interface, which has a learning curve for Objective-C/Swift developers.

  • You must also handle custom request flows or encapsulate HTTP logic, such as for cookies, redirection, and caching.

6. Summary

You can choose a solution based on your business needs, such as whether you need to support SNI, multiple domain names, and certificate verification. The following table compares the different solutions:

Solution

Scenarios

Pros

Cons

EMASCurl (Recommended)

All scenarios

For simple integration on iOS

- Provides a good wrapper for libcurl

- Simple integration with HTTPDNS

- Implements SNI and certificate verification

- Supports HTTP/3

- Depends on a third-party library, requires attention to compatibility and upgrades

- Special requirements may require reading the source code for customization

Set Host and Header only

Plain HTTP scenarios

- Lowest integration cost

- Only for plain HTTP protocol

NSURLSession + Hook certificate verification

(Still requires setting Host and Header)

HTTPS (non-SNI) scenarios

- Low integration cost

- Uses system APIs, no extra libraries needed

- Does not support SNI

Custom NSURLProtocol

All scenarios

Requires more flexible low-level control

- Completely based on low-level system APIs

- High flexibility

- High development and maintenance costs

- No connection reuse, average performance

- Requires manual handling of redirection, cookies, caching, encoding, and other special cases

libcurl

All scenarios

Cross-platform or custom HTTP flows

- Mature and stable

- Supports SNI field setting and rich protocols

- Flexible certificate verification extension

- C interface has a learning curve for Objective-C/Swift developers

- Requires manual encapsulation for cookies, redirection, caching, etc.

You can evaluate these solutions based on your business's multi-domain needs, network security requirements, compatibility, maintenance costs, and acceptance of third-party libraries. You must choose the most suitable integration solution and thoroughly test the availability and security of network requests before the solution is published.