I have a need to connect to FTPS server to which I am able to connect successfully using lftp. However, when I try with Python ftplib.FTP_TLS, it times out, the stack trace shows that it is waiting for the server to send welcome message or like. Does anyone know what the issue is and how to overcome? I wonder if there is something needs to be done on server side, but how come lftp client is working fine. Any help is greatly appreciated.
Here is the stack trace:
ftp = ftplib.FTP_TLS()
ftp.connect(cfg.HOST, cfg.PORT, timeout=60)
File "C:\Users\username\Softwares\Python27\lib\ftplib.py", line 135, in connect
self.welcome = self.getresp()
File "C:\Users\username\Softwares\Python27\lib\ftplib.py", line 210, in getresp
resp = self.getmultiline()
File "C:\Users\username\Softwares\Python27\lib\ftplib.py", line 196, in getmultiline
line = self.getline()
File "C:\Users\username\Softwares\Python27\lib\ftplib.py", line 183, in getline
line = self.file.readline()
File "C:\Users\username\Softwares\Python27\lib\socket.py", line 447, in readline
data = self._sock.recv(self._rbufsize)
socket.timeout: timed out
A successful login using lftp to the same ftps server:
$ lftp
lftp :~> open ftps://ip_address:990
lftp ip_address:~> set ftps:initial-prot P
lftp ip_address:~> login ftps_user_id ftps_user_passwd
lftp sftp_user_id@ip_address:~> ls
ls: Fatal error: SSL_connect: self signed certificate
lftp ftps_user_id@ip_address:~> set ssl:verif-certificate off
lftp ftps_user_id@ip_address:~> ls
lftp ftps_user_id@ip_address:/>
BTW, I am using Python 2.7.3. I did quite a bit of search using Google but have not found anything helpful.
I am still having this issue, appreciate if someone can help. On looking closely the FTP.connect() the connection to server is not a problem but getting acknowledgement (or the welcome message) from server is an issue. lftp does not have this issue and FileZilla does not have any issue either as in the log here -
Status: Connecting to xx.xx.xx.xxx:990...
Status: Connection established, initializing TLS...
Status: Verifying certificate...
Status: TLS/SSL connection established, waiting for welcome message...
Response: 220- Vous allez vous connecter sur un serveur prive
Response: 220- Seules les personnes habilitees y sont autorisees
Response: 220 Les contrevenants s'exposent aux poursuites prevues par la loi.
Command: USER xxxxxxxxxxxxx
Response: 331 Password required for xxxxxxxxxxxxx.
Command: PASS **********
Response: 230 Login OK. Proceed.
Command: PBSZ 0
Response: 200 PBSZ Command OK. Protection buffer size set to 0.
Command: PROT P
Response: 200 PROT Command OK. Using Private data connection
Status: Connected
Status: Retrieving directory listing...
Command: PWD
Response: 257 "/" is current folder.
Command: TYPE I
Response: 200 Type set to I.
Command: PASV
Response: 227 Entering Passive Mode (81,93,20,199,4,206).
Command: MLSD
Response: 150 Opening BINARY mode data connection for MLSD /.
Response: 226 Transfer complete. 0 bytes transferred. 0 bps.
Status: Directory listing successful
The FTP client can implement TLS security by itself, or the FTP client can be configured to use Application Transparent Transport Layer Security (AT-TLS) as a controlling application. For more information on AT-TLS, see Application Transparent Transport Layer Security data protection.
This module defines the class FTP and a few related items. The FTP class implements the client side of the FTP protocol. You can use this to write Python programs that perform a variety of automated FTP jobs, such as mirroring other FTP servers.
The ftplib module included in Python allows you to use Python scripts to quickly attach to an FTP server, locate files, and then download them to be processed locally. To open a connection to the FTP server, create an FTP server object using the ftplib. FTP([host [, user [, passwd]]]) method.
Extending the solutions that have been proposed so far, the issue is that implicit FTPS connections need the socket to be ssl wrapped automatically, before we get a chance to call login(). A lot of the subclasses that people are proposing do this in the context of the connect method, we can more generally manage this by modifying the get/set of self.sock with a property to auto-wrap on set:
import ftplib import ssl class ImplicitFTP_TLS(ftplib.FTP_TLS): """FTP_TLS subclass that automatically wraps sockets in SSL to support implicit FTPS.""" def __init__(self, *args, **kwargs): 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
Usage is essentially the same as with the standard FTP_TLS class:
ftp_client = ImplicitFTP_TLS() ftp_client.connect(host='ftp.example.com', port=990) ftp_client.login(user='USERNAME', passwd='PASSWORD') ftp_client.prot_p()
I've worked on the same problem for half a day and finally figured it out.
For the implicit FTP TLS/SSL(defualt port 990), our client program must build a TLS/SSL connection right after the socket is created. But python's class FTP_TLS
doesn't reload the connect function from class FTP. We need to fix it:
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
This derived class reloads the connect function and builds a wrapper around the socket to TLS. After you successfully connect and login to FTP server, you need to call: FTP_TLS.prot_p()
before executing any FTP command!
Hope this will help ^_^
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With