Node.js 非同期処理:dns.resolveNs() と Promise/async/await の連携

2025-05-27

機能

具体的には、dns.resolveNs() を呼び出すと、DNS サーバーに対してクエリが送信され、指定されたホスト名の権威ネームサーバーのリストが返されます。これらのネームサーバーは、そのドメインに関する DNS 情報を管理しているサーバーです。

構文

dns.resolveNs(hostname, callback)

  • callback: 非同期処理の結果を受け取るためのコールバック関数です。このコールバック関数は、以下の2つの引数を受け取ります。
    • err: エラーオブジェクト。エラーが発生しなかった場合は null になります。
    • addresses: ホスト名に関連付けられたネームサーバーのホスト名の配列。各要素は文字列です(例: ['ns1.example.com', 'ns2.example.net'])。
  • hostname: 解決したいホスト名(例: 'example.com')。文字列で指定します。

動作

  1. dns.resolveNs() が呼び出されると、Node.js はバックグラウンドで DNS サーバーに対して、指定された hostname の NS レコードを問い合わせるリクエストを送信します。
  2. DNS サーバーからの応答を受信すると、Node.js はその結果を callback 関数に渡します。
  3. もし DNS の解決に成功した場合、errnull となり、addresses にはネームサーバーのホスト名の配列が格納されます。

使用例

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

const hostname = 'google.com';

dns.resolveNs(hostname, (err, addresses) => {
  if (err) {
    console.error(`NS レコードの解決に失敗しました: ${err}`);
    return;
  }
  console.log(`${hostname} のネームサーバー:`);
  addresses.forEach((address) => {
    console.log(`- ${address}`);
  });
});

console.log('NS レコードの解決をリクエストしました...');

この例では、google.com のネームサーバーを dns.resolveNs() を使って非同期的に解決し、その結果をコンソールに出力しています。コールバック関数は、DNS クエリが完了した後に実行されます。

  • Node.js の dns モジュールには、他の DNS レコードタイプ(A、MX、TXT など)を解決するための関数も用意されています。dns.resolveNs() は、特にネームサーバー情報を取得したい場合に利用します。
  • dns.resolveNs() は非同期処理であるため、呼び出し元のコードは DNS クエリの完了を待たずに次の処理に進みます。結果はコールバック関数を通じて通知されます。


一般的なエラー

    • 原因
      指定された hostname が存在しないか、DNS サーバーが見つからない場合に発生します。ネットワーク接続の問題や、DNS サーバーの設定ミスなども考えられます。
    • トラブルシューティング
      • 指定したホスト名が正しいかどうか再度確認してください。スペルミスやタイプミスがないか注意が必要です。
      • インターネット接続が正常に機能しているか確認してください。他のウェブサイトにアクセスできるか試してみましょう。
      • 使用している DNS サーバーが正しく設定されているか確認してください。ルーターの設定やネットワーク設定を確認する必要があります。
      • 一時的な DNS サーバーの問題である可能性もあります。しばらく時間をおいて再度試してみてください。
      • 他の DNS 解決ツール(例: ping <hostname>nslookup <hostname> コマンドなど)を使って、同じホスト名が解決できるか確認してみるのも有効です。もし他のツールでも解決できない場合は、ホスト名自体に問題がある可能性が高いです。
  1. Error: getaddrinfo EAI_NODATA <hostname> (または同様のエラー)

    • 原因
      指定された hostname は存在するものの、NS レコードが存在しない場合に発生します。
    • トラブルシューティング
      • ホスト名が正しいことを確認してください。
      • そのドメインが正しく登録されており、NS レコードが設定されているかを確認する必要があります。ドメイン登録業者や DNS ホスティングサービスの管理画面などを確認してください。
      • DNS 情報の伝播には時間がかかる場合があります。最近 NS レコードを変更した場合、しばらく待ってから再度試してみてください。
  2. Error: Timeout (タイムアウト)

    • 原因
      DNS サーバーへの接続がタイムアウトした場合に発生します。ネットワークの遅延、ファイアウォールの設定、DNS サーバーの応答遅延などが考えられます。
    • トラブルシューティング
      • インターネット接続が安定しているか確認してください。
      • ファイアウォールが DNS (通常は UDP/TCP ポート 53) の通信をブロックしていないか確認してください。
      • 別の DNS サーバーを試してみるのも有効です。(例: Google Public DNS (8.8.8.8, 8.8.4.4) や Cloudflare DNS (1.1.1.1) など)Node.js の dns モジュールでは、サーバーを指定するオプションはありませんが、システム全体の DNS 設定を変更することで影響を与えられます。
      • ネットワーク環境に問題がないか(ルーターの再起動など)確認してください。
  3. コールバック関数が呼ばれない

    • 原因
      何らかの理由で dns.resolveNs() の処理が完了せず、コールバック関数が実行されない場合があります。ネットワークの不安定さや、Node.js の内部的なエラーなどが考えられます。
    • トラブルシューティング
      • より堅牢なエラーハンドリングを追加し、予期せぬエラーが発生した場合にログを出力するようにしてください。
      • Node.js のバージョンを最新の安定版にアップデートしてみるのも有効かもしれません。
      • ネットワークの状態を監視し、不安定な状況がないか確認してください。

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

  • ログ出力を活用する
    エラーが発生した際に、関連する情報をログに出力するようにしておくと、問題の原因特定に役立ちます。
  • エラーハンドリングを実装する
    try...catch ブロックや、コールバック関数の err 引数を適切に処理することで、エラー発生時の挙動を制御し、デバッグに役立つ情報を得ることができます。
  • シンプルなホスト名でテストする
    まずは google.com のような、一般的に安定しているホスト名でテストし、問題が特定のホスト名に依存するのかどうかを確認します。
  • 他の DNS ツールを試す
    pingnslookupdig などのコマンドラインツールを使用して、DNS の解決状況を अलगに確認してみることで、問題の切り分けに役立ちます。
  • ネットワーク接続を確認する
    DNS 解決はネットワークに依存するため、インターネット接続が正常であることを確認してください。
  • エラーメッセージをよく読む
    エラーメッセージには、問題の原因に関する重要な情報が含まれています。


基本的な使用例

これは、指定されたホスト名のネームサーバーを取得し、コンソールに出力する基本的な例です。

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

const hostname = 'example.com';

dns.resolveNs(hostname, (err, addresses) => {
  if (err) {
    console.error(`[エラー] ${hostname} の NS レコード解決に失敗しました:`, err);
    return;
  }
  console.log(`${hostname} のネームサーバー:`);
  addresses.forEach((address) => {
    console.log(`- ${address}`);
  });
});

console.log(`${hostname} の NS レコード解決を試行中...`);

解説

  1. require('node:dns'): dns モジュールをロードします。
  2. hostname: 解決したいホスト名を文字列で定義します。
  3. dns.resolveNs(hostname, (err, addresses) => { ... });: dns.resolveNs() 関数を呼び出し、ホスト名とコールバック関数を渡します。
  4. コールバック関数:
    • err: エラーが発生した場合、この引数にエラーオブジェクトが渡されます。エラーがない場合は null になります。
    • addresses: DNS 解決に成功した場合、ネームサーバーのホスト名を含む配列がこの引数に渡されます。
  5. エラーハンドリング (if (err) { ... }): エラーが発生した場合、エラーメッセージをコンソールに出力して処理を終了します。
  6. 成功時の処理 (addresses.forEach(...)): addresses 配列の各要素(ネームサーバーのホスト名)をコンソールに出力します。
  7. console.log(...): 非同期処理が開始されたことを示すメッセージを最初に表示します。

複数のホスト名を処理する例

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

const hostnames = ['google.com', 'facebook.com', 'invalid-domain-example'];

hostnames.forEach((hostname) => {
  dns.resolveNs(hostname, (err, addresses) => {
    console.log(`\n${hostname} の NS レコード:`);
    if (err) {
      console.error(`  [エラー] 解決に失敗しました:`, err.message);
      return;
    }
    addresses.forEach((address) => {
      console.log(`  - ${address}`);
    });
  });
  console.log(`  ${hostname} の NS レコード解決を試行中...`);
});

解説

  1. hostnames: 解決したいホスト名の配列を定義します。
  2. hostnames.forEach(...): 配列内の各ホスト名に対して dns.resolveNs() を呼び出します。
  3. 各ホスト名ごとの処理結果を分かりやすくするために、ホスト名を出力しています。
  4. エラーが発生した場合、エラーメッセージのみを出力します。

Promise を使用した例 (async/await)

dns.promises.resolveNs() を使用して、Promise ベースの非同期処理を行う例です。async/await を使うことで、非同期処理を同期的なコードのように記述できます。

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

async function resolveNameServers(hostname) {
  try {
    const addresses = await dns.resolveNs(hostname);
    console.log(`${hostname} のネームサーバー:`);
    addresses.forEach((address) => {
      console.log(`- ${address}`);
    });
  } catch (err) {
    console.error(`[エラー] ${hostname} の NS レコード解決に失敗しました:`, err.message);
  }
}

const hostnames = ['twitter.com', 'youtube.com', 'another-invalid-domain'];

hostnames.forEach(async (hostname) => {
  console.log(`${hostname} の NS レコード解決を開始...`);
  await resolveNameServers(hostname);
});
  1. const dns = require('node:dns').promises;: dns.promises API を使用します。これにより、Promise を返す resolveNs() が利用できます。
  2. async function resolveNameServers(hostname) { ... }: 非同期関数を定義します。
  3. try...catch: エラーハンドリングを行います。Promise が reject された場合、catch ブロック内の処理が実行されます。
  4. await dns.resolveNs(hostname);: Promise が解決されるまで処理を一時停止し、結果を addresses に格納します。
  5. hostnames.forEach(async (hostname) => { ... });: 配列内の各ホスト名に対して非同期関数 resolveNameServers を呼び出します。forEach 内で await を使用するため、コールバック関数を async として定義する必要があります。


dns.resolve() を使用して NS レコードを指定する

dns.resolve() 関数は、指定されたホスト名とレコードタイプに基づいて DNS クエリを実行できます。NS レコードを明示的に指定することで、dns.resolveNs() と同様の情報を取得できます。

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

const hostname = 'example.com';

dns.resolve(hostname, 'NS', (err, addresses) => {
  if (err) {
    console.error(`[エラー] ${hostname} の NS レコード解決に失敗しました:`, err);
    return;
  }
  console.log(`${hostname} のネームサーバー (dns.resolve):`);
  addresses.forEach((address) => {
    console.log(`- ${address}`);
  });
});

console.log(`${hostname} の NS レコード解決を試行中 (dns.resolve)...`);

解説

  • コールバック関数の addresses は、ネームサーバーのホスト名を含む配列として返されます。
  • dns.resolve(hostname, 'NS', callback) のように、第二引数に 'NS' を指定することで、NS レコードのクエリを実行できます。

利点

  • 他のレコードタイプ(MX、TXT など)を取得する際にも同じ関数を使用できるため、一貫性があります。
  • 標準モジュールのみで完結します。

欠点

  • dns.resolveNs() ほど意味が明確ではありません。

Promise ベースの dns.promises.resolve() を使用する

dns.promises.resolve() を使用すると、async/await 構文を利用してより簡潔に非同期処理を記述できます。

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

async function resolveNameServersWithPromise(hostname) {
  try {
    const addresses = await dns.resolve(hostname, 'NS');
    console.log(`${hostname} のネームサーバー (Promise):`);
    addresses.forEach((address) => {
      console.log(`- ${address}`);
    });
  } catch (err) {
    console.error(`[エラー] ${hostname} の NS レコード解決に失敗しました (Promise):`, err.message);
  }
}

const hostname = 'google.com';
resolveNameServersWithPromise(hostname);

解説

  • await dns.resolve(hostname, 'NS') で、NS レコードの解決を非同期的に待ち、結果を addresses に格納します。

利点

  • エラーハンドリングが try...catch でより直感的に記述できます。
  • async/await による可読性の向上。

欠点

  • 標準モジュールのみで完結しますが、Promise ベースの API を使用します。

第三者 DNS クライアントライブラリの使用

Node.js のエコシステムには、より高度な機能や柔軟性を提供するサードパーティの DNS クライアントライブラリが存在します。これらのライブラリは、標準の dns モジュールよりも詳細な制御や、異なる DNS プロトコル(例: DNS-over-HTTPS, DNS-over-TLS)のサポートを提供することがあります。

  • node-dig-dns
    dig コマンドの機能をプログラムから利用できるライブラリです。
  • dns2
    高機能な非同期 DNS クライアントライブラリで、様々な DNS レコードタイプやオプションをサポートしています。

以下は dns2 を使用して NS レコードを取得する例です。

const dns2 = require('dns2');
const { Resolver } = dns2;

const resolver = new Resolver();
const hostname = 'cloudflare.com';

async function resolveNameServersWithDns2(hostname) {
  try {
    const response = await resolver.resolve(hostname, 'NS');
    console.log(`${hostname} のネームサーバー (dns2):`);
    response.answers.forEach((answer) => {
      if (answer.type === dns2.Packet.TYPE.NS) {
        console.log(`- ${answer.data}`);
      }
    });
  } catch (err) {
    console.error(`[エラー] ${hostname} の NS レコード解決に失敗しました (dns2):`, err);
  }
}

resolveNameServersWithDns2(hostname);

解説

  • resolver.resolve(hostname, 'NS') は Promise を返します。
  • Resolver クラスを使用して DNS クエリを実行します。
  • dns2 ライブラリをインストールする必要があります (npm install dns2).

利点

  • 高度な機能(タイムアウト設定、リトライなど)を利用できる場合があります。
  • 異なる DNS プロトコルのサポート。
  • より詳細な DNS クエリの制御が可能。

欠点

  • 標準モジュールよりも学習コストが高い可能性があります。
  • 外部ライブラリへの依存が発生します。

外部 DNS 解決 API の利用

Node.js アプリケーションが直接 DNS クエリを実行するのではなく、HTTP 経由で DNS 解決サービスを提供する外部 API を利用する方法もあります。

  • Cloudflare DNS Resolver API
    同様の API を提供しています。
  • Google Public DNS API
    https://dns.google/resolve?name=<hostname>&type=NS のようなエンドポイントにリクエストを送信することで、JSON 形式で DNS レコードを取得できます。

以下は node-fetch を使用して Google Public DNS API から NS レコードを取得する例です。

const fetch = require('node-fetch');

async function resolveNameServersWithApi(hostname) {
  const apiUrl = `https://dns.google/resolve?name=${hostname}&type=NS`;
  try {
    const response = await fetch(apiUrl);
    const data = await response.json();
    if (data.Answer) {
      console.log(`${hostname} のネームサーバー (API):`);
      data.Answer.forEach((record) => {
        if (record.type === 2) { // NS レコードのタイプコードは 2
          console.log(`- ${record.data}`);
        }
      });
    } else {
      console.error(`[エラー] ${hostname} の NS レコードが見つかりませんでした (API):`, data);
    }
  } catch (err) {
    console.error(`[エラー] DNS API の呼び出しに失敗しました:`, err);
  }
}

const hostname = 'amazon.com';
resolveNameServersWithApi(hostname);

解説

  • Google Public DNS API のエンドポイントにホスト名とレコードタイプを指定して GET リクエストを送信します。
  • node-fetch ライブラリをインストールする必要があります (npm install node-fetch).

利点

  • 異なるプログラミング言語や環境からも利用しやすい。
  • Node.js アプリケーションが直接 DNS サーバーにアクセスする必要がないため、ネットワーク構成によっては有効な場合があります。
  • API のレスポンス形式を理解し、適切に処理する必要があります。
  • API の利用制限やレイテンシを考慮する必要があります。
  • 外部サービスへの依存が発生します。