# 機能設計書 74-nativesockets

## 概要

本ドキュメントは、Nim標準ライブラリのnativesocketsモジュールの機能設計について記載する。本モジュールは低レベルのクロスプラットフォームソケットインターフェースを提供し、OSのネイティブソケットAPIに直接アクセスする機能を実装している。

### 本機能の処理概要

nativesocketsモジュールは、WindowsおよびPOSIX系OSのネイティブソケットAPIをラップし、Nimから直接利用できる低レベルインターフェースを提供する。Domain（アドレスファミリー）、SockType（ソケットタイプ）、Protocol（プロトコル）の列挙型を定義し、ソケットの作成、接続、データ送受信などの基本操作をサポートする。

**業務上の目的・背景**：高レベルのnetモジュールでは対応できない特殊なソケット操作や、パフォーマンスが重要な場面でOSネイティブのソケットAPIに直接アクセスする必要がある。本モジュールは、そのような要件に対応するための基盤を提供する。

**機能の利用シーン**：
- カスタムプロトコルの実装
- Raw socketを使用したネットワークツール開発
- 高レベルモジュール（net、asyncnet）の内部実装
- Unixドメインソケット通信
- 特殊なソケットオプションの設定

**主要な処理内容**：
1. ネイティブソケットの作成（createNativeSocket）
2. アドレス情報の取得（getAddrInfo）
3. ホスト名/IPアドレスの解決（getHostByName、getHostByAddr）
4. サービス情報の取得（getServByName、getServByPort）
5. バイト順変換（ntohl、ntohs、htonl、htons）
6. ソケットアドレスの取得（getLocalAddr、getPeerAddr）
7. ソケットオプションの設定（setSockOptInt、getSockOptInt）
8. ブロッキングモードの制御（setBlocking）
9. select操作（selectRead、selectWrite）

**関連システム・外部連携**：winleanモジュール（Windows）、posixモジュール（POSIX系OS）と連携する。

**権限による制御**：OSレベルのソケット権限に依存。SOCK_RAWの使用には管理者/root権限が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 本モジュールはGUI/画面を持たないライブラリ機能 |

## 機能種別

ネットワーク通信 / 低レベルソケット操作 / システムAPI

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| domain | Domain | No | アドレスファミリー（AF_UNSPEC/AF_UNIX/AF_INET/AF_INET6） | デフォルト: AF_INET |
| sockType | SockType | No | ソケットタイプ（SOCK_STREAM/SOCK_DGRAM/SOCK_RAW/SOCK_SEQPACKET） | デフォルト: SOCK_STREAM |
| protocol | Protocol | No | プロトコル（IPPROTO_TCP/IPPROTO_UDP等） | デフォルト: IPPROTO_TCP |
| inheritable | bool | No | 子プロセスへの継承可否 | デフォルト: defined(nimInheritHandles) |
| address | string | Yes | 接続先アドレス | 有効なホスト名またはIPアドレス |
| port | Port | Yes | ポート番号 | 0-65535 |
| timeout | int | No | タイムアウト（ミリ秒） | -1で無制限 |

### 入力データソース

- ホスト名/IPアドレス文字列
- ポート番号
- ソケットハンドル

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| SocketHandle | distinct cint | OSネイティブソケットハンドル |
| Port | distinct uint16 | ポート番号 |
| Servent | object | サービス情報 |
| Hostent | object | ホスト情報 |
| AddrInfo | ptr | アドレス情報ポインタ |

### 出力先

- ソケットハンドル経由でのネットワーク操作
- アドレス/ホスト情報の取得結果

## 処理フロー

### 処理シーケンス

```
1. ソケット作成（createNativeSocket）
   ├─ Domain/SockType/Protocolを指定
   └─ OSのsocket()システムコールを呼び出し
2. アドレス解決（getAddrInfo）
   └─ ホスト名からアドレス情報を取得
3. バインド/リッスン
   ├─ bindAddr: アドレスをバインド
   └─ listen: 接続待機開始
4. 接続/受け入れ
   ├─ connect: サーバーへ接続
   └─ accept: クライアント接続受け入れ
5. データ送受信
   ├─ send/sendto: データ送信
   └─ recv/recvfrom: データ受信
6. ソケットクローズ（close）
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[createNativeSocket]
    B --> C{用途}
    C -->|クライアント| D[getAddrInfo]
    D --> E[connect]
    C -->|サーバー| F[bindAddr]
    F --> G[listen]
    G --> H[accept]
    E --> I[send/recv]
    H --> J[クライアントハンドル取得]
    J --> I
    I --> K{タイムアウト?}
    K -->|Yes| L[selectRead/selectWrite]
    L --> M{準備完了?}
    M -->|Yes| I
    M -->|No| N[タイムアウト処理]
    K -->|No| O[継続]
    O --> P[close]
    N --> P
    P --> Q[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-74-01 | ソケット継承 | デフォルトでSOCK_CLOEXECを設定し子プロセスへの継承を防止 | Linux/BSD |
| BR-74-02 | バイト順変換 | ネットワークバイト順（ビッグエンディアン）への変換が必要 | マルチバイトデータ |
| BR-74-03 | アドレス解決 | AI_V4MAPPEDフラグでIPv4をIPv6にマップ | IPv6ソケット使用時 |
| BR-74-04 | Unixソケット | AF_UNIXはWindowsでは未サポート | Unixドメインソケット使用時 |

### 計算ロジック

バイト順変換（ntohl/htonl）:
- リトルエンディアンCPU: 4バイトスワップを実行
- ビッグエンディアンCPU: そのまま返す

タイムアウト計算（timeValFromMilliseconds）:
- 秒数 = timeout / 1000
- マイクロ秒 = (timeout - 秒数 * 1000) * 1000

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| - | - | - | 本モジュールはデータベース操作を行わない |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| OSError | システムエラー | ソケット操作失敗 | osLastError()で詳細取得 |
| IOError | 入出力エラー | 不明なソケットファミリー | ドメイン指定を確認 |
| ValueError | 値エラー | Unixソケットパスが長すぎる | パスを短くする |
| WSAEWOULDBLOCK | Windows非ブロッキング | 操作がブロックする | 再試行またはselect使用 |
| EAGAIN/EWOULDBLOCK | POSIX非ブロッキング | 操作がブロックする | 再試行またはselect使用 |

### リトライ仕様

- selectRead/selectWriteでソケットの準備状態を確認
- 非ブロッキングモードではEAGAIN/EWOULDBLOCK時に再試行

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

本モジュールはネットワーク通信を行うものであり、データベーストランザクション管理は行わない。

## パフォーマンス要件

- selectによる効率的なI/O多重化
- SOCK_CLOEXECによるfork時のオーバーヘッド削減
- inet_ntopによる効率的なアドレス文字列変換

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

- SOCK_CLOEXECによる子プロセスへのソケットリーク防止
- setInheritableによる継承制御
- SOCK_RAWは管理者権限が必要（悪用防止）

## 備考

- 高レベルのソケット操作にはnetモジュールを使用推奨
- nimNetLite定義時は一部機能が制限される
- Solaris環境では-lsocket -lnslリンクが必要
- Windows環境ではWSAStartupが自動的に呼び出される

---

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

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

### 推奨読解順序

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

まず、基本的な列挙型とデータ構造を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | nativesockets.nim | `lib/pure/nativesockets.nim` | Port型（60行目）: ポート番号のdistinct型 |
| 1-2 | nativesockets.nim | `lib/pure/nativesockets.nim` | Domain列挙型（62-70行目）: アドレスファミリー定義 |
| 1-3 | nativesockets.nim | `lib/pure/nativesockets.nim` | SockType列挙型（72-76行目）: ソケットタイプ定義 |
| 1-4 | nativesockets.nim | `lib/pure/nativesockets.nim` | Protocol列挙型（78-85行目）: プロトコル定義 |
| 1-5 | nativesockets.nim | `lib/pure/nativesockets.nim` | Servent型（87-91行目）: サービス情報構造体 |
| 1-6 | nativesockets.nim | `lib/pure/nativesockets.nim` | Hostent型（93-98行目）: ホスト情報構造体 |

**読解のコツ**: Domain.AF_INET6の値はOS依存（macOS: 30、Windows: 23、その他: 10）。whenディレクティブで条件分岐されている。

#### Step 2: ソケット作成を理解する

ソケットの生成処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | nativesockets.nim | `lib/pure/nativesockets.nim` | createNativeSocket（244-262行目）: cint版オーバーロード |
| 2-2 | nativesockets.nim | `lib/pure/nativesockets.nim` | createNativeSocket（264-272行目）: 列挙型版オーバーロード |

**主要処理フロー**:
1. **253-257行目**: Linux/BSDではSOCK_CLOEXECフラグを設定
2. **258行目**: OSのsocket()を呼び出し
3. **259-262行目**: 必要に応じてsetInheritableを呼び出し

#### Step 3: アドレス解決を理解する

ホスト名からアドレス情報を取得する処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | nativesockets.nim | `lib/pure/nativesockets.nim` | getAddrInfo（288-299行目）: AddrInfoヒント版 |
| 3-2 | nativesockets.nim | `lib/pure/nativesockets.nim` | getAddrInfo（301-321行目）: Domain/SockType/Protocol版 |
| 3-3 | nativesockets.nim | `lib/pure/nativesockets.nim` | getHostByName（475-504行目）: ホスト名解決 |
| 3-4 | nativesockets.nim | `lib/pure/nativesockets.nim` | getHostByAddr（402-473行目）: 逆引き |

**主要処理フロー**:
- **294行目**: getaddrinfo()システムコールを呼び出し
- **317-320行目**: IPv6使用時にAI_V4MAPPEDフラグを設定（FreeBSD/OpenBSD/NetBSD/Haiku/Android除く）

#### Step 4: バイト順変換を理解する

ネットワークバイト順とホストバイト順の変換を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | nativesockets.nim | `lib/pure/nativesockets.nim` | ntohl（323-331行目）: 32ビット変換 |
| 4-2 | nativesockets.nim | `lib/pure/nativesockets.nim` | ntohs（333-338行目）: 16ビット変換 |
| 4-3 | nativesockets.nim | `lib/pure/nativesockets.nim` | htonl/htons（340-350行目）: テンプレートで逆方向変換 |

**読解のコツ**: cpuEndianがbigEndianの場合は変換不要（no-op）。リトルエンディアンの場合のみバイトスワップを実行。

#### Step 5: select操作を理解する

I/O多重化のためのselect操作を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | nativesockets.nim | `lib/pure/nativesockets.nim` | timeValFromMilliseconds（788-798行目）: タイムアウト変換 |
| 5-2 | nativesockets.nim | `lib/pure/nativesockets.nim` | createFdSet/pruneSocketSet（800-815行目）: fd_set操作 |
| 5-3 | nativesockets.nim | `lib/pure/nativesockets.nim` | selectRead（817-836行目）: 読み取り可能チェック |
| 5-4 | nativesockets.nim | `lib/pure/nativesockets.nim` | selectWrite（838-858行目）: 書き込み可能チェック |

**主要処理フロー**:
- **829行目**: FD_ZEROでfd_setを初期化、FD_SETでソケットを追加
- **832-834行目**: select()を呼び出し
- **836行目**: 準備できていないソケットをリストから削除

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

```
createNativeSocket(domain, sockType, protocol, inheritable)
    │
    ├─ toInt(domain) / toInt(sockType) / toInt(protocol)
    │     └─ 列挙型をOS依存のcintに変換
    │
    ├─ socket() [OS API]
    │
    └─ setInheritable() [オプション]

getAddrInfo(address, port, domain, sockType, protocol)
    │
    ├─ AddrInfo構造体を初期化
    │
    ├─ toInt(domain) / toInt(sockType) / toInt(protocol)
    │
    └─ getaddrinfo() [OS API]
          │
          └─ raiseOSError() [エラー時]

selectRead(readfds, timeout)
    │
    ├─ timeValFromMilliseconds(timeout)
    │
    ├─ createFdSet(fd, readfds, m)
    │     ├─ FD_ZERO(fd)
    │     └─ FD_SET(socket, fd) [各ソケット]
    │
    ├─ select() [OS API]
    │
    └─ pruneSocketSet(readfds, fd)
          └─ 準備できていないソケットを削除
```

### データフロー図

```
[アプリケーション]              [nativesockets]              [OS]

createNativeSocket() ──────────────────────────────────▶ socket()
         │                                                   │
         ▼                                                   ▼
    SocketHandle ◀─────────────────────────────────────── fd/SOCKET
         │
         │
getAddrInfo() ─────────────────────────────────────────▶ getaddrinfo()
         │                                                   │
         ▼                                                   ▼
    ptr AddrInfo ◀─────────────────────────────────────── addrinfo*
         │
         │
ntohl/ntohs() ────────────────────────────────────────▶ [バイトスワップ]
         │                                                   │
         ▼                                                   ▼
    ホストバイト順 ◀───────────────────────────────────── 変換結果
         │
         │
selectRead() ──────────────────────────────────────────▶ select()
         │                                                   │
         ▼                                                   ▼
    準備完了数 ◀────────────────────────────────────────── 結果
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| nativesockets.nim | `lib/pure/nativesockets.nim` | ソース | 低レベルソケットインターフェース |
| net.nim | `lib/pure/net.nim` | ソース | 高レベルソケット（本モジュール使用） |
| asyncnet.nim | `lib/pure/asyncnet.nim` | ソース | 非同期ソケット（本モジュール使用） |
| winlean.nim | `lib/windows/winlean.nim` | ソース | Windows APIラッパー |
| posix.nim | `lib/posix/posix.nim` | ソース | POSIX APIラッパー |
| os.nim | `lib/pure/os.nim` | ソース | OS操作（osLastError等） |
