全部產品
Search
文件中心

HTTPDNS:iOS端Native情境使用HTTPDNS

更新時間:Jan 01, 2026

本文主要介紹iOS端App整合HTTPDNS時實現“IP直連”的解決方案。關於iOS上如何整合HTTPDNS,請先查看iOS SDK接入

1. 前言

在移動端網路環境中,DNS劫持、LocalDNS緩衝汙染等問題時常導致網域名稱無法正確解析、網路請求失敗。針對這些情境,阿里雲HTTPDNS提供了可靠的網域名稱遞迴解析服務,協助移動App繞過本地DNS的潛在風險,提升網路請求的成功率和穩定性。

但是,在iOS平台上使用HTTPDNS,需要將請求原本的網域名稱替換為實際解析出來的IP再直接發起請求,這就可能引發額外的問題,尤其是在HTTPS和SNI等複雜情境下。因此,在整合HTTPDNS之前,需要對可能出現的問題和可行的解決方案有一個全面的認識,以便在業務中安全且正確地使用HTTPDNS。

本文將介紹在iOS上使用HTTPDNS會遇到的主要問題,並給出在不同情境下的整合方案和各自的利弊,希望協助開發人員快速完成HTTPDNS的接入。

2. 在iOS上使用HTTPDNS會遇到哪些問題

在移動端應用中,如果我們將原始URL中的網域名稱(如 example.com)替換為HTTPDNS解析得到的IP,往往會遇到以下幾個方面的問題。之所以會產生這些問題,與HTTPS協議在不同層次(TLS/SSL層和HTTP層)對Host欄位的使用方式密切相關:

  • TLS/SSL層: 在HTTPS情境下,用戶端會先進行TLS/SSL握手,其中會使用到URL中的Host來完成以下任務:

    1. 認證校正:驗證伺服器憑證的網域名稱(Common Name或Subject Alternative Name)是否與請求的Host一致。

    2. SNI(Server Name Indication):在建立TLS串連時,用戶端會將所請求的網域名稱資訊(即URL中的Host)發送給伺服器,以便伺服器返回對應的認證。

  • HTTP層: 在完成TLS握手後,用戶端會在HTTP請求的Header中包含Host欄位,用於告訴伺服器此請求對應的具體網站或資源。如果將URL中的網域名稱替換為IP,又未手動設定HTTP Header裡的Host,就可能讓伺服器無法識別要訪問的實際網域名稱,從而導致請求失敗或返回異常內容。

根據上述Host在HTTPS協議棧各層的作用描述,可以看到,若只把URL中的網域名稱直接替換為HTTPDNS解析出來的IP,則會引起以下技術問題:

  1. 網域名稱與認證不匹配 對於HTTPS請求,如果URL裡直接使用HTTPDNS解析出來的IP作為請求的Host,TLS層面無法匹配到正確的認證網域名稱(Common Name或SAN擴充網域名稱),會導致SSL握手失敗。

  2. SNI(Server Name Indication)問題 在SNI情境中,同一伺服器IP可能對應多個網域名稱的認證。如果用戶端在SSL握手階段沒有傳遞正確的網域名稱資訊(只發送了IP),服務端就無法返回匹配該網域名稱的認證,導致SSL握手失敗。由於iOS的高層網路API(如 NSURLSession)並未暴露直接配置SNI的介面,SNI問題常難以通過簡單方式解決。

  3. Host頭與業務定址 如果僅把URL中的網域名稱替換為IP,卻忘記在HTTP要求標頭中顯式設定Host為原始網域名稱,那麼在HTTP層伺服器端可能無法識別具體的網站或資源。例如CDN情境下,伺服器需要依賴Host欄位來分發正確的內容,一旦Host為IP,將導致服務異常。

  4. 底層網路程式庫的選擇 iOS內建的高層API(NSURLSession等)在定製SNI或手動認證校正方面擴充性較弱。如果開發人員想自行處理SNI或修改TLS握手邏輯,就需要使用更底層的介面(如 CFNetworklibcurl 等),但這會帶來更高的開發和維護成本。

綜上所述,直接將URL中的網域名稱替換為HTTPDNS解析出來的IP,在HTTPS情境下會影響TLS層的認證校正與SNI傳遞,並在HTTP層可能導致Host頭資訊異常。因此在iOS端整合HTTPDNS,需要有針對性地解決這些問題,才能確保網路請求的可靠性與安全性。

3. 普通HTTP情境、HTTPS+非SNI情境接入方案

在針對普通HTTPHTTPS + 非SNI這兩種情境下,我們通常可以繼續使用系統內建的NSURLSession以及常規的網路請求邏輯,只需做一些相對簡單的處理即可。需要注意的是,普通HTTP情境並不涉及TLS握手和認證校正;而HTTPS + 非SNI情境需要考慮到認證校正,但可以通過在NSURLSession中Hook驗證流程的方式來應對。

3.1 普通HTTP情境標題

對於普通HTTP請求,網路鏈路中不存在TLS/SSL握手,也無需認證校正,因此整合HTTPDNS的核心操作僅在HTTP層進行:

  1. 替換請求URL中的Host為HTTPDNS解析出的IP

    • 例如原始請求URL為 http://example.com/api,HTTPDNS解析後得到IP 1.2.3.4,則把URL修改為 http://1.2.3.4/api

  2. 在HTTP Header中顯式設定Host為原始網域名稱

    • 若使用NSMutableURLRequest,可在要求標頭中添加 request.allHTTPHeaderFields[@"Host"] = @"example.com"; 確保伺服器在應用程式層能夠識別到正確的網域名稱。

說明

優點:實現簡單;只需在現有HTTP請求中替換Host+設定Header,開發量較低。

缺點:僅適用於HTTP明文協議,無法解決HTTPS情境下的認證校正和SNI相關問題。

3.2 HTTPS + 非SNI情境標題

對於不使用SNI機制或僅包含少量固定網域名稱的HTTPS網站(認證中只涵蓋這些網域名稱),可以在NSURLSession層通過以下方式來完成HTTPDNS接入與認證校正:

  1. 替換請求URL中的Host為HTTPDNS解析出的IP

    • 例如原始請求URL為 https://example.com/api,HTTPDNS解析後得到IP 1.2.3.4,則把URL修改為 https://1.2.3.4/api

  2. 在HTTP Header中顯式設定Host為原始網域名稱

    • 同理,可以在NSMutableURLRequest裡設定 request.allHTTPHeaderFields[@"Host"] = @"example.com";

  3. Hook認證校正過程

    • 由於這是HTTPS請求,需要在TLS握手階段進行認證校正。此時,若直接拿IP作為Host檢查,就會出現網域名稱與認證不匹配的問題。

    • 可以在 NSURLSessionDelegate 的回調方法 URLSession:didReceiveChallenge:completionHandler: 中,將系統擷取到的 serverTrust 進行驗證時,改用原始網域名稱(example.com)替換掉IP,從而通過認證校正。

    • 程式碼範例:

      - (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]) {
                  // 認證校正通過
                  NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust];
                  completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
              } else {
                  // 認證校正失敗,使用預設處理
                  completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
              }
          } else {
              completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
          }
      }
      
      - (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);
          // 評估當前serverTrust是否可信任,官方建議在result = kSecTrustResultUnspecified 或 kSecTrustResultProceed的情況下serverTrust可以被驗證通過,https://developer.apple.com/library/ios/technotes/tn2232/_index.html
          // 關於SecTrustResultType的詳細資料請參考SecTrust.h
          SecTrustResultType result;
          SecTrustEvaluate(serverTrust, &result);
          return (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);
      }

說明

優點

  • 不需要引入額外的第三方庫,直接使用系統原生NSURLSession和認證校正邏輯.

  • 實現成本相對可控,適合不涉及SNI或只需少量網域名稱認證的情境。

缺點

  • 無法處理SNI情境。若在同一IP上部署多個網域名稱(如CDN情境),則仍然會因伺服器返回錯誤的認證而握手失敗。

4. HTTPS+SNI情境接入方案

針對SNI(單IP多HTTPS網域名稱)的情境,簡單的NSURLSession方案中無法在SSL握手階段發送正確的網域名稱資訊,導致握手失敗。為瞭解決此問題,需要在更底層的Socket層級修改或指定SNI欄位。常見的做法有以下三種:

4.1 自訂NSURLProtocol實現

iOS允許開發人員通過繼承NSURLProtocol來攔截系統發起的網路請求,並在底層自行實現HTTP/HTTPS請求邏輯。可以基於CFNetwork或者NSInputStream/NSOutputStream等介面,手動完成所有網路操作:

  1. 攔截請求

    • canInitWithRequest:方法中判斷是否要攔截當前請求。

    • startLoading方法裡,將原始請求URL中的網域名稱替換為IP,並保留原始網域名稱用於後續的認證校正和SNI設定。

  2. 設定SNI

    • 通過CFStream相關API或者SecureTransport介面,指定kCFStreamSSLPeerName為原始網域名稱,這樣在SSL握手階段,底層會帶上正確的網域名稱資訊。

  3. 認證校正

    • 手動執行認證驗證流程,確保認證中包含的網域名稱與原始網域名稱匹配。

說明

優點:不依賴第三方庫,完全基於系統底層API,靈活度高。

缺點:實現成本較高,需要開發人員手動處理重新導向、Cookie、緩衝、編碼、流量統計等;不支援串連複用,效能一般;且維護風險大,升級系統或網路環境時需要額外適配。

如果需要參考樣本,阿里雲提供了httpdns_ios_demoHttpDnsNSURLProtocolImpl.m的樣本實現,可根據業務需求進行修改或複用。

4.2 自行使用libcurl實現網路請求

libcurl是C語言實現的跨平台網路程式庫,支援手動設定SNI欄位,從而在SSL握手階段傳遞正確的網域名稱資訊,以完成多網域名稱共用同一IP的認證校正情境。大致流程如下:

  1. 解析網域名稱,得到對應IP

    • 例如使用HTTPDNS提供的API resolveHostSyncNonBlocking:等方法,擷取到目標網域名稱的IP地址。

  2. 設定SNI和IP映射

    • 通過CURLOPT_RESOLVE或其他API,把“網域名稱:連接埠:解析到的IP”映射寫入到curl內部DNS緩衝。

    • 依然使用原始網域名稱作為CURLOPT_URL的訪問目標,保證TLS握手時會帶上正確的網域名稱資訊。

  3. 認證校正

    • libcurl預設開啟認證校正,也可以根據需求使用相應的回調對認證進行更細粒度的檢查。

下面是一個核心範例程式碼片段(虛擬碼),示範如何在iOS中通過libcurl使用HTTPDNS解析的結果並完成請求:

CURL *curl_handle = curl_easy_init();
if (curl_handle) {
    // 例如從HTTPDNS得到IP = 1.2.3.4,目標網域名稱 = example.com,連接埠 = 443
    struct curl_slist *dnsResolve = NULL;
    dnsResolve = curl_slist_append(dnsResolve, "example.com:443:1.2.3.4");
    // 設定網域名稱-IP映射
    curl_easy_setopt(curl_handle, CURLOPT_RESOLVE, dnsResolve);

    // 依然使用原始網域名稱作為URL
    curl_easy_setopt(curl_handle, CURLOPT_URL, "https://example.com");

    // 開啟SSL驗證
    curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 1L);
    curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 2L);

    // 發起請求
    CURLcode res = curl_easy_perform(curl_handle);

    // 檢查結果
    if (res != CURLE_OK) {
        fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
    }

    // 清理
    curl_easy_cleanup(curl_handle);
    curl_slist_free_all(dnsResolve);
}
說明

優點

  • 成熟、穩定,支援豐富的協議,能適應複雜的網路環境;對SNI情境有內建支援。

缺點

  • 需要將libcurl編譯進iOS專案,使用純C介面,對Objective-C/Swift專案的開發人員有一定學習成本。

  • 同時也要處理自訂的請求流程或自行封裝HTTP邏輯(如Cookie、重新導向、緩衝等)。

4.3 使用EMASCurl

為降低在iOS上直接使用libcurl的接入門檻,阿里雲EMAS團隊提供了EMASCurl庫,它對libcurl進行封裝,並支援與HTTPDNS的直接對接。

  1. 安裝與攔截

    • 提供了兩種主要使用方式: 1)攔截指定NSURLSessionConfiguration建立的NSURLSession; 2)攔截系統全域[NSURLSession sharedSession]

    • API介面請查詢github上的README檔案。

  2. 與HTTPDNS配合

    • 實現EMASCurlProtocolDNSResolver協議即可將HTTPDNS解析結果交給EMASCurl。

    • resolveDomain:方法中調用[HttpDnsService resolveHostSyncNonBlocking:]擷取IP地址,然後返回給EMASCurl來完成後續的SNI設定和請求發送。

  3. 認證校正

    • EMASCurl依賴libcurl的認證校正機制,開發人員也可通過相應介面擴充或自訂認證校正,以適配業務需求。

  4. HTTP/3支援

    • EMASCurl 提供了基於支援 QUIC 的 libcurl 封裝版本 EMASCurl/HTTP3,開發人員可按需接入以使用 HTTP/3 能力,無需額外適配。

說明

優點:

  • 封裝了libcurl的底層能力,API更符合iOS開發人員的使用習慣。

  • 通過DNS Hook機制與HTTPDNS輕鬆對接。

  • 同時解決了SNI情境的網域名稱傳遞和認證校正問題,降低整合成本。

  • 支援 HTTP/3,開箱即用,無需自行編譯適配。

缺點

  • 依賴第三方庫(EMASCurl+libcurl),需要注意相容性、版本升級等。

  • 對於非常複雜的HTTP特性或自訂需求,仍需要閱讀和理解EMASCurl內部封裝實現,以確保業務可用性。

重要
  • 在接入EMASCurl時,需要測試常見的HTTP/HTTPS特性(如重新導向、Cookie、並發請求等)是否符合業務需求。

  • 若業務對安全或網路效能有嚴格要求,需要評估EMASCurl在當前iOS系統版本下的表現。

  • 確保在不同網路環境(Wi-Fi/蜂窩網路/代理等)下都能正常完成請求和握手。

5. 總結

根據業務中是否需要支援SNI、多網域名稱及認證校正等需求,可結合以下方案進行選擇。下表對各方案進行對比:

方案

適用情境

優點

缺點

僅設定Host和Header

普通HTTP 情境

- 整合成本最低

-僅適用於HTTP明文協議

NSURLSession + Hook認證校正

(仍需設定Host和Header)

HTTPS+非SNI情境

- 整合成本低

- 沿用系統API,無需額外庫

- 不支援SNI

自訂NSURLProtocol

全部情境

需要更靈活的底層控制

- 完全基於系統底層API

- 自由度高

- 開發、維護成本高

- 無串連複用,效能一般

- 需要自行處理重新導向、Cookie、緩衝、編碼等特殊情境

libcurl

全部情境

跨平台或自訂HTTP流程

- 成熟、穩定

- 可設定SNI欄位、豐富協議支援

- 認證校正可靈活擴充

- C介面對ObjC/Swift開發人員有一定學習成本

- 需要自行封裝Cookie、重新導向、緩衝等

EMASCurl

全部情境

期望iOS端簡單整合

- 對libcurl封裝較好

- 與HTTPDNS對接簡單

- 實現SNI與認證校正

- 支援HTTP/3

- 依賴第三方庫,需要注意相容與升級

- 特殊需求需閱讀源碼進行二次開發

開發人員應結合自身業務的多網域名稱需求網路安全要求相容性維護成本以及對第三方庫的接受度進行綜合評估,選擇合適的接入方案,並在上線前充分測試網路請求的可用性和安全性。