すべてのプロダクト
Search
ドキュメントセンター

HTTPDNS:iOS クライアントで DoH を使用する

最終更新日:Nov 09, 2025

このドキュメントでは、iOS クライアントで DoH を使用する方法について説明します。

背景

iOS に HTTPDNS を統合する一般的な方法は、HTTPDNS ソフトウェア開発キット (SDK) をインポートすることです。これにより、HTTPS 証明書の検証やサーバ名表示 (SNI) 拡張などの問題に対処できます。これにより、必要に応じてアプリで HTTPDNS の名前解決機能を使用できます。詳細については、「ネイティブ iOS シナリオで HTTPDNS を使用する」をご参照ください。

Apple は iOS 14 以降でセキュア DNS を導入し、DoH を介して HTTPDNS を統合するための新しい実現可能なソリューションを追加しました。次の 2 つの WWDC ビデオを参考にしてください。

重要

セキュア DNS の使用は iOS でネイティブにサポートされており、コードレベルでネットワークリクエストの詳細を変更することなく、アプリまたはデバイスのグローバルネットワーク処理レベルで直接有効になるため、ソリューション全体がより洗練されます。ただし、重大な制限もあります。セキュア DNS を介して HTTPDNS が統合されている場合:

  • アプリレベルまたはデバイスレベルでのみ有効になり、対応する範囲内のすべてのドメイン名解決は、アプリ内のサードパーティ SDK からのネットワークリクエストを含め、HTTPDNS を経由します。きめ細かいレベルで制御することはできません。

  • デバイスレベルで有効にすることを選択した場合、エンドユーザーは特別なアクセス許可を付与する必要があります。通常、ネットワークユーティリティアプリケーションのみがこのアクセス許可を申請できます。

前提条件

DoH を有効にし、DoH インジェストエンドポイントを取得します。詳細については、「DoH サービスを構成する」をご参照ください。

説明
  • DoH が有効になっていない場合、解決リクエストは失敗し、HTTPDNS サーバーは 400 エラーコードを返します。

  • 「ドメイン解決範囲」を「ドメインリスト内のドメイン」に設定した場合、リストにないドメインに対して、HTTPDNS サーバーは 200 ステータスコードを返しますが、解決結果は返しません。

  • 「ドメイン解決の範囲」が「すべてのドメイン」に設定されている場合、ブラックリスト内のドメインの場合、HTTPDNS サーバーは 200 状態コードを返しますが、解決結果は返しません。

アプリケーションレベルの DoH 構成の使用

iOS 14 以降の network.framework は、privacyContext を提供して、アプリケーションに対して DoH を個別に構成し、アプリケーションのライフサイクル中に DNS 解決プロセスを制御できるようにします。

サンプルコード

NSURLSession を管理するために DataTaskManager を作成します。

@interface DataTaskManager : NSObject <NSURLSessionTaskDelegate>
#import "DataTaskManager.h"
@import Network;
@import Foundation;

- (instancetype)init {
    self = [super init];
    if (self) {
        _networkQueue = dispatch_queue_create("com.taskmanager.queue", DISPATCH_QUEUE_SERIAL);
        [self setupDoHConfiguration];
    }
    return self;
}
- (void)setupDoHConfiguration {
    dispatch_async(self.networkQueue, ^{
        NSLog(@"DoH 構成を設定しています...");
        
        // HTTPDNS DoH の URL エンドポイントを作成します
        const char *dohServerURL = "https://1xxxx3.aliyunhttpdns.com/dns-query";
        nw_endpoint_t urlEndpoint = nw_endpoint_create_url(dohServerURL);
        NSLog(@"DoH サーバーを使用しています: %s", dohServerURL);
        
        nw_resolver_config_t resolverConfig = nw_resolver_config_create_https(urlEndpoint);
        nw_privacy_context_require_encrypted_name_resolution(NW_DEFAULT_PRIVACY_CONTEXT, true, resolverConfig);
        NSLog(@"プライバシーコンテキストに DoH 構成が適用されました");
        
    });
}

ViewController.viewDidLoad で初期化を完了します。

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    ......
    self.dataTaskManager = [[DataTaskManager alloc] init];
    ......
}
重要

URLSession と URLSession に基づくサードパーティのネットワークライブラリは、ネットワークリクエストを行う際にデフォルトの PrivacyContext インスタンスを使用します。DoH 構成が完了すると、現在のアプリのすべての URLSession リクエストは DNS 解決に DoH を使用します。

解決パフォーマンスのイベントトラッキング

NSURLSessionTaskMetrics を使用して DNS プロセスを計測できます。これにより、DoH 解決が機能しているかどうかを確認し、DNS 解決時間を監視できます。

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics {
    NSLog(@"\n=== %@ へのリクエストのメトリックを収集しています ===\n", task.originalRequest.URL);
    
    task.taskDescription = [NSString stringWithFormat:@"%.2f,%@", 
                          metrics.taskInterval.duration,
                          task.originalRequest.URL.absoluteString];
    
    if ([metrics.transactionMetrics count] > 0) {   
        [metrics.transactionMetrics enumerateObjectsUsingBlock:^(NSURLSessionTaskTransactionMetrics *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) {
            NSString *fetchTypeStr = @"不明";
            switch (obj.resourceFetchType) {
                case NSURLSessionTaskMetricsResourceFetchTypeNetworkLoad:
                    fetchTypeStr = @"ネットワークロード";
                    break;
                case NSURLSessionTaskMetricsResourceFetchTypeServerPush:
                    fetchTypeStr = @"サーバープッシュ";
                    break;
                case NSURLSessionTaskMetricsResourceFetchTypeLocalCache:
                    fetchTypeStr = @"ローカルキャッシュ";
                    break;
            }
            NSLog(@"フェッチタイプ: %@", fetchTypeStr);
            
            if (obj.resourceFetchType == NSURLSessionTaskMetricsResourceFetchTypeNetworkLoad) {
                NSURLSessionTaskMetricsDomainResolutionProtocol dnsProtocol = obj.domainResolutionProtocol;
                NSString *dnsProtocolStr = @"不明 (0)";
                BOOL isDoH = NO;
                
                switch (dnsProtocol) {
                    case NSURLSessionTaskMetricsDomainResolutionProtocolUDP:
                        dnsProtocolStr = @"UDP (1)";
                        break;
                    case NSURLSessionTaskMetricsDomainResolutionProtocolTCP:
                        dnsProtocolStr = @"TCP (2)";
                        break;
                    case NSURLSessionTaskMetricsDomainResolutionProtocolTLS:
                        dnsProtocolStr = @"TLS (3)";
                        break;
                    case NSURLSessionTaskMetricsDomainResolutionProtocolHTTPS:
                        dnsProtocolStr = @"HTTPS/DoH (4)";
                        isDoH = YES;
                        break;
                }
                
                NSLog(@"DNS プロトコル: %@", dnsProtocolStr);
                
                #if TARGET_OS_SIMULATOR
                    NSLog(@"シミュレータで実行中 - DNS プロトコル検出はサポートされていません");
                #else
                    if (!isDoH) {
                        NSLog(@"DoH が検出されませんでした");
                    }
                #endif
                
                // DNS 解決パフォーマンスデータを取得します
                if (obj.domainLookupStartDate && obj.domainLookupEndDate) {
                    int dnsLookupTime = ceil([obj.domainLookupEndDate timeIntervalSinceDate:obj.domainLookupStartDate] * 1000);
                    NSLog(@"DNS ルックアップの詳細:");
                    NSLog(@"  開始: %@", obj.domainLookupStartDate);
                    NSLog(@"  終了: %@", obj.domainLookupEndDate);
                    NSLog(@"  期間: %d ミリ秒", dnsLookupTime);
                } else {
                    NSLog(@"DNS ルックアップは実行されませんでした (キャッシュされている可能性があります)");
                }
                
                // ネットワークリクエストパフォーマンスデータを取得します
                if (obj.connectStartDate && obj.connectEndDate) {
                    NSTimeInterval connectionTime = [obj.connectEndDate timeIntervalSinceDate:obj.connectStartDate];
                    NSLog(@"接続時間: %.3f 秒", connectionTime);
                }
            }
        }];
    } else {
        NSLog(@"トランザクションメトリックは利用できません");
    }
}

フォールバックメカニズム

重要
  1. フォールバックの完了後、アプリケーション内の後続のすべての URLSession ベースのリクエストに影響します。

  2. DoH 構成は、実際の iOS 14 以降のデバイスおよびエミュレーターで動作します。ただし、エミュレーターでアプリを実行すると、NSURLSessionTaskMetrics によって読み取られる NSURLSessionTaskMetricsDomainResolutionProtocol は常に 0 (不明) になります。エミュレーターで関連機能をテストする際は、この違いに注意してください。

  1. DNS 解決が失敗した場合に例外情報を迅速に取得するために、タイムアウト設定を構成します。

// 接続タイムアウトを設定します
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
config.waitsForConnectivity = NO;
config.timeoutIntervalForRequest = 5;
config.timeoutIntervalForResource = 10;
  1. 上記の計測チェーンでは、適切なフォールバック戦略を策定できます。DNS 例外が検出されたときにグローバルに LocalDNS にフォールバックする例を次に示します。

  ......
            
    if (obj.resourceFetchType == NSURLSessionTaskMetricsResourceFetchTypeNetworkLoad) {
        NSURLSessionTaskMetricsDomainResolutionProtocol dnsProtocol = obj.domainResolutionProtocol;
        NSString *dnsProtocolStr = @"不明 (0)";
        BOOL isDoH = NO;
        
        switch (dnsProtocol) {
            case NSURLSessionTaskMetricsDomainResolutionProtocolUDP:
                dnsProtocolStr = @"UDP (1)";
                break;
            case NSURLSessionTaskMetricsDomainResolutionProtocolTCP:
                dnsProtocolStr = @"TCP (2)";
                break;
            case NSURLSessionTaskMetricsDomainResolutionProtocolTLS:
                dnsProtocolStr = @"TLS (3)";
                break;
            case NSURLSessionTaskMetricsDomainResolutionProtocolHTTPS:
                dnsProtocolStr = @"HTTPS/DoH (4)";
                isDoH = YES;
                break;
        }
        
        NSLog(@"DNS プロトコル: %@", dnsProtocolStr);
        
        #if TARGET_OS_SIMULATOR
            NSLog(@"シミュレータで実行中 - DNS プロトコル検出はサポートされていません");
        #else
            if (!isDoH) {
                NSLog(@"DoH が検出されませんでした。ローカル DNS にフォールバックしています");
                dispatch_async(dispatch_get_main_queue(), ^{
                    // DoH を無効にして LocalDNS を使用します
                    nw_privacy_context_require_encrypted_name_resolution(NW_DEFAULT_PRIVACY_CONTEXT, false, nil);
                });
            }
        #endif  
    }
    
  ......
説明
  • 「ドメイン解決の範囲」が「ドメインリスト内のドメイン」に設定されている場合。

    • ドメインリストにないドメインの場合、この例の dnsProtocolStr は "Unknown (0)" と出力します。

    • ドメインリスト内のドメインの場合、この例の dnsProtocolStr は "HTTPS/DoH (4)" と出力します。

  • 「ドメイン解決の範囲」が「すべてのドメイン」に設定されている場合。

    • ブラックリスト内のドメインの場合、例の dnsProtocolStr は「不明 (0)」と出力されます。

    • ブラックリストにないドメインの場合、例の dnsProtocolStr は「HTTPS/DoH (4)」と出力されます。

システムレベルの DoH 構成の使用

システムプロファイルを構成することで、iOS デバイスの DoH を構成できます。システムレベルの DoH はデバイス上のすべてのアプリケーションに影響することに注意してください。

DoH アドレスを構成するには、次の手順に従います。

  1. サンプルコンテンツ内の DoH アドレスを置き換え、コンテンツを .mobileconfig ファイル (例: my_company_doh.mobileconfig) として保存します。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
   <key>PayloadContent</key>
   <array>
      <dict>
         <key>DNSSettings</key>
         <dict>
            <key>DNSProtocol</key>
            <string>HTTPS</string>
            <key>ServerURL</key>
            <!---- ここでアドレスを DoH アクセスアドレスに置き換えます --->
            <string>https://1xxxx3.aliyunhttpdns.com/dns-query</string>
         </dict>
         <key>PayloadDescription</key>
         <string>EMAS HTTPDNS DoH を使用するように iOS を構成します</string>
         <key>PayloadDisplayName</key>
         <string>EMAS HTTPDNS DoH</string>
         <key>PayloadIdentifier</key>
         <string>com.apple.dnsSettings.managed.9B498EC0C-EF6C-44F0-BFB7-0000658B99AC</string>
         <key>PayloadType</key>
         <string>com.apple.dnsSettings.managed</string>
         <key>PayloadUUID</key>
         <string>465AB183-5E34-4794-9BEB-B5327CF61F27</string>
         <key>PayloadVersion</key>
         <integer>1</integer>
         <key>ProhibitDisablement</key>
         <false/>
      </dict>
   </array>
   <key>PayloadDescription</key>
   <string>iOS に EMAS HTTPDNS DoH 構成を追加します</string>
   <key>PayloadDisplayName</key>
   <string>EMAS HTTPDNS DoH 構成</string>
   <key>PayloadIdentifier</key>
   <string>com.emas.apple-dns</string>
   <key>PayloadRemovalDisallowed</key>
   <false/>
   <key>PayloadType</key>
   <string>Configuration</string>
   <key>PayloadUUID</key>
   <string>130E6D6F-69A2-4515-9D77-99342CB9AE76</string>
   <key>PayloadVersion</key>
   <integer>1</integer>
</dict>
</plist>
  1. my_company_doh.mobileconfig をファイルストレージサーバーに公開するか、電子メールで iOS デバイスに送信します。

  2. iOS デバイスで、ブラウザまたは電子メールクライアントを使用して my_company_doh.mobileconfig をダウンロードします。

  3. 設定全般VPN およびデバイス管理 > > でこのプロファイルをインストールします。