Node.jsでDNS情報を自在に操る!resolveAny()とその他の活用例

2025-05-27

dnsPromises.resolveAny() は、Node.js の dns モジュールが提供する Promise ベースのAPIの一部です。これは、特定のホスト名(ドメイン名)に対して、利用可能な**全てのDNSリソースレコード(RR: Resource Record)**を取得するために使用されます。

通常のDNSクエリでは、Aレコード(IPv4アドレス)、AAAAレコード(IPv6アドレス)、MXレコード(メールサーバー)、CNAMEレコード(別名)、TXTレコード(テキスト情報)など、特定のリソースレコードタイプを指定して情報を取得します。しかし、resolveAny() は、これらの特定のタイプを指定するのではなく、そのホスト名に関連付けられているあらゆる種類のレコードを一度に取得しようとします。

これは、DNSの「ANY」または「*」クエリに相当します。

特徴と使い方

  1. Promiseベース
    dnsPromises オブジェクトは、非同期操作の結果をPromiseで返します。これにより、async/await構文を使ってコードをより読みやすく、管理しやすく記述できます。

    const dnsPromises = require('dns').promises;
    
    async function getAllDnsRecords(hostname) {
      try {
        const records = await dnsPromises.resolveAny(hostname);
        console.log(`Resolved records for ${hostname}:`, records);
        return records;
      } catch (error) {
        console.error(`Error resolving records for ${hostname}:`, error);
        throw error;
      }
    }
    
    // 例: google.com の全DNSレコードを取得
    getAllDnsRecords('google.com');
    
  2. エラーハンドリング
    DNSルックアップ中にエラーが発生した場合(例: ホスト名が存在しない、DNSサーバーに到達できないなど)、Promiseは拒否されます。try...catch ブロックを使用して、これらのエラーを適切に処理する必要があります。

resolveAny() の用途

  • セキュリティ関連
    関連するすべてのDNSエントリを確認することで、潜在的なセキュリティリスクを特定するのに役立つ場合があります(例: 不審なTXTレコード)。
  • トラブルシューティング
    DNS設定の問題を診断する際に、網羅的な情報を取得できます。
  • DNS情報の調査
    特定のドメイン名について、どのようなDNSレコードが設定されているかを包括的に調査したい場合に非常に便利です。

注意点

  • 古いDNSサーバー
    一部の古いDNSサーバーでは、「ANY」クエリが正しく処理されないか、部分的な情報しか返さない場合があります。
  • パフォーマンス
    resolveAny() は、複数のDNSクエリを内部的に実行する必要があるため、特定のレコードタイプ(例: resolve4()resolveMx())を直接解決するよりもパフォーマンスが低下する可能性があります。特定の情報だけが必要な場合は、対応する専用のresolveメソッドを使用する方が効率的です。


dnsPromises.resolveAny() の一般的なエラーとトラブルシューティング

dnsPromises.resolveAny() を使用する際に遭遇する可能性のある一般的なエラーは、主にDNSルックアップの失敗やネットワーク関連の問題に起因します。

エラーの種類と原因

resolveAny() が失敗した場合、Error オブジェクトが返され、その code プロパティに特定のエラーコードが含まれます。一般的なエラーコードとその原因を以下に示します。

  • ECONNREFUSED / ECONNRESET (Connection refused/reset)

    • 原因
      DNSサーバーへの接続が拒否されたり、リセットされたりした場合に発生します。ファイアウォール、ネットワーク設定、またはDNSサーバー自体の問題が考えられます。
    • トラブルシューティング
      • ネットワーク設定、ファイアウォール、DNSサーバーの稼働状況を確認します。
  • ESERVFAIL (Server failure)

    • 原因
      DNSサーバーが内部的なエラーのためにクエリを処理できなかった場合に発生します。サーバー側の問題です。
    • トラブルシューティング
      • DNSサーバーの管理者に連絡するか、別のDNSサーバーを使用することを検討します。一時的な問題であることも多いため、時間をおいて再試行するのも有効です。
  • EFORMERR (Format error)

    • 原因
      DNSサーバーがクエリの形式が不正であると判断した場合に発生します。Node.jsの内部的な問題である可能性は低いですが、非常に特殊なケースで発生することがあります。
    • トラブルシューティング
      • Node.jsのバージョンを最新に保つことで解決する場合があります。
  • ETIMEOUT (Query timeout)

    • 原因
      DNSサーバーからの応答が設定された時間内に得られなかった場合に発生します。これは、DNSサーバーがダウンしている、ネットワークの遅延が大きい、またはDNSサーバーが過負荷状態にある場合に起こります。
    • トラブルシューティング
      • ネットワーク接続の確認
        インターネット接続が正常であることを確認します。
      • DNSサーバーの可用性
        指定されたDNSサーバーが稼働しているか確認します。
      • リトライ戦略
        アプリケーション側で、一時的なタイムアウトであればリトライ(再試行)のロジックを導入することを検討します。
      • DNSサーバーの変更
        応答の遅いDNSサーバーを使用している場合、より高速で信頼性の高いDNSサーバーに変更することを検討します。
  • EREFUSED (Query refused)

    • 原因
      クエリがDNSサーバーによって拒否された場合に発生します。これは、DNSサーバーがリクエストを受け付けない設定になっている、または特定のIPアドレスからのクエリをブロックしている場合に起こりえます。
    • トラブルシューティング
      • DNSサーバーのアクセス許可
        使用しているネットワーク環境やDNSサーバーの設定を確認し、クエリが許可されていることを確認します。
      • ファイアウォール
        クライアント側のファイアウォールやネットワークデバイスがDNSクエリをブロックしていないか確認します。
    • 原因
      解決しようとしているホスト名(ドメイン名)が存在しないか、DNSサーバーがそのホスト名を見つけられなかった場合に発生します。これは最も一般的なエラーの一つです。

    • example.com の代わりに exmaple.com のようにタイプミスをした場合など。
    • トラブルシューティング
      • ホスト名の確認
        まず、指定したホスト名が正しいスペルであることを確認してください。
      • 手動での確認
        nslookupdig コマンド(Windowsの場合はnslookup、Linux/macOSの場合はdigが一般的)を使って、そのホスト名が実際に解決できるか手動で確認してみます。
        • nslookup example.com
        • dig example.com ANY (ANYクエリで試す)
      • DNSサーバーの確認
        使用しているシステムやネットワーク設定で指定されているDNSサーバーが正常に動作しているか確認します。場合によっては、パブリックDNSサーバー(Google Public DNS: 8.8.8.8, 8.8.4.4 など)に一時的に切り替えて試すことも有効です。

一般的なトラブルシューティングのヒント

  • Node.jsのバージョン
    使用しているNode.jsのバージョンが古い場合、既知のバグが含まれている可能性があります。最新の安定版にアップデートすることで問題が解決する場合もあります。

  • ネットワーク環境の確認
    Node.jsアプリケーションが実行されているサーバーや環境のネットワーク設定(ファイアウォールルール、プロキシ設定など)がDNSクエリを妨げていないか確認してください。

  • DNSサーバーの指定(dns.setServers())
    もし特定のDNSサーバーを使用したい場合、dns.setServers() を使用して、Node.jsがDNSルックアップに使用するサーバーを明示的に指定できます。これにより、特定の信頼できるDNSサーバーを使用したり、ネットワークの問題を切り分けたりするのに役立ちます。

    const dns = require('dns');
    const dnsPromises = dns.promises;
    
    // Google Public DNS を使用する例
    dns.setServers(['8.8.8.8', '8.8.4.4']);
    
    async function resolveWithSpecificDns(hostname) {
      try {
        const records = await dnsPromises.resolveAny(hostname);
        console.log(`Resolved records for ${hostname} using specific DNS:`, records);
      } catch (error) {
        console.error(`Error resolving ${hostname} with specific DNS:`, error.code);
      }
    }
    
    resolveWithSpecificDns('example.com');
    

    注意
    dns.setServers() はグローバルな設定であり、一度設定すると以降のすべてのDNSリクエストに影響します。アプリケーション全体に影響を与えるため、注意して使用してください。

  • ホスト名のバリデーション
    ユーザーからの入力値など、外部から渡されるホスト名を使用する場合は、入力値のバリデーション(検証)を行い、不正な形式のホスト名が渡されないようにすることが重要です。

  • エラーハンドリングの実装
    最も重要なのは、resolveAny() を呼び出す際に必ず try...catch ブロックを使用し、Promiseが拒否された場合に発生するエラーを適切に捕捉することです。

    const dnsPromises = require('dns').promises;
    
    async function resolveAndHandleErrors(hostname) {
      try {
        const records = await dnsPromises.resolveAny(hostname);
        console.log(`Successfully resolved records for ${hostname}:`, records);
      } catch (error) {
        console.error(`Error resolving ${hostname}:`);
        console.error(`  Code: ${error.code}`);
        console.error(`  Message: ${error.message}`);
        // エラーコードに基づいて特定の処理を行う
        if (error.code === 'ENOTFOUND') {
          console.error('  -> The hostname does not exist or could not be found.');
        } else if (error.code === 'ETIMEOUT') {
          console.error('  -> DNS query timed out. Check network or DNS server.');
        }
        // 必要に応じてエラーを再スロー
        // throw error;
      }
    }
    
    resolveAndHandleErrors('nonexistent-domain-12345.com'); // 存在しないドメイン
    resolveAndHandleErrors('google.com'); // 正常なドメイン
    

dnsPromises.resolveAny() はDNSクエリを実行するため、ネットワークやDNSの設定に関連する様々なエラーが発生する可能性があります。ここでは、よく遭遇するエラーとその解決策について説明します。

ENOTFOUND (Host not found)

  • 考えられる原因
    • タイプミス
      ホスト名にスペルミスがある。
    • ドメインの不存在
      そのドメイン名が登録されていないか、期限切れになっている。
    • DNS伝播の遅延
      新しく設定したドメイン名やDNSレコードが、まだ世界中のDNSサーバーに伝播されていない。
    • ローカルDNSキャッシュの問題
      クライアント(Node.jsを実行しているマシン)のDNSキャッシュが古くなっている。
    • 設定されたDNSサーバーの問題
      Node.jsが使用しているDNSサーバーが、そのドメイン名を解決できない。
  • エラー内容
    最も一般的なエラーの一つで、指定されたホスト名(ドメイン名)に対応するDNSレコードが見つからない場合に発生します。DNSサーバーがそのホスト名を知らない、または存在しないドメインである可能性が高いです。

ETIMEOUT (Operation timed out)

  • トラブルシューティング
    1. ネットワーク接続の確認
      インターネット接続が正常であることを確認します。
    2. DNSサーバーの変更
      上記のENOTFOUNDと同様に、dnsPromises.setServers() を使って、より高速で信頼性の高いDNSサーバーに変更してみます。
    3. ファイアウォールの確認
      ローカルマシンやネットワークのファイアウォール設定を確認し、DNS通信が許可されていることを確認します。
    4. リトライロジックの実装
      一時的なネットワークの問題の場合があるため、指数バックオフなどのリトライロジックを実装することで、エラーの発生を減らすことができます。
  • 考えられる原因
    • DNSサーバーの応答遅延
      使用しているDNSサーバーが過負荷状態にあるか、応答が非常に遅い。
    • ネットワーク接続の問題
      クライアントからDNSサーバーへのネットワーク経路に問題がある。
    • ファイアウォールのブロック
      ファイアウォールがDNS(UDP/TCPポート53)の通信をブロックしている。
  • エラー内容
    DNSクエリが指定された時間内に応答を返さなかった場合に発生します。

EADDRINFO (getaddrinfo ENOTFOUND)

  • トラブルシューティング
    1. ENOTFOUND のトラブルシューティング手順を試します。
    2. hosts ファイルに問題のホスト名に関する不正確なエントリがないか確認します。
  • 考えられる原因
    • ENOTFOUND と同様の理由。
    • hosts ファイル(/etc/hosts または C:\Windows\System32\drivers\etc\hosts)の設定が不正である。
  • エラー内容
    このエラーは、通常 dns.lookup() に関連して発生することが多いですが、dns.resolveAny() などのDNS解決メソッドでも、基盤となるシステム呼び出しでアドレス情報が見つからない場合に発生する可能性があります。ENOTFOUND と似ていますが、よりシステムレベルのエラーを示唆することがあります。

EAI_AGAIN (Temporary failure in name resolution)

  • トラブルシューティング
    1. 時間をおいて再試行
      一時的な問題であることが多いため、数秒待ってから再試行します。
    2. DNSサーバーの変更
      別のDNSサーバーを試します(上記参照)。
    3. ネットワークの安定性確認
      ネットワークが安定しているか確認します。
  • 考えられる原因
    • DNSサーバーの一時的な障害または過負荷。
    • ネットワークの混雑や一時的な切断。
  • エラー内容
    DNS解決中に一時的なシステムエラーが発生した場合に発生します。これは通常、DNSサーバーが一時的に利用できない、またはネットワークの問題があることを示します。

空の配列が返される

  • トラブルシューティング
    1. dig your-domain.com ANY で確認
      コマンドラインで dig your-domain.com ANY を実行し、本当に何もレコードが存在しないのかを確認します。もしコマンドラインでレコードが返ってくるのにNode.jsで返ってこない場合、Node.jsが使用しているDNSサーバーやネットワーク環境に問題がある可能性があります。
    2. 特定のレコードタイプの解決を試す
      resolveAny() の代わりに、resolve4(), resolveMx(), resolveTxt() など、特定のレコードタイプを指定して解決を試みます。これにより、個々のレコードタイプは取得できるが ANY クエリが問題なのかを特定できます。
  • 考えられる原因
    • レコードが存在しない
      指定されたホスト名には、実際に公開されているDNSレコードが一つもない。これは稀ですが、テスト用のドメインなどで発生する可能性があります。
    • DNSサーバーの応答制限
      一部のDNSサーバーやファイアウォールが、特定のレコードタイプ(例: ANY クエリ)への応答を制限している場合がある。
  • エラー内容
    resolveAny() はエラーをスローしないものの、期待するDNSレコードが何も返ってこない(空の配列 [] が返される)場合があります。

全体的なベストプラクティス

  • タイムアウト設定の検討
    必要であれば、Node.jsのDNSリゾルバのタイムアウト値を調整することもできますが、通常はデフォルトで十分な場合が多いです。dns.Resolver クラスのインスタンスを作成し、setTimeout() メソッドを使用します。

    const dns = require('dns');
    const dnsPromises = dns.promises;
    const resolver = new dns.Resolver();
    
    resolver.setTimeout(5000); // タイムアウトを5秒に設定
    
    async function resolveWithCustomTimeout(hostname) {
      try {
        const records = await resolver.resolveAny(hostname);
        console.log(`Resolved records for ${hostname}:`, records);
      } catch (error) {
        console.error(`Error resolving records for ${hostname}:`, error);
      }
    }
    
    resolveWithCustomTimeout('example.com');
    
  • ロギング
    エラーが発生した際に、詳細なログを出力するようにすることで、問題の特定とデバッグが容易になります。

  • 信頼できるDNSサーバーの利用
    運用環境では、Google Public DNS、Cloudflare DNS、またはISPが提供する安定したDNSサーバーを使用することを検討してください。

  • try...catch によるエラーハンドリング
    dnsPromises.resolveAny() はPromiseを返すため、必ず async/awaittry...catch または .catch() を使ってエラーを捕捉し、適切に処理するようにしてください。



dnsPromises.resolveAny() は、指定されたホスト名に対するあらゆる種類のDNSリソースレコード(A, AAAA, MX, NS, CNAME, TXTなど)を非同期に取得するために使用されます。PromiseベースのAPIなので、async/await 構文で扱いやすいのが特徴です。

基本的な使用例

最もシンプルな形で、ドメイン名の全てのレコードを取得します。

const dnsPromises = require('dns').promises;

async function resolveAllRecords(hostname) {
  try {
    const records = await dnsPromises.resolveAny(hostname);
    console.log(`--- ${hostname} の全DNSレコード ---`);
    records.forEach(record => {
      console.log(record);
    });
  } catch (error) {
    console.error(`エラーが発生しました: ${error.message}`);
    // エラーコードに基づいて詳細なメッセージを表示
    if (error.code === 'ENOTFOUND') {
      console.error(`ホスト名 '${hostname}' が見つかりませんでした。スペルミスを確認してください。`);
    } else if (error.code === 'ETIMEOUT') {
      console.error(`DNSクエリがタイムアウトしました。ネットワーク接続を確認してください。`);
    }
  }
}

// 例: google.com のDNSレコードを取得
resolveAllRecords('google.com');

// 例: 存在しないドメインでエラーを確認
resolveAllRecords('nonexistent-domain-123456789.com');

// 例: 日本の有名サイト
resolveAllRecords('yahoo.co.jp');

解説

  • try...catch ブロックでエラーハンドリングを行い、ネットワークの問題やドメイン名が見つからない場合などに対応します。
  • records は、様々なDNSレコードオブジェクトを含む配列です。各オブジェクトは rrtype プロパティを持ち、レコードのタイプ(例: 'A', 'AAAA', 'MX')を示します。
  • await を使用してPromiseの解決を待ち、結果を records 変数に格納します。
  • resolveAny(hostname) は、指定された hostname の全てのDNSレコードを非同期で解決し、その結果をPromiseで返します。
  • require('dns').promises; でPromiseベースのDNS APIを取得します。

取得したレコードをタイプ別に処理する例

resolveAny() で取得したレコードは様々なタイプが混在しているため、それを分類して表示・処理する例です。

const dnsPromises = require('dns').promises;

async function categorizeDnsRecords(hostname) {
  try {
    const records = await dnsPromises.resolveAny(hostname);
    console.log(`--- ${hostname} のタイプ別DNSレコード ---`);

    const categorized = {
      A: [],
      AAAA: [],
      MX: [],
      NS: [],
      CNAME: [],
      TXT: [],
      SRV: [],
      PTR: [],
      // その他のレコードタイプもここに追加可能
      OTHER: [] // 未知のタイプや処理しないタイプ
    };

    records.forEach(record => {
      switch (record.rrtype) {
        case 'A':
          categorized.A.push(record.address);
          break;
        case 'AAAA':
          categorized.AAAA.push(record.address);
          break;
        case 'MX':
          categorized.MX.push({ exchange: record.exchange, priority: record.priority });
          break;
        case 'NS':
          categorized.NS.push(record.value); // NSレコードは 'value' プロパティ
          break;
        case 'CNAME':
          categorized.CNAME.push(record.value); // CNAMEレコードは 'value' プロパティ
          break;
        case 'TXT':
          // TXTレコードのデータは配列の配列になっていることがあるのでフラット化
          categorized.TXT.push(record.entries.flat().join(' '));
          break;
        case 'SRV':
          categorized.SRV.push(record); // SRVレコードは詳細な情報を持つ
          break;
        case 'PTR':
            categorized.PTR.push(record.value);
            break;
        default:
          categorized.OTHER.push(record);
          break;
      }
    });

    // 結果の表示
    if (categorized.A.length > 0) console.log('A (IPv4):', categorized.A);
    if (categorized.AAAA.length > 0) console.log('AAAA (IPv6):', categorized.AAAA);
    if (categorized.MX.length > 0) console.log('MX (メールサーバー):', categorized.MX);
    if (categorized.NS.length > 0) console.log('NS (ネームサーバー):', categorized.NS);
    if (categorized.CNAME.length > 0) console.log('CNAME (エイリアス):', categorized.CNAME);
    if (categorized.TXT.length > 0) console.log('TXT (テキスト情報):', categorized.TXT);
    if (categorized.SRV.length > 0) console.log('SRV (サービスレコード):', categorized.SRV);
    if (categorized.PTR.length > 0) console.log('PTR (逆引き):', categorized.PTR);
    if (categorized.OTHER.length > 0) console.log('OTHER (その他):', categorized.OTHER);

  } catch (error) {
    console.error(`エラーが発生しました: ${error.message}`);
  }
}

// 例: cloudflare.com のDNSレコードをタイプ別に取得
categorizeDnsRecords('cloudflare.com');

// 例: google.com のDNSレコードをタイプ別に取得
categorizeDnsRecords('google.com');

解説

  • TXT レコードの entries は配列の配列になっていることがあるため、.flat().join(' ') で整形しています。
  • 各レコードタイプによって、オブジェクトのプロパティ名が異なるため、それに応じてアクセスしています(例: A / AAAAaddressMXexchangepriorityNS / CNAMEvalueTXTentries)。
  • 取得した records 配列をループし、record.rrtype の値に基づいて適切なカテゴリに分類しています。

カスタムDNSサーバーを指定して解決する例

デフォルトのシステムDNSサーバーではなく、特定のDNSサーバー(例: Google Public DNS)を使用してクエリを実行する例です。

const dnsPromises = require('dns').promises;

async function resolveWithSpecificDnsServer(hostname, dnsServers) {
  // 新しいResolverインスタンスを作成
  const resolver = new dnsPromises.Resolver();

  // ResolverインスタンスにカスタムDNSサーバーを設定
  resolver.setServers(dnsServers);

  try {
    const records = await resolver.resolveAny(hostname);
    console.log(`--- '${hostname}' のDNSレコード (使用DNSサーバー: ${dnsServers.join(', ')}) ---`);
    records.forEach(record => {
      console.log(record);
    });
  } catch (error) {
    console.error(`エラーが発生しました: ${error.message}`);
    console.error(`使用DNSサーバー: ${dnsServers.join(', ')}`);
  }
}

// 例: Google Public DNS (8.8.8.8, 8.8.4.4) を使用して解決
resolveWithSpecificDnsServer('example.jp', ['8.8.8.8', '8.8.4.4']);

// 例: Cloudflare DNS (1.1.1.1, 1.0.0.1) を使用して解決
resolveWithSpecificDnsServer('amazon.co.jp', ['1.1.1.1', '1.0.0.1']);

// 例: 存在しないDNSサーバーを指定した場合 (エラーになる可能性が高い)
resolveWithSpecificDnsServer('example.com', ['192.0.2.1']); // RFC 5737 でドキュメント化されたテスト用アドレス
  • その後の resolver.resolveAny() は、設定されたカスタムDNSサーバーに対してクエリを実行します。
  • resolver.setServers(dnsServers) で、そのインスタンスが使用するDNSサーバーのIPアドレスの配列を設定します。
  • new dnsPromises.Resolver() を使用して、新しいDNSリゾルバインスタンスを作成します。これにより、グローバルな dnsPromises.setServers() を変更することなく、特定のクエリに対してのみ異なるDNSサーバーを使用できます。


dnsPromises.resolveAny() は、特定のホスト名に関連する全てのDNSリソースレコードを一度に取得する非常に便利な方法です。しかし、状況によっては、より特化したメソッドや、非同期処理のスタイルが異なる代替手段を検討することも有効です。

主な代替方法は以下の通りです。

  1. 特定のレコードタイプを個別に解決する (dnsPromises.resolveXxx())
  2. 従来のコールバックベースのDNS API (dns.resolveAny())
  3. 外部ライブラリの利用
  4. OSのネイティブコマンドを実行する

それぞれについて詳しく見ていきましょう。

特定のレコードタイプを個別に解決する (dnsPromises.resolveXxx())

resolveAny() は「全て」を取得しますが、もし特定のレコードタイプ(例:AレコードとMXレコードだけ)にしか興味がない場合、対応する専用のresolveメソッドを使う方が効率的で、取得するデータの構造も明確になります。

利点

  • エラーハンドリング
    各クエリのエラーを個別に処理できます。
  • 明確なデータ構造
    返されるデータは、そのレコードタイプに特化した構造を持つため、処理がシンプルになります。
  • パフォーマンス
    不要なDNSクエリを減らし、必要な情報だけを取得するため、多くの場合 resolveAny() よりも高速です。

欠点

  • 複数のレコードタイプが必要な場合、複数のawait呼び出しが必要になります。

主要な dnsPromises.resolveXxx() メソッド

  • dnsPromises.resolveSrv(hostname): サービス (SRVレコード) の配列を解決します。
  • dnsPromises.resolveTxt(hostname): ホスト名に対するテキスト (TXTレコード) の配列を解決します。
  • dnsPromises.resolveCname(hostname): ホスト名に対する正規名 (CNAMEレコード) の配列を解決します。
  • dnsPromises.resolveNs(hostname): ホスト名に対するネームサーバー (NSレコード) の配列を解決します。
  • dnsPromises.resolveMx(hostname): ホスト名に対するメールエクスチェンジャー (MXレコード) の配列を解決します。
  • dnsPromises.resolve6(hostname): ホスト名に対するIPv6アドレス (AAAAレコード) の配列を解決します。
  • dnsPromises.resolve4(hostname): ホスト名に対するIPv4アドレス (Aレコード) の配列を解決します。

コード例
AレコードとMXレコードのみを取得する場合

const dnsPromises = require('dns').promises;

async function resolveSpecificRecords(hostname) {
  try {
    console.log(`--- ${hostname} のAレコードとMXレコード ---`);

    const ipv4Addresses = await dnsPromises.resolve4(hostname);
    console.log('IPv4 (A) レコード:', ipv4Addresses);

    const mxRecords = await dnsPromises.resolveMx(hostname);
    console.log('MX (メールサーバー) レコード:', mxRecords);

    // 同時に複数のレコードを取得する場合は Promise.all を使う
    // const [ipv4, mx] = await Promise.all([
    //   dnsPromises.resolve4(hostname),
    //   dnsPromises.resolveMx(hostname)
    // ]);
    // console.log('IPv4 (A) レコード:', ipv4);
    // console.log('MX (メールサーバー) レコード:', mx);

  } catch (error) {
    console.error(`エラーが発生しました: ${error.message}`);
    if (error.code === 'ENOTFOUND') {
      console.error(`ホスト名 '${hostname}' の特定のレコードが見つかりませんでした。`);
    }
    // 特定のレコードタイプが見つからない場合も ENOTFOUND が返されることが多い
  }
}

resolveSpecificRecords('google.com');
resolveSpecificRecords('nonexistent-domain-for-test.com'); // 存在しないドメインの場合

従来のコールバックベースのDNS API (dns.resolveAny())

dnsPromises.resolveAny() はPromiseを返しますが、Node.jsのdnsモジュールには、従来のコールバックベースのAPIも存在します。Promiseに慣れていない場合や、古いコードベースとの互換性を保ちたい場合に選択肢となります。

利点

  • 古いNode.jsのスタイルに慣れている開発者には馴染みやすい。

欠点

  • エラーハンドリングが複雑になる場合がある。
  • async/awaitのようなモダンな非同期構文と組み合わせにくい。
  • コールバック地獄 (Callback Hell) に陥りやすい。

コード例

const dns = require('dns'); // promisesではない方

function resolveAnyWithCallback(hostname) {
  dns.resolveAny(hostname, (err, records) => {
    if (err) {
      console.error(`エラーが発生しました: ${err.message}`);
      if (err.code === 'ENOTFOUND') {
        console.error(`ホスト名 '${hostname}' が見つかりませんでした。`);
      }
      return;
    }
    console.log(`--- ${hostname} の全DNSレコード (コールバック) ---`);
    records.forEach(record => {
      console.log(record);
    });
  });
}

resolveAnyWithCallback('example.org');
resolveAnyWithCallback('another-nonexistent-domain.net');

外部ライブラリの利用

Node.jsの標準dnsモジュールは強力ですが、より高レベルの抽象化や特定の機能(例: DNSSEC検証、より複雑なDNSクエリオプション)が必要な場合、外部のnpmパッケージを検討できます。

例として、dns-lookup-tracednative-dns などがありますが、多くの場合、Node.jsの組み込みdnsモジュールで十分です。

利点

  • より簡潔なAPIを提供する可能性がある。
  • 特定の高度な機能や使いやすさを提供する場合がある。

欠点

  • ライブラリのメンテナンス状況に依存する。
  • 追加の依存関係が増える。

OSのネイティブコマンドを実行する

dnsPromises.resolveAny() はNode.jsの内部でDNSクエリを実行しますが、最終的にはOSのDNSリゾルバに依存します。そのため、Node.jsアプリケーション内でOSのDNS解決コマンド(dig, nslookup)を子プロセスとして実行し、その出力をパースするという方法も理論上は可能です。

利点

  • Node.jsのdnsモジュールでは提供されていない詳細な情報(例: クエリ時間、DNSSECステータスなど)を取得できる場合がある。
  • OSのDNS解決と同じ結果が確実に得られる。

欠点

  • オーバーヘッドが大きく、パフォーマンスが低下します。
  • セキュリティリスク(コマンドインジェクションなど)に注意が必要です。
  • 非推奨
    Node.jsのクロスプラットフォーム性を損なう可能性があり、OSやバージョンによってコマンドの出力形式が異なるため、パースが非常に複雑でエラーを起こしやすいです。

コード例 (非推奨)

const { exec } = require('child_process');

function resolveAnyWithDig(hostname) {
  // macOS/Linux の 'dig' コマンドを使用
  const command = `dig ${hostname} ANY +short`; // '+short' でシンプルな出力に

  exec(command, (error, stdout, stderr) => {
    if (error) {
      console.error(`dig コマンド実行エラー: ${error.message}`);
      return;
    }
    if (stderr) {
      console.error(`dig stderr: ${stderr}`);
      // return; // stderrがあってもstdoutに結果がある場合がある
    }
    console.log(`--- ${hostname} の全DNSレコード (digコマンド) ---`);
    console.log(stdout); // digの生の出力
    // ここで出力をパースする必要がある
  });
}

// resolveAnyWithDig('google.com'); // 注意: 実際のプロダクションコードでは避けるべき

ほとんどのNode.jsアプリケーションでは、dnsPromises.resolveAny() または dnsPromises.resolveXxx() メソッド群を使用するのが最も適切で推奨される方法です。

  • 特定のレコードタイプのみ必要な場合
    dnsPromises.resolve4(), resolveMx() などの特化されたメソッド
  • 全てのレコードが必要な場合
    dnsPromises.resolveAny()