【Node.js】dnsPromises.lookup()の詳しい解説とプログラミング例

2025-05-27

dnsPromises.lookup() は、Node.js の dns モジュールが提供する非同期処理のための API の一つです。具体的には、指定されたホスト名(例:google.com)に対応する IP アドレス(IPv4 または IPv6)を取得する 処理を行います。

従来のコールバックベースの dns.lookup() と異なり、dnsPromises.lookup()Promise を返します。Promise を使うことで、非同期処理の結果をより扱いやすく、モダンな JavaScript の async/await 構文と組み合わせて記述することも可能です。

基本的な使い方

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

async function getIPAddress(hostname) {
  try {
    const result = await dns.lookup(hostname);
    console.log(`ホスト名: ${hostname}`);
    console.log(`IP アドレス: ${result.address}`);
    console.log(`ファミリー: IPv${result.family}`); // 4 または 6
  } catch (err) {
    console.error(`エラーが発生しました: ${err}`);
  }
}

getIPAddress('google.com');
getIPAddress('example.com');

この例では、getIPAddress という非同期関数の中で await dns.lookup('google.com') を呼び出しています。await キーワードは、Promise が解決(成功)または拒否(失敗)するまで関数の実行を一時停止します。

返り値

dnsPromises.lookup() が成功した場合、解決された Promise は以下のプロパティを持つオブジェクトを返します。

  • family: IP アドレスのファミリー(数値)。IPv4 の場合は 4、IPv6 の場合は 6
  • address: ホスト名に対応する IP アドレス(文字列)。

オプション

dnsPromises.lookup() は、オプションのオブジェクトを第二引数に取ることができます。主なオプションは以下の通りです。

  • all: 真偽値を指定します。true を指定すると、見つかったすべての IP アドレスの配列を返します。省略した場合または false を指定した場合は、最初に見つかったアドレスのみを含むオブジェクトを返します。
  • hints: アドレスの解決方法に関するヒントを提供します。dns.ADDRCONFIGdns.V4MAPPED などの定数を指定できます。
  • family: 取得する IP アドレスのファミリーを指定します。4 を指定すると IPv4 のアドレスのみ、6 を指定すると IPv6 のアドレスのみを取得しようとします。省略した場合、DNS サーバーの設定に基づいて両方を試みます。

例(オプションの使用)

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

async function getAllIPv6Addresses(hostname) {
  try {
    const result = await dns.lookup(hostname, { family: 6, all: true });
    console.log(`${hostname} の IPv6 アドレス:`);
    result.forEach(addr => console.log(addr.address));
  } catch (err) {
    console.error(`エラーが発生しました: ${err}`);
  }
}

getAllIPv6Addresses('google.com');

この例では、family: 6 を指定して IPv6 アドレスのみを取得し、all: true を指定して見つかったすべての IPv6 アドレスを配列として取得しています。



Error: getaddrinfo ENOTFOUND <hostname>

  • トラブルシューティング
    • ホスト名の確認
      指定したホスト名にタイプミスがないか、正しいホスト名であることを再確認してください。
    • ネットワーク接続の確認
      Node.js が実行されている環境がインターネットに接続されているか確認してください。
    • DNS サーバーの確認
      使用している DNS サーバーが正常に動作しているか確認してください。他のホスト名であれば解決できる場合、一時的な DNS サーバーの問題かもしれません。
    • 別のツールの使用
      ping コマンドや nslookup コマンドなど、他のネットワークツールを使ってホスト名が解決できるか試してみてください。もし他のツールでも解決できない場合は、ネットワーク環境または DNS サーバーに問題がある可能性が高いです。
  • 原因
    指定されたホスト名 <hostname> が DNS サーバーによって解決できなかった場合に発生します。これは、ホスト名が存在しない、スペルミスがある、または DNS サーバーが一時的に利用できないなどの理由で起こり得ます。

Error: getaddrinfo EAI_AGAIN <hostname>

  • トラブルシューティング
    • 再試行
      一定時間後に再度 dnsPromises.lookup() を実行してみてください。一時的な問題であれば、再試行で成功する可能性があります。
    • ネットワーク状況の確認
      ネットワークの遅延や不安定さがないか確認してください。
    • DNS サーバーの変更
      可能であれば、別の DNS サーバー(例:Google Public DNS (8.8.8.8, 8.8.4.4) や Cloudflare DNS (1.1.1.1))を使用するように設定を変更してみるのも有効かもしれません。
  • 原因
    DNS サーバーが一時的に応答しない場合に発生することがあります。これは、ネットワークの混雑や DNS サーバーの高負荷などが原因と考えられます。

Error: getaddrinfo EAI_NODATA <hostname>

  • トラブルシューティング
    • オプション family の確認
      lookup のオプションで family を指定している場合、その指定が正しいか確認してください。例えば、IPv4 アドレスが必要なのに family: 6 を指定しているとこのエラーが発生する可能性があります。
    • ホストのレコード確認
      そのホストが実際に要求しているアドレスタイプの DNS レコードを持っているか確認してください。
  • 原因
    指定されたホスト名は解決できたものの、要求されたアドレスタイプ(例えば、IPv4 のみを要求したが IPv6 のアドレスしか存在しないなど)のアドレスが見つからなかった場合に発生します。

タイムアウト

  • トラブルシューティング
    • ネットワーク状況の確認
      ネットワークの遅延が大きい場合、タイムアウトのように見えることがあります。
    • DNS サーバーの応答速度の確認
      使用している DNS サーバーの応答速度が遅い可能性があります。別の DNS サーバーを試すことを検討してください。
    • より高レベルな HTTP クライアントライブラリの検討
      HTTP リクエストを行う場合は、タイムアウトなどの設定が可能な node-fetchaxios などのライブラリの使用を検討するのも良いでしょう。これらのライブラリは DNS 解決を内部で行い、より柔軟な設定が可能です。
  • 原因
    DNS サーバーからの応答が指定された時間内に得られない場合に、Promise がタイムアウトする可能性があります。Node.js の dnsPromises.lookup() 自体に直接的なタイムアウト設定はありませんが、ネットワーク環境や DNS サーバーの応答速度によっては、処理が完了しないことがあります。
  • 上記以外にも、ネットワーク環境やシステムの設定によって予期しないエラーが発生する可能性があります。エラーメッセージをよく読み、状況に応じて調査する必要があります。
  • Node.js のバージョン
    古い Node.js のバージョンを使用している場合、稀に既知のバグに遭遇することがあります。可能であれば、最新の安定版にアップデートすることを検討してください。
  • ネットワーク環境の調査
    ファイアウォールやプロキシサーバーの設定が DNS 解決を妨げている可能性も考慮してください。
  • 簡単なホスト名でテスト
    まずは google.com のような、一般的に安定しているホスト名でテストを行い、基本的な DNS 解決が機能するか確認します。
  • ログ出力
    try...catch ブロックでエラーをキャッチし、エラーの詳細(エラーコード、メッセージなど)をログに出力するようにすると、問題の特定に役立ちます。
  • エラーメッセージをよく読む
    エラーメッセージには、問題の原因に関する重要な情報が含まれています。


基本的な使用例

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

async function getIPAddress(hostname) {
  try {
    const result = await dns.lookup(hostname);
    console.log(`ホスト名: ${hostname}`);
    console.log(`IP アドレス: ${result.address}`);
    console.log(`ファミリー: IPv${result.family}`);
  } catch (err) {
    console.error(`エラーが発生しました: ${err}`);
  }
}

getIPAddress('google.com');
getIPAddress('example.com');

この例では、getIPAddress 関数が指定されたホスト名に対応する最初の IP アドレスを取得し、そのアドレスとファミリー(IPv4 または IPv6)をコンソールに出力します。エラーが発生した場合は、エラーメッセージをキャッチして表示します。

特定の IP アドレスファミリーを指定する例 (IPv4 のみ)

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

async function getIPv4Address(hostname) {
  try {
    const result = await dns.lookup(hostname, { family: 4 });
    console.log(`${hostname} の IPv4 アドレス: ${result.address}`);
  } catch (err) {
    console.error(`IPv4 アドレスの取得に失敗しました: ${err}`);
  }
}

getIPv4Address('google.com');

ここでは、lookup の第二引数に { family: 4 } というオプションを渡すことで、IPv4 アドレスのみを取得しようとしています。もし IPv4 アドレスが存在しない場合はエラーが発生します。

特定の IP アドレスファミリーを指定する例 (IPv6 のみ)

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

async function getIPv6Address(hostname) {
  try {
    const result = await dns.lookup(hostname, { family: 6 });
    console.log(`${hostname} の IPv6 アドレス: ${result.address}`);
  } catch (err) {
    console.error(`IPv6 アドレスの取得に失敗しました: ${err}`);
  }
}

getIPv6Address('google.com');

同様に、{ family: 6 } を指定することで、IPv6 アドレスのみを取得しようとします。

すべての IP アドレスを取得する例

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

async function getAllAddresses(hostname) {
  try {
    const result = await dns.lookup(hostname, { all: true });
    console.log(`${hostname} のすべての IP アドレス:`);
    result.forEach(addrInfo => {
      console.log(`  アドレス: ${addrInfo.address}, ファミリー: IPv${addrInfo.family}`);
    });
  } catch (err) {
    console.error(`すべてのアドレスの取得に失敗しました: ${err}`);
  }
}

getAllAddresses('google.com');

{ all: true } オプションを指定すると、見つかったすべての IP アドレスが配列として返されます。配列の各要素は、addressfamily プロパティを持つオブジェクトです。

hints オプションを使用する例

hints オプションは、アドレスの解決方法に関するヒントを提供します。例えば、IPv4 マッピングされた IPv6 アドレス (AAAA レコードで返される IPv4 アドレス) を優先する場合などに使用します。

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

async function getAddressWithHint(hostname) {
  try {
    const result = await dns.lookup(hostname, { hints: dnsConstants.ADDRCONFIG | dnsConstants.V4MAPPED });
    console.log(`${hostname} の IP アドレス (hints 適用): ${result.address}, ファミリー: IPv${result.family}`);
  } catch (err) {
    console.error(`アドレスの取得に失敗しました (hints 適用): ${err}`);
  }
}

getAddressWithHint('example.com');

この例では、dns.ADDRCONFIGdns.V4MAPPED をビット演算子 | で組み合わせて hints に渡しています。ADDRCONFIG はシステムの設定されたアドレスファミリーを優先し、V4MAPPED は IPv6 アドレスが見つからない場合に IPv4 マッピングされた IPv6 アドレスを返すように指示します。

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

async function lookupWithErrorHandler(hostname) {
  try {
    const result = await dns.lookup(hostname);
    console.log(`ホスト名: ${hostname}, IP アドレス: ${result.address}`);
  } catch (err) {
    if (err.code === 'ENOTFOUND') {
      console.error(`${hostname} は見つかりませんでした。`);
    } else if (err.code === 'EAI_AGAIN') {
      console.error(`${hostname} の解決を再試行してください。`);
    } else {
      console.error(`不明なエラーが発生しました: ${err}`);
    }
  }
}

lookupWithErrorHandler('nonexistent-domain.example');
lookupWithErrorHandler('google.com');


dns.lookup() (コールバックベースの API)

  • 使い方

<!-- end list -->

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

dns.lookup('google.com', (err, address, family) => {
  if (err) {
    console.error(`エラーが発生しました: ${err}`);
    return;
  }
  console.log(`IP アドレス: ${address}`);
  console.log(`ファミリー: IPv${family}`);
});

dns.lookup('example.com', { family: 6 }, (err, address, family) => {
  if (err) {
    console.error(`IPv6 アドレスの取得に失敗しました: ${err}`);
    return;
  }
  console.log(`example.com の IPv6 アドレス: ${address}`);
  console.log(`ファミリー: IPv${family}`);
});
  • 欠点
    コールバック地獄(ネストが深くなる)に陥りやすく、エラーハンドリングが Promise よりも煩雑になることがあります。async/await 構文との連携も直接的にはできません。
  • 利点
    Promise をサポートしていない古い環境や、コールバック関数に慣れている場合に利用できます。

第三者製 DNS 解決ライブラリ

  • 欠点
    新しい依存関係を追加する必要があり、学習コストがかかる場合があります。
  • 利点
    より細かい制御が可能になったり、標準の dns モジュールにはない機能を利用できたりする場合があります。
  • 使い方
    各ライブラリによって API が異なるため、それぞれのドキュメントを参照する必要があります。

    • dnspromise
      dns モジュールの Promise API をラップしたライブラリで、追加の機能(タイムアウト設定など)を提供している場合があります。
    • native-dns
      より低レベルな DNS プロトコルを直接操作できるライブラリで、カスタム DNS クエリの作成などに利用できます。
    • node-resolve
      Node.js の require.resolve() アルゴリズムと同様の機能を提供し、モジュールの解決だけでなく、一般的な名前解決にも利用できる場合があります。

OS のネットワークユーティリティの利用 (非推奨)

  • 使い方
const { exec } = require('node:child_process');

function lookupWithNslookup(hostname) {
  return new Promise((resolve, reject) => {
    exec(`nslookup ${hostname}`, (error, stdout, stderr) => {
      if (error) {
        reject(`nslookup エラー: ${error}`);
        return;
      }
      const lines = stdout.split('\n');
      let address = null;
      for (const line of lines) {
        if (line.includes('Address:')) {
          address = line.split(':')[1].trim();
          break;
        }
      }
      if (address) {
        resolve(address);
      } else {
        reject(`IP アドレスが見つかりませんでした: ${stdout}`);
      }
    });
  });
}

async function main() {
  try {
    const ip = await lookupWithNslookup('google.com');
    console.log(`google.com の IP アドレス (nslookup): ${ip}`);
  } catch (err) {
    console.error(err);
  }
}

main();
  • 欠点
    • プラットフォーム依存性が高く、異なる OS で動作しない可能性があります。
    • コマンドの出力形式を解析する必要があり、実装が複雑になる場合があります。
    • セキュリティ上のリスクも考慮する必要があります。
    • 一般的には、Node.js の標準 API または信頼できるサードパーティライブラリを使用する方が推奨されます。
  • 利点
    OS に標準搭載されているツールを利用するため、追加のライブラリが不要な場合があります。

キャッシュの利用 (間接的な代替)

  • 欠点
    キャッシュの有効期限や更新ポリシーを適切に管理する必要があります。
  • 利点
    DNS サーバーへの不要なリクエストを減らし、応答時間を短縮できます。
  • 実装
    自分でキャッシュの仕組みを実装することもできますし、キャッシュ機能を提供するライブラリを利用することもできます。
  • パフォーマンス
    キャッシュ戦略を検討することで、どの方法を使用する場合でも効率を向上させることができます。
  • OS の低レベルなツール
    特殊な状況を除き、標準 API またはサードパーティライブラリの使用を推奨します。
  • 高度な機能や特別な要件
    サードパーティ製の DNS ライブラリが適している場合があります。
  • レガシー環境やコールバックに慣れている場合
    dns.lookup() を検討できますが、Promise への移行を推奨します。
  • モダンな非同期処理
    async/await を使用する場合は dnsPromises.lookup() が最も自然で扱いやすい選択肢です。