Red Hat Bugzilla – Bug 466317
nss_compat_ossl doesn't support async I/O
Last modified: 2015-05-26 11:33:06 EDT
openssl lets you do asynchronous I/O, but nss_compat_ossl doesn't. In particular:
* nss_compat_ossl's SSL_read() and SSL_write() will never return
SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE, even if you set the fd
* You can't poll() for readability/writability of an SSL*, even if you
know its underlying fd, because rehandshaking makes it impossible to
know when you need to poll for readability and when you need to poll
This bug has been triaged
SSL_ERROR_WANT_READ and SSL_ERROR_WANT_WRITE don't apply to NSS since it hides the SSL implementation from the caller in its NSPR layer. The first time you read or write an SSL connection it does all the work necessary for the handshake in the background.
It may be possible to sort of fake it by storing a couple of booleans that are set to TRUE until the first read/write occurs on the socket.
Do you have some code examples where this is causing problems?
(In reply to comment #2)
> The first time you
> read or write an SSL connection it does all the work necessary for the
> handshake in the background.
Right, that works fine for synchronous I/O, but if you're doing non-blocking I/O, then that first read or write is usually going to have to return partway through the handshake, and then you need to know whether to poll for readability or writability before continuing.
(And even ignoring the handshaking problem, SSL_ERROR_WANT_READ and SSL_ERROR_WANT_WRITE are also OpenSSL's equivalent of EAGAIN. If you call SSL_read() when no data is available to read, then it returns SSL_ERROR_WANT_READ. The fact that nss_compat_ossl never uses SSL_ERROR_WANT_READ/WRITE is therefore proof that it doesn't support non-blocking I/O.)
> Do you have some code examples where this is causing problems?
No. My existing code (libsoup, bug 347491) is using GNUTLS, and I haven't bothered to port it to OpenSSL (since it wouldn't work right with nss_compat_ossl) and I can't port it directly to NSS (since NSS has no async API that can integrate with the glib main loop).
Any program that uses OpenSSL with non-blocking sockets would run into this problem with nss_compat_ossl. Everything will still compile, but the socket won't behave non-blockingly, so the program's UI will freeze at random points, when SSL_read and SSL_write calls that should have returned right away instead end up blocking.
This package has changed ownership in the Fedora Package Database. Reassigning to the new owner of this component.
I figured out how to fix this at some point. In the PRIOMethods for the identity layer, override read/recv and write/send to notice when they get a PR_WOULD_BLOCK_ERROR from the lower level, and record either SSL_ERROR_WANT_READ (if it happened in read or recv) or SSL_ERROR_WANT_WRITE (if it happened in write/send) in ossl->error.
More or less. There might be other little bits and pieces.
Anyway, glib-networking will be using NSS directly (doing something very much like the above in order to make async I/O work), so this bug doesn't actually block it. (Though it may still be important to other potential nss_compat_ossl uses.)
(In reply to Dan Winship from comment #5)
> I figured out how to fix this at some point. In the PRIOMethods for the
> identity layer, override read/recv and write/send to notice when they get a
> PR_WOULD_BLOCK_ERROR from the lower level, and record either
> SSL_ERROR_WANT_READ (if it happened in read or recv) or SSL_ERROR_WANT_WRITE
> (if it happened in write/send) in ossl->error.
I have implemented something like this for libcurl: