GoでTFTPサーバ自作(RFC 1350編)
この記事の内容
この記事ではRFC 1350に従ってTFTPサーバを自作します
学習目的の記事なのでAIは使用しません
TFTP関連のRFC
TFTPサーバの使用は以下のRFCに記載されています
| RFC | 内容 |
|---|---|
| RFC 1350 | TFTPの基本的な仕様 |
| RFC 2347 | TFTPの拡張(オプションの追加) |
| RFC 2348 | ブロックサイズオプションの仕様(blksize) |
| RFC 2349 | タイムアウトオプションの仕様(timeout)、ファイルサイズオプションの仕様(tsize) |
| RFC 7440 | ウィンドウサイズオプションの仕様(windowsize) |
RFC 2347はオプションそのものの仕様です
TFTPの通信
詳細はRFCをご覧ください
概要
TFTPによるファイル転送はクライアントのリクエストから開始します
リクエストの種類は読み込みと書き込みの二種類です ※1
読み込みリクエストに対してサーバは512バイト固定長のデータをクライアントに返します ※2
このデータをブロックと呼びます
ブロックには連続した番号が振られます
ブロック番号は1始まりです ※3
クライアントはブロックを受信したらACKパケットを送り返します
サーバはACKパケットを受け取ったら次のブロックを送信します ※4
送信/受信したブロックが512バイト未満の場合、通信を終了します
ただし、クライアントは最後の受信ブロックに対してもACKパケットを送ります
※1 今回の実装では読み込みのみ実装します
※2 RFC 2348でブロック長を変更可能になっています
※3 読み込みの場合
※4 RFC 7440で一度に送るブロックの数を変更可能になっています
詳細
全てのTFTPパケットは必ずopcodeを持ち、以下の5種類に分類されます
| 種類 | opcode | 内容 |
|---|---|---|
| RRQ | 01 | 読み込み対象のファイル名と通信に必要な設定 |
| WRQ | 02 | 書き込み対象のファイル名と通信に必要な設定 |
| DATA | 03 | ファイルデータとそのブロック番号 |
| ACK | 04 | 受信完了したブロック番号 |
| ERROR | 05 | エラーコードとエラーメッセージ |
さらに具体的にはそれぞれ以下のフォーマットで記述されます
| 種類 | フォーマット |
|---|---|
| RRQ | Opcode: 2 bytes Filename: string 0: 1 bytes Mode: string 0: 1 byte |
| WRQ | Opcode: 2 bytes Filename: string 0: 1 bytes Mode: string 0: 1 byte |
| DATA | Opcode: 2 bytes Block #: 2 bytes Data: n bytes |
| ACK | Opcode: 2 bytes Block #: 2 bytes |
| ERROR | Opcode: 2 bytes ErrorCode: 2 bytes ErrMsg: string 0: 1 byte |
区切り文字としてNULL文字0が使われます
stringは任意長です
Modeは以下三種類です。今回はoctetのみ実装します
| Mode | 内容 |
|---|---|
| netascii | ascii文字 |
| octet | バイナリ |
| 詳細不明 |
ErrorCodeは以下の8種類が定義されています
| ErrorCode | 内容 |
|---|---|
| 0 | Not defined, see error message (if any). |
| 1 | File not found. |
| 2 | Access violation. |
| 3 | Disk full or allocation exceeded. |
| 4 | Illegal TFTP operation. |
| 5 | Unknown transfer ID. |
| 6 | File already exists. |
| 7 | No such user. |
transfer IDはUDPポートのことです
実装
概要
大雑把なシーケンスは以下です

今回はブロック長512バイト、ブロックの最大個数は65535個です
最大で送信できるファイルサイズは32MBになります
また、ファイルは全てメモリに載せてしまいます
ブロック長の変更とブロック番号のロールオーバーは今後対応します
動作確認
サーバ側
$ head -c 10000 /dev/urandom > file10k
$ make ; sudo ./build/tao
クライアント側
$ scp 192.168.1.101:/home/ubuntu/file10k ./correct_file10k
$ curl tftp://192.168.1.101/file10k -o file10k
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 10000 0 10000 0 0 234k 0 --:--:-- --:--:-- --:--:-- 234k
100 10000 0 10000 0 0 233k 0 --:--:-- --:--:-- --:--:-- 233k
$ cmp file10k correct_file10k
$
Discussion