【Node.js】dnsPromises.lookup()の詳しい解説とプログラミング例
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.ADDRCONFIG
やdns.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 レコードを持っているか確認してください。
- オプション family の確認
- 原因
指定されたホスト名は解決できたものの、要求されたアドレスタイプ(例えば、IPv4 のみを要求したが IPv6 のアドレスしか存在しないなど)のアドレスが見つからなかった場合に発生します。
タイムアウト
- トラブルシューティング
- ネットワーク状況の確認
ネットワークの遅延が大きい場合、タイムアウトのように見えることがあります。 - DNS サーバーの応答速度の確認
使用している DNS サーバーの応答速度が遅い可能性があります。別の DNS サーバーを試すことを検討してください。 - より高レベルな HTTP クライアントライブラリの検討
HTTP リクエストを行う場合は、タイムアウトなどの設定が可能なnode-fetch
やaxios
などのライブラリの使用を検討するのも良いでしょう。これらのライブラリは 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 アドレスが配列として返されます。配列の各要素は、address
と family
プロパティを持つオブジェクトです。
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.ADDRCONFIG
と dns.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()
アルゴリズムと同様の機能を提供し、モジュールの解決だけでなく、一般的な名前解決にも利用できる場合があります。
- dnspromise
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()
が最も自然で扱いやすい選択肢です。