Node.jsのdns.resolvePtr()徹底解説:PTRレコード解決の基本と応用

2025-05-27

PTRレコードとは?

PTR (Pointer) レコードは、IPアドレスからホスト名(ドメイン名)への逆引き変換を行うためのレコードです。通常、ウェブサイトにアクセスする際は、ドメイン名(例: example.com)からIPアドレス(例: 192.0.2.1)を解決する「正引き」が行われます。一方、PTRレコードは、この逆の操作、つまりIPアドレスからそのIPアドレスに対応するホスト名を調べたいときに使われます。

dns.resolvePtr()の機能

dns.resolvePtr(hostname, callback)は、指定されたhostname(この場合は通常、IPアドレスを逆引き形式にしたもの)に対応するPTRレコードをDNSサーバーに問い合わせ、その結果を非同期で返します。

引数

  • callback: 解決が完了したときに呼び出されるコールバック関数です。このコールバック関数は、callback(err, hostnames) の形式で呼び出されます。
    • err: エラーが発生した場合に、エラーオブジェクトが渡されます。
    • hostnames: 解決されたホスト名の配列が渡されます。PTRレコードは複数のホスト名を指す可能性があるため、配列で返されます。
  • hostname: 逆引きしたいIPアドレスを逆引き形式で指定します。例えば、192.0.2.1を逆引きしたい場合は、"1.2.0.192.in-addr.arpa" のように指定します。IPv6アドレスの場合は、対応する逆引きドメイン形式になります。

使用例

const dns = require('dns');

// 例: 8.8.8.8 のPTRレコードを解決
// 8.8.8.8 の逆引き形式は 8.8.8.8.in-addr.arpa
dns.resolvePtr('8.8.8.8.in-addr.arpa', (err, hostnames) => {
  if (err) {
    console.error('PTRレコードの解決中にエラーが発生しました:', err);
    return;
  }
  console.log('8.8.8.8 に関連付けられたホスト名:', hostnames);
});

// 通常は、dns.reverse() を使う方が便利です。
// dns.reverse() は内部的にPTRレコードの解決を行います。
dns.reverse('8.8.8.8', (err, hostnames) => {
    if (err) {
        console.error('IPアドレスの逆引き中にエラーが発生しました:', err);
        return;
    }
    console.log('8.8.8.8 の逆引き結果 (dns.reverse):', hostnames);
});

dns.resolvePtr()は、より低レベルなDNS操作を行いたい場合に有用ですが、ほとんどのユースケースでは、Node.jsのdns.reverse(ip, callback)関数を使う方が便利です。dns.reverse()は、IPアドレスを直接渡し、内部で自動的にPTRレコードの問い合わせを行ってくれます。

dns.resolvePtr()は、特定のDNSレコードタイプ(PTRレコード)を明示的に解決したい場合や、より詳細な制御が必要な場合に利用されます。例えば、DNSサーバーの応答を直接解析する必要があるような高度なシナリオで役立つことがあります。



ENOTFOUND / EADDRINFO (No such host)

エラーの内容
指定されたIPアドレスのPTRレコードが見つからない場合に発生します。これは、DNSサーバーがそのIPアドレスに対応するホスト名を解決できなかったことを意味します。

原因

  • ネットワーク接続の問題
    ネットワークが切断されているか、DNSサーバーへの通信がブロックされている可能性があります。
  • DNSサーバーの問題
    問い合わせ先のDNSサーバーがダウンしている、応答が遅い、または正しい情報を保持していない場合があります。
  • PTRレコードが存在しない
    そのIPアドレスに対してPTRレコードが設定されていない可能性があります。特に、個人利用のIPアドレスや、一部のVPSプロバイダーでは、デフォルトでPTRレコードが設定されていないことがあります。
  • 不正なIPアドレスの逆引き形式
    dns.resolvePtr() の引数には、IPアドレスを逆引き形式(例: 192.0.2.1 -> "1.2.0.192.in-addr.arpa")で正確に指定する必要があります。間違った形式を使用していると、DNSサーバーは正しい情報を返せません。

トラブルシューティング

  • ネットワーク接続の確認
    サーバーがインターネットに接続されているか、ファイアウォールでDNSポート(UDP 53番)がブロックされていないか確認します。
  • DNSサーバーの確認
    ローカルのDNS設定(/etc/resolv.confなど)や、Node.jsアプリケーションが使用しているDNSサーバーが正しく設定されているか確認します。必要であれば、Google Public DNS (8.8.8.88.8.4.4) などの信頼できるDNSサーバーを一時的に使用してみるのも有効です。
    const dns = require('dns');
    dns.setServers(['8.8.8.8', '8.8.4.4']); // 一時的にDNSサーバーを変更
    // ... dns.resolvePtr() の呼び出し
    
  • オンラインのDNSルックアップツールで確認
    pingnslookup (Windows/Linux) 、 dig (Linux/macOS) などのコマンドラインツールや、オンラインのDNSルックアップツールを使って、問題のIPアドレスのPTRレコードを手動で確認してみてください。
    • 例: dig -x 8.8.8.8 または nslookup 8.8.8.8
  • dns.reverse()の使用を検討
    ほとんどの場合、dns.reverse('IPアドレス', callback) を使用する方が便利です。dns.reverse() は内部で自動的に逆引き形式に変換してくれます。
  • IPアドレスの逆引き形式の確認
    引数に渡すIPアドレスが正しい逆引き形式になっているか再確認してください。特にIPv6アドレスの場合は、より複雑な形式になります。

ETIMEOUT (Operation timed out)

エラーの内容
DNSサーバーからの応答がタイムアウトした場合に発生します。

原因

  • ファイアウォールによるブロック
    ファイアウォールがDNSクエリをブロックしている可能性があります。
  • ネットワーク遅延
    ネットワークの状態が悪く、DNSリクエストが時間内に届かない、または応答が時間内に返ってこない場合があります。
  • DNSサーバーがダウンしている、または非常に遅い
    問い合わせ先のDNSサーバーが応答していないか、応答が遅すぎてNode.jsのタイムアウト設定を超えている可能性があります。

トラブルシューティング

  • Node.jsのタイムアウト設定の調整(直接は難しいが考慮)
    dns.resolvePtr() 自体には直接タイムアウトを設定するオプションはありませんが、DNS解決はシステムのネットワークスタックに依存するため、OSレベルのDNSタイムアウト設定が影響する可能性があります。
  • ネットワークの帯域幅と遅延の確認
    ネットワークの状態を監視し、ボトルネックがないか確認します。
  • DNSサーバーの変更
    上記のsetServers()を使用して、別の信頼できるDNSサーバーを試してみます。
  • DNSサーバーの状態確認
    ping コマンドでDNSサーバーに到達できるか確認し、応答時間を調べます。

EFORMERR (Malformed DNS query)

エラーの内容
送信されたDNSクエリの形式が不正である場合に発生します。

原因

  • 不正なhostname引数
    dns.resolvePtr() に渡されるhostname引数が、DNSクエリとして解釈できない形式である場合に発生します。これは、IPアドレスを逆引き形式に変換する際に間違いがあった場合によく起こります。

トラブルシューティング

  • hostname引数の厳密な確認
    引数に渡す文字列が、RFCに準拠した逆引きドメイン名(例: x.y.z.w.in-addr.arpa)であることを徹底的に確認してください。タイポや余分な文字、欠落した文字がないか注意深くチェックします。

ESERVFAIL (Server failure)

エラーの内容
DNSサーバーがクエリを処理できなかったが、具体的なエラー理由を示さなかった場合に発生します。

原因

  • DNSゾーンの設定ミス
    該当するDNSゾーンに設定ミスがある可能性も考えられます。
  • DNSサーバーの内部エラー
    問い合わせ先のDNSサーバーが内部的な問題でクエリを処理できなかった可能性があります。

トラブルシューティング

  • 時間をおいて再試行
    DNSサーバーの一時的な問題である可能性もあるため、少し時間をおいてから再試行します。
  • 別のDNSサーバーを試す
    別のDNSサーバー(例: Google Public DNS)に切り替えてみて、問題が解決するかどうかを確認します。

EBADNAME (Bad domain name)

エラーの内容
渡されたドメイン名(この場合は逆引き形式のIPアドレス)が無効な場合に発生します。

原因

  • 不正な文字の使用
    ドメイン名に使用できない文字が含まれている場合。
  • 入力値の検証
    hostname引数がDNSドメイン名の命名規則に準拠しているか確認します。
  • dns.reverse()の利用
    繰り返しになりますが、ほとんどのIPアドレスの逆引きシナリオでは、dns.resolvePtr()よりも dns.reverse() の方がシンプルで、エラーのリスクも低くなります。特殊な要件がない限り、dns.reverse() の使用を検討してください。
  • システムレベルのDNS設定の確認
    Node.jsのdnsモジュールは、通常、オペレーティングシステム(OS)のDNS設定(/etc/resolv.conf や Windowsのネットワークアダプター設定など)を使用します。OSレベルでDNS解決が正常に行われているかを確認することが重要です。
  • ログ出力
    console.log を使って、dns.resolvePtr() に渡す引数や、コールバックで返されるエラー情報を詳細にログに出力することで、デバッグの手がかりを得られます。


dns.resolvePtr() は、IPアドレスからそのIPアドレスに関連付けられたホスト名(ドメイン名)を逆引きする際に使用する、PTRレコードを解決するための関数です。

基本的な使い方

最も基本的な使い方は、特定のIPアドレスのPTRレコードを解決するものです。IPアドレスを逆引き形式(in-addr.arpa ドメイン)に変換して渡す必要があります。

const dns = require('dns');

// 解決したいIPアドレス
const ipAddress = '8.8.8.8'; // Google Public DNS

// IPアドレスをPTRレコードのクエリ形式に変換
// IPv4の場合: x.y.z.w -> w.z.y.x.in-addr.arpa
const ptrQuery = ipAddress.split('.').reverse().join('.') + '.in-addr.arpa';

console.log(`PTRレコードクエリ: ${ptrQuery}`);

dns.resolvePtr(ptrQuery, (err, hostnames) => {
  if (err) {
    console.error(`エラーが発生しました: ${err.message}`);
    // 一般的なエラーコードのハンドリング
    if (err.code === 'ENOTFOUND') {
      console.error('このIPアドレスのPTRレコードは見つかりませんでした。');
    } else if (err.code === 'ETIMEOUT') {
      console.error('DNSサーバーからの応答がタイムアウトしました。');
    } else {
      console.error('その他のDNS解決エラーです。');
    }
    return;
  }

  if (hostnames && hostnames.length > 0) {
    console.log(`IPアドレス ${ipAddress} に関連付けられたホスト名:`);
    hostnames.forEach(hostname => {
      console.log(`- ${hostname}`);
    });
  } else {
    console.log(`IPアドレス ${ipAddress} に関連付けられたホスト名はありませんでした。`);
  }
});

実行結果例

PTRレコードクエリ: 8.8.8.8.in-addr.arpa
IPアドレス 8.8.8.8 に関連付けられたホスト名:
- dns.google

IPv6アドレスのPTRレコード解決

IPv6アドレスのPTRレコードも同様に解決できますが、逆引き形式のルールが異なります。IPv6は16進数で表現され、各16進数文字が逆順で .ip6.arpa ドメインにマッピングされます。

const dns = require('dns');

// 解決したいIPv6アドレス (例: Google Public DNS IPv6)
const ipv6Address = '2001:4860:4860::8888';

// IPv6アドレスをPTRレコードのクエリ形式に変換
// 各16進数文字を逆順にし、ドットで区切り、.ip6.arpa を付与
function getIPv6PtrQuery(ipv6) {
  // ':' を削除し、必要に応じて0を埋めて32文字にする
  const normalized = ipv6.replace(/:/g, '')
                        .padStart(32, '0'); // 足りない場合は先頭に0を埋める(例: :: が含まれる場合)

  // 各文字を逆順にし、ドットで区切る
  return normalized.split('').reverse().join('.') + '.ip6.arpa';
}

const ptrQueryIPv6 = getIPv6PtrQuery(ipv6Address);

console.log(`IPv6 PTRレコードクエリ: ${ptrQueryIPv6}`);

dns.resolvePtr(ptrQueryIPv6, (err, hostnames) => {
  if (err) {
    console.error(`IPv6 PTR解決エラー: ${err.message}`);
    return;
  }

  if (hostnames && hostnames.length > 0) {
    console.log(`IPv6アドレス ${ipv6Address} に関連付けられたホスト名:`);
    hostnames.forEach(hostname => {
      console.log(`- ${hostname}`);
    });
  } else {
    console.log(`IPv6アドレス ${ipv6Address} に関連付けられたホスト名はありませんでした。`);
  }
});

注意
IPv6の逆引き形式変換は手動で行うと間違いやすいので、ライブラリなどを使用することをお勧めします。ただし、dns.resolvePtr() の説明のために手動で変換する例を挙げています。

dns.reverse() との比較

多くのユースケースでは、dns.resolvePtr() を直接使うよりも、Node.js が提供するより高レベルな関数である dns.reverse() を使う方が簡単で安全です。dns.reverse() は内部で自動的にIPアドレスをPTRクエリ形式に変換してくれます。

const dns = require('dns');

const ipAddress = '8.8.8.8';

// dns.reverse() を使用した例
dns.reverse(ipAddress, (err, hostnames) => {
  if (err) {
    console.error(`dns.reverse() エラー: ${err.message}`);
    return;
  }

  if (hostnames && hostnames.length > 0) {
    console.log(`dns.reverse() による ${ipAddress} の逆引き結果:`);
    hostnames.forEach(hostname => {
      console.log(`- ${hostname}`);
    });
  } else {
    console.log(`dns.reverse() で ${ipAddress} のホスト名が見つかりませんでした。`);
  }
});

const ipv6Address = '2001:4860:4860::8888';

dns.reverse(ipv6Address, (err, hostnames) => {
  if (err) {
    console.error(`dns.reverse() IPv6 エラー: ${err.message}`);
    return;
  }

  if (hostnames && hostnames.length > 0) {
    console.log(`dns.reverse() による ${ipv6Address} の逆引き結果:`);
    hostnames.forEach(hostname => {
      console.log(`- ${hostname}`);
    });
  } else {
    console.log(`dns.reverse() で ${ipv6Address} のホスト名が見つかりませんでした。`);
  }
});

実行結果例

dns.reverse() による 8.8.8.8 の逆引き結果:
- dns.google
dns.reverse() による 2001:4860:4860::8888 の逆引き結果:
- dns.google

見ての通り、dns.reverse() を使う方がコードがシンプルで、IPv4とIPv6の両方に対応できるため、通常はこちらの使用が推奨されます。

Node.js v10.0.0 以降では、dns.promises を使用してPromiseベースのAPIを利用できます。これにより、async/await 構文を使って非同期処理をより読みやすく記述できます。

const dns = require('dns').promises; // promises APIをインポート

async function resolvePtrPromise(ipAddress) {
  try {
    // IPアドレスをPTRレコードのクエリ形式に変換
    const ptrQuery = ipAddress.split('.').reverse().join('.') + '.in-addr.arpa';
    console.log(`PromiseベースのPTRクエリ: ${ptrQuery}`);

    const hostnames = await dns.resolvePtr(ptrQuery);

    if (hostnames && hostnames.length > 0) {
      console.log(`Promise: IPアドレス ${ipAddress} に関連付けられたホスト名:`);
      hostnames.forEach(hostname => {
        console.log(`- ${hostname}`);
      });
    } else {
      console.log(`Promise: IPアドレス ${ipAddress} に関連付けられたホスト名はありませんでした。`);
    }
  } catch (err) {
    console.error(`PromiseベースのPTR解決エラー: ${err.message}`);
    if (err.code === 'ENOTFOUND') {
      console.error('このIPアドレスのPTRレコードは見つかりませんでした。');
    }
  }
}

// 例としてGoogle Public DNSを使用
resolvePtrPromise('8.8.8.8');

// dns.promises.reverse() の例
async function reversePromise(ipAddress) {
  try {
    const hostnames = await dns.reverse(ipAddress);
    if (hostnames && hostnames.length > 0) {
      console.log(`Promise: dns.reverse() による ${ipAddress} の逆引き結果:`);
      hostnames.forEach(hostname => {
        console.log(`- ${hostname}`);
      });
    } else {
      console.log(`Promise: dns.reverse() で ${ipAddress} のホスト名が見つかりませんでした。`);
    }
  } catch (err) {
    console.error(`Promise: dns.reverse() エラー: ${err.message}`);
  }
}

reversePromise('1.1.1.1'); // Cloudflare DNS


最も一般的で推奨される代替方法は、Node.jsのDNSモジュールに組み込まれている dns.reverse() 関数を使用することです。この関数は、IPアドレス(IPv4またはIPv6)を直接引数として受け取り、内部で適切なPTRレコードのクエリ形式に変換してDNS解決を行います。

メリット

  • Promise対応
    dns.promises.reverse() を使用すれば、async/await で非同期処理をよりクリーンに記述できます。
  • 使いやすさ
    ほとんどのIPアドレス逆引きのユースケースに適しています。
  • シンプルさ
    IPアドレスを直接渡すだけでよく、in-addr.arpaip6.arpa 形式への手動変換が不要です。
const dns = require('dns');

// コールバック形式
dns.reverse('8.8.8.8', (err, hostnames) => {
  if (err) {
    console.error('dns.reverse() エラー:', err);
    return;
  }
  console.log('dns.reverse() (コールバック) の結果:', hostnames); // 例: [ 'dns.google' ]
});

// Promise形式 (async/await)
const dnsPromises = require('dns').promises;

async function lookupWithReversePromise(ip) {
  try {
    const hostnames = await dnsPromises.reverse(ip);
    console.log(`dns.reverse() (Promise) の結果 for ${ip}:`, hostnames);
  } catch (error) {
    console.error(`dns.reverse() (Promise) エラー for ${ip}:`, error);
  }
}

lookupWithReversePromise('1.1.1.1'); // Cloudflare DNS
lookupWithReversePromise('2001:4860:4860::8888'); // Google Public DNS IPv6