diff options
author | Antoine Pitrou <solipsis@pitrou.net> | 2010-04-23 00:16:21 (GMT) |
---|---|---|
committer | Antoine Pitrou <solipsis@pitrou.net> | 2010-04-23 00:16:21 (GMT) |
commit | 2c4f98b3c58365845539f0d921c9d12b466b5e30 (patch) | |
tree | 2cd4c117bd36284ea749776aa1c2088817c28508 /Modules/_ssl.c | |
parent | 582c0a6ac2cba5fd41222db04b94e7fc51389a08 (diff) | |
download | cpython-2c4f98b3c58365845539f0d921c9d12b466b5e30.zip cpython-2c4f98b3c58365845539f0d921c9d12b466b5e30.tar.gz cpython-2c4f98b3c58365845539f0d921c9d12b466b5e30.tar.bz2 |
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.
........
Diffstat (limited to 'Modules/_ssl.c')
-rw-r--r-- | Modules/_ssl.c | 69 |
1 files changed, 63 insertions, 6 deletions
diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 3232df5..e06b7e0 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -9,6 +9,9 @@ directly. XXX should partial writes be enabled, SSL_MODE_ENABLE_PARTIAL_WRITE? + + XXX integrate several "shutdown modes" as suggested in + http://bugs.python.org/issue8108#msg102867 ? */ #include "Python.h" @@ -116,6 +119,7 @@ typedef struct { SSL_CTX* ctx; SSL* ssl; X509* peer_cert; + int shutdown_seen_zero; } PySSLObject; @@ -1392,7 +1396,8 @@ Read up to len bytes from the SSL socket."); static PyObject *PySSL_SSLshutdown(PySSLObject *self) { - int err; + int err, ssl_err, sockstate, nonblocking; + int zeros = 0; PySocketSockObject *sock = (PySocketSockObject *) PyWeakref_GetObject(self->Socket); @@ -1403,13 +1408,65 @@ static PyObject *PySSL_SSLshutdown(PySSLObject *self) return NULL; } - PySSL_BEGIN_ALLOW_THREADS - err = SSL_shutdown(self->ssl); - if (err == 0) { - /* we need to call it again to finish the shutdown */ + /* Just in case the blocking state of the socket has been changed */ + nonblocking = (sock->sock_timeout >= 0.0); + BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking); + BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); + + while (1) { + PySSL_BEGIN_ALLOW_THREADS + /* Disable read-ahead so that unwrap can work correctly. + * Otherwise OpenSSL might read in too much data, + * eating clear text data that happens to be + * transmitted after the SSL shutdown. + * Should be safe to call repeatedly everytime this + * function is used and the shutdown_seen_zero != 0 + * condition is met. + */ + if (self->shutdown_seen_zero) + SSL_set_read_ahead(self->ssl, 0); err = SSL_shutdown(self->ssl); + PySSL_END_ALLOW_THREADS + /* If err == 1, a secure shutdown with SSL_shutdown() is complete */ + if (err > 0) + break; + if (err == 0) { + /* Don't loop endlessly; instead preserve legacy + behaviour of trying SSL_shutdown() only twice. + This looks necessary for OpenSSL < 0.9.8m */ + if (++zeros > 1) + break; + /* Shutdown was sent, now try receiving */ + self->shutdown_seen_zero = 1; + continue; + } + + /* Possibly retry shutdown until timeout or failure */ + ssl_err = SSL_get_error(self->ssl, err); + if (ssl_err == SSL_ERROR_WANT_READ) + sockstate = check_socket_and_wait_for_timeout(sock, 0); + else if (ssl_err == SSL_ERROR_WANT_WRITE) + sockstate = check_socket_and_wait_for_timeout(sock, 1); + else + break; + if (sockstate == SOCKET_HAS_TIMED_OUT) { + if (ssl_err == SSL_ERROR_WANT_READ) + PyErr_SetString(PySSLErrorObject, + "The read operation timed out"); + else + PyErr_SetString(PySSLErrorObject, + "The write operation timed out"); + return NULL; + } + else if (sockstate == SOCKET_TOO_LARGE_FOR_SELECT) { + PyErr_SetString(PySSLErrorObject, + "Underlying socket too large for select()."); + return NULL; + } + else if (sockstate != SOCKET_OPERATION_OK) + /* Retain the SSL error code */ + break; } - PySSL_END_ALLOW_THREADS if (err < 0) return PySSL_SetError(self, err, __FILE__, __LINE__); |