mirror of
https://github.com/python/cpython.git
synced 2025-10-17 04:08:28 +00:00
Merged revisions 80392 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk ........ r80392 | antoine.pitrou | 2010-04-23 01:33:02 +0200 (ven., 23 avril 2010) | 9 lines Issue #8108: Fix the unwrap() method of SSL objects when the socket has a non-infinite timeout. Also make that method friendlier with applications wanting to continue using the socket in clear-text mode, by disabling OpenSSL's internal readahead. Thanks to Darryl Miles for guidance. Issue #8108: test_ftplib's non-blocking SSL server now has proper handling of SSL shutdowns. ........
This commit is contained in:
parent
582c0a6ac2
commit
2c4f98b3c5
3 changed files with 108 additions and 18 deletions
|
@ -28,6 +28,7 @@ NLST_DATA = 'foo\r\nbar\r\n'
|
|||
|
||||
|
||||
class DummyDTPHandler(asynchat.async_chat):
|
||||
dtp_conn_closed = False
|
||||
|
||||
def __init__(self, conn, baseclass):
|
||||
asynchat.async_chat.__init__(self, conn)
|
||||
|
@ -38,8 +39,13 @@ class DummyDTPHandler(asynchat.async_chat):
|
|||
self.baseclass.last_received_data += self.recv(1024).decode('ascii')
|
||||
|
||||
def handle_close(self):
|
||||
self.baseclass.push('226 transfer complete')
|
||||
self.close()
|
||||
# XXX: this method can be called many times in a row for a single
|
||||
# connection, including in clear-text (non-TLS) mode.
|
||||
# (behaviour witnessed with test_data_connection)
|
||||
if not self.dtp_conn_closed:
|
||||
self.baseclass.push('226 transfer complete')
|
||||
self.close()
|
||||
self.dtp_conn_closed = True
|
||||
|
||||
def push(self, what):
|
||||
super(DummyDTPHandler, self).push(what.encode('ascii'))
|
||||
|
@ -254,6 +260,7 @@ if ssl is not None:
|
|||
"""An asyncore.dispatcher subclass supporting TLS/SSL."""
|
||||
|
||||
_ssl_accepting = False
|
||||
_ssl_closing = False
|
||||
|
||||
def secure_connection(self):
|
||||
self.del_channel()
|
||||
|
@ -280,15 +287,36 @@ if ssl is not None:
|
|||
else:
|
||||
self._ssl_accepting = False
|
||||
|
||||
def _do_ssl_shutdown(self):
|
||||
self._ssl_closing = True
|
||||
try:
|
||||
self.socket = self.socket.unwrap()
|
||||
except ssl.SSLError as err:
|
||||
if err.args[0] in (ssl.SSL_ERROR_WANT_READ,
|
||||
ssl.SSL_ERROR_WANT_WRITE):
|
||||
return
|
||||
except socket.error as err:
|
||||
# Any "socket error" corresponds to a SSL_ERROR_SYSCALL return
|
||||
# from OpenSSL's SSL_shutdown(), corresponding to a
|
||||
# closed socket condition. See also:
|
||||
# http://www.mail-archive.com/openssl-users@openssl.org/msg60710.html
|
||||
pass
|
||||
self._ssl_closing = False
|
||||
super(SSLConnection, self).close()
|
||||
|
||||
def handle_read_event(self):
|
||||
if self._ssl_accepting:
|
||||
self._do_ssl_handshake()
|
||||
elif self._ssl_closing:
|
||||
self._do_ssl_shutdown()
|
||||
else:
|
||||
super(SSLConnection, self).handle_read_event()
|
||||
|
||||
def handle_write_event(self):
|
||||
if self._ssl_accepting:
|
||||
self._do_ssl_handshake()
|
||||
elif self._ssl_closing:
|
||||
self._do_ssl_shutdown()
|
||||
else:
|
||||
super(SSLConnection, self).handle_write_event()
|
||||
|
||||
|
@ -308,7 +336,7 @@ if ssl is not None:
|
|||
except ssl.SSLError as err:
|
||||
if err.args[0] in (ssl.SSL_ERROR_WANT_READ,
|
||||
ssl.SSL_ERROR_WANT_WRITE):
|
||||
return ''
|
||||
return b''
|
||||
if err.args[0] in (ssl.SSL_ERROR_EOF, ssl.SSL_ERROR_ZERO_RETURN):
|
||||
self.handle_close()
|
||||
return b''
|
||||
|
@ -318,12 +346,9 @@ if ssl is not None:
|
|||
raise
|
||||
|
||||
def close(self):
|
||||
try:
|
||||
if isinstance(self.socket, ssl.SSLSocket):
|
||||
if self.socket._sslobj is not None:
|
||||
self.socket.unwrap()
|
||||
finally:
|
||||
super(SSLConnection, self).close()
|
||||
if (isinstance(self.socket, ssl.SSLSocket) and
|
||||
self.socket._sslobj is not None):
|
||||
self._do_ssl_shutdown()
|
||||
|
||||
|
||||
class DummyTLS_DTPHandler(SSLConnection, DummyDTPHandler):
|
||||
|
@ -606,21 +631,21 @@ class TestTLS_FTPClass(TestCase):
|
|||
sock = self.client.transfercmd('list')
|
||||
self.assertNotIsInstance(sock, ssl.SSLSocket)
|
||||
sock.close()
|
||||
self.client.voidresp()
|
||||
self.assertEqual(self.client.voidresp(), "226 transfer complete")
|
||||
|
||||
# secured, after PROT P
|
||||
self.client.prot_p()
|
||||
sock = self.client.transfercmd('list')
|
||||
self.assertIsInstance(sock, ssl.SSLSocket)
|
||||
sock.close()
|
||||
self.client.voidresp()
|
||||
self.assertEqual(self.client.voidresp(), "226 transfer complete")
|
||||
|
||||
# PROT C is issued, the connection must be in cleartext again
|
||||
self.client.prot_c()
|
||||
sock = self.client.transfercmd('list')
|
||||
self.assertNotIsInstance(sock, ssl.SSLSocket)
|
||||
sock.close()
|
||||
self.client.voidresp()
|
||||
self.assertEqual(self.client.voidresp(), "226 transfer complete")
|
||||
|
||||
def test_login(self):
|
||||
# login() is supposed to implicitly secure the control connection
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue