]> granicus.if.org Git - python/commitdiff
Merged revisions 80394 via svnmerge from
authorAntoine Pitrou <solipsis@pitrou.net>
Fri, 23 Apr 2010 21:11:10 +0000 (21:11 +0000)
committerAntoine Pitrou <solipsis@pitrou.net>
Fri, 23 Apr 2010 21:11:10 +0000 (21:11 +0000)
svn+ssh://pythondev@svn.python.org/python/branches/py3k

................
  r80394 | antoine.pitrou | 2010-04-23 02:16:21 +0200 (ven., 23 avril 2010) | 15 lines

  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.
  ........
................

Misc/NEWS
Modules/_ssl.c

index 980cd69d1cd329af461ee4d29bf74733ef4375e1..a619192ff0dbae9d4fb8f5ca218cfc22849ba9fa 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -40,6 +40,11 @@ Core and Builtins
 Library
 -------
 
+- 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 #8468: bz2.BZ2File() accepts str with surrogates and bytes filenames
 
 - Issue #8496: make mailcap.lookup() always return a list, rather than an
index 5311f771e737c2faf0395282df227d3c4a115f00..5e0f473d405c2f6786ed6e88af0fdf7b88782836 100644 (file)
@@ -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;
 
@@ -1377,7 +1381,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);
 
@@ -1388,13 +1393,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__);