连接到 Python 中的 'Explicit FTP over TLS' (??)
Connecting to 'Explicit FTP over TLS' in Python (??)
我不知道如何使用 ftplib 查看 FTP 站点的文件内容。
我可以使用 WinSCP 连接到 FTP 站点,并且可以看到根目录中的 6 个文件。
在Python 3.4中,我使用了以下代码:
from ftplib import FTP_TLS
ftps = FTP_TLS(timeout=100)
ftps.connect(ipAddress, 21)
ftps.auth()
ftps.prot_p()
ftps.login('username', 'password')
产生:
Out[72]: '230 User logged in.'
然后我可以运行这个:
ftps.pwd()
...我看到我在根目录中:
Out[73]: '/'
一切似乎都是肉汁。但是,当我尝试使用 ftps.dir() 或 ftps.retrlines('NLST') 或我尝试过的任何其他方法查看目录中的内容时,出现超时:
TimeoutError: [WinError 10060] A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond
我用谷歌搜索了一下,发现有几个人说这可能是 'passive vs active' 连接设置问题。但是,我问了FTP的管理员是被动还是主动,他表示不相信,只说,"It's not SFTP. It is explicit FTP over TLS!!"
我对此一无所知。我只是想使用 ftplib(或任何其他包!)连接到他的 FTP 并下载一些文件。
我错过了什么??
编辑:
我刚刚发现 WinSCP 使用的是被动模式,所以这似乎是可行的。据我了解,ftplib 默认情况下是被动的,但我还是继续将标志设置为 true。这什么也没做,我仍然遇到同样的问题。
我也检查了我的防火墙(摸索),它完全关闭了,所以那里应该没有问题。
我也试过使用'.sendcmd()'方法发送显式命令,它似乎能够发送命令,但不会获取任何响应(或者它看不到响应)。
是否有某种原因导致我能够看到我所在的目录,但看不到任何其他数据?我显然已连接并与服务器通信,而且目录对我来说可用和可见似乎意味着数据可以来回传输。数据是否可能以某种 ftplib 无法识别的方式返回?关于我应该从哪里开始对此进行故障排除有什么想法吗?
最后,再一次,似乎一切都已正确设置,因为我可以在 WinSCP 中看到一切正常。我应该检查哪些设置以与 ftplib 进行比较?
编辑 2:
按照要求,我设置了调试级别(感谢这个提示),下面是输出:
ftps = FTP_TLS(timeout=15)
ftps.set_debuglevel(2)
ftps.connect(ipAddress, 21)
ftps.set_pasv(True)
ftps.auth()
ftps.prot_p()
ftps.login('username', 'password')
ftps.retrlines('NLST')
*get* '220 Microsoft FTP Service\n'
*resp* '220 Microsoft FTP Service'
*cmd* 'AUTH TLS'
*put* 'AUTH TLS\r\n'
*get* '234 AUTH command ok. Expecting TLS Negotiation.\n'
*resp* '234 AUTH command ok. Expecting TLS Negotiation.'
*cmd* 'PBSZ 0'
*put* 'PBSZ 0\r\n'
*get* '200 PBSZ command successful.\n'
*resp* '200 PBSZ command successful.'
*cmd* 'PROT P'
*put* 'PROT P\r\n'
*get* '200 PROT command successful.\n'
*resp* '200 PROT command successful.'
*cmd* 'USER username'
*put* 'USER username\r\n'
*get* '331 Password required for username.\n'
*resp* '331 Password required for username.'
*cmd* 'PASS ****************'
*put* 'PASS ****************\r\n'
*get* '230 User logged in.\n'
*resp* '230 User logged in.'
*cmd* 'TYPE A'
*put* 'TYPE A\r\n'
*get* '200 Type set to A.\n'
*resp* '200 Type set to A.'
*cmd* 'PASV'
*put* 'PASV\r\n'
*get* '227 Entering Passive Mode (10,19,1,137,195,128).\n'
*resp* '227 Entering Passive Mode (10,19,1,137,195,128).'
Traceback (most recent call last):
File "<ipython-input-13-a79eb3c23dc5>", line 8, in <module>
ftps.retrlines('NLST')
File "C:\Anaconda3\lib\ftplib.py", line 467, in retrlines
with self.transfercmd(cmd) as conn,
File "C:\Anaconda3\lib\ftplib.py", line 398, in transfercmd
return self.ntransfercmd(cmd, rest)[0]
File "C:\Anaconda3\lib\ftplib.py", line 793, in ntransfercmd
conn, size = FTP.ntransfercmd(self, cmd, rest)
File "C:\Anaconda3\lib\ftplib.py", line 360, in ntransfercmd
source_address=self.source_address)
File "C:\Anaconda3\lib\socket.py", line 516, in create_connection
raise err
File "C:\Anaconda3\lib\socket.py", line 507, in create_connection
sock.connect(sa)
timeout: timed out
编辑 3:
我试图将 passive 标志设置为 false,但是当我请求文件时,我得到以下信息:
ftps.retrlines('NLST')
*cmd* 'TYPE A'
*put* 'TYPE A\r\n'
*get* '200 Type set to A.\n'
*resp* '200 Type set to A.'
*cmd* 'PORT 10,1,10,100,223,39'
*put* 'PORT 10,1,10,100,223,39\r\n'
*get* '501 Server cannot accept argument.\n'
*resp* '501 Server cannot accept argument.'
编辑 4:
我在 WinSCP 上启用了日志记录,看起来很有帮助的 Steffen Ullrich 是对的!这是在 WinSCP 的日志中:
< 2017-05-19 08:44:27.880 227 Entering Passive Mode (10,19,1,137,195,139).
< 2017-05-19 08:44:27.880 Server sent passive reply with unroutable address 10.19.1.137, using host address instead.
那么,如何让 ftplib 做同样的事情呢?
编辑 5:
找到 THIS - 这只是重写源代码以使其在主机上看起来。还有其他人有更简单、更少 intrusive/possibly-destructive 的方法吗?
作为第二个选择,因为管理员似乎不太合格,我可以告诉他明确的事情让他去做东道主以使这项工作更好(即 - 被动 return正确的 IP)?
*get* '227 Entering Passive Mode (10,19,1,137,195,128).\n'
问题是服务器在对 PASV 命令的响应中返回了错误的 IP 地址。这对于某些防火墙后面的某些内部网络中的服务器来说是典型的。在这种情况下返回 10.19.1.137,这是一个只能在本地网络中使用的 IP 地址。
这是 FTP 服务器的错误设置。但不幸的是,这种错误的设置很常见,因此许多客户端通过忽略响应中给定的 IP 地址并使用控制连接的 IP 地址来解决它。 ftplib 不支持此类解决方法。但是可以猴子修补它来提供这样的支持:
from ftplib import FTP_TLS
# replace original makepasv function with one which always returns
# the peerhost of the control connections as peerhost for the data
# connection
_old_makepasv = FTP_TLS.makepasv
def _new_makepasv(self):
host,port = _old_makepasv(self)
host = self.sock.getpeername()[0]
return host,port
FTP_TLS.makepasv = _new_makepasv
ftp = FTP_TLS(ipAddress)
ftp.login(...)
ftp.nlst()
已通过 Python 2.7.12 和 Python 3.5.2
成功测试
我不知道如何使用 ftplib 查看 FTP 站点的文件内容。
我可以使用 WinSCP 连接到 FTP 站点,并且可以看到根目录中的 6 个文件。
在Python 3.4中,我使用了以下代码:
from ftplib import FTP_TLS
ftps = FTP_TLS(timeout=100)
ftps.connect(ipAddress, 21)
ftps.auth()
ftps.prot_p()
ftps.login('username', 'password')
产生:
Out[72]: '230 User logged in.'
然后我可以运行这个:
ftps.pwd()
...我看到我在根目录中:
Out[73]: '/'
一切似乎都是肉汁。但是,当我尝试使用 ftps.dir() 或 ftps.retrlines('NLST') 或我尝试过的任何其他方法查看目录中的内容时,出现超时:
TimeoutError: [WinError 10060] A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond
我用谷歌搜索了一下,发现有几个人说这可能是 'passive vs active' 连接设置问题。但是,我问了FTP的管理员是被动还是主动,他表示不相信,只说,"It's not SFTP. It is explicit FTP over TLS!!"
我对此一无所知。我只是想使用 ftplib(或任何其他包!)连接到他的 FTP 并下载一些文件。
我错过了什么??
编辑: 我刚刚发现 WinSCP 使用的是被动模式,所以这似乎是可行的。据我了解,ftplib 默认情况下是被动的,但我还是继续将标志设置为 true。这什么也没做,我仍然遇到同样的问题。
我也检查了我的防火墙(摸索),它完全关闭了,所以那里应该没有问题。
我也试过使用'.sendcmd()'方法发送显式命令,它似乎能够发送命令,但不会获取任何响应(或者它看不到响应)。
是否有某种原因导致我能够看到我所在的目录,但看不到任何其他数据?我显然已连接并与服务器通信,而且目录对我来说可用和可见似乎意味着数据可以来回传输。数据是否可能以某种 ftplib 无法识别的方式返回?关于我应该从哪里开始对此进行故障排除有什么想法吗?
最后,再一次,似乎一切都已正确设置,因为我可以在 WinSCP 中看到一切正常。我应该检查哪些设置以与 ftplib 进行比较?
编辑 2:
按照要求,我设置了调试级别(感谢这个提示),下面是输出:
ftps = FTP_TLS(timeout=15)
ftps.set_debuglevel(2)
ftps.connect(ipAddress, 21)
ftps.set_pasv(True)
ftps.auth()
ftps.prot_p()
ftps.login('username', 'password')
ftps.retrlines('NLST')
*get* '220 Microsoft FTP Service\n'
*resp* '220 Microsoft FTP Service'
*cmd* 'AUTH TLS'
*put* 'AUTH TLS\r\n'
*get* '234 AUTH command ok. Expecting TLS Negotiation.\n'
*resp* '234 AUTH command ok. Expecting TLS Negotiation.'
*cmd* 'PBSZ 0'
*put* 'PBSZ 0\r\n'
*get* '200 PBSZ command successful.\n'
*resp* '200 PBSZ command successful.'
*cmd* 'PROT P'
*put* 'PROT P\r\n'
*get* '200 PROT command successful.\n'
*resp* '200 PROT command successful.'
*cmd* 'USER username'
*put* 'USER username\r\n'
*get* '331 Password required for username.\n'
*resp* '331 Password required for username.'
*cmd* 'PASS ****************'
*put* 'PASS ****************\r\n'
*get* '230 User logged in.\n'
*resp* '230 User logged in.'
*cmd* 'TYPE A'
*put* 'TYPE A\r\n'
*get* '200 Type set to A.\n'
*resp* '200 Type set to A.'
*cmd* 'PASV'
*put* 'PASV\r\n'
*get* '227 Entering Passive Mode (10,19,1,137,195,128).\n'
*resp* '227 Entering Passive Mode (10,19,1,137,195,128).'
Traceback (most recent call last):
File "<ipython-input-13-a79eb3c23dc5>", line 8, in <module>
ftps.retrlines('NLST')
File "C:\Anaconda3\lib\ftplib.py", line 467, in retrlines
with self.transfercmd(cmd) as conn,
File "C:\Anaconda3\lib\ftplib.py", line 398, in transfercmd
return self.ntransfercmd(cmd, rest)[0]
File "C:\Anaconda3\lib\ftplib.py", line 793, in ntransfercmd
conn, size = FTP.ntransfercmd(self, cmd, rest)
File "C:\Anaconda3\lib\ftplib.py", line 360, in ntransfercmd
source_address=self.source_address)
File "C:\Anaconda3\lib\socket.py", line 516, in create_connection
raise err
File "C:\Anaconda3\lib\socket.py", line 507, in create_connection
sock.connect(sa)
timeout: timed out
编辑 3:
我试图将 passive 标志设置为 false,但是当我请求文件时,我得到以下信息:
ftps.retrlines('NLST')
*cmd* 'TYPE A'
*put* 'TYPE A\r\n'
*get* '200 Type set to A.\n'
*resp* '200 Type set to A.'
*cmd* 'PORT 10,1,10,100,223,39'
*put* 'PORT 10,1,10,100,223,39\r\n'
*get* '501 Server cannot accept argument.\n'
*resp* '501 Server cannot accept argument.'
编辑 4:
我在 WinSCP 上启用了日志记录,看起来很有帮助的 Steffen Ullrich 是对的!这是在 WinSCP 的日志中:
< 2017-05-19 08:44:27.880 227 Entering Passive Mode (10,19,1,137,195,139).
< 2017-05-19 08:44:27.880 Server sent passive reply with unroutable address 10.19.1.137, using host address instead.
那么,如何让 ftplib 做同样的事情呢?
编辑 5:
找到 THIS - 这只是重写源代码以使其在主机上看起来。还有其他人有更简单、更少 intrusive/possibly-destructive 的方法吗?
作为第二个选择,因为管理员似乎不太合格,我可以告诉他明确的事情让他去做东道主以使这项工作更好(即 - 被动 return正确的 IP)?
*get* '227 Entering Passive Mode (10,19,1,137,195,128).\n'
问题是服务器在对 PASV 命令的响应中返回了错误的 IP 地址。这对于某些防火墙后面的某些内部网络中的服务器来说是典型的。在这种情况下返回 10.19.1.137,这是一个只能在本地网络中使用的 IP 地址。
这是 FTP 服务器的错误设置。但不幸的是,这种错误的设置很常见,因此许多客户端通过忽略响应中给定的 IP 地址并使用控制连接的 IP 地址来解决它。 ftplib 不支持此类解决方法。但是可以猴子修补它来提供这样的支持:
from ftplib import FTP_TLS
# replace original makepasv function with one which always returns
# the peerhost of the control connections as peerhost for the data
# connection
_old_makepasv = FTP_TLS.makepasv
def _new_makepasv(self):
host,port = _old_makepasv(self)
host = self.sock.getpeername()[0]
return host,port
FTP_TLS.makepasv = _new_makepasv
ftp = FTP_TLS(ipAddress)
ftp.login(...)
ftp.nlst()
已通过 Python 2.7.12 和 Python 3.5.2
成功测试