#はじめに
MFC使用中に名状しがたいFTPの異常動作に悩まされ苦労して真実を見つけたのでここに残す。
##CFtpConnection errorcode 12003
古いプログラムでFTPを使う際、エラーコード12003が返ってきて異常動作をしていた。
確率はとても低く最初は許容できそうだと思っていたが、イーサネットドライバ辺りがメモリを破壊するのか、PCによっては致命的不具合が起きそうなので対策を講ずることにした。
FTPでダウンロード、サーバ上でリネームを行っていただけであり、コードに問題はない。
WireSharkでパケットを解析すると、RenameしたときのFTPのレスポンスがおかしいことが分かった。
##環境
クライアント側:XP,WIN7 CFtpConnection使用
サーバ側:Windows CE 他不明
アクティブモード
##RFC959非準拠とFtpCommandの相性
RFC959によるとRNTOへのレスポンスで許容されるのは250であるが、Windows CEのFTPサーバは以下のようなものであった。(手打ちなのでレスポンスコード以外は適当)
RNFR hoge
350 file exists
RNTO huga
226 closing connection
250 file action is okey
226 Transfer complete
// 最初の226が226-でないかはうろ覚えなので不明
何故かRNTOに3個もレスポンスを投げやがるのである。というかほかのコマンドでも226を返しまくる挙動をしていた。だがその割に問題が起きる確率は低かった。
なお、先に記述した通り、期待値は250であり、WireSharkでパケットをよく見ると、2度目の226のパケットが250と連結して送られていれば問題が発生しない規則性を発見した。
これによって未処理のレスポンスコードがコントロール側のポートに残存していると推測した。
その考えでパケットを読むと、発生状況と一致しており、それが起きている確率が高いと判断した。
以上の理由によってレスポンスコードと要求の対応がずれていき、未定義値が返ってきたと判断されるタイミングで12003エラーが起きる。
そのため、原因と結果がかなり離れて発生していた。
再掲
RNFR hoge
350 file exists
RNTO huga
226 closing connection <-レスポンス処理済み
250 file action is okey <-レスポンス処理済み
226 Transfer complete <-未処理で受信バッファに残る
RETR foo //ここでGetFile()すると、上記の226 Transfer completeがレスポンスだと判断され、1つずつレスポンスがずれていく。
この問題の発端は226を返すサーバだが、CFtpConnectionの要求動作にも問題がある。
受信バッファをクリアしてから要求していれば、問題は起きなかった。そして、手動でクリアする方法も用意されていなかった。
なお、ウェイトを入れて2回目の226を待っても意味はなかった。CFtpConnection::Rename()は250が来た段階でreturnしているようである。
##解決方法
受信バッファクリアが正解だが、APIなので方法がない。
そのため、Renameコマンドを使用するたび、切断して再ログインするのが正解だった。
その後、問題は再発していない。
##ほかのコマンドは安全か?
GetFile()に関しては226が返って来ているが、連続でやり続けても何も起きていない。
#結論
エラーコード12003が来たら、FTPサーバが余計なレスポンスを返してないか調べよう。
CFtpConnectionはWininetをそのまま使っているらしいので、不具合も引き継いでいる。
MFCはいい加減捨てよう。
この問題は深刻である。受信バッファ上に何か残っているとありとあらゆる問題が起きる。
###参考
RFC959 FTP 日本語訳