Node.js DNSプログラミングの決定版!dns.Resolver の詳細と代替案比較

2025-05-27

Node.js の dns モジュールには、ネットワークに関する低レベルな DNS 機能を扱うためのクラスがいくつか用意されています。その中でも dns.Resolver クラスは、カスタム DNS サーバーに対して DNS クエリを実行するための独立したリゾルバーオブジェクトを提供します。

簡単に言うと、Node.js がデフォルトで使用する DNS リゾルバーとは別に、特定の DNS サーバーを指定して名前解決を行いたい場合dns.Resolver クラスのインスタンスを作成して利用します。

dns.Resolver クラスの主な特徴と用途

  1. カスタム DNS サーバーの指定
    dns.Resolver のインスタンスを作成する際に、使用する DNS サーバーのアドレス(IP アドレスとポート番号)を設定できます。これにより、システム全体の DNS 設定に依存せず、アプリケーションごとに異なる DNS サーバーを利用できます。

  2. 非同期操作
    dns.Resolver のメソッドは、主に非同期的に動作します。これは、DNS クエリがネットワークを介した処理であり、時間がかかる可能性があるため、Node.js のノンブロッキング I/O モデルを活かすためです。コールバック関数や Promises を使用して、クエリの結果を受け取ります。

  3. 多様な DNS レコードタイプのサポート
    dns.Resolver は、A レコード、AAAA レコード、MX レコード、TXT レコード、SRV レコード、PTR レコード、NAPTR レコード、CNAME レコード、SOA レコード、NS レコードなど、様々な DNS レコードタイプのクエリをサポートしています。

dns.Resolver クラスの基本的な使い方

  1. dns モジュールのインポート

    const dns = require('node:dns').promises; // Promises API を使用する場合
    // または
    const dns = require('node:dns'); // コールバック API を使用する場合
    
  2. dns.Resolver インスタンスの作成
    使用したい DNS サーバーのアドレスを指定してインスタンスを作成します。

    const customResolver = new dns.Resolver({
      servers: ['8.8.8.8', '8.8.4.4'], // Google Public DNS サーバーの例
    });
    
  3. DNS クエリの実行
    作成した customResolver インスタンスのメソッド(例: resolve4, resolveMx, reverse など)を使用して、特定のホスト名や IP アドレスに対する DNS クエリを実行します。

    Promises API の場合

    customResolver.resolve4('example.com')
      .then((addresses) => {
        console.log('IPv4 アドレス:', addresses);
      })
      .catch((err) => {
        console.error('エラー:', err);
      });
    

    コールバック API の場合

    customResolver.resolve4('example.com', (err, addresses) => {
      if (err) {
        console.error('エラー:', err);
        return;
      }
      console.log('IPv4 アドレス:', addresses);
    });
    

dns.Resolver クラスの主なメソッド

  • resolver.setTTL(ttl): キャッシュの Time-To-Live (TTL) を設定します。
  • resolver.setLocalAddress(localAddress[, localPort]): DNS クエリの送信元となるローカルアドレスとポートを設定します。
  • resolver.setServers(servers): このリゾルバーで使用する DNS サーバーのリストを設定します。
  • resolver.reverse(ip[, callback]): 指定された IP アドレスに対する逆引き DNS クエリ (PTR レコード) を実行します。
  • resolver.resolveCname(hostname[, callback]): CNAME (正規名) レコードのクエリを実行します。
  • resolver.resolveNs(hostname[, callback]): NS (ネームサーバー) レコードのクエリを実行します。
  • resolver.resolveSoa(hostname[, callback]): SOA (権威開始) レコードのクエリを実行します。
  • resolver.resolveNaptr(hostname[, callback]): NAPTR (ネームアドレス変換ポインタ) レコードのクエリを実行します。
  • resolver.resolveSrv(hostname[, callback]): SRV (サービスロケーション) レコードのクエリを実行します。
  • resolver.resolveTxt(hostname[, callback]): TXT (テキスト) レコードのクエリを実行します。
  • resolver.resolveMx(hostname[, callback]): MX (メール交換) レコードのクエリを実行します。
  • resolver.resolve6(hostname[, callback]): AAAA (IPv6 アドレス) レコードのクエリを実行します。
  • resolver.resolve4(hostname[, callback]): A (IPv4 アドレス) レコードのクエリを実行します。
  • resolver.resolve(hostname, rrtype[, callback]): 指定された hostname に対して、指定されたレコードタイプ (rrtype) の DNS クエリを実行します。


一般的なエラー

    • 原因
      指定されたホスト名が存在しないか、DNS サーバーがそのホスト名を解決できません。ネットワーク接続の問題や、DNS サーバーの設定ミスも考えられます。
    • トラブルシューティング
      • 指定したホスト名が正しいか再確認してください。
      • ネットワーク接続が正常であることを確認してください(例: ping コマンドで他のホストに接続できるか試す)。
      • 使用している DNS サーバーが正しく設定され、動作しているか確認してください。
      • 別の DNS サーバー(例: Google Public DNS の 8.8.8.88.8.4.4)を試してみてください。
      • ファイアウォールが DNS クエリ(通常は UDP/TCP ポート 53)をブロックしていないか確認してください。
  1. Error: queryA ETIMEOUT <hostname> (DNS クエリがタイムアウトしました)

    • 原因
      DNS サーバーが指定時間内に応答しなかった場合に発生します。ネットワークの遅延、DNS サーバーの負荷が高い、DNS サーバーがダウンしているなどの理由が考えられます。
    • トラブルシューティング
      • ネットワーク接続が安定しているか確認してください。
      • 指定した DNS サーバーが応答しているか確認してください(例: nslookup コマンドを使用)。
      • resolver.setTimeout() メソッドでタイムアウト時間を長く設定することを検討してください(ただし、根本的な解決にはならない場合があります)。
      • 別の、より応答性の高い DNS サーバーを試してみてください。
      • ネットワーク経路に問題がないか確認してください。
  2. Error: queryA ECONNREFUSED <address>:<port> (接続が拒否されました)

    • 原因
      指定された DNS サーバーのアドレスとポートで接続を確立できませんでした。DNS サーバーが指定されたポートでリスンしていない、ファイアウォールによって接続が拒否されているなどが考えられます。
    • トラブルシューティング
      • DNS サーバーのアドレスとポート番号が正しいか確認してください(通常はポート 53 です)。
      • DNS サーバーが稼働しており、指定されたポートでリッスンしているか確認してください。
      • ファイアウォールが Node.js プロセスからの DNS クエリをブロックしていないか、また DNS サーバーへの接続をブロックしていないか確認してください。
  3. Error: queryA EDNS SERVFAIL <hostname> (DNS サーバーがエラー応答を返しました)

    • 原因
      DNS サーバーがクエリを処理できず、サーバー側のエラーを返しました。これは、DNS サーバーの設定ミスや内部的な問題が原因である可能性があります。
    • トラブルシューティング
      • 別の DNS サーバーを試してみてください。
      • 問題が特定のホスト名でのみ発生するか確認してください。
      • DNS サーバーの管理者に連絡して状況を確認する必要があるかもしれません。
  4. TypeError: resolver.resolveX is not a function (存在しないメソッドを呼び出した)

    • 原因
      存在しない resolveX のようなメソッド名を誤って使用した場合に発生します。
    • トラブルシューティング
      • dns.Resolver クラスのドキュメントを確認し、正しいメソッド名を使用しているか確認してください(例: resolve4, resolve6, resolveMx など)。

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

  • 簡単なテストコードの作成
    問題を再現する最小限のコードを作成し、切り分けを行います。
  • ログの記録
    DNS クエリの送信と応答に関するログを記録することで、問題発生時の状況を把握しやすくなります。
  • Node.js のバージョン確認
    古い Node.js のバージョンを使用している場合、既知のバグが存在する可能性があります。最新の安定版にアップデートすることを検討してください。
  • ファイアウォールの確認
    ローカルのファイアウォールやネットワークのファイアウォールが DNS クエリをブロックしていないか確認します。
  • DNS サーバーの変更
    問題が特定の DNS サーバーに依存するかどうかを確認するために、別の DNS サーバー(パブリック DNS など)を試してみます。
  • ネットワーク接続の確認
    pingtraceroutenslookup などのネットワークユーティリティを使用して、基本的なネットワーク接続や DNS 解決の動作を確認します。
  • エラーメッセージをよく読む
    エラーメッセージには、問題の原因に関する重要な情報が含まれています。

特定のレコードタイプに関する問題

  • TXT レコード
    TXT レコードの形式が正しくない場合や、DNS サーバーが TXT レコードを正しく処理できない場合に問題が発生することがあります。
  • MX レコード
    メールサーバーの設定ミスや、DNS サーバーの MX レコードの不整合が原因でエラーが発生することがあります。


特定の DNS サーバーを使用して A レコードを解決する例

const dns = require('node:dns').promises;

async function resolveAWithCustomServer(hostname) {
  const customResolver = new dns.Resolver({
    servers: ['8.8.8.8', '8.8.4.4'], // Google Public DNS サーバーを指定
  });

  try {
    const addresses = await customResolver.resolve4(hostname);
    console.log(`ホスト名 ${hostname} の IPv4 アドレス:`, addresses);
  } catch (err) {
    console.error('エラー:', err);
  }
}

resolveAWithCustomServer('example.com');

この例では、まず dns.Resolver のインスタンスを作成し、servers オプションで使用する DNS サーバーとして Google Public DNS を指定しています。次に、resolve4 メソッドを使用して example.com の IPv4 アドレスを非同期的に解決し、結果を表示またはエラーを処理しています。

複数の DNS サーバーを設定し、MX レコードを解決する例

const dns = require('node:dns').promises;

async function resolveMXWithMultipleServers(hostname) {
  const customResolver = new dns.Resolver({
    servers: ['1.1.1.1', '8.8.8.8'], // Cloudflare DNS と Google DNS を指定
  });

  try {
    const exchangeRecords = await customResolver.resolveMx(hostname);
    console.log(`ホスト名 ${hostname} の MX レコード:`, exchangeRecords);
  } catch (err) {
    console.error('エラー:', err);
  }
}

resolveMXWithMultipleServers('google.com');

ここでは、servers オプションに複数の DNS サーバーのアドレスを配列で指定しています。resolveMx メソッドを使用して google.com の MX レコード(メール交換レコード)を取得し、結果を表示しています。

タイムアウトを設定して A レコードを解決する例

const dns = require('node:dns').promises;

async function resolveAWithTimeout(hostname) {
  const customResolver = new dns.Resolver({
    servers: ['192.0.2.1'], // 存在しない可能性のある IP アドレス
  });
  customResolver.setTimeout(1000); // タイムアウトを 1 秒に設定

  try {
    const addresses = await customResolver.resolve4(hostname);
    console.log(`ホスト名 ${hostname} の IPv4 アドレス:`, addresses);
  } catch (err) {
    console.error('エラー (タイムアウトの可能性あり):', err);
  }
}

resolveAWithTimeout('example.com');

この例では、setTimeout() メソッドを使用して DNS クエリのタイムアウト時間を 1000 ミリ秒(1 秒)に設定しています。指定した DNS サーバーが応答しない場合、設定された時間後にエラーが発生します。

ローカルアドレスとポートを指定して A レコードを解決する例

const dns = require('node:dns').promises;

async function resolveAWithLocalAddress(hostname) {
  const customResolver = new dns.Resolver();
  customResolver.setLocalAddress('0.0.0.0', 0); // すべてのインターフェースと任意のポートを使用

  try {
    const addresses = await customResolver.resolve4(hostname);
    console.log(`ホスト名 ${hostname} の IPv4 アドレス (特定のローカルアドレスを使用):`, addresses);
  } catch (err) {
    console.error('エラー:', err);
  }
}

resolveAWithLocalAddress('example.com');

setLocalAddress() メソッドを使用すると、DNS クエリの送信元となるローカルの IP アドレスとポートを指定できます。上記の例では、すべての利用可能なインターフェース (0.0.0.0) と任意の利用可能なポート (0) を使用しています。

逆引き DNS 検索 (IP アドレスからホスト名を取得する) の例

const dns = require('node:dns').promises;

async function reverseLookupWithCustomServer(ipAddress) {
  const customResolver = new dns.Resolver({
    servers: ['8.8.8.8'],
  });

  try {
    const hostnames = await customResolver.reverse(ipAddress);
    console.log(`${ipAddress} のホスト名:`, hostnames);
  } catch (err) {
    console.error('逆引き DNS 検索エラー:', err);
  }
}

reverseLookupWithCustomServer('8.8.8.8');

reverse() メソッドを使用すると、指定された IP アドレスに対応するホスト名を検索できます。

異なるレコードタイプを解決する例 (TXT レコード)

const dns = require('node:dns').promises;

async function resolveTXT(hostname) {
  const customResolver = new dns.Resolver();

  try {
    const txtRecords = await customResolver.resolveTxt(hostname);
    console.log(`ホスト名 ${hostname} の TXT レコード:`, txtRecords);
  } catch (err) {
    console.error('TXT レコード解決エラー:', err);
  }
}

resolveTXT('google.com');

resolveTxt() メソッドを使用して、指定されたホスト名の TXT レコードを取得します。同様に、resolveMx(), resolveSrv(), resolveCname() など、他のレコードタイプに対応したメソッドも利用できます。



dns モジュールのトップレベル関数 (デフォルトリゾルバーの使用)

dns モジュールには、dns.lookup(), dns.resolve(), dns.resolve4(), dns.resolve6(), dns.resolveMx(), dns.resolveTxt(), dns.resolveSrv(), dns.resolveNaptr(), dns.resolveSoa(), dns.resolveNs(), dns.resolveCname(), dns.reverse() などのトップレベル関数が用意されています。


  • const dns = require('node:dns').promises;
    
    async function resolveADefault(hostname) {
      try {
        const addresses = await dns.resolve4(hostname);
        console.log(`デフォルトリゾルバーによる ${hostname} の IPv4 アドレス:`, addresses);
      } catch (err) {
        console.error('エラー:', err);
      }
    }
    
    resolveADefault('example.com');
    

    この例では、dns.resolve4() 関数を直接使用して example.com の IPv4 アドレスを解決しています。dns.Resolver のインスタンスを作成する必要はありません。

  • 用途
    一般的な名前解決や、システムデフォルトの DNS 設定で十分な場合に適しています。

  • 特徴
    これらの関数は、システムに設定されたデフォルトの DNS リゾルバーを使用して DNS クエリを実行します。dns.Resolver クラスのようにカスタムサーバーや詳細な設定を行う必要がないため、手軽に使用できます。

dns.lookup() 関数 (アドレスとホスト名の両方を取得)

dns.lookup() 関数は、指定されたホスト名に関連付けられた最初のアドレス(IPv4 または IPv6)を取得し、必要に応じてホスト名も返します。