Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 096dcb1

Browse files
committed
Issue 12139: add CCC command support to FTP_TLS class to revert the SSL connection back to clear-text.
1 parent 5047839 commit 096dcb1

4 files changed

Lines changed: 47 additions & 2 deletions

File tree

Doc/library/ftplib.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,14 @@ FTP_TLS Objects
426426

427427
Set up secure control connection by using TLS or SSL, depending on what specified in :meth:`ssl_version` attribute.
428428

429+
.. method:: FTP_TLS.ccc()
430+
431+
Revert control channel back to plaintex. This can be useful to take
432+
advantage of firewalls that know how to handle NAT with non-secure FTP
433+
without opening fixed ports.
434+
435+
.. versionadded:: 3.3
436+
429437
.. method:: FTP_TLS.prot_p()
430438

431439
Set up secure data connection.

Doc/whatsnew/3.3.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,17 @@ The :mod:`ssl` module has new functions:
192192
* :func:`~ssl.RAND_pseudo_bytes`: generate pseudo-random bytes.
193193

194194

195+
ftplib
196+
------
197+
198+
The :class:`~ftplib.FTP_TLS` class now provides a new
199+
:func:`~ftplib.FTP_TLS.ccc` function to revert control channel back to
200+
plaintex. This can be useful to take advantage of firewalls that know how to
201+
handle NAT with non-secure FTP without opening fixed ports.
202+
203+
(Patch submitted by Giampaolo Rodolà in :issue:`12139`.)
204+
205+
195206
Optimizations
196207
=============
197208

Lib/ftplib.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,14 @@ def auth(self):
708708
self.file = self.sock.makefile(mode='r', encoding=self.encoding)
709709
return resp
710710

711+
def ccc(self):
712+
'''Switch back to a clear-text control connection.'''
713+
if not isinstance(self.sock, ssl.SSLSocket):
714+
raise ValueError("not using TLS")
715+
resp = self.voidcmd('CCC')
716+
self.sock = self.sock.unwrap()
717+
return resp
718+
711719
def prot_p(self):
712720
'''Set up secure data connection.'''
713721
# PROT defines whether or not the data channel is to be protected.

Lib/test/test_ftplib.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,11 +303,11 @@ class SSLConnection(asyncore.dispatcher):
303303
_ssl_closing = False
304304

305305
def secure_connection(self):
306-
self.del_channel()
307306
socket = ssl.wrap_socket(self.socket, suppress_ragged_eofs=False,
308307
certfile=CERTFILE, server_side=True,
309308
do_handshake_on_connect=False,
310309
ssl_version=ssl.PROTOCOL_SSLv23)
310+
self.del_channel()
311311
self.set_socket(socket)
312312
self._ssl_accepting = True
313313

@@ -342,7 +342,10 @@ def _do_ssl_shutdown(self):
342342
# http://www.mail-archive.com/[email protected]/msg60710.html
343343
pass
344344
self._ssl_closing = False
345-
super(SSLConnection, self).close()
345+
if getattr(self, '_ccc', False) == False:
346+
super(SSLConnection, self).close()
347+
else:
348+
pass
346349

347350
def handle_read_event(self):
348351
if self._ssl_accepting:
@@ -410,12 +413,18 @@ class DummyTLS_FTPHandler(SSLConnection, DummyFTPHandler):
410413
def __init__(self, conn):
411414
DummyFTPHandler.__init__(self, conn)
412415
self.secure_data_channel = False
416+
self._ccc = False
413417

414418
def cmd_auth(self, line):
415419
"""Set up secure control channel."""
416420
self.push('234 AUTH TLS successful')
417421
self.secure_connection()
418422

423+
def cmd_ccc(self, line):
424+
self.push('220 Reverting back to clear-text')
425+
self._ccc = True
426+
self._do_ssl_shutdown()
427+
419428
def cmd_pbsz(self, line):
420429
"""Negotiate size of buffer for secure data transfer.
421430
For TLS/SSL the only valid value for the parameter is '0'.
@@ -872,6 +881,15 @@ def test_context(self):
872881
self.assertIs(sock.context, ctx)
873882
self.assertIsInstance(sock, ssl.SSLSocket)
874883

884+
def test_ccc(self):
885+
self.assertRaises(ValueError, self.client.ccc)
886+
self.client.login(secure=True)
887+
self.assertIsInstance(self.client.sock, ssl.SSLSocket)
888+
self.client.ccc()
889+
self.assertRaises(ValueError, self.client.sock.unwrap)
890+
self.client.sendcmd('noop')
891+
self.client.quit()
892+
875893

876894
class TestTimeouts(TestCase):
877895

0 commit comments

Comments
 (0)