Skip to content

WANT_WRITE_ERROR occurs for sendall #176

Open
@Lothsahn

Description

@Lothsahn

The OpenSSL documentation says that in the event of a WANT_WRITE_ERROR or WANT_READ_ERROR, the same OpenSSL method call is to be repeated, otherwise you will get a bad write retry error.

See here:
https://www.openssl.org/docs/ssl/SSL_write.html
http://stackoverflow.com/questions/2997218/why-am-i-getting-error1409f07fssl-routinesssl3-write-pending-bad-write-retr

For pyOpenSSL.sendAll() this is problematic. Because python's sendAll() call does not return the number of bytes already sent if an error is thrown, it's impossible to continue with an identical SSL.Write() call. The state of the last SSL.Write() call is only known inside the pyOpenSSL.sendAll() function, and since this function has thrown an exception, this state is lost.

To illustrate the problem, see the following source for SSL.sendall() in pyOpenSSL:

    left_to_send = len(buf)
    total_sent = 0
    data = _ffi.new("char[]", buf)

    while left_to_send:
        result = _lib.SSL_write(self._ssl, data + total_sent, left_to_send)
        self._raise_ssl_error(self._ssl, result)
        total_sent += result
        left_to_send -= result

Specifically, inside the while loop, there are repeated calls to _lib.SSL_write(). _lib is the openSSL library, so the correct contract is that if SSL_write throws a WantWriteError or WantReadError, it should be handled by sleeping for a short duration and re-issuing the same call. However, pyOpenSSL does not do this, and the caller cannot either.

As a workaround, I implemented a wrapper around pyOpenSSL, and implemented my own sendAll() to handle any WANT_WRITE_ERROR or WANT_READ_ERRORS in a loop, re-issuing the same send() call that the error threw (after a small sleep).

Finally, keep in mind that any write or read calls that are made to pyOpenSSL can fail with either a WANT_WRITE_ERROR or a WANT_READ_ERROR, because these calls could trigger an SSL handshake. If network buffers are full or nearly full when the handshake triggers, these errors can be thrown. For this reason, both error conditions must be handled by all network calls, either in pyOpenSSL or by developer using pyOpenSSL.

Recommended fix:
pyOpenSSL should write exception handlers to catch WANT_WRITE_ERROR and WANT_READ_ERRORS inside all functions to catch those exceptions, sleep for a small period of time, and re-issue the same SSL_write() or SSL_read() call. The specific implementation will need to be considerate of blocking vs non-blocking mode, as well as timeouts, and is likely not trivial.

Thank you for all the work you do on pyOpenSSL. It really is a fantastic library.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions