Node.js DNSモジュール徹底解説:lookup(), resolve() の違い

2025-05-27

dns.lookup() は、Node.js の dns モジュールに用意されている関数の一つで、与えられたホスト名(例えば、google.com)に対応する IP アドレスを取得するために使用されます。 これは、低レベルなオペレーティングシステムの機能を利用して名前解決を行うため、非同期処理でありながら、他の dns モジュールの関数(例えば dns.resolve())とは動作が少し異なります。

主な特徴と動作

  1. 名前解決
    指定されたホスト名を IP アドレス(IPv4 または IPv6)に解決します。
  2. 最初のレコード
    ホスト名に関連付けられた最初に見つかった A レコード(IPv4 アドレス)または AAAA レコード(IPv6 アドレス)を返します。
  3. オプション
    dns.lookup() は、アドレスファミリー(IPv4 または IPv6)やヒントなどのオプションを指定できます。
  4. コールバック関数
    結果はコールバック関数を通じて非同期的に返されます。コールバック関数は、エラーオブジェクト(エラーが発生した場合)と、解決された IP アドレス、そしてアドレスファミリー('IPv4' または 'IPv6')の3つの引数を受け取ります。

基本的な使用例

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

dns.lookup('example.com', (err, address, family) => {
  if (err) {
    console.error('名前解決エラー:', err);
    return;
  }
  console.log('IP アドレス:', address);
  console.log('アドレスファミリー:', family);
});

この例では、example.com の IP アドレスを検索し、成功すれば IP アドレスとアドレスファミリーをコンソールに出力します。エラーが発生した場合は、エラーメッセージを出力します。

オプションの指定例

アドレスファミリーを IPv6 に限定する場合:

dns.lookup('example.com', { family: 6 }, (err, address, family) => {
  // ... (エラー処理と結果の出力)
});

dns.resolve() との違い

dns.lookup() とよく似た関数に dns.resolve() がありますが、これらは重要な点で異なります。

  • dns.resolve()
    ネットワーク経由で実際の DNS サーバーにクエリを送信し、指定されたレコードタイプ(A、AAAA、MX、TXT など)のすべてのレコードを配列として返します。
  • dns.lookup()
    オペレーティングシステムの基盤となる名前解決機能を使用します。通常、システムの設定(例えば、/etc/hosts ファイルや設定された DNS サーバー)に従って名前解決を行います。また、最初に見つかった A または AAAA レコードのみを返します。

一般的に、特定の IP アドレスを一つだけ取得したい場合は dns.lookup() が、特定のレコードタイプのすべての情報を取得したい場合は dns.resolve() が適しています。



一般的なエラー

    • 原因
      指定された hostname が DNS サーバーで見つからなかった場合に発生します。スペルミス、存在しないドメイン名、または一時的な DNS サーバーの問題などが考えられます。
    • トラブルシューティング
      • hostname のスペルを再確認してください。
      • 他のツール(例えば、ping コマンドやブラウザ)でそのホスト名が解決できるか試してみてください。
      • ネットワーク接続や DNS サーバーの設定に問題がないか確認してください。
      • 一時的な DNS サーバーの不具合であれば、しばらく待ってから再度試してみてください。
  1. Error: getaddrinfo ENODATA hostname (または同様のエラー)

    • 原因
      DNS サーバーはホスト名を見つけたものの、要求されたアドレスファミリー(例えば、IPv4 を要求したが A レコードが存在しない)に対応するレコードが見つからなかった場合に発生します。
    • トラブルシューティング
      • family オプションを指定している場合は、その指定が正しいか確認してください。例えば、IPv6 のみを探しているのに、そのホストに IPv4 アドレスしか登録されていない場合などです。
      • ホスト名が本当にそのアドレスファミリーに対応するレコードを持っているか確認してください(dns.resolve() など他の DNS ユーティリティを使用)。
  2. Error: getaddrinfo EAI_NODNS (または同様のエラー)

    • 原因
      システムに設定された DNS サーバーにアクセスできない場合に発生します。ネットワーク接続の問題や、DNS サーバーがダウンしているなどが考えられます。
    • トラブルシューティング
      • ネットワーク接続が正常であることを確認してください。
      • 他のネットワーク機能(例えば、他のウェブサイトへのアクセス)が正常に動作するか確認してください。
      • システムの DNS サーバー設定を確認してください。
      • ルーターやモデムを再起動してみるのも有効な場合があります。
  3. タイムアウト

    • 原因
      DNS サーバーからの応答が指定された時間内に得られない場合に発生することがあります。ネットワークの遅延や DNS サーバーの負荷が高いなどが考えられます。
    • トラブルシューティング
      • ネットワーク接続の状態を確認してください。
      • 他の DNS サーバーを試してみる(一時的に Google Public DNS (8.8.8.8, 8.8.4.4) などを使用してみる)のも有効かもしれません。
      • アプリケーション全体のタイムアウト設定を見直す必要がある場合もあります。

トラブルシューティングのヒント

  • エラーハンドリングの重要性
    try...catch ブロックは dns.lookup() のコールバック関数では直接機能しません。必ずコールバック関数内でエラーオブジェクト (err) の存在を確認し、適切なエラーハンドリングを行うようにしてください。
  • dns.resolve() の検討
    特定のレコードタイプ(MX レコードなど)や、ホストに関連付けられたすべての IP アドレスを取得したい場合は、dns.lookup() ではなく dns.resolve() の使用を検討してください。
  • 非同期処理の理解
    dns.lookup() は非同期処理であるため、結果がすぐに得られるわけではありません。コールバック関数が適切に処理されるようにコードを記述する必要があります。
  • ネットワーク環境の確認
    特に開発環境と本番環境で異なるエラーが発生する場合は、それぞれのネットワーク環境(DNS サーバー、ファイアウォールなど)の設定を確認してください。
  • 他の DNS ツールとの比較
    nslookupdig などのコマンドライン DNS ツールを使用して、同じホスト名の解決を試してみると、Node.js 特有の問題か、ネットワークや DNS サーバーの問題かを切り分けることができます。
  • ログ出力
    エラーが発生した際に、関連する情報(試したホスト名、オプションなど)をログに出力するようにしておくと、問題の特定に役立ちます。
  • エラーオブジェクトの確認
    dns.lookup() のコールバック関数に渡されるエラーオブジェクト (err) には、エラーの種類や詳細な情報が含まれています。エラーメッセージを注意深く確認することが重要です。


基本的な使用例

この例では、example.com の IP アドレスを検索し、成功すれば IP アドレスとアドレスファミリーをコンソールに出力します。

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

dns.lookup('example.com', (err, address, family) => {
  if (err) {
    console.error('名前解決エラー:', err);
    return;
  }
  console.log('IP アドレス:', address);
  console.log('アドレスファミリー:', family);
});

console.log('DNS lookup を開始しました...');

解説

  • console.log('DNS lookup を開始しました...')dns.lookup() は非同期処理なので、この行は名前解決が完了する前に実行される可能性があります。
  • 成功時の処理:IP アドレスとアドレスファミリーをコンソールに出力します。
  • エラーチェック (if (err)):エラーが発生した場合、エラーメッセージをコンソールに出力して処理を終了します。
  • コールバック関数 (err, address, family) => { ... }:検索が完了した後に呼び出されます。
    • err:エラーが発生した場合、エラーオブジェクトが渡されます。成功した場合は null になります。
    • address:解決された IP アドレス(文字列)が渡されます。
    • family:IP アドレスのファミリー ('IPv4' または 'IPv6') が渡されます。
  • dns.lookup('example.com', ...)example.com というホスト名の IP アドレスを非同期的に検索します。
  • require('node:dns')dns モジュールをロードします。

オプションを指定した使用例 (アドレスファミリーの指定)

この例では、example.com の IPv6 アドレスのみを検索するように family オプションを指定しています。

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

dns.lookup('example.com', { family: 6 }, (err, address, family) => {
  if (err) {
    console.error('IPv6 名前解決エラー:', err);
    return;
  }
  console.log('IPv6 アドレス:', address);
  console.log('アドレスファミリー:', family);
});

console.log('IPv6 DNS lookup を開始しました...');

解説

  • { family: 6 }options オブジェクトとして渡され、アドレスファミリーを IPv6 に限定します。IPv4 を指定する場合は { family: 4 } を使用します。

オプションを指定した使用例 (ヒントの指定)

hints オプションは、解決されるアドレスの種類に関するヒントを提供します。dns.ADDRCONFIG は、システムの利用可能なアドレス設定に基づいてアドレスの種類を選択するのに役立ちます。

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

dns.lookup('example.com', { hints: dns.ADDRCONFIG }, (err, address, family) => {
  if (err) {
    console.error('名前解決エラー (hints あり):', err);
    return;
  }
  console.log('IP アドレス (hints あり):', address);
  console.log('アドレスファミリー (hints あり):', family);
});

console.log('DNS lookup (hints あり) を開始しました...');

解説

  • { hints: dns.ADDRCONFIG }dns.ADDRCONFIG は、システムが IPv6 アドレスを使用するように設定されている場合は IPv6 アドレスを優先し、そうでない場合は IPv4 アドレスを優先するヒントを提供します。

Promise を使用した例 (Node.js 15.0.0 以降)

Node.js 15.0.0 以降では、dns.promises.lookup() を使用して Promise ベースの API を利用できます。

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

async function lookupExample() {
  try {
    const result = await dns.lookup('example.com');
    console.log('IP アドレス (Promise):', result.address);
    console.log('アドレスファミリー (Promise):', result.family);
  } catch (err) {
    console.error('Promise ベースの名前解決エラー:', err);
  }
}

lookupExample();
console.log('Promise ベースの DNS lookup を開始しました...');
  • try...catch:Promise が reject された場合(エラーが発生した場合)にエラーをキャッチします。
  • result.addressresult.family:解決された IP アドレスとアドレスファミリーにアクセスできます。
  • await dns.lookup('example.com')dns.lookup() が返す Promise を待ち、結果を result オブジェクトに格納します。
  • async function lookupExample() { ... }:非同期関数内で await を使用して Promise の完了を待ちます。
  • require('node:dns').promises:Promise ベースの API をロードします。


dns.resolve() ファミリー

dns モジュールには、dns.lookup() よりも詳細な DNS クエリを実行できる dns.resolve() ファミリーの関数があります。これらは特定のレコードタイプ(A、AAAA、MX、TXT、SRV など)の情報を取得するのに適しています。

  • dns.reverse(ip, callback)
    IP アドレスからホスト名を逆引き(PTR レコード検索)します。
  • dns.resolveCname(hostname, callback)
    正式名(CNAME)レコードをクエリします。結果は正式名の配列としてコールバック関数に渡されます。
  • dns.resolveNs(hostname, callback)
    ネームサーバー(NS)レコードをクエリします。結果はネームサーバー名の配列としてコールバック関数に渡されます。
  • dns.resolveSrv(hostname, callback)
    サービス(SRV)レコードをクエリします。結果は優先度、ウェイト、ポート、ターゲットなどを持つオブジェクトの配列としてコールバック関数に渡されます。
  • dns.resolveTxt(hostname, callback)
    テキスト(TXT)レコードをクエリします。結果はテキストレコードの配列(各要素は文字列の配列)としてコールバック関数に渡されます。
  • dns.resolveMx(hostname, callback)
    メール交換(MX)レコードをクエリします。結果は優先度と交換ホスト名を持つオブジェクトの配列としてコールバック関数に渡されます。
  • dns.resolve6(hostname, callback)
    IPv6 アドレス(AAAA レコード)のみをクエリします。結果は IPv6 アドレスの配列としてコールバック関数に渡されます。
  • dns.resolve4(hostname, callback)
    IPv4 アドレス(A レコード)のみをクエリします。結果は IPv4 アドレスの配列としてコールバック関数に渡されます。
  • dns.resolve(hostname[, rrtype], callback)
    指定された hostname の DNS レコードを指定された rrtype(リソースレコードタイプ)でクエリし、結果を配列としてコールバック関数に渡します。rrtype を省略すると、A レコード(IPv4 アドレス)がクエリされます。

例 (dns.resolveMx)

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

dns.resolveMx('google.com', (err, addresses) => {
  if (err) {
    console.error('MX レコード解決エラー:', err);
    return;
  }
  console.log('MX レコード:', addresses);
});

dns.promises API

Node.js 15.0.0 以降では、dns.resolve() ファミリーにも Promise ベースの API が提供されています。

  • dns.promises.reverse(ip)
  • dns.promises.resolveCname(hostname)
  • dns.promises.resolveNs(hostname)
  • dns.promises.resolveSrv(hostname)
  • dns.promises.resolveTxt(hostname)
  • dns.promises.resolveMx(hostname)
  • dns.promises.resolve6(hostname)
  • dns.promises.resolve4(hostname)
  • dns.promises.resolve(hostname[, rrtype])

例 (dns.promises.resolveTxt)

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

async function resolveTxtExample() {
  try {
    const records = await dns.resolveTxt('example.com');
    console.log('TXT レコード (Promise):', records);
  } catch (err) {
    console.error('Promise ベースの TXT レコード解決エラー:', err);
  }
}

resolveTxtExample();

第三者製 DNS クライアントライブラリ

Node.js のエコシステムには、より高度な機能や柔軟性を提供するサードパーティ製の DNS クライアントライブラリがいくつか存在します。これらのライブラリは、より細かな DNS クエリの制御、カスタム DNS サーバーの使用、DNSSEC のサポートなどを提供する場合があります。

  • dnscache
    DNS クエリの結果をキャッシュすることで、パフォーマンスを向上させるためのライブラリです。
  • node-dns
    フル機能の非同期 DNS クライアントライブラリで、様々なレコードタイプをサポートし、低レベルな DNS メッセージの構築も可能です。

例 (node-dns の基本的な使用)

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

const query = dns.Request({
  question: dns.Question({ name: 'example.com', type: 'A' }),
  server: { address: '8.8.8.8', port: 53, type: 'udp' },
  timeout: 1000,
});

query.send((err, response) => {
  if (err) {
    console.error('DNS クエリエラー:', err);
    return;
  }
  if (response.answer.length > 0) {
    console.log('IP アドレス (node-dns):', response.answer[0].address);
  } else {
    console.log('A レコードが見つかりませんでした。');
  }
});
  • サードパーティ製ライブラリ
    より高度な DNS 制御が必要な場合(カスタム DNS サーバーの指定、低レベルな DNS メッセージの操作、DNSSEC の検証など)に検討すると良いでしょう。
  • dns.resolve() ファミリー
    特定の DNS レコードタイプ(MX レコードや TXT レコードなど)の情報を取得したい場合や、ホストに関連付けられたすべての IP アドレスを取得したい場合に適しています。ネットワーク経由で DNS サーバーに直接クエリを行います。
  • dns.lookup()
    ホスト名に対応する最初の IP アドレス(A または AAAA)をシンプルに取得したい場合に適しています。オペレーティングシステムの名前解決機能を利用するため、システムの設定(/etc/hosts など)が考慮されます。