# 機能設計書 102-node:dns

## 概要

本ドキュメントは、BunにおけるNode.js互換の`node:dns`モジュールの機能設計を記述したものである。このモジュールは、ホスト名のIPアドレス解決やDNSレコードの問い合わせなど、ドメイン名システム（DNS）関連の機能を提供する。

### 本機能の処理概要

`node:dns`モジュールは、DNSルックアップ、逆引き、各種DNSレコードタイプの解決機能を提供する。Bunでは、c-aresライブラリ、libinfo（macOS）、libuv（Windows）、libcなど複数のバックエンドを使い分けて最適なパフォーマンスを実現している。

**業務上の目的・背景**：ネットワークアプリケーションにおいて、ホスト名からIPアドレスへの解決は不可欠な機能である。WebサーバーへのHTTPリクエスト、メールサーバーへの接続、マイクロサービス間の通信など、あらゆるネットワーク処理の基盤となる。Node.js互換のDNS APIを提供することで、既存のNode.jsアプリケーションの移行を容易にする。

**機能の利用シーン**：
- ホスト名からIPアドレスへの解決（lookup）
- 逆引きDNS（IPアドレスからホスト名）
- DNSレコードの問い合わせ（A、AAAA、MX、TXT、SRV等）
- カスタムDNSサーバーの設定
- 接続前のホスト名検証

**主要な処理内容**：
1. `lookup()`: ホスト名をIPアドレスに解決（OS標準のgetaddrinfoを使用）
2. `resolve*()`系関数: 特定のDNSレコードタイプを問い合わせ
3. `reverse()`: IPアドレスからホスト名を逆引き
4. `lookupService()`: アドレスとポートからサービス名を取得
5. `Resolver`クラス: カスタムDNS設定でのクエリ実行

**関連システム・外部連携**：
- c-ares: DNSリゾルバライブラリ
- libinfo: macOS固有の非同期DNS API
- libuv: Windows環境でのDNS解決
- システムDNSリゾルバ（/etc/resolv.conf等）

**権限による制御**：特になし（ネットワークアクセス権限が必要）

## 関連画面

本機能はバックエンドモジュールであり、直接的な関連画面は存在しない。

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | - |

## 機能種別

データ連携 / ネットワークユーティリティ

## 入力仕様

### 入力パラメータ

#### `dns.lookup(hostname, options, callback)`

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| hostname | string | Yes | 解決するホスト名 | 文字列であること |
| options | object \| number | No | オプション設定またはfamily | - |
| options.family | 0 \| 4 \| 6 \| 'IPv4' \| 'IPv6' | No | IPバージョン（0=両方） | 0,4,6のいずれか |
| options.hints | number | No | getaddrinfoフラグ | ADDRCONFIG,ALL,V4MAPPEDの組み合わせ |
| options.all | boolean | No | 全アドレスを返すか | boolean |
| options.order | 'ipv4first' \| 'ipv6first' \| 'verbatim' | No | 結果の並び順 | 指定値のみ |
| options.verbatim | boolean | No | 並び替えを行わない（非推奨） | boolean |
| callback | function | Yes | 結果を受け取るコールバック | 関数であること |

#### `dns.resolve(hostname, rrtype, callback)`

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| hostname | string | Yes | 解決するホスト名 | 文字列であること |
| rrtype | string | No | レコードタイプ（デフォルト: 'A'） | 文字列であること |
| callback | function | Yes | 結果を受け取るコールバック | 関数であること |

#### サポートされるレコードタイプ（rrtype）

| rrtype | 説明 | 戻り値の形式 |
|--------|------|-------------|
| A | IPv4アドレス | string[] |
| AAAA | IPv6アドレス | string[] |
| MX | メールエクスチェンジ | {priority, exchange}[] |
| TXT | テキストレコード | string[][] |
| SRV | サービスレコード | {priority, weight, port, name}[] |
| NS | ネームサーバー | string[] |
| CNAME | 正規名 | string[] |
| SOA | Start of Authority | {nsname, hostmaster, serial, ...} |
| PTR | ポインターレコード | string[] |
| CAA | 認証局認可 | {critical, tag, value}[] |
| NAPTR | 命名規則ポインター | {flags, service, regexp, ...}[] |
| ANY | 全レコード | mixed[] |

### 入力データソース

- 関数呼び出しの引数として直接渡される
- DNS設定: システム設定（/etc/resolv.conf等）またはsetServers()で設定

## 出力仕様

### 出力データ

#### `dns.lookup()`の戻り値

**all: false（デフォルト）の場合**
| 項目名 | 型 | 説明 |
|--------|-----|------|
| address | string | 解決されたIPアドレス |
| family | 4 \| 6 | IPバージョン |

**all: trueの場合**
| 項目名 | 型 | 説明 |
|--------|-----|------|
| 配列 | Array<{address, family}> | 全アドレスの配列 |

#### `dns.resolve()`の戻り値

レコードタイプに応じた形式で結果を返す（上記「サポートされるレコードタイプ」を参照）。

### 出力先

- コールバック関数の引数として返却
- `dns.promises`の場合はPromiseで返却

## 処理フロー

### 処理シーケンス

```
1. 入力パラメータの検証
   └─ hostname、options、callbackの型チェック
2. オプションの正規化
   └─ familyの文字列→数値変換
   └─ orderの設定（デフォルト: ipv4first）
3. IPアドレス直接入力のチェック
   └─ isIP()でチェック、該当すればそのまま返却
4. バックエンド選択
   └─ macOS: libinfo優先、フォールバックでlibc
   └─ Windows: libuv使用
   └─ Linux: libc使用
5. DNS問い合わせ実行
   └─ 非同期でDNSサーバーに問い合わせ
6. 結果の変換
   └─ エラーコードの変換（DNS_→E）
   └─ 結果の並び替え（order設定に基づく）
7. コールバック呼び出し
   └─ 成功時: (null, result)
   └─ 失敗時: (error)
```

### フローチャート

```mermaid
flowchart TD
    A[dns.lookup呼び出し] --> B{入力検証}
    B -->|不正| C[TypeError]
    B -->|正常| D{IPアドレス直接?}
    D -->|Yes| E[そのまま返却]
    D -->|No| F{バックエンド選択}
    F -->|macOS| G[libinfo/libc]
    F -->|Windows| H[libuv]
    F -->|Linux| I[libc]
    G --> J[非同期DNS問い合わせ]
    H --> J
    I --> J
    J --> K{成功?}
    K -->|Yes| L[結果変換・並び替え]
    K -->|No| M[エラーコード変換]
    L --> N[callback(null, result)]
    M --> O[callback(error)]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-201 | デフォルト結果順序 | IPv4アドレスを優先（ipv4first） | order未指定時 |
| BR-202 | localhost特殊処理 | .localや.localhostはc-aresを使わない | c-aresバックエンド時 |
| BR-203 | エラーコード変換 | DNS_プレフィックスを除去してNode互換に | 全エラー |
| BR-204 | 空ホスト名警告 | DEP0118として非推奨警告を出力 | hostname が falsy |
| BR-205 | IANAポート | DNSデフォルトポートは53 | setServers時 |

### 計算ロジック

**結果の並び替え**：
- `ipv4first`: family昇順ソート（IPv4=4が先）
- `ipv6first`: family降順ソート（IPv6=6が先）
- `verbatim`: ソートなし（DNSサーバーの返答順）

## データベース操作仕様

本機能はデータベース操作を行わない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| ENODATA | DNSException | レコードが見つからない | ホスト名を確認 |
| EFORMERR | DNSException | クエリ形式エラー | クエリを確認 |
| ESERVFAIL | DNSException | DNSサーバーエラー | サーバー状態を確認 |
| ENOTFOUND | DNSException | ドメインが存在しない | ホスト名を確認 |
| ENOTIMP | DNSException | 未実装の操作 | - |
| EREFUSED | DNSException | 接続拒否 | DNSサーバー設定を確認 |
| ETIMEOUT | DNSException | タイムアウト | ネットワーク状態を確認 |
| ECONNREFUSED | DNSException | 接続拒否 | DNSサーバーを確認 |
| ECANCELLED | DNSException | クエリがキャンセル | - |
| ERR_INVALID_ARG_TYPE | TypeError | 引数型が不正 | 正しい型を指定 |
| ERR_INVALID_ARG_VALUE | RangeError | 引数値が不正 | 有効な値を指定 |
| ERR_MISSING_ARGS | TypeError | 必須引数が不足 | 引数を追加 |
| ERR_INVALID_IP_ADDRESS | Error | IPアドレス形式が不正 | 正しい形式で指定 |

### リトライ仕様

- Resolverクラスで`tries`オプションを設定可能
- デフォルトはシステム設定に依存

## トランザクション仕様

本機能はトランザクションを使用しない。

## パフォーマンス要件

- DNS問い合わせは非同期で実行
- キャッシュ機能によりinflight中のリクエストは重複実行を避ける
- `timeout`オプションでタイムアウト時間を制御可能（-1: 無効、デフォルト: システム依存）

## セキュリティ考慮事項

- DNSキャッシュポイズニング対策はDNSサーバー側の責務
- setServers()で信頼できるDNSサーバーを指定することを推奨
- 機密情報をホスト名に含めない（DNSクエリは平文）

## 備考

- `dns.promises`でPromiseベースのAPIを提供
- `util.promisify()`を使用してコールバック関数をPromise化可能
- `Resolver`クラスでインスタンスごとに異なるDNS設定を使用可能

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

まず、DNS問い合わせに使用されるデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | dns.zig | `src/dns.zig` | GetAddrInfo構造体、Options、Family、SocketType |

**読解のコツ**:
- **5-16行目**: `GetAddrInfo`構造体でDNSクエリの基本情報を定義
- **42-118行目**: `Options`でfamily、socktype、protocol、backend、flagsを管理
- **120-172行目**: `Family`列挙型でIPv4/IPv6の指定方法を定義

#### Step 2: JavaScriptエントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | dns.ts | `src/js/node/dns.ts` | Node.js互換APIの実装 |

**主要処理フロー**:
1. **2行目**: `Bun.dns`からネイティブDNS APIを取得
2. **13-38行目**: `errorCodes`でDNSエラーコードを定義
3. **60-66行目**: `getServers`/`setServers`でDNSサーバー管理
4. **81-96行目**: `defaultResultOrder`で結果の並び順を管理
5. **233-253行目**: `translateLookupOptions`でオプションを正規化
6. **263-327行目**: `lookup`関数のメイン実装
7. **371-648行目**: `InternalResolver`クラスで各種resolve関数を実装
8. **719-948行目**: `promises`でPromiseベースAPIを提供

#### Step 3: Zigネイティブ実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | dns.zig | `src/bun.js/api/bun/dns.zig` | DNSリゾルバのネイティブ実装 |

**主要処理フロー**:
- **7-128行目**: `LibInfo`構造体でmacOS固有のlibinfoを使用した非同期DNS
- **130-165行目**: `LibC`構造体でPOSIX標準のgetaddrinfoを使用
- **167-243行目**: `LibUVBackend`でWindows環境でのlibuv使用
- **245-264行目**: `normalizeDNSName`でc-aresの制約に対応

#### Step 4: バックエンドの使い分けを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | dns.zig | `src/bun.js/api/bun/dns.zig` | バックエンド選択ロジック |

**読解のコツ**:
- macOSでは`libinfo`を優先使用（`getaddrinfo_async_start`）
- Windowsでは`libuv`を使用（`uv_getaddrinfo`）
- Linuxでは`libc`のgetaddrinfoを使用
- c-aresは`.local`や`.localhost`で問題があるためシステムリゾルバにフォールバック

### プログラム呼び出し階層図

```
JavaScript (dns.ts)
    │
    ├─ dns.lookup(hostname, options, callback)
    │      └─ Bun.dns.lookup(hostname, options)
    │             │
    │             ├─ [macOS] LibInfo.lookup()
    │             │      └─ getaddrinfo_async_start()
    │             │
    │             ├─ [Windows] LibUVBackend.lookup()
    │             │      └─ uv_getaddrinfo()
    │             │
    │             └─ [Linux] LibC.lookup()
    │                    └─ getaddrinfo()
    │
    ├─ dns.resolve(hostname, rrtype, callback)
    │      └─ Resolver.resolve()
    │             └─ c-ares / system resolver
    │
    └─ dns.promises.lookup(hostname, options)
           └─ 同上（Promiseラッパー）
```

### データフロー図

```
[入力]                    [処理]                           [出力]

hostname, options ──────▶ 入力検証 ───────────────────────▶ Error (検証失敗)
                              │
                              ▼
                         IPアドレス直接判定 ─────(Yes)───▶ {address, family}
                              │ (No)
                              ▼
                         バックエンド選択
                              │
                              ├─ macOS: libinfo
                              ├─ Windows: libuv
                              └─ Linux: libc
                              │
                              ▼
                         DNS問い合わせ ───────────────────▶ DNSException (失敗)
                              │ (成功)
                              ▼
                         結果変換・並び替え
                              │
                              ▼
                         callback(null, result) ─────────▶ {address, family} | Array
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| dns.ts | `src/js/node/dns.ts` | ソース | Node.js互換DNSモジュールの実装 |
| dns.promises.ts | `src/js/node/dns.promises.ts` | ソース | PromiseベースAPIのエクスポート |
| dns.zig | `src/dns.zig` | ソース | DNS関連のデータ構造定義 |
| dns.zig | `src/bun.js/api/bun/dns.zig` | ソース | DNSリゾルバのZig実装 |
| isIP.ts | `src/js/internal/net/isIP.ts` | ソース | IPアドレス判定ユーティリティ |
| validators.ts | `src/js/internal/validators.ts` | ソース | 入力検証関数群 |
