GoでFTPサーバ書く
5.1. 最小の実装
不要なエラーメッセージなしに FTP を動作させるために、全てのサーバーに最低限以下の実装が必須である:
型 - ASCII Non-print
モード - ストリーム
構造 - ファイル、レコード
コマンド - USER, QUIT, PORT,
TYPE, MODE, STRU,(デフォルト値のためのもの)
RETR, STOR,
NOOP.
転送パラメータのデフォルト値:
TYPE - ASCII Non-print
MODE - ストリーム
STRU - ファイル
全てのホストは標準的なデフォルトとして上記の値を受け入れなければならない。
型: ASCII NON PRINT
CRLFだけ気にすればOK
あとは普通にASCII
構造
ファイル
内部構造を持たず、ファイルはデータバイトの連続シーケンスとみなされる。
レコード
ファイルは連続的なレコードから構成される。
モード: STREAM
全てのデータ転送は、明示的に示されるファイル終端(EOF)か、データ接続を閉じることによる暗黙的なファイル終端(EOF)かによって完了しなければならない。レコード構造を持つファイルでは、最後のものを含む全レコード終端マーカー(EOR)は明示的である。
レコード構造を持たない ASCII ファイル内または EBCDIC ファイル内の行端は、それぞれ <CRLF> あるいは <NL> で示されるべきである。
3.4.1. ストリームモード
データはバイトのストリームとして転送される。使用される表現型に制限は無く、レコード構造も許可される。
レコード構造のファイルの場合、EOR と EOF はそれぞれ 2 バイトの制御コードで表される。この制御コードの 1 バイト目は全ビットが 1 のエスケープ文字である。2 バイト目は、最下位ビットのみ 1 なら EOFを、最下位から 2 番目のビットのみ 1 なら EOF を表す。つまりこのバイトは、EOR なら値 1、EOF なら値 2 となる。下位 2 ビットを共に 1 にする(つまり値 3 にする)ことによって、EOR と EOF とを同時に表すこともできる。全てのビットが 1 のバイトをデータとして扱いたい場合、その次のバイトにも同じ値を繰り返さなければならない。
ファイル構造における EOF は送信側ホストがデータ接続を閉じることによって表され、全てのバイトがデータバイトである。
EOR: End of Record, EOF: End of Fileかな?
接続について
21ポートでユーザー側。データ接続は1引いた番号なので20。
PASVを使えば、異なる2拠点のリモートサーバのデータを移動できるの面白い。 mactkg.icon
Aに接続して、PASVにさせる。
Bに接続して、データはAのPASVポートと通信するようにうながす。
コマンドとリプライ
接続後
120 => 220
220
421
USER <SP> <ユーザー名> <CRLF>
ユーザー名: string
レス
230
530
500, 501, 421
331, 332
課金なし、パスワードなし
QUIT <CRLF>
レス
221
500
PORT <SP> <ホスト-ポート> <CRLF>
ホスト-ポート: <数値>,<数値>,<数値>,<数値>,<数値>,<数値>
最初4つがIPで、後ろがポート
ポートの計算は、p1 * 256 + p2
レス
200
500, 501, 421, 530
TYPE <SP> <型コード> <CRLF>
型コード
レス
200
500, 501, 504, 421, 530
MODE <SP> <モードコード> <CRLF>
モードコード: S, B, C
レス
200
500, 501, 504, 421, 530
STRU <SP> <構造コード> <CRLF>
構造コード: F, R, P
レス
200
500, 501, 504, 421, 530
RETR <SP> <パス名> <CRLF>
パス名: string
データを取得する
レス
125, 150 => (110) => 226, 250 => 425, 426, 530
450, 550
500, 501, 421, 530
STOR <SP> <パス名> <CRLF>
パス名: string
データを送る。(上書き)
レス
125, 150 => (110) => 226, 250 => 425, 426, 451, 551, 552
532, 450, 452, 553
500, 501, 421, 530
NOOP <CRLF>
なにもしない
レス
200
500, 421
☆ おまけ
LIST <SP> <パス名>
パス名: string
ls
レス
125, 150 => (110) => 226, 250 => 425, 426, 451
450
500, 501, 502, 421, 530
CWD <SP> <パス名>
パス名: string
cd
レス
250
500, 501, 502, 421, 530, 550
返すコード
使いそうなやつ
125 Data connection already open; transfer starting.
(データ接続はすでに開かれている。転送が開始される。)
150 File status okay; about to open data connection.
(ファイル状態は正常。データ接続を開こうとしている)
200 Command okay.
(コマンド成功)
202 Command not implemented, superfluous at this site.
(そのコマンドはこのサイトでは不要であり、実装されていない)
220 Service ready for new user.
(新しいユーザーに対するサービスの準備完了)
221 Service closing control connection.
(サービスはコントロール接続を閉じようとしている)
適切ならログアウトする。
225 Data connection open; no transfer in progress.
(データ接続は開かれた。実行中の転送はない。)
226 Closing data connection.
(データ接続は閉じられようとしている)
要求されたファイル操作は成功した(例えばファイル転送や中断)。
230 User logged in, proceed.
(ユーザーはログインした。次の処理を)
250 Requested file action okay, completed.
(要求されたファイル操作は正常に完了した)
331 User name okay, need password.
(ユーザー名は正常。パスワードが必要)
421 Service not available, closing control connection.
(サービスは利用不可であり、コントロール接続は閉じられようとしている)
シャットダウンしなければならないことをサービスが分かっている場合、
これは任意のコマンドに対するリプライとなりうる。
425 Can't open data connection.
(データ接続を開けない)
426 Connection closed; transfer aborted.
(接続は閉じられた。転送が中断された)
450 Requested file action not taken.
(要求されたファイル操作は実行されない)
ファイルを使用できない(例えば使用中など)
451 Requested action aborted. Local error in processing.
(要求された操作は中止された。処理中にローカルエラー発生)
500 Syntax error, command unrecognized.
(文法エラー、そのコマンドは認識されない)
コマンド行が長すぎるといったエラーもこれに含まれて良い。
501 Syntax error in parameters or arguments.
(パラメータや引数の文法エラー)
502 Command not implemented.
(そのコマンドは実装されていない)
504 Command not implemented for that parameter.
(そのコマンドに対するその引数は実装されていない)
530 Not logged in.
(ログインしていない)
550 Requested action not taken.
(要求された操作は実行されない)
ファイルを使用できない(例えばファイルが見つからない、アクセス不可など)
551 Requested action aborted. Page type unknown.
(要求された操作は中止された。ページタイプが不明である)
553 Requested action not taken.
(要求された操作は実行されない)
許可されないファイル名
552 Requested file action aborted.
(要求されたファイル操作は中止された)
現在のディレクトリやデータセットのための)保存領域を超過した
いらなそう
110 Restart marker reply.
(再開マーカーリプライ)
このテキスト部分は特定の実装に依存するものであってはならず、
以下の形式でなければならない:
MARK yyyy = mmmm
ここで yyyy はユーザープロセスのデータストリームマーカー、
mmmm はそれに対応するサーバー側のマーカーである。
(マーカーと "=" との間の空白文字に注意)
120 Service ready in nnn minutes.
(サービスは nnn 分以内に利用可能になる)
211 System status, or system help reply.
(システム状態、またはシステムヘルプのリプライ)
212 Directory status.
(ディレクトリの状態)
213 File status.
(ファイルの状態)
214 Help message.
(ヘルプメッセージ)
サーバーの使用方法や特定の非標準コマンドの意味を示す。
このリプライは人間のユーザーにとってのみ有用である。
215 NAME system type.
(システムタイプ NAME)
ここで NAME は、Assigned Numbers document に示されている公式な
システム名である。
227 Entering Passive Mode (h1,h2,h3,h4,p1,p2).
(パッシブモードに入る (h1,h2,h3,h4,p1,p2))
257 "PATHNAME" created.
"PATHNAME" が作成された
332 Need account for login.
(ログインには課金情報が必要)
350 Requested file action pending further information.
(要求されたファイル操作は保留されている。更に情報が必要)
452 Requested action not taken.
(要求された操作は実行されない)
システムに十分な空き領域がない
503 Bad sequence of commands.
(コマンドの順序が不正)
532 Need account for storing files.
(ファイルを保存するには課金情報が必要)
すごい雑だけどLSができた(PWD)
code:lftp.log
$ lftp -d -p 9001 localhost
---- Resolving host address...
---- 1 address found: 127.0.0.1
lftp localhost:~> ls
---- Connecting to localhost (127.0.0.1) port 9001
**** server bug: single <NL>
<--- 220 Service ready for new user.
---> FEAT
**** server bug: single <NL>
<--- 502 Command not implemented.
---> USER anonymous
**** server bug: single <NL>
<--- 230 User logged in, proceed.
---> PWD
**** server bug: single <NL>
<--- 425 Can't open data connection.
---> PASV
**** server bug: single <NL>
<--- 502 Command not implemented.
---- Switching passive mode off
---> LIST
---> ABOR
---- Closing aborted data socket
---- Closing control socket
---- Connecting to localhost (127.0.0.1) port 9001
**** server bug: single <NL>
<--- 220 Service ready for new user.
---> FEAT
**** server bug: single <NL>
<--- 502 Command not implemented.
---> USER anonymous
**** server bug: single <NL>
<--- 230 User logged in, proceed.
---> PWD
**** server bug: single <NL>
<--- 425 Can't open data connection.
---> PORT 127,0,0,1,200,30
**** server bug: single <NL>
<--- 200 Command okay.
---> LIST
---- Accepted data connection from (127.0.0.1) port 51238
**** server bug: single <NL>
<--- 150 File status okay; about to open data connection.
**** server bug: single <NL>
<--- 226 Closing data connection.
---- Got EOF on data connection
---- Closing data socket
commands.go main.go main_test.go
code:goftp.log
$ go run main.go
2019/10/08 02:15:57 FEAT 1 2019/10/08 02:16:12 Login user 'anonymous'
2019/10/08 02:17:49 PWD 1 2019/10/08 02:18:42 PASV 1 2019/10/08 02:19:12 ��� 1 2019/10/08 02:19:17 FEAT 1 2019/10/08 02:19:27 Login user 'anonymous'
2019/10/08 02:19:49 PWD 1 2019/10/08 02:19:57 Create data connection to 127.0.0.1:51230
2019/10/08 02:20:42 LIST 1 ls ../してみる
code:ls_up.log
lftp localhost:~> ls ../
---> PASV
**** server bug: single <NL>
<--- 502 Command not implemented.
---- Switching passive mode off
---> LIST ../
---> ABOR
---- Closing aborted data socket
---- Closing control socket
---- Connecting to localhost (127.0.0.1) port 9001
**** server bug: single <NL>
<--- 220 Service ready for new user.
---> FEAT
**** server bug: single <NL>
<--- 502 Command not implemented.
---> USER anonymous
**** server bug: single <NL>
<--- 230 User logged in, proceed.
---> PWD
**** server bug: single <NL>
<--- 425 Can't open data connection.
---> PORT 127,0,0,1,200,62
**** server bug: single <NL>
<--- 200 Command okay.
---> LIST ../
---- Accepted data connection from (127.0.0.1) port 51269
---- Got EOF on data connection
---- Closing data socket
../.gitkeep ../ex01 ../ex02 ../ex06
**** server bug: single <NL>
<--- 150 File status okay; about to open data connection.
**** server bug: single <NL>
<--- 226 Closing data connection.
code:goftp_up.log
2019/10/08 02:22:55 PASV 1 2019/10/08 02:23:25 ��� 1 2019/10/08 02:23:30 FEAT 1 2019/10/08 02:23:40 Login user 'anonymous'
2019/10/08 02:23:47 PWD 1 2019/10/08 02:23:55 Create data connection to 127.0.0.1:51262
Passive Modeを切ってみる
code:ftp_active.log
lftp localhost:~> set ftp:passive-mode off
lftp localhost:~> ls /usr/local
---> PORT 127,0,0,1,200,95
**** server bug: single <NL>
<--- 200 Command okay.
---> LIST /usr/local
---- Accepted data connection from (127.0.0.1) port 51299
**** server bug: single <NL>
<--- 150 File status okay; about to open data connection.
**** server bug: single <NL>
<--- 226 Closing data connection.
---- Got EOF on data connection
---- Closing data socket
/usr/local/.com.apple.installer.keep /usr/local/Caskroom /usr/local/Cellar /usr/local/FlashcardService /usr/local/Frameworks /usr/local/Homebrew /usr/local/bin /usr/local/cuda /usr/local/etc /usr/local/include /usr/local/juniper /usr/local/lib /usr/local/library /usr/local/lmm /usr/local/man /usr/local/modules /usr/local/opt /usr/local/pcsx2 /usr/local/remotedesktop /usr/local/sbin /usr/local/share /usr/local/template /usr/local/var
lftp localhost:~>
code:goftp_active.log
2019/10/08 02:26:12 Create data connection to 127.0.0.1:51295
ちゃんと最初からつなぎに行っていることがわかる
放置してたら接続切った
code:ftp_timeout.log
---- Closing idle connection
---> QUIT
lftp localhost:~>
code:goftp_timeout.log
2019/10/08 02:29:47 QUIT 1 2019/10/08 02:29:47 Bye~