# 機能設計書 91-libfetch

## 概要

本ドキュメントは、FreeBSDのlibfetchライブラリの機能設計を記述する。libfetchはURL指定によるファイル取得を提供する汎用ライブラリであり、HTTP/HTTPS/FTP/ファイルプロトコルに対応したデータ転送機能を実装している。

### 本機能の処理概要

libfetchは、URLを指定してネットワークまたはローカルファイルシステムからファイルを取得・送信するためのCライブラリである。FreeBSDのパッケージ管理システム（pkg）やbsdinstallのファイルダウンロード機能など、多くのシステムユーティリティから利用される基盤ライブラリである。

**業務上の目的・背景**：FreeBSDシステムにおいて、パッケージのダウンロード、配布ファイルの取得、ネットワーク経由でのファイル転送といった基本的なファイル取得機能を統一的なAPIで提供する必要がある。libfetchはこれらの要求を満たすために設計された軽量なファイル取得ライブラリである。

**機能の利用シーン**：システムインストール時のミラーサイトからの配布ファイルダウンロード（bsdinstall）、パッケージマネージャによるパッケージ取得、fetchコマンドによるファイルダウンロード、各種スクリプトからのプログラム的なファイル取得などで使用される。

**主要な処理内容**：
1. URL文字列のパース（スキーム、ホスト、ポート、パス、認証情報の抽出）
2. HTTP/HTTPSプロトコルによるファイル取得・送信（リダイレクト追従、チャンク転送、Basic/Digest認証対応）
3. FTPプロトコルによるファイル取得・送信（パッシブ/アクティブモード、匿名アクセス対応）
4. ローカルファイルへのアクセス（file://スキーム）
5. SSL/TLS暗号化通信のサポート（HTTPS）
6. SOCKS5プロキシ対応
7. .netrcファイルによる認証情報の自動取得
8. ファイルのメタ情報（サイズ、更新日時）の取得
9. ディレクトリ一覧の取得

**関連システム・外部連携**：OpenSSLライブラリ（HTTPS通信のTLS処理）、DNS resolver（ホスト名解決）、SOCKS5プロキシサーバ

**権限による制御**：特別な権限制御は行わない。ファイルシステムアクセスにはプロセスのUID/GIDに基づく通常のファイルパーミッションが適用される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 13 | ミラーサイト選択画面 | 主機能 | 配布ファイルダウンロード先ミラーサイトURLの選択・BSDINSTALL_DISTSITE設定 |
| 14 | カスタムミラーURL入力画面 | 主機能 | カスタムミラーサイトURLの手動入力・設定処理 |
| 18 | 配布ファイルダウンロード画面 | 主機能 | libfetchを使用した配布ファイルのネットワークダウンロード・進捗表示 |

## 機能種別

データ連携（ネットワークファイル転送ライブラリ）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| URL文字列 | const char * | Yes | 取得対象のURL（scheme://[user[:pwd]@]host[:port]/doc形式） | スキーム長16文字以内、ユーザ名256文字以内、パスワード256文字以内、ホスト名MAXHOSTNAMELEN以内、ポート1-65535 |
| flags | const char * | No | 動作制御フラグ文字列（各文字が個別フラグを表す） | NULL許容 |
| struct url * | struct url * | Yes（構造体API使用時） | パース済みURL構造体 | scheme, host, docの各フィールドが適切に設定されていること |
| struct url_stat * | struct url_stat * | No | ファイルメタ情報の格納先 | NULL許容（NULLの場合メタ情報は取得しない） |

### 入力データソース

- アプリケーションプログラムからのAPI呼び出し（URL文字列またはstruct url構造体）
- .netrcファイル（~/.netrc）からの認証情報自動読み込み
- 環境変数（HTTP_PROXY, FTP_PROXY, SOCKS5_PROXY等）によるプロキシ設定

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| FILE * | FILE * | 取得/送信用のストリームポインタ（fetchGet/fetchPut系） |
| struct url_stat | struct url_stat | ファイルサイズ(size)、アクセス時刻(atime)、更新時刻(mtime) |
| struct url_ent * | struct url_ent * | ディレクトリエントリ配列（fetchList系） |
| fetchLastErrCode | int | 最後のエラーコード（FETCH_OK, FETCH_AUTH等） |
| fetchLastErrString | char[256] | 最後のエラーメッセージ文字列 |

### 出力先

- 呼び出し元アプリケーションへのFILE *ストリーム返却
- グローバル変数fetchLastErrCode/fetchLastErrStringによるエラー情報

## 処理フロー

### 処理シーケンス

```
1. URL解析（fetchParseURL）
   └─ スキーム、ユーザ情報、ホスト、ポート、ドキュメントパスを分離
2. プロトコル選択（fetchXGet/fetchGet/fetchPut/fetchStat/fetchList）
   └─ スキームに基づいてfile/ftp/http/httpsハンドラへディスパッチ
3. 接続確立（fetch_connect）
   └─ DNS解決 → ソケット作成 → 接続（SOCKS5プロキシ経由の場合はプロキシ接続）
4. SSL/TLSネゴシエーション（fetch_ssl、HTTPSの場合）
   └─ OpenSSL APIによるハンドシェイク・証明書検証
5. プロトコル固有の通信処理
   └─ HTTP: リクエスト送信 → ヘッダ解析 → リダイレクト追従 → ボディ読み取り
   └─ FTP: 制御接続 → 認証 → パッシブモード設定 → データ接続 → 転送
   └─ FILE: ローカルファイルオープン → シーク
6. データストリーム返却
   └─ FILE *ポインタを呼び出し元に返す
```

### フローチャート

```mermaid
flowchart TD
    A[fetchGetURL/fetchXGetURL呼び出し] --> B[fetchParseURL: URL解析]
    B --> C{スキーム判定}
    C -->|file| D[fetchXGetFile]
    C -->|ftp| E[fetchXGetFTP]
    C -->|http/https| F[fetchXGetHTTP]
    C -->|不明| G[FETCH_URL エラー返却]
    D --> H[fopen/fseeko]
    E --> I[FTP制御接続・認証]
    I --> J[パッシブモード設定]
    J --> K[データ接続確立・転送]
    F --> L[HTTP接続確立]
    L --> M{HTTPS?}
    M -->|Yes| N[SSL/TLSハンドシェイク]
    M -->|No| O[HTTPリクエスト送信]
    N --> O
    O --> P[レスポンスヘッダ解析]
    P --> Q{リダイレクト?}
    Q -->|Yes| R[リダイレクト先URLへ再接続]
    R --> L
    Q -->|No| S[FILE*ストリーム返却]
    H --> S
    K --> S
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-91-1 | リダイレクト上限 | HTTPリダイレクトは最大20回まで追従する | HTTP/HTTPSプロトコル使用時 |
| BR-91-2 | デフォルトポート | FTP:21, HTTP:80, プロキシ:3128をデフォルトポートとする | ポート未指定時 |
| BR-91-3 | 匿名FTP | ユーザ名未指定の場合"anonymous"で接続する | FTPプロトコル使用時 |
| BR-91-4 | URLエスケープ | HTTP/HTTPSのURLパスに含まれる空白文字はパーセントエンコードする | HTTP/HTTPSプロトコル使用時 |
| BR-91-5 | フラグメント除去 | HTTP/HTTPSのURLからフラグメント識別子（#以降）を除去する | HTTP/HTTPSプロトコル使用時（RFC 9110準拠） |

### 計算ロジック

特に複雑な計算ロジックはない。URLパース時のパーセントデコード処理（fetch_pctdecode）において、%XX形式の16進エスケープを元の文字に変換する。

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

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

本機能はデータベースを使用しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| FETCH_ABORT (1) | 中断 | ユーザによる中断 | 操作を再試行 |
| FETCH_AUTH (2) | 認証エラー | 認証失敗 | 認証情報を確認して再試行 |
| FETCH_DOWN (3) | サービス停止 | サーバがダウン | 時間をおいて再試行 |
| FETCH_MEMORY (7) | メモリ不足 | malloc失敗 | メモリを解放して再試行 |
| FETCH_MOVED (8) | リダイレクト | リソース移動（上限超過時エラー） | 新しいURLで再試行 |
| FETCH_NETWORK (9) | ネットワークエラー | 接続失敗 | ネットワーク状態を確認 |
| FETCH_PROTO (11) | プロトコルエラー | プロトコル違反 | URLを確認 |
| FETCH_RESOLV (12) | 名前解決失敗 | DNS解決不可 | ホスト名を確認 |
| FETCH_TIMEOUT (15) | タイムアウト | 接続/転送タイムアウト | fetchTimeout値を調整して再試行 |
| FETCH_URL (18) | URL不正 | URLパース失敗 | URL形式を修正 |

### リトライ仕様

ライブラリ自体にリトライ機構は組み込まれていない。fetchRestartCallsフラグ（デフォルト1=有効）により、シグナルで中断されたシステムコールは自動的に再開される。リトライは呼び出し元アプリケーションの責務である。

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

ライブラリレベルでのトランザクション制御はない。HTTP接続は各リクエストごとに独立して処理される。FTPではデータ接続と制御接続が分離されており、制御接続上のコマンドシーケンスが事実上のトランザクション単位となる。

## パフォーマンス要件

- I/Oタイムアウト: fetchTimeout変数で制御（秒単位、デフォルト0=無制限）
- バッファリング: 接続ごとにバッファ（conn_t.buf）を使用
- 接続の再利用: HTTP Keep-Aliveには非対応（リクエストごとに新規接続）

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

- SSL/TLS: WITH_SSLコンパイルオプションによりHTTPS対応。OpenSSLを使用したサーバ証明書検証（fetch_ssl_cb_verify_crt）を実施
- 認証情報保護: パスワードはstruct urlのpwdフィールドに平文保持（メモリ上）。.netrcファイルの認証情報読み込みサポート
- プロキシ: SOCKS5プロキシ対応により、直接接続できないネットワーク環境でも利用可能
- URLパース安全性: パーセントエンコードの不正なシーケンス（%00等）を検出してNULL返却
- バッファ境界: URL各フィールドに固定長バッファを使用（URL_SCHEMELEN=16, URL_USERLEN=256, URL_PWDLEN=256）し、長さ検証を実施

## 備考

- libfetchはFreeBSD固有のライブラリであり、NetBSD/OpenBSDには存在しない
- バージョン文字列は"libfetch/2.0"
- fetchコマンド（usr.bin/fetch）がこのライブラリの主要な利用者の一つである

---

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

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

### 推奨読解順序

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

まず、libfetchのコアとなるデータ構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | fetch.h | `lib/libfetch/fetch.h` | struct url（40-51行目）、struct url_stat（53-57行目）、struct url_ent（59-62行目）の定義。エラーコード定数（71-88行目）。公開API宣言（93-133行目） |
| 1-2 | common.h | `lib/libfetch/common.h` | struct fetchconn（49-62行目）: ソケット・バッファ・SSL情報を保持する接続構造体。struct fetcherr（65-69行目）: エラーメッセージ構造体。SOCKS5定数（79-101行目） |

**読解のコツ**: fetch.hは公開ヘッダであり、外部アプリケーションがincludeするファイル。common.hはライブラリ内部でのみ使用される非公開ヘッダ。struct urlのdocフィールドは動的確保（strdup/malloc）される点に注意。

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

汎用API関数群がプロトコル固有の処理へどのようにディスパッチされるかを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | fetch.c | `lib/libfetch/fetch.c` | fetchXGet（75-93行目）: スキームに基づくプロトコルディスパッチ。fetchParseURL（347-483行目）: URL解析の中核ロジック |

**主要処理フロー**:
1. **75-93行目**: fetchXGet - スキーム文字列比較でfile/ftp/http/httpsハンドラを選択
2. **172-185行目**: fetchXGetURL - URL文字列からstruct urlをパースしfetchXGetを呼ぶ便利関数
3. **347-483行目**: fetchParseURL - URLを各コンポーネントに分解する主要パーサー
4. **253-291行目**: fetchMakeURL - プログラム的にstruct urlを構築する関数
5. **315-339行目**: fetch_pctdecode - パーセントエンコーディングのデコード処理

#### Step 3: HTTP処理を理解する

最も複雑なプロトコル実装であるHTTP処理を読み解く。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | http.c | `lib/libfetch/http.c` | HTTP/HTTPSリクエストの構築・送信・レスポンス解析。2148行のファイル。MAX_REDIRECT=20（98行目）。http_request関数がHTTP処理の中核 |

**主要処理フロー**:
- **98行目**: MAX_REDIRECT定数定義（リダイレクト上限20回）
- http_request関数: 接続確立 → リクエストヘッダ構築 → 送信 → レスポンスヘッダ読取 → リダイレクト処理 → ストリーム返却

#### Step 4: FTP処理を理解する

FTPプロトコル固有の制御接続・データ接続の二重構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | ftp.c | `lib/libfetch/ftp.c` | FTPプロトコル実装（1211行）。FTP_ANONYMOUS_USER定義（80行目）。制御接続上のコマンド送受信とデータ接続の確立 |

#### Step 5: ファイルアクセス処理を理解する

最もシンプルなfile://スキームの実装を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | file.c | `lib/libfetch/file.c` | fetchXGetFile（42-64行目）: fopen+fseeko。fetchPutFile（72-94行目）: 追記/上書きモード選択。fetchListFile（119-153行目）: ディレクトリ走査 |

**主要処理フロー**:
- **42-64行目**: fetchXGetFile - ローカルファイルをfopenで開き、offsetが指定されていればfseeko
- **96-111行目**: fetch_stat_file - stat(2)でファイルメタ情報取得
- **119-153行目**: fetchListFile - opendir/readdirでディレクトリエントリを列挙

#### Step 6: 共通ネットワーク処理を理解する

接続確立、SSL、I/Oなどの共通基盤を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 6-1 | common.c | `lib/libfetch/common.c` | ネットワーク接続（fetch_connect）、SSL処理（fetch_ssl）、SOCKS5プロキシ（fetch_socks5_init）、.netrc解析（fetch_netrc_auth）。1784行のファイル |

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

```
fetchGetURL(URL文字列)
    │
    ├─ fetchParseURL(URL) → struct url *
    │
    ├─ fetchXGet(url, us, flags)
    │      │
    │      ├─ [file://] fetchXGetFile()
    │      │      └─ fopen() / fseeko()
    │      │
    │      ├─ [ftp://] fetchXGetFTP()
    │      │      ├─ fetch_connect() → conn_t *
    │      │      │      ├─ fetch_resolve() [DNS解決]
    │      │      │      └─ fetch_socks5_init() [SOCKS5プロキシ]
    │      │      ├─ ftp_authenticate() [認証]
    │      │      └─ ftp_transfer() [データ転送]
    │      │
    │      └─ [http(s)://] fetchXGetHTTP()
    │             ├─ http_request()
    │             │      ├─ fetch_connect() → conn_t *
    │             │      ├─ fetch_ssl() [HTTPS時]
    │             │      ├─ http_request送信
    │             │      └─ レスポンス解析・リダイレクト追従
    │             └─ FILE *ストリーム返却
    │
    └─ fetchFreeURL(url)
```

### データフロー図

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

URL文字列         ───▶ fetchParseURL()              ───▶ struct url *
                         │
struct url *       ───▶ fetchXGet()                  ───▶ FILE * (読取ストリーム)
                         ├─ file.c (ローカルI/O)
                         ├─ ftp.c (FTPプロトコル)
                         └─ http.c (HTTPプロトコル)
                              │
                              ├─ common.c fetch_connect()  ───▶ conn_t * (接続)
                              ├─ common.c fetch_ssl()      ───▶ SSL接続確立
                              └─ common.c fetch_read/write ───▶ データ転送

環境変数           ───▶ fetch_socks5_getenv()        ───▶ プロキシ設定
(SOCKS5_PROXY等)

~/.netrc           ───▶ fetch_netrc_auth()           ───▶ 認証情報(user/pwd)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| fetch.h | `lib/libfetch/fetch.h` | ヘッダ | 公開API宣言、データ構造定義、エラーコード定義 |
| common.h | `lib/libfetch/common.h` | ヘッダ | 内部API宣言、接続構造体定義、SOCKS5定数 |
| fetch.c | `lib/libfetch/fetch.c` | ソース | 汎用API実装、URLパーサー |
| http.c | `lib/libfetch/http.c` | ソース | HTTP/HTTPSプロトコル実装 |
| ftp.c | `lib/libfetch/ftp.c` | ソース | FTPプロトコル実装 |
| file.c | `lib/libfetch/file.c` | ソース | ローカルファイルアクセス実装 |
| common.c | `lib/libfetch/common.c` | ソース | 共通ネットワーク処理（接続、SSL、I/O、.netrc） |
| Makefile | `lib/libfetch/Makefile` | ビルド | ライブラリビルド設定 |
| ftp.errors | `lib/libfetch/ftp.errors` | 定義 | FTPエラーコード定義 |
| http.errors | `lib/libfetch/http.errors` | 定義 | HTTPエラーコード定義 |
| fetch.3 | `lib/libfetch/fetch.3` | マニュアル | manページ |
