RX マイコン C++ で、FTPサーバーを実装した。
※動作検査は、GR-KAEDE で行っている。
車輪の再発明的要素は高いが、学習的要素もあるしで、取り組んでみた。
※Arduino の FtpServer プログラムとネットの情報を参考とした。
Arduino 系の C++ ソースは、自分的には気に入らない部分が多い、なので応答メ
ッセージや、全体の流れなど参考とした。
・Arduino の構成は、最新の C++ からは乖離しており、あまり使う気にならない。
・必要の無い継承関係(継承は便利な機能ではあるが、結びつきが強すぎるので、
最近はあまり使わない、どうしても使う必要がある場合だけ使う)
・テンプレートを使わないスタイル(テンプレートを使う事で、柔軟性と拡張性
が導入できると思うので、最近は良く使うようになっている)
・ヘッダーとソースに分かれている。(C++ では、定義と実装を分離する必要が
無く、1つのソース(ヘッダー)のみで管理できる)
・他、細かい部分で色々・・
-----
ある程度実装して、クライアントを接続してみた。
最初、何とか、FFFTP のみ使える状態になった。
※データ転送は PASV モードで行った。
次に FileZilla を試したが、正常に動作しない・・・
MSYS2 上のコンソールから、ftp コマンドで接続してみたが、やはり駄目・・・
原因を調査すると、「SYST」コマンドのタイミングのようだった。
・FFFTP では、SYST は使っていないようだったのでサポートしていなかった。
・コンソールの ftp コマンドでは、SYST のリクエストが来るタイミングが、USER
認証の前だった。
FileZilla では、PASS 認証の後だった。
これを改修したら、接続は出来るようになった。
次に問題になったのは、データ転送のやり方だった。
・FFFTP では、PASV モードで転送を行っていたので、PORT コマンドに対応してい
なかった、そこで、PORT コマンドの対応も行った。
※PORT コマンドでは、FTP サーバーのデータ転送は、クライアント接続となる。
そこで、関連するデータ転送部分で、PASV モードと、PORT モードで、サーバー
動作か、クライアント動作を分けた。
これを行ってから、PORT モードでの転送も行えるようになり、ftp コマンド、
FileZilla での接続等、出来るようになった。
各種クライアントによる接続状況:
FFFTP:
・PASV 有効 ---> OK
・PASV 無効 ---> NG
※PASV 無効 では PORT モードで接続するが、ネットワークが複数(WiFiや
セカンダリーネットワーク)ある機種では、利用している IP アドレスを正しく
取得しない為、正常に接続できないようだ、これは、FFFTP のバグ(仕様)と
思える。
FileZilla:
・既定値(PORT モード)---> OK
・アクティブ(PASV モード) ---> NG
・パッシブ(PASV モード)---> OK
※「アクティブ」の仕様が不明
ftp:(MSYS2 上のコマンド、Windows の ftp コマンドでは無い)
たぶん PORT モード ---> OK
-----
まだ、現在、ネットスタックが調整中であり、中途な状態ではあるが・・
ftp_server.hpp
に、ftp_server のソースコードがある。
サポートコマンド一覧:(※一部、実装中)
// RFC 959
ABOR, ///< ファイルの転送を中止する。
ACCT, ///< アカウント情報。引数はユーザアカウントを示す文字列。
ALLO, ///< ファイルを受け取るために十分なディスクスペースを割り当てる。引数は予約するサイズ。
APPE, ///< 引数に示したファイルに対して追記する。
CDUP, ///< 親ディレクトリに移動する。
CWD, ///< 作業ディレクトリの変更。引数は移動するディレクトリ。
DELE, ///< ファイルを削除する。引数は削除するファイル。
HELP, ///< コマンドの一覧。引数を指定するとより詳しいコマンド情報を返す。
LIST, ///< 引数に指定したファイルの情報やディレクトリの一覧。
// 指定しない場合、現在のディレクトリの情報を一覧。
MKD, ///< 引数に指定した名前のディレクトリを作成する。
NLST, ///< 引数に指定したディレクトリのファイル一覧を返す。
NOOP, ///< 何もしない。接続維持のためダミーパケットとして使われることがほとんど。
MODE, ///< 転送モードの設定(ストリーム、ブロック、圧縮)。
PASS, ///< 認証パスワード。
PASV, ///< パッシブモードに移行する。
PORT, ///< サーバが接続すべきポートとアドレスを指定する。
PWD, ///< 作業ディレクトリを取得する。
XPWD, ///< 作業ディレクトリを取得する。(拡張)
QUIT, ///< 接続を終了する。
REIN, ///< 接続を再初期化する。
REST, ///< ファイルの転送を指定した箇所から再開する。
RETR, ///< リモートファイルをダウンロード(Retrieve)する。
RMD, ///< 引数に指定したディレクトリを削除する。
RNFR, ///< 引数に指定した名前のファイル(ディレクトリ)をリネームする。
RNTO, ///< 引数に指定した名前のファイル(ディレクトリ)にリネームする。
SITE, ///< RFCで定義されていないようなリモートサーバ特有のコマンドを送信する。
SMNT, ///< ファイル構造をマウントする
STAT, ///< 現在の状態を取得する。
STOR, ///< ファイルをアップロード(Stor)する。
STOU, ///< ファイル名が重複しないようにファイルをアップロードする。
STRU, ///< 転送するファイルの構造を設定する。
SYST, ///< システムの種別を返す。
TYPE, ///< 転送モードを設定する(アスキーモード、バイナリモード)。
USER, ///< 認証するユーザー名
// RFC 2389
FEAT, ///< サーバに実装されている拡張コマンドのリストを取得する。
OPTS, ///< 拡張機能の設定。
// RFC 3659
MDTM, ///< 引数に指定したファイルの最終更新時間の詳細を返す。
MLSD, ///< 引数に指定したディレクトリのファイル一覧を詳細な最終更新時間をつけて返す。
MLST, ///< 引数に指定したディレクトリの詳細な情報を返す。
SIZE, ///< ファイルサイズを返す
ftp_server クラスは、ファイルアクセスクラスを参照する為、以下のように
SDC(ファイルアクセスクラス「utils::sdc_io」)を typedef しておく。
typedef net::ftp_server<SDC> FTP;
FTP ftp_;
コンストラクターで、SDC の実態、及び、Ethernet クラス(net スタッククラス)
を渡す。
ftp_(ethernet_, sdc_io_)
開始時、ユーザー名、パスワードを引数とする。
ftp_.start("user", "pass");
実行時、1/100秒間隔でサービスを呼び出す。
ftp_.service();
-----
ネットスタックは、GR-KAEDE の WEB コンパイラ用ソースを元にしているが、
かろうじて動いている状態なので、回収中。
※あまりにも、酷いソースコードなので、前面的に修正中・・・