]> granicus.if.org Git - python/commitdiff
Issue 12139: add CCC command support to FTP_TLS class to revert the SSL connection...
authorGiampaolo Rodola' <g.rodola@gmail.com>
Mon, 27 Jun 2011 09:17:51 +0000 (11:17 +0200)
committerGiampaolo Rodola' <g.rodola@gmail.com>
Mon, 27 Jun 2011 09:17:51 +0000 (11:17 +0200)
Doc/library/ftplib.rst
Doc/whatsnew/3.3.rst
Lib/ftplib.py
Lib/test/test_ftplib.py

index f4205f4a9ef890dbe5ecb2b2ff44d3abc488fa40..087e200424c53f67df1d964acd3047a82bdf8a36 100644 (file)
@@ -426,6 +426,14 @@ FTP_TLS Objects
 
    Set up secure control connection by using TLS or SSL, depending on what specified in :meth:`ssl_version` attribute.
 
+.. method:: FTP_TLS.ccc()
+
+   Revert control channel back to plaintex.  This can be useful to take
+   advantage of firewalls that know how to handle NAT with non-secure FTP
+   without opening fixed ports.
+
+   .. versionadded:: 3.3
+
 .. method:: FTP_TLS.prot_p()
 
    Set up secure data connection.
index 4374d0267d4e418b33400d2111c312b5d0d05794..63f8b155c8752eb9e25ba3df5ed6ca5b83f87c9e 100644 (file)
@@ -192,6 +192,17 @@ The :mod:`ssl` module has new functions:
   * :func:`~ssl.RAND_pseudo_bytes`: generate pseudo-random bytes.
 
 
+ftplib
+------
+
+The :class:`~ftplib.FTP_TLS` class now provides a new
+:func:`~ftplib.FTP_TLS.ccc` function to revert control channel back to
+plaintex.  This can be useful to take advantage of firewalls that know how to
+handle NAT with non-secure FTP without opening fixed ports.
+
+(Patch submitted by Giampaolo RodolĂ  in :issue:`12139`.)
+
+
 Optimizations
 =============
 
index d15a1353d8b8883adbf8b4d0b3066ff1f00bbe72..eaaa6fd69c0938949a33050e47a88b3782808545 100644 (file)
@@ -708,6 +708,14 @@ else:
             self.file = self.sock.makefile(mode='r', encoding=self.encoding)
             return resp
 
+        def ccc(self):
+            '''Switch back to a clear-text control connection.'''
+            if not isinstance(self.sock, ssl.SSLSocket):
+                raise ValueError("not using TLS")
+            resp = self.voidcmd('CCC')
+            self.sock = self.sock.unwrap()
+            return resp
+
         def prot_p(self):
             '''Set up secure data connection.'''
             # PROT defines whether or not the data channel is to be protected.
index 78e0fb4977ea1b12115ca876989307a55c498fa6..5734fd3453e8ef84fa18b98005787d0a7f89667e 100644 (file)
@@ -303,11 +303,11 @@ if ssl is not None:
         _ssl_closing = False
 
         def secure_connection(self):
-            self.del_channel()
             socket = ssl.wrap_socket(self.socket, suppress_ragged_eofs=False,
                                      certfile=CERTFILE, server_side=True,
                                      do_handshake_on_connect=False,
                                      ssl_version=ssl.PROTOCOL_SSLv23)
+            self.del_channel()
             self.set_socket(socket)
             self._ssl_accepting = True
 
@@ -342,7 +342,10 @@ if ssl is not None:
                 # http://www.mail-archive.com/openssl-users@openssl.org/msg60710.html
                 pass
             self._ssl_closing = False
-            super(SSLConnection, self).close()
+            if getattr(self, '_ccc', False) == False:
+                super(SSLConnection, self).close()
+            else:
+                pass
 
         def handle_read_event(self):
             if self._ssl_accepting:
@@ -410,12 +413,18 @@ if ssl is not None:
         def __init__(self, conn):
             DummyFTPHandler.__init__(self, conn)
             self.secure_data_channel = False
+            self._ccc = False
 
         def cmd_auth(self, line):
             """Set up secure control channel."""
             self.push('234 AUTH TLS successful')
             self.secure_connection()
 
+        def cmd_ccc(self, line):
+            self.push('220 Reverting back to clear-text')
+            self._ccc = True
+            self._do_ssl_shutdown()
+
         def cmd_pbsz(self, line):
             """Negotiate size of buffer for secure data transfer.
             For TLS/SSL the only valid value for the parameter is '0'.
@@ -872,6 +881,15 @@ class TestTLS_FTPClass(TestCase):
             self.assertIs(sock.context, ctx)
             self.assertIsInstance(sock, ssl.SSLSocket)
 
+    def test_ccc(self):
+        self.assertRaises(ValueError, self.client.ccc)
+        self.client.login(secure=True)
+        self.assertIsInstance(self.client.sock, ssl.SSLSocket)
+        self.client.ccc()
+        self.assertRaises(ValueError, self.client.sock.unwrap)
+        self.client.sendcmd('noop')
+        self.client.quit()
+
 
 class TestTimeouts(TestCase):