Python FTP隐式TLS连接问题

Python FTP隐式TLS连接问题,python,ssl,ftp,ftplib,ftps,Python,Ssl,Ftp,Ftplib,Ftps,我需要连接到FTPS服务器,我可以使用lftp成功连接到该服务器。但是,当我尝试使用Python ftplib.FTP_TLS时,它超时,堆栈跟踪显示它正在等待服务器发送欢迎消息或类似消息。有人知道问题是什么以及如何克服吗?我想知道服务器端是否需要做些什么,但为什么lftp客户端工作得很好呢。非常感谢您的帮助 以下是堆栈跟踪: ftp=ftplib.ftp\u TLS() ftp.connect(cfg.HOST,cfg.PORT,超时=60) 文件“C:\Users\username\Soft

我需要连接到FTPS服务器,我可以使用lftp成功连接到该服务器。但是,当我尝试使用Python ftplib.FTP_TLS时,它超时,堆栈跟踪显示它正在等待服务器发送欢迎消息或类似消息。有人知道问题是什么以及如何克服吗?我想知道服务器端是否需要做些什么,但为什么lftp客户端工作得很好呢。非常感谢您的帮助

以下是堆栈跟踪:

ftp=ftplib.ftp\u TLS()
ftp.connect(cfg.HOST,cfg.PORT,超时=60)
文件“C:\Users\username\Softwares\Python27\lib\ftplib.py”,第135行,在connect中
self.welcome=self.getresp()
文件“C:\Users\username\Softwares\Python27\lib\ftplib.py”,第210行,在getresp中
resp=self.getmultiline()
文件“C:\Users\username\Softwares\Python27\lib\ftplib.py”,第196行,在getmultiline中
line=self.getline()
文件“C:\Users\username\Softwares\Python27\lib\ftplib.py”,第183行,在getline中
line=self.file.readline()
文件“C:\Users\username\Softwares\Python27\lib\socket.py”,第447行,在readline中
数据=self.\u sock.recv(self.\r bufsize)
socket.timeout:超时
使用lftp成功登录到同一ftps服务器:

$lftp
lftp:~>打开ftps://ip_address:990
lftp ip_地址:~>设置ftps:初始保护
lftp ip\u地址:~>登录ftps\u用户\u id ftps\u用户\u密码
lftp sftp_用户_id@ip_address:~>ls
ls:致命错误:SSL\U connect:自签名证书
lftp ftps_用户_id@ip_address:~>设置ssl:verif证书关闭
lftp ftps_用户_id@ip_address:~>ls
lftp ftps_用户_id@ip_address:/>
顺便说一句,我使用的是Python 2.7.3。我用谷歌做了很多搜索,但没有发现任何有用的东西

我仍然有这个问题,如果有人能帮助我,我将不胜感激。仔细查看FTP.connect()时,与服务器的连接不是问题,但从服务器获得确认(或欢迎消息)是一个问题。lftp没有此问题,FileZilla也没有任何问题,如此处的日志中所示-

状态:正在连接到xx.xx.xx.xxx:990。。。
状态:已建立连接,正在初始化TLS。。。
状态:正在验证证书。。。
状态:TLS/SSL连接已建立,正在等待欢迎消息。。。
回复:220-您在私人服务区的所有连接处
答复:220-有能力的人和有能力的人
回应:220 LES传染病患者暴露的副作用。
命令:用户xxxxxxxx
回复:XXXXXXXXXXXX需要331密码。
命令:通过*********
回复:230登录正常。继续
命令:PBSZ 0
响应:200 PBSZ命令正常。保护缓冲区大小设置为0。
命令:protp
响应:200保护命令正常。使用专用数据连接
状态:已连接
状态:正在检索目录列表。。。
命令:PWD
响应:257“/”是当前文件夹。
命令:I型
响应:200类型设置为I。
命令:PASV
响应:227进入被动模式(81,93,20199,4206)。
命令:MLSD
响应:150打开MLSD/的二进制模式数据连接。
答复:226传输完成。已传输0个字节。0个基点。
状态:目录列表成功

我花了半天时间研究同一个问题,最后终于解决了

对于隐式FTP TLS/SSL(defualt端口990),我们的客户端程序必须在创建套接字之后立即构建TLS/SSL连接。但是python的类
FTP\u TLS
没有从类FTP重新加载connect函数。我们需要解决这个问题:

class tyFTP(ftplib.FTP_TLS):
  def __init__(self,
               host='',
               user='',
               passwd='',
               acct='',
               keyfile=None,
               certfile=None,
               timeout=60):

    ftplib.FTP_TLS.__init__(self,
                            host=host,
                            user=user,
                            passwd=passwd,
                            acct=acct,
                            keyfile=keyfile,
                            certfile=certfile,
                            timeout=timeout)

  def connect(self, host='', port=0, timeout=-999):
    """Connect to host.  Arguments are:
    - host: hostname to connect to (string, default previous host)
    - port: port to connect to (integer, default previous port)
    """
    if host != '':
        self.host = host
    if port > 0:
        self.port = port
    if timeout != -999:
        self.timeout = timeout
    try:
        self.sock = socket.create_connection((self.host, self.port), self.timeout)
        self.af = self.sock.family
        # add this line!!!
        self.sock = ssl.wrap_socket(self.sock,
                                    self.keyfile,
                                    self.certfile,
                                    ssl_version=ssl.PROTOCOL_TLSv1)
        # add end
        self.file = self.sock.makefile('rb')
        self.welcome = self.getresp()
    except Exception as e:
        print(e)
    return self.welcome
这个派生类重新加载connect函数,并围绕套接字向TLS构建一个包装器。成功连接并登录到FTP服务器后,在执行任何FTP命令之前,需要调用:
FTP\u TLS.prot\u p()


希望这将有助于^ ^

扩展NERV的响应-这对我有很大帮助,下面是我如何在需要身份验证的端口990上解决隐式TLS连接的问题

文件名:ImplicitTLS.py

from ftplib import FTP_TLS
import socket
import ssl

class tyFTP(FTP_TLS):
    def __init__(self, host='', user='', passwd='', acct='', keyfile=None, certfile=None, timeout=60):
        FTP_TLS.__init__(self, host, user, passwd, acct, keyfile, certfile, timeout)
    def connect(self, host='', port=0, timeout=-999):
        if host != '':
            self.host = host
        if port > 0:
            self.port = port
        if timeout != -999:
            self.timeout = timeout

        try: 
            self.sock = socket.create_connection((self.host, self.port), self.timeout)
            self.af = self.sock.family
            self.sock = ssl.wrap_socket(self.sock, self.keyfile, self.certfile, ssl_version=ssl.PROTOCOL_TLSv1)
            self.file = self.sock.makefile('rb')
            self.welcome = self.getresp()
        except Exception as e:
            print e
        return self.welcome
然后在我的主要应用程序中,我做了以下操作:

from ImplicityTLS import tyFTP
server = tyFTP()
server.connect(host="xxxxx", port=990)
server.login(user="yyyy", passwd="fffff")
server.prot_p()

就是这样,我可以下载文件,等等。道具去NERV获取原始答案

NERV的回答和Brad Decker的样本非常有用。他们的荣誉。他们节省了我几个小时

不幸的是,最初它对我不起作用

在我的例子中,当我从
ssl.wrap\u套接字
方法中删除
ssl\u version
参数后,连接就开始工作了。 另外,为了向服务器发送任何命令,我必须从
FTP\u TLS
类中覆盖
ntransfercmd
方法,并删除那里的
ssl\u version
参数

这就是对我有用的代码:

from ftplib import FTP_TLS, FTP
import socket
import ssl

class IMPLICIT_FTP_TLS(FTP_TLS):
    def __init__(self, host='', user='', passwd='', acct='', keyfile=None,
        certfile=None, timeout=60):
        FTP_TLS.__init__(self, host, user, passwd, acct, keyfile, certfile, timeout)

    def connect(self, host='', port=0, timeout=-999):
        '''Connect to host.  Arguments are:
        - host: hostname to connect to (string, default previous host)
        - port: port to connect to (integer, default previous port)
        '''
        if host != '':
            self.host = host
        if port > 0:
            self.port = port
        if timeout != -999:
            self.timeout = timeout
        try:
            self.sock = socket.create_connection((self.host, self.port), self.timeout)
            self.af = self.sock.family
            self.sock = ssl.wrap_socket(self.sock, self.keyfile, self.certfile)
            self.file = self.sock.makefile('rb')
            self.welcome = self.getresp()
        except Exception as e:
            print (e)
        return self.welcome

    def ntransfercmd(self, cmd, rest=None):
        conn, size = FTP.ntransfercmd(self, cmd, rest)
        if self._prot_p:
            conn = ssl.wrap_socket(conn, self.keyfile, self.certfile)
        return conn, size
以及强制性样本:

>>> ftps = IMPLICIT_FTP_TLS()
>>> ftps.connect(host='your.ftp.host', port=990)
>>> ftps.login(user="your_user", passwd="your_passwd")
>>> ftps.prot_p()
>>> ftps.retrlines('LIST')

我知道这个帖子已经很老了,ftp也不像以前那么流行了,但无论如何,如果它能帮助任何人的话,我想做出额外的贡献。在被动模式下使用隐式(端口990)ftps连接ftp服务器时,我遇到了类似的情况。在这种情况下,在协商初始连接后,服务器通常会提供一个新的主机IP地址和端口,可能与用于进行初始连接的主机IP地址和端口不同,实际的数据传输应该在这些地址和端口上进行。没什么大不了的,ftps客户端,包括python,可以处理这个问题,只有这个特定的服务器提供了一个不可路由(可能是防火墙内部)的IP地址。我注意到FileZilla连接没有问题,但python ftplib不能。然后我遇到了这样一条线索:

这让我陷入了困境。使用Grzegorz-Wierzowiecki方法,我扩展了该线程中提到的方法,并提出了这个方法,解决了我的问题

import ftplib, os, sys
import socket
import ssl
FTPS_OBJ = ftplib.FTP_TLS



def conn_i_ftps(FTP_Site, Login_Name, Login_Password):
    print "Starting IMPLICIT ftp_tls..."
    ftps = tyFTP()
    print ftps.connect(host=FTP_Site, port=990, timeout=120)
    ftps.prot_p()
    ftps.login(user=Login_Name, passwd=Login_Password)
    print "Logged In"
    ftps.retrlines('LIST')
    # return ftps


class tyFTP(FTPS_OBJ):
    def __init__(self, host='', user='', passwd='', acct='', keyfile=None, certfile=None, timeout=60):
        FTPS_OBJ.__init__(self, host, user, passwd, acct, keyfile, certfile, timeout)

    def connect(self, host='', port=0, timeout=-999):
        if host != '':
            self.host = host
        if port > 0:
            self.port = port
        if timeout != -999:
            self.timeout = timeout

        try:
            self.sock = socket.create_connection((self.host, self.port), self.timeout)
            self.af = self.sock.family
            self.sock = ssl.wrap_socket(self.sock, self.keyfile, self.certfile)
            self.file = self.sock.makefile('rb')
            self.welcome = self.getresp()
        except Exception as e:
            print e
        return self.welcome

    def makepasv(self):
        print port #<---Show passively assigned port
        print host #<---Show the non-routable, passively assigned IP
        host, port = FTPS_OBJ.makepasv(self)
        host = socket.gethostbyname(self.host) #<---- This changes the host back to the original IP that was used for the connection
        print 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
        print host #<----Showing the original IP
        return host, port
我想我们可以用一个I来包装主机被换回来的线路
FTP_Site       =  "ftp.someserver.com"
Login_Name     =  "some_name"
Login_Password =  "some_passwd"

conn_i_ftps(FTP_Site, Login_Name, Login_Password)
if host.split(".")[0] in (10, 192, 172):
     host = socket.gethostbyname(self.host)
     .
     .
     .
INFO - FTPS connect() done: 220 (vsFTPd 3.0.2)
INFO - FTPS prot_p() done: 200 PROT now Private.
INFO - FTPS login() done: 230 Login successful.
INFO - FTPS session successfully opened
-rw-------    1 ftp      ftp         86735 Mar 22 16:55 MyModel.yaml
-rw-------    1 ftp      ftp          9298 Mar 22 16:55 MyData.csv
226 Directory send OK.
INFO - FTPS close() done
INFO - FTPS session successfully closed
450 TLS session of data connection has not resumed or the session does not match the control connection
class ImplicitFTP_TLS(ftplib.FTP_TLS):
    """
    FTP_TLS subclass that automatically wraps sockets in SSL to support implicit FTPS.
    Prefer explicit TLS whenever possible.
    """

    def __init__(self, *args, **kwargs):
        """Initialise self."""
        super().__init__(*args, **kwargs)
        self._sock = None

    @property
    def sock(self):
        """Return the socket."""
        return self._sock

    @sock.setter
    def sock(self, value):
        """When modifying the socket, ensure that it is SSL wrapped."""
        if value is not None and not isinstance(value, ssl.SSLSocket):
            value = self.context.wrap_socket(value)
        self._sock = value

    def ntransfercmd(self, cmd, rest=None):
        """Override the ntransfercmd method"""
        conn, size = ftplib.FTP.ntransfercmd(self, cmd, rest)
        conn = self.sock.context.wrap_socket(
            conn, server_hostname=self.host, session=self.sock.session
        )
        return conn, size