[tor-commits] [ooni-probe/develop] Add method handleWantRead() for handling SSL_ERROR_WANT_READ until OpenSSL's

isis at torproject.org isis at torproject.org
Thu Jun 6 16:41:37 UTC 2013

commit c6f6ce0516b544b6ae25da6dc940978c11fdb544
Author: Isis Lovecruft <isis at torproject.org>
Date:   Thu Feb 28 04:19:58 2013 +0000

    Add method handleWantRead() for handling SSL_ERROR_WANT_READ until OpenSSL's
    memory BIO state machine reports that it has been handled, and also jump to
    the SSL_ERROR_WANT_WRITE handler when/if that error occurs.
 nettests/experimental/tls_handshake.py |   79 ++++++++++++++++++++++++++++++--
 1 file changed, 75 insertions(+), 4 deletions(-)

diff --git a/nettests/experimental/tls_handshake.py b/nettests/experimental/tls_handshake.py
index 81cab6a..128812e 100644
--- a/nettests/experimental/tls_handshake.py
+++ b/nettests/experimental/tls_handshake.py
@@ -295,11 +295,82 @@ class TLSHandshakeTest(nettest.NetTestCase):
                     log.debug("State: %s" % connection.state_string())
             return connection
+        def handleWantRead(connection):
+            """
+            From OpenSSL memory BIO documentation on ssl_read():
+                If the underlying BIO is blocking, SSL_read() will only
+                return, once the read operation has been finished or an error
+                occurred, except when a renegotiation take place, in which
+                case a SSL_ERROR_WANT_READ may occur. This behaviour can be
+                controlled with the SSL_MODE_AUTO_RETRY flag of the
+                SSL_CTX_set_mode(3) call.
+                If the underlying BIO is non-blocking, SSL_read() will also
+                return when the underlying BIO could not satisfy the needs of
+                SSL_read() to continue the operation. In this case a call to
+                SSL_get_error(3) with the return value of SSL_read() will
+                yield SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE. As at any
+                time a re-negotiation is possible, a call to SSL_read() can
+                also cause write operations!  The calling process then must
+                repeat the call after taking appropriate action to satisfy the
+                needs of SSL_read(). The action depends on the underlying
+                BIO. When using a non-blocking socket, nothing is to be done,
+                but select() can be used to check for the required condition.
+            And from the OpenSSL memory BIO documentation on ssl_get_error():
+                The operation did not complete; the same TLS/SSL I/O function
+                should be called again later. If, by then, the underlying BIO
+                has data available for reading (if the result code is
+                SSL_ERROR_WANT_READ) or allows writing data
+                (SSL_ERROR_WANT_WRITE), then some TLS/SSL protocol progress
+                will take place, i.e. at least part of an TLS/SSL record will
+                be read or written. Note that the retry may again lead to a
+                SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE condition. There
+                is no fixed upper limit for the number of iterations that may
+                be necessary until progress becomes visible at application
+                protocol level.
+                For socket BIOs (e.g. when SSL_set_fd() was used), select() or
+                poll() on the underlying socket can be used to find out when
+                the TLS/SSL I/O function should be retried.
+                Caveat: Any TLS/SSL I/O function can lead to either of
+                SSL_ERROR_WANT_READ and SSL_ERROR_WANT_WRITE. In particular,
+                SSL_read() or SSL_peek() may want to write data and
+                SSL_write() may want to read data. This is mainly because
+                TLS/SSL handshakes may occur at any time during the protocol
+                (initiated by either the client or the server); SSL_read(),
+                SSL_peek(), and SSL_write() will handle any pending
+                handshakes.
+            Also, see http://stackoverflow.com/q/3952104
+            """
-                connection.do_handshake()
-            except SSL.WantReadError():
-                log.msg("Timeout exceeded.")
-                connection.shutdown()
+                while connection.want_read():
+                    log.debug("Connection to %s HAS want_read" % host)
+                    _read_buffer = connection.pending()
+                    log.debug("Rereading %d bytes..." % _read_buffer)
+                    sleep(1)
+                    rereceived = connection.recv(int(_read_buffer))
+                    log.debug("Received %d bytes" % rereceived)
+                    log.debug("State: %s" % connection.state_string())
+                else:
+                    peername, peerport = connection.getpeername()
+                    log.debug("Connection to %s:%s DOES NOT HAVE want_read"
+                              % (peername, peerport))
+                    log.debug("State: %s" % connection.state_string())
+            except SSL.WantWriteError, wwe:
+                log.debug("Got WantWriteError while handling want_read")
+                log.debug("WantWriteError: %s" % wwe.message)
+                log.debug("Switching to handleWantWrite()...")
+                handleWantWrite(connection)
+            return connection
                 log.msg("State: %s" % connection.state_string())
                 log.msg("Transmitted %d bytes" % connection.send("o\r\n"))

More information about the tor-commits mailing list