NodeJS 速習チュートリアル

Node.js DNS モジュール

1. DNS モジュールの紹介

DNS (Domain Name System) モジュールは、Node.js において名前解決(Name Resolution)機能を提供します。
このモジュールは主に 2 つの API を提供しています。

  • コールバックベース API: 従来の Node.js スタイルであるコールバック関数を使用した API
  • プロミスベース API: dns.promises を介した、モダンな async/await 対応の API

主な機能は以下の通りです。

  • ドメイン名を IP アドレス(A/AAAA レコード)に解決する
  • 逆引き DNS ルックアップ(PTR レコード)の実行
  • 各種 DNS レコードタイプ(MX, TXT, SRV 等)のクエリ
  • 特定の設定を持つカスタム DNS リゾルバー の作成
  • プログラムによる DNS サーバー設定の構成

       注意: DNS モジュールには、オペレーティングシステム(OS)の機能を利用するモードと、ネットワーク経由で直接 DNS クエリを実行するモードの 2 つの異なる動作モードがあります。これは、アプリケーション内でのホスト名解決の挙動に影響を与えます。

2. DNS 操作の開始

以下は、DNS モジュールを使用してドメインの IP アドレスをルックアップする簡単な例です。

2.1 基本的な DNS ルックアップ

const dns = require('dns');

// ドメイン名をルックアップ
dns.lookup('example.com', (err, address, family) => {
  if (err) {
    console.error('ルックアップエラー:', err);
    return;
  }
  console.log(`IPアドレス: ${address}`);
  console.log(`IPバージョン: IPv${family}`);
});

2.2 インポートとセットアップ

Node.js アプリケーションで DNS モジュールを使用するには、コールバックベースまたはプロミスベースの API のいずれかをインポートします。

コールバックベース API

const dns = require('dns');

dns.lookup('example.com', (err, address, family) => {
  if (err) throw err;
  console.log(`解決済み: ${address} (IPv${family})`);
});

プロミスベース API (Node.js 10.0.0+)

// プロミス API をインポート
const { promises: dns } = require('dns');
// または: const dns = require('dns').promises;

// async/await を使用した例
async function lookupDomain(domain) {
  try {
    const address = await dns.lookup(domain);
    console.log(`解決済み: ${address.address} (IPv${address.family})`);
  } catch (err) {
    console.error('ルックアップ失敗:', err);
  }
}
lookupDomain('example.com');

       注意: モダンな async/await パターンと親和性が高く、エラーハンドリングが容易なため、新規のコードではプロミスベースの API が一般的に推奨されます。

3. 基本的な DNS ルックアップ

DNS モジュールは、ドメイン名と IP アドレスをルックアップするためのいくつかのメソッドを提供します。最も一般的な操作は以下の通りです。

  • dns.lookup(): オペレーティングシステムの機能を使用してホスト名を解決
  • dns.resolve*(): ネームサーバーに対して直接 DNS クエリを実行
  • dns.reverse(): 逆引き DNS ルックアップ(IP からホスト名)を実行

3.1 ドメイン名を IP アドレスに解決する

コールバックベース API

const dns = require('dns');

dns.lookup('www.example.com', (err, address, family) => {
  if (err) throw err;
  console.log('IPアドレス: %s', address);
  console.log('IPバージョン: IPv%s', family);
});

プロミスベース API

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

async function lookupExample() {
  try {
    const result = await dns.lookup('www.example.com');
    console.log('IPアドレス:', result.address);
    console.log('IPバージョン: IPv' + result.family);
  } catch (err) {
    console.error('ルックアップ失敗:', err);
  }
}

lookupExample();

       注意:dns.lookup() メソッドは名前解決に OS の機能を使用するため、必ずしもネットワーク通信が発生するとは限りません(hosts ファイルなどが参照される場合があります)。

3.2 ドメインのすべての IP アドレスを取得する

const dns = require('dns');

// すべての IPv4 アドレスを取得
dns.resolve4('www.google.com', (err, addresses) => {
  if (err) throw err;

  console.log('IPv4 アドレス一覧:');
  addresses.forEach(address => {
    console.log(` ${address}`);
  });

  // 最初に見つかった IP に対して逆引きを実行
  dns.reverse(addresses[0], (err, hostnames) => {
    if (err) throw err;

    console.log(`${addresses[0]} の逆引き結果:`);
    hostnames.forEach(hostname => {
      console.log(` ${hostname}`);
    });
  });
});

4. DNS レコードタイプ

DNS モジュールは、さまざまな リソースレコード タイプのルックアップをサポートしています。

メソッドレコードタイプ説明
resolve4()AIPv4 アドレス
resolve6()AAAAIPv6 アドレス
resolveMx()MXメール交換レコード
resolveTxt()TXTテキストレコード
resolveSrv()SRVサービスレコード
resolveNs()NSネームサーバーレコード
resolveCname()CNAME正規名レコード
resolveSoa()SOA権威開始レコード
resolvePtr()PTRポインターレコード
resolveAny()ANY利用可能なすべてのレコード

5. 高度な DNS 操作

5.1 カスタム DNS リゾルバー

DNS ルックアップをより細かく制御するために、特定の設定を持つカスタムリゾルバーを作成できます。

const dns = require('dns');

// 新しいリゾルバーを作成
const resolver = new dns.Resolver();

// カスタムサーバーを設定 (Google Public DNS)
resolver.setServers(['8.8.8.8', '8.8.4.4']);

// カスタムリゾルバーを使用
resolver.resolve4('www.example.com', (err, addresses) => {
  if (err) throw err;

  console.log('Google DNS を使用して解決されたアドレス:');
  addresses.forEach(addr => {
    console.log(` ${addr}`);
  });
});

// 設定されているサーバーを確認
console.log('現在のリゾルバーサーバー:', resolver.getServers());

5.2 ネットワークレベル vs OS レベルの解決

DNS モジュールには、名前解決に対して 2 つの異なるアプローチがあります。

関数実装ネットワークコール用途
dns.lookup()getaddrinfo() システムコールを使用直接のネットワークコールなし(通常)ローカル設定(hosts ファイル等)に従う
dns.resolve*(), dns.reverse()実際のネットワークリクエストを実行常に DNS サーバーに接続ローカル設定をバイパスし、直接クエリを行う

       警告: これらの違いにより、特にカスタムホスト設定がある環境では、dns.lookup()dns.resolve*() の結果が常に一致するとは限りません。

5.3 エラーハンドリング

堅牢な DNS 処理には適切なエラー管理が不可欠です。

const dns = require('dns');

function lookupWithErrorHandling(domain) {
  dns.lookup(domain, (err, address, family) => {
    if (err) {
      console.error(`${domain} の DNS ルックアップに失敗しました`);

      // 特定のエラーコードをチェック
      switch (err.code) {
        case 'ENOTFOUND':
          console.error(' ドメイン名が見つかりません');
          break;
        case 'ETIMEDOUT':
          console.error(' DNS ルックアップがタイムアウトしました');
          break;
        case 'ENODATA':
          console.error(' ドメインは存在しますが、リクエストされたタイプのデータがありません');
          break;
        case 'ESERVFAIL':
          console.error(' DNS サーバーが一般的な失敗を返しました');
          break;
        default:
          console.error(` エラーコード: ${err.code}`);
      }
      return;
    }

    console.log(`${domain} のルックアップ成功`);
    console.log(` IPアドレス: ${address}`);
  });
}

6. パフォーマンス最適化

DNS ルックアップはアプリケーションの ボトルネック になる可能性があります。以下の戦略で最適化を図りましょう。

6.1 キャッシュの実装

同じドメインに対する繰り返しのルックアップを避けるため、シンプルな DNS キャッシュを実装します。

const dns = require('dns');
const util = require('util');
const lookup = util.promisify(dns.lookup);
const dnsCache = new Map();

async function cachedLookup(domain) {
  if (dnsCache.has(domain)) {
    console.log('キャッシュヒット:', domain);
    return dnsCache.get(domain);
  }
  
  console.log('キャッシュミス:', domain);
  const result = await lookup(domain);
  dnsCache.set(domain, result);
  return result;
}

6.2 並列ルックアップ

Promise.all() を使用して、複数の DNS ルックアップを並列に実行します。

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

async function lookupMultiple(domains) {
  try {
    const lookups = domains.map(domain => dns.lookup(domain));
    const results = await Promise.all(lookups);
    return domains.map((domain, i) => ({
      domain,
      ...results[i]
    }));
  } catch (err) {
    console.error('1つ以上のルックアップに失敗しました:', err);
    throw err;
  }
}

7. DNS モジュール vs サードパーティライブラリ

機能Node.js DNS モジュールサードパーティライブラリ
インストール標準内蔵、依存関係なしインストールと管理が必要
機能セット基本的な DNS 操作より包括的な機能が多い
キャッシュ内蔵されていないキャッシュ機能を含むことが多い
高度な機能限定的DNSSEC, DoH, DoT サポートなど

人気のサードパーティライブラリ:

  • dns-packet: 低レベルの DNS パケットエンコード/デコード
  • native-dns: より完全な DNS 実装
  • dns2: プロミスをサポートするモダンな DNS ライブラリ

8. まとめ

Node.js の DNS モジュールは、ドメインネームシステムと対話するための不可欠な機能を提供します。

  • ドメイン名に対する IP アドレスの取得
  • 多様な DNS レコードタイプ(A, AAAA, MX, TXT 等)の解決
  • 逆引き DNS ルックアップの実行
  • カスタムサーバーを使用したリゾルバーの構成
  • コールバックとプロミスの両 API のサポート

ドメイン名を介してネットワークリソースにアクセスしたり、カスタムの名前解決ロジックを実装したり、ドメイン関連の情報を検証したりする必要があるアプリケーションにとって、このモジュールの理解は非常に重要です。