FTPで接続は出来るがデータ取得が出来ない場合の対応についてご紹介します。
状況
FTPコマンド
- 接続は出来る。
- データを取得しようとすると「425 Cannot open data connection.」となる。
WinSCPを用いたFTP接続
- 接続、データ取得ともに可能。
WinSCPのログ確認
FTPコマンドではデータ取得が出来ず、WinSCPではデータ取得が出来るので、WinSCPの動作を確認するためにログを確認しました。
. 2019-03-13 09:40:10.581 ディレクトリ一覧の取得が成功しました . 2019-03-13 09:40:10.581 LIST with -a switch returned empty directory listing, will try pure LIST . 2019-03-13 09:40:10.581 ディレクトリ一覧を取得中... > 2019-03-13 09:40:10.581 TYPE A < 2019-03-13 09:40:10.614 200 Command okay. > 2019-03-13 09:40:10.615 PASV < 2019-03-13 09:40:10.629 227 Entering Passive Mode (192,168,10,2,45,152) . 2019-03-13 09:40:10.629 Server sent passive reply with unroutable address 192.168.10.2, using host address instead. > 2019-03-13 09:40:10.629 LIST . 2019-03-13 09:40:10.630 121.XXX.XXX.XXX:11672 に接続中... < 2019-03-13 09:40:10.715 150 File status okay; about to open data connection.
ログを見ると「Passive Mode」でFTPが動作し、「192,168,10,2」というプライベートIPが返されていることが分かります。
通常、「Passive Mode」ではグローバルIPが返されるはずですが、接続先のネットワーク設定がおかしいようです。
ただし、WinSCPは賢いため改めてホストアドレスで接続を試みて接続に成功しています。
(Server sent passive reply with unroutable address 192.168.10.2, using host address instead.)
対応
上記のように、FTPが「Passive Mode」でライベートIPが返される場合、残念ながらFTPコマンドでは対処できません。
サーバー側のネットワーク設定を修正し、グローバルIPが返されるようにする必要があります。
一方、プログラムを用いたFTPであれば、WinSCPのような対応が可能となります。
以下は、PythonでFTPを行うためのサンプルです。
(ftplibを使用します)
from ftplib import FTP host = 'ホスト名' port = ポート(21など) user = 'ユーザ名' passwd = 'パスワード' _old_makepasv = FTP.makepasv def _new_makepasv(self): host,port = _old_makepasv(self) host = self.sock.getpeername()[0] return host,port FTP.makepasv = _new_makepasv ftps = FTP() ftps.connect(host, int(port)) ftps.login(user, passwd) ftps.cwd('s:/2019/') ftps.set_pasv(True) print('Current directory:') print(ftps.pwd()) files = ftps.dir() ftps.quit()
以下の記述がポイントです。
データ情報取得時にホスト名で接続するようになります。
_old_makepasv = FTP.makepasv def _new_makepasv(self): host,port = _old_makepasv(self) host = self.sock.getpeername()[0] return host,port FTP.makepasv = _new_makepasv
参考
stackoverflow
https://stackoverflow.com/questions/44057732/connecting-to-explicit-ftp-over-tls-in-python