All Products
Search
Document Center

Alibaba Cloud DNS:Direct IP connection solutions for iOS in HTTP and HTTPS (including SNI) scenarios

Last Updated:Sep 15, 2025

This topic describes solutions for establishing direct IP connections on iOS in HTTP and HTTPS (including Server Name Indication (SNI)) scenarios.

Overview

HTTP is an application-layer protocol for distributed, collaborative, and hypermedia information systems. It is the foundation of data communication for the World Wide Web. HTTP works on a client-server model. It allows browsers to request web page content from web servers and display the results. HTTP transmits data in plaintext and does not provide an encryption mechanism. This makes the data vulnerable to eavesdropping, tampering, or man-in-the-middle attacks during transmission.

HTTPS is an extension of HTTP used for secure communication over a computer network. It uses Secure Sockets Layer (SSL)/Transport Layer Security (TLS) to establish a secure communication channel and encrypt data packets. The main purpose of HTTPS is to authenticate website servers and protect the privacy and integrity of exchanged data. TLS is a transport-layer encryption protocol and the successor to SSL. HTTPS has two common scenarios: common HTTPS and SNI.

Server Name Indication (SNI) is an extension to the SSL and TLS protocols that improves server and client communication. It is primarily used when a single server provides services for multiple domain names.

  • HTTP scenario

    In an HTTP scenario, the network link does not involve an SSL/TLS handshake or require certificate verification. To implement a direct IP connection, replace the host in the request URL with an IP address and set the Host field in the HTTP header to the original domain name.

  • Common HTTPS scenario

    In a common HTTPS scenario, you can establish a direct IP connection by replacing the host in the request URL with an IP address and setting the Host field in the HTTP header to the original domain name. Then, during certificate verification, you must replace the IP address with the original domain name.

  • SNI scenario

    In an SNI (single IP with multiple HTTPS certificates) scenario, the upper-layer network libraries NSURLConnection/NSURLSession in iOS do not provide interfaces to configure the SNI field. Therefore, a socket-level underlying network library such as CFNetwork is needed to implement an adaptation solution for IP direct connection network requests. However, solutions based on CFNetwork require developers to consider issues such as data transmission and reception, redirection, decoding, and caching (CFNetwork is a very low-level network implementation). We recommend that developers properly evaluate the usage risks of this scenario. We recommend that developers refer to iOS14 native encrypted DNS solution to solve SNI scenario issues.

Important

If you use CDN services on the server side, refer to the SNI scenario solution.

Best practices

  • Solution for HTTP scenarios

1. Replace the host in the request URL with the IP address.

2. Set the Host field in the HTTP header to the original domain name.

// Construct the request.
- (NSMutableURLRequest *)createRequest {
    // Assume the domain name is example.com and the IP address resolved by HTTPDNS is 1.2.3.4.
    NSString *urlString = @"http://example.com/api";
    // 1. Replace the host in the request URL with the IP address.
    NSString *httpDnsString = [urlString stringByReplacingOccurrencesOfString:@"example.com" withString:@"1.2.3.4"];
    NSURL *httpDnsURL = [NSURL URLWithString:httpDnsString];
    NSMutableURLRequest *mutableReq = [NSMutableURLRequest requestWithURL:httpDnsURL];
    // 2. Set the Host field in the HTTP header to the original domain name.
    [mutableReq setValue:@"example.com" forHTTPHeaderField:@"Host"];
    return mutableReq;
}

  • Solution for common HTTPS scenarios

1. Replace the host in the request URL with the IP address.

2. Set the Host field in the HTTP header to the original domain name.

3. To resolve the domain mismatch issue during certificate verification, replace the IP address with the original domain name.

The following example shows the solution when the NSURLSession and NSURLConnection APIs are called.

// Construct the request.
- (NSMutableURLRequest *)createRequest {
    // Assume the domain name is example.com and the IP address resolved by HTTPDNS is 1.2.3.4.
    NSString *urlString = @"https://example.com/api";
    // 1. Replace the host in the request URL with the IP address.
    NSString *httpDnsString = [urlString stringByReplacingOccurrencesOfString:@"example.com" withString:@"1.2.3.4"];
    NSURL *httpDnsURL = [NSURL URLWithString:httpDnsString];
    NSMutableURLRequest *mutableReq = [NSMutableURLRequest requestWithURL:httpDnsURL];
    // 2. Set the Host field in the HTTP header to the original domain name.
    [mutableReq setValue:@"example.com" forHTTPHeaderField:@"Host"];
    return mutableReq;
}

// 3. During certificate verification, replace the IP address with the original domain name.

/*
 * NSURLConnection
 */
- (void)connection:(NSURLConnection *)connection
willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
    if (!challenge) {
        return;
    }
    /*
     * When HTTPDNS is used, the host in the URL is set to an IP address. 
     * Obtain the real domain name from the HTTP header.
     */
    NSString* host = [[self.request allHTTPHeaderFields] objectForKey:@"host"];
    if (!host) {
        host = self.request.URL.host;
    }
    /*
     * Check whether the authentication method for the challenge is NSURLAuthenticationMethodServerTrust.
     * This authentication process occurs in HTTPS mode.
     * If no authentication method is configured, the default network request flow is used.
     */
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
    {
        if ([self evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:host]) {
            /*
             * After verification, construct an NSURLCredential and send it to the initiator.
             */
            NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
            [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
        } else {
            /*
             * If verification fails, proceed with the default process.
             */
            [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
        }
    } else {
        /*
         * For other authentication methods, proceed directly with the process.
         */
        [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
    }
}
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
/*
 * NSURLSession
 */
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
 completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * __nullable credential))completionHandler
{
    if (!challenge) {
        return;
    }
    NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
    NSURLCredential *credential = nil;
    /*
     * Obtain the original domain name.
     */
    NSString* host = [[self.request allHTTPHeaderFields] objectForKey:@"host"];
    if (!host) {
        host = self.request.URL.host;
    }
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        if ([self evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:host]) {
            disposition = NSURLSessionAuthChallengeUseCredential;
            credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
        } else {
            disposition = NSURLSessionAuthChallengePerformDefaultHandling;
        }
    } else {
        disposition = NSURLSessionAuthChallengePerformDefaultHandling;
    }
    // Use the default authentication method for other challenges.
    completionHandler(disposition,credential);
}	

- (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()];
    }
    /*
     * Attach the verification policy to the server certificate.
     */
    SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
    /*
     * Evaluate whether the current serverTrust is trusted.
     * Apple Inc. recommends that 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);
}

For IP direct connection under AFNetworking network library in common scenarios, refer to the following solution:

// Use this return value as the request URL for AFHTTPSessionManager.
+(NSString *)getIPStringFromAliCloudDNSResolverWithURLString: (NSString *)URLString manager: (AFHTTPSessionManager *)manager {
    NSURL *originUrl = [NSURL URLWithString :URLString];
    NSString *host = originUrl.host;
    NSString *ip= [[DNSResolver share] getIpsByCacheWithDomain:host andExpiredIPEnabled:YES].firstObject;
    NSString *ipURLString = URLString;
    if (ip) {
        // 1. Replace the host in the request URL with the IP address.
        ipURLString = [URLString stringByReplacingOccurrencesOfString:host withString:ip];
        // 2. Set the Host field in the HTTP header to the original domain name.
        [manager.requestSerializer setValue:originUrl.host forHTTPHeaderField:@"Host"];
        __weak typeof (AFHTTPSessionManager *) weakSessionManager = manager;
        // 3. During certificate verification, replace the IP address with the original domain name.
        [manager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession*_Nonnullsession,
                                                                                                        NSURLAuthenticationChallenge * _Nonnull challenge, NSURLCredential * _Nullable __autoreleasing * _Nullable credential) {
            NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
            // Obtain the original domain name.
            NSString *host = [[weakSessionManager.requestSerializer HTTPRequestHeaders] objectForKey:@"host" ];
            if (!host) {
                host = challenge.protectionSpace.host;
            }
            if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
                if ([self evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:host]) {
                    disposition = NSURLSessionAuthChallengeUseCredential;
                    *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
                } else {
                    disposition = NSURLSessionAuthChallengePerformDefaultHandling;
                }
            } else {
                disposition = NSURLSessionAuthChallengePerformDefaultHandling;
            }
            return disposition;
        }];
    }
        return ipURLString;
}

+(BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain {
     NSMutableArray *policies = [NSMutableArray array];
     if (domain) {
         [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
     } else {
         [policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
     }
     SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
     SecTrustResultType result;
     SecTrustEvaluate(serverTrust, &result);
     return (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);
 }
 

Important

If you initiate a network request based on this solution and an SSL verification error is reported, such as the iOS system error kCFStreamErrorDomainSSL, -9813; The certificate for this server is invalid, check whether the application scenario is SNI (single IP with multiple HTTPS domain names).

  • Solution for SNI HTTPS scenarios

1. Custom NSURLProtocol solution

For solutions in SNI scenarios, refer to the Demo sample project source code. The following section describes some current challenges and coping strategies:

Support for POST requests

If NSURLProtocol is used to intercept an NSURLSession request, the request body is missing. You can use the following method to resolve this issue:

Use HTTPBodyStream to obtain the request body and assign a value to the body. Sample code:

//
//  NSURLRequest+NSURLProtocolExtension.h
//
//
#import <Foundation/Foundation.h>
@interface NSURLRequest (NSURLProtocolExtension)
- (NSURLRequest *)alidns_getPostRequestIncludeBody;
@end
//
//  NSURLRequest+NSURLProtocolExtension.h
//
//
#import "NSURLRequest+NSURLProtocolExtension.h"
@implementation NSURLRequest (NSURLProtocolExtension)
- (NSURLRequest *)alidns_getPostRequestIncludeBody {
    return [[self alidns_getMutablePostRequestIncludeBody] copy];
}
- (NSMutableURLRequest *)alidns_getMutablePostRequestIncludeBody {
    NSMutableURLRequest * req = [self mutableCopy];
    if ([self.HTTPMethod isEqualToString:@"POST"]) {
        if (!self.HTTPBody) {
            NSInteger maxLength = 1024;
            uint8_t d[maxLength];
            NSInputStream *stream = self.HTTPBodyStream;
            NSMutableData *data = [[NSMutableData alloc] init];
            [stream open];
            BOOL endOfStreamReached = NO;
            //[stream hasBytesAvailable] cannot be used to determine a while loop. When the system processes image files, the system returns YES for [stream hasBytesAvailable]. As a result, an infinite while loop occurs.
            while (!endOfStreamReached) {
                NSInteger bytesRead = [stream read:d maxLength:maxLength];
                if (bytesRead == 0) { //The system reads to the end of the file
                    endOfStreamReached = YES;
                } else if (bytesRead == -1) { //An error occurs when the system reads the file
                    endOfStreamReached = YES;
                } else if (stream.streamError == nil) {
                    [data appendBytes:(void *)d length:bytesRead];
                }
            }
            req.HTTPBody = [data copy];
            [stream close];
        }
    }
    return req;
}
@end

Usage:

Implement the +canonicalRequestForRequest: method in the NSURLProtocol subclass used to intercept requests and process the request object:

+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
    return [request alidns_getPostRequestIncludeBody];
}

The following describes the functions of related methods:

//NSURLProtocol.h
/*!
 *  @method: Create an NSURLProtocol instance. After NSURLProtocol is registered, all NSURLConnections will check whether to hold the HTTP request through this method.
 @param:
 @return: YES: Hold the HTTP request       NO: Do not hold the HTTP request
 */
+ (BOOL)canInitWithRequest:(NSURLRequest *)request
/*!
 *  @method: The NSURLProtocol abstract class must be implemented. In most cases, the protocols that are used by input and output requests must be consistent. This is a basic requirement. This way, the request can be directly returned. In most cases, you do not need to modify the request. If you modify a request, a new request is returned. For example, if you add a title to an HTTP request to generate a new request, the new request is returned.
 @param: Local HttpRequest: request
 @return: Direct forwarding
 */
+ (NSURLRequest*)canonicalRequestForRequest:(NSURLRequest *)request

Summary:

  • +[NSURLProtocol canInitWithRequest:] is responsible for filtering which network requests need to be intercepted

  • +[NSURLProtocol canonicalRequestForRequest:] is responsible for reconstructing the NSURLRequest of the network request that needs to be intercepted.

Note: The execution condition of +[NSURLProtocol canonicalRequestForRequest:] is that the return value of +[NSURLProtocol canInitWithRequest:] is YES.

When intercepting NSURLSession requests, you need to add the NSURLProtocol subclass used to intercept requests to NSURLSessionConfiguration, as follows:

NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSArray *protocolArray = @[ [CUSTOMEURLProtocol class] ];
configuration.protocolClasses = protocolArray;
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:[NSOperationQueue mainQueue]];

For IP direct connection under AFNetworking network library in SNI scenarios, refer to the following solution:

    // Create NSURLSessionConfiguration instance
    NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
    
    // Set to use custom HTTP DNS resolution protocol
    config.protocolClasses = @[[CFHTTPDNSHTTPProtocol class]];

    // Initialize AFHTTPSessionManager
    AFHTTPSessionManager *sessionManager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:config];
    sessionManager.responseSerializer = [AFHTTPResponseSerializer serializer];

    // Create data task
    NSURLSessionDataTask *task = [sessionManager dataTaskWithHTTPMethod:@"GET"
                                                             URLString:@"Your request URL"
                                                            parameters:nil
                                                               headers:nil
                                                            uploadProgress:^(NSProgress *uploadProgress) {
        NSLog(@"Upload progress: %@", uploadProgress.localizedDescription);
    } downloadProgress:^(NSProgress *downloadProgress) {
        NSLog(@"Download progress: %@", downloadProgress.localizedDescription);
    } success:^(NSURLSessionDataTask *task, id responseObject) {
        // Handle successful response
        NSLog(@"Success: %@", responseObject);
    } failure:^(NSURLSessionDataTask *task, NSError *error) {
        // Handle the error
        NSLog(@"Failure: %@", error.localizedDescription);
    }];
    
    // Start the object download task
    [task resume];

2. Other underlying network library solutions

Taking libcurl as an example, libcurl / cURL at least 7.18.1 (March 30, 2008) compiled with an SSL/TLS toolkit with SNI support, curl has a --resolve method that can implement accessing HTTPS websites using specified IP addresses.

Sample code for iOS:

// {HTTPS domain name}:443:{IP address}
NSString *curlHost = ...;
_hosts_list = curl_slist_append(_hosts_list, curlHost.UTF8String);
curl_easy_setopt(_curl, CURLOPT_RESOLVE, _hosts_list);

Where curlHost is like: {HTTPS domain name}:443:{IP address}

_hosts_list is a struct type hosts_list, which can set multiple mappings between IP and Host. The curl_easy_setopt method passes CURLOPT_RESOLVE to set this mapping in the HTTPS request. Then, SNI is implemented.

Summary

Solution

Applicable scenarios

Pros

Cons

1. Replace URL

2. Set Host

HTTP scenarios

Simplest method

Only suitable for the plaintext HTTP protocol

1. Replace URL

2. Set Host

3. Replace IP with the original domain name during certificate verification

Common HTTPS scenarios (non-SNI)

Simple integration, support for common system networking libraries, NSURLSession,

NSURLConnection,

AFHTTPSessionManager

Does not support SNI

Custom NSURLProtocol

All scenarios

Based entirely on low-level system APIs

Based on a low-level network library. Development and maintenance costs are high. You must handle data transmission and reception, redirection, decoding, and caching.

libcurl

All scenarios

Cross-platform

Mature community and extensive documentation.

Lets you set SNI.

Strong cross-platform compatibility.

The C language API has a learning curve for iOS developers.

You must handle issues such as cookies, redirection, and caching.