# 機能設計書 121-アドレス解決

## 概要

本ドキュメントは、libuvが提供するDNSアドレス解決機能 `uv_getaddrinfo()` の設計仕様を記述する。ホスト名やサービス名からネットワークアドレス情報を非同期で取得するための機能である。

### 本機能の処理概要

**業務上の目的・背景**：ネットワークプログラミングにおいて、ホスト名（例："example.com"）からIPアドレスを取得するDNS正引きは不可欠な処理である。標準Cライブラリの `getaddrinfo()` はブロッキング関数であるため、イベント駆動型プログラミングモデルにおいてはメインスレッドをブロックしてしまう。libuvはこの処理をスレッドプールに委譲することで、非同期DNS解決を実現している。

**機能の利用シーン**：TCPやUDPソケットの接続先アドレスを動的に解決する場合に使用される。例えば、HTTPクライアントがドメイン名でサーバーに接続する際に、事前にアドレス解決を行う。また、コールバックをNULLにすることで同期的な呼び出しも可能である。

**主要な処理内容**：
1. 入力パラメータ（hostname, service, hints）のバリデーションとIDNA変換
2. リクエスト構造体の初期化とメモリ確保（hostname, service, hintsを連続メモリ領域にコピー）
3. コールバックが指定された場合はスレッドプール（SLOW_IO種別）にワークを投入して非同期実行
4. コールバックがNULLの場合はその場で同期的にgetaddrinfo()を呼び出し、結果を返却
5. ワーカースレッド上でシステムの `getaddrinfo()` を呼び出し、結果をリクエスト構造体に格納
6. 完了時にコールバックを通じて結果（addrinfo構造体のリンクリスト）をユーザーに返却

**関連システム・外部連携**：OSのDNSリゾルバ（`getaddrinfo()` システムコール）を内部的に使用する。IDNA（国際化ドメイン名）変換のために `uv__idna_toascii()` を呼び出す。スレッドプールシステム（`uv__work_submit()`）と連携する。

**権限による制御**：特段の権限制御はないが、DNS解決はOSのネットワーク設定（/etc/resolv.conf等）に依存する。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 26 | DNS | 主機能 | uv_getaddrinfo()による非同期DNS正引きのAPI定義 |

## 機能種別

データ連携（非同期DNS正引き処理）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| loop | uv_loop_t* | Yes（非同期時） | イベントループ。同期呼び出し時はcb=NULLならNULL可 | cb != NULLの場合はNULL不可 |
| req | uv_getaddrinfo_t* | Yes | リクエスト構造体 | NULL不可 |
| getaddrinfo_cb | uv_getaddrinfo_cb | No | 完了コールバック。NULLの場合は同期実行 | - |
| node | const char* | No（serviceとどちらか必須） | ホスト名またはIPアドレス文字列 | nodeとserviceの両方がNULLの場合はUV_EINVAL |
| service | const char* | No（nodeとどちらか必須） | サービス名またはポート番号文字列 | nodeとserviceの両方がNULLの場合はUV_EINVAL |
| hints | const struct addrinfo* | No | アドレス解決のヒント情報（ファミリ、ソケットタイプ等） | - |

### 入力データソース

ユーザーからの関数呼び出しパラメータ。ホスト名はIDNA変換（国際化ドメイン名のASCII変換）が適用される。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 戻り値 | int | 非同期: 0（成功）/ エラーコード、同期: retcode値 |
| req->addrinfo | struct addrinfo* | 解決されたアドレス情報のリンクリスト |
| req->retcode | int | 処理結果コード（0:成功, UV_EAI_*:エラー） |

### 出力先

コールバック関数への引数として返却。同期呼び出し時は関数の戻り値として返却。

## 処理フロー

### 処理シーケンス

```
1. 入力パラメータのバリデーション
   └─ req==NULL、hostname/service両方NULL、loop==NULL&&cb!=NULLの場合UV_EINVAL
2. IDNA変換（hostname指定時）
   └─ uv__idna_toascii()でホスト名をASCII互換エンコーディングに変換
3. メモリ確保
   └─ hostname_len + service_len + hints_lenの連続領域をuv__malloc()で確保
4. リクエスト初期化
   └─ UV_REQ_INIT(req, UV_GETADDRINFO)でリクエスト型を設定
   └─ ループへのリクエスト登録（loop != NULLの場合）
5. データコピー
   └─ hints → service → hostname の順でバッファにコピー（解放順序との整合性のため）
6. ワーク投入またはインライン実行
   └─ cb != NULL: uv__work_submit()でSLOW_IOとしてスレッドプールに投入
   └─ cb == NULL: uv__getaddrinfo_work() + uv__getaddrinfo_done()を直接呼び出し
7. ワーカー処理（非同期の場合）
   └─ getaddrinfo()システムコールを呼び出し、結果をreq->addrinfo/retcodeに格納
8. 完了処理
   └─ ループからリクエスト登録解除
   └─ 確保したメモリ（hints/service/hostname）を解放
   └─ キャンセル時はretcodeをUV_EAI_CANCELEDに設定
   └─ コールバックを呼び出し
```

### フローチャート

```mermaid
flowchart TD
    A[uv_getaddrinfo呼び出し] --> B{パラメータ検証}
    B -->|不正| Z[UV_EINVAL返却]
    B -->|正常| C[IDNA変換]
    C --> D[メモリ確保]
    D -->|失敗| Y[UV_ENOMEM返却]
    D -->|成功| E[リクエスト初期化・データコピー]
    E --> F{cb != NULL?}
    F -->|Yes| G[uv__work_submit SLOW_IO]
    G --> H[ワーカースレッドでgetaddrinfo実行]
    H --> I[uv__getaddrinfo_done]
    I --> J[コールバック呼び出し]
    F -->|No| K[同期的にgetaddrinfo実行]
    K --> L[uv__getaddrinfo_done]
    L --> M[retcode返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-121-1 | メモリコピー順序 | hints, service, hostnameの順にバッファにコピーする。これはuv__getaddrinfo_done()での解放順序と対応している | 常時 |
| BR-121-2 | SLOW_IO分類 | DNS解決はSLOW_IOとしてスレッドプールに投入される（通常のCPUワークではない） | 非同期実行時 |
| BR-121-3 | IDNA変換 | 国際化ドメイン名はASCII互換エンコーディング（Punycode）に変換される | z/OS以外のプラットフォーム |
| BR-121-4 | 同期モード | cbにNULLを渡すと同期実行となり、結果が戻り値で返される | cb == NULL |

### 計算ロジック

メモリ確保サイズの算出:
```
buf_size = hostname_len + service_len + hints_len
hostname_len = hostname ? strlen(hostname) + 1 : 0
service_len = service ? strlen(service) + 1 : 0
hints_len = hints ? sizeof(struct addrinfo) : 0
```

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

### 操作別データベース影響一覧

本機能にデータベース操作はない。OSのDNSリゾルバを使用する。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| UV_EINVAL | パラメータエラー | req==NULL、hostname/service両方NULL、loop==NULL&&cb!=NULL | 入力パラメータを修正 |
| UV_ENOMEM | メモリ不足 | uv__malloc()失敗 | メモリ使用量を確認 |
| UV_EAI_AGAIN | 一時的失敗 | DNSサーバー一時エラー | リトライ |
| UV_EAI_BADFLAGS | 不正フラグ | hintsのai_flagsが不正 | hints設定を修正 |
| UV_EAI_FAIL | 永続的失敗 | DNSサーバー永続エラー | DNS設定を確認 |
| UV_EAI_FAMILY | ファミリ不正 | hintsのai_familyが不正 | ファミリ指定を修正 |
| UV_EAI_MEMORY | メモリ不足 | getaddrinfo内部メモリ不足 | メモリ使用量を確認 |
| UV_EAI_NODATA | データなし | 有効なアドレスが見つからない | ホスト名を確認 |
| UV_EAI_NONAME | 名前不明 | ホスト名が解決できない | ホスト名を確認 |
| UV_EAI_CANCELED | キャンセル | uv_cancel()によりキャンセルされた | 必要に応じて再実行 |

### リトライ仕様

UV_EAI_AGAIN発生時はユーザーが自身でリトライを実装する必要がある。libuv側に自動リトライ機構はない。

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

該当なし。

## パフォーマンス要件

DNS解決はスレッドプール上でSLOW_IO種別として実行される。スレッドプールのスレッド数の半分以上がSLOW_IOに使用されることを防ぐスロットリング機構が適用される（`slow_work_thread_threshold() = (nthreads + 1) / 2`）。

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

- IDNA変換により国際化ドメイン名のホモグラフ攻撃対策が間接的に行われる（ASCII変換後のドメイン名で解決が行われる）
- 入力文字列はバッファにコピーされるため、呼び出し元が解放しても安全
- DNS応答の真正性は本機能の範囲外（DNSSECはOSレベルの設定に依存）

## 備考

- z/OS環境ではIDNA変換が無効化されている（EBCDIC環境のため）
- `uv_freeaddrinfo()` で結果のアドレス情報を解放する必要がある（No.122参照）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | uv.h | `include/uv.h` | **993-999行目**: `uv_getaddrinfo_s`構造体の定義。UV_REQ_FIELDS（リクエスト基底フィールド）、loopポインタ、プラットフォーム固有のプライベートフィールドを持つ |
| 1-2 | uv.h | `include/uv.h` | **1002-1008行目**: `uv_getaddrinfo()`と`uv_freeaddrinfo()`の関数シグネチャ |

**読解のコツ**: `UV_GETADDRINFO_PRIVATE_FIELDS`はプラットフォーム別ヘッダ（unix/internal.h等）で定義され、hostname, service, hints, addrinfo, retcode, work_req等のフィールドを含む。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | getaddrinfo.c | `src/unix/getaddrinfo.c` | **138-222行目**: `uv_getaddrinfo()`関数本体。入力バリデーション、IDNA変換、メモリ確保、リクエスト初期化、非同期/同期分岐の全体フローを理解する |

**主要処理フロー**:
1. **152-155行目**: パラメータバリデーション（req==NULL、hostname/service両方NULL）
2. **163-171行目**: IDNA変換（`uv__idna_toascii()`呼び出し）
3. **174-180行目**: メモリ確保サイズ計算と`uv__malloc()`
4. **182-192行目**: リクエスト初期化（`UV_REQ_INIT`、フィールド初期化）
5. **197-208行目**: データコピー（hints → service → hostname の順）
6. **210-221行目**: 非同期/同期の分岐処理

#### Step 3: ワーカー処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | getaddrinfo.c | `src/unix/getaddrinfo.c` | **96-103行目**: `uv__getaddrinfo_work()`。スレッドプールのワーカースレッドで実行される実体処理。`getaddrinfo()` システムコールを呼び出す |
| 3-2 | getaddrinfo.c | `src/unix/getaddrinfo.c` | **106-135行目**: `uv__getaddrinfo_done()`。完了時のクリーンアップとコールバック呼び出し |

**主要処理フロー**:
- **101行目**: `getaddrinfo(req->hostname, req->service, req->hints, &req->addrinfo)` でシステムDNS解決
- **102行目**: EAIエラーコードをlibuvエラーコードに変換
- **111-122行目**: メモリ解放（hints→service→hostnameの逆順で判定。最初に非NULLのフィールドのバッファポインタを解放）
- **128-131行目**: キャンセル時のretcode設定

#### Step 4: エラー変換を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | getaddrinfo.c | `src/unix/getaddrinfo.c` | **39-93行目**: `uv__getaddrinfo_translate_error()`。EAI_*エラーコードをUV_EAI_*に変換するマッピング関数 |

#### Step 5: スレッドプール連携を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | threadpool.c | `src/threadpool.c` | **276-286行目**: `uv__work_submit()`。ワークをスレッドプールに投入する共通関数 |
| 5-2 | threadpool.c | `src/threadpool.c` | **143-161行目**: `post()`。SLOW_IO種別のワークは専用キューに入れられ、スロットリングが適用される |

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

```
uv_getaddrinfo() [src/unix/getaddrinfo.c:138]
    |
    +-- uv__idna_toascii() [src/idna.c]
    |       └── 国際化ドメイン名のASCII変換
    |
    +-- uv__malloc() [src/uv-common.c]
    |       └── メモリ確保
    |
    +-- uv__work_submit() [src/threadpool.c:276]  ※非同期時
    |       +-- uv_once(&once, init_once)
    |       └── post(&w->wq, UV__WORK_SLOW_IO)
    |               └── uv_cond_signal() → ワーカースレッド起動
    |
    +-- uv__getaddrinfo_work() [src/unix/getaddrinfo.c:96]
    |       +-- getaddrinfo() [libc]
    |       └── uv__getaddrinfo_translate_error()
    |
    └-- uv__getaddrinfo_done() [src/unix/getaddrinfo.c:106]
            +-- uv__req_unregister()
            +-- uv__free() メモリ解放
            └── req->cb() コールバック呼び出し
```

### データフロー図

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

hostname ──────┐
               ├──▶ uv_getaddrinfo() ──▶ [IDNA変換]
service ───────┤                              │
               │                         [メモリ確保・コピー]
hints ─────────┘                              │
                                     ┌────────┴────────┐
                                     │                 │
                              [非同期]              [同期]
                                     │                 │
                           uv__work_submit()    直接実行
                                     │                 │
                              [ワーカースレッド]         │
                                     │                 │
                              getaddrinfo()     getaddrinfo()
                                     │                 │
                              [メインスレッド]          │
                                     │                 │
                              uv__getaddrinfo_done ────┘
                                     │
                              コールバック ──▶ req->addrinfo (struct addrinfo*)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| uv.h | `include/uv.h` | ヘッダ | API定義（uv_getaddrinfo_t構造体、関数宣言） |
| getaddrinfo.c | `src/unix/getaddrinfo.c` | ソース | Unix系プラットフォームの実装 |
| getaddrinfo.c | `src/win/getaddrinfo.c` | ソース | Windowsプラットフォームの実装 |
| threadpool.c | `src/threadpool.c` | ソース | スレッドプール（ワーク投入・完了処理） |
| idna.c | `src/idna.c` | ソース | 国際化ドメイン名のASCII変換 |
| uv-common.c | `src/uv-common.c` | ソース | メモリ管理（uv__malloc/uv__free） |
| uv-common.h | `src/uv-common.h` | ヘッダ | 内部共通定義 |
