[openssl-users] Problem with not knowing how much data is available to read
Michael Wojcik
Michael.Wojcik at microfocus.com
Mon Dec 21 14:07:50 UTC 2015
> From: openssl-users [mailto:openssl-users-bounces at openssl.org] On Behalf
> Of counterpoint
> Sent: Saturday, December 19, 2015 10:00
>
> This is a further question, related to my earlier question "Find size of
> available data prior to ssl_read". The conclusion seemed to be that there
> isn't a reliable way to know how much data can be requested with ssl_read.
>
> I guess there's still something wrong with my understanding. My code now
> reads data into a fixed sized buffer, and keeps reading until no data is
> received. But the problem is, the last read gets an SSL_ERROR_WANT_READ
> error. That seems to imply that I can't do any more reads or writes until
> the read is successfully retried, but being prevented from writing will
> block the system because the client is expecting a reply (which has to be
> written) before sending anything more for me to read. Do I really need to
> suppress writes while waiting for a retry of a read that encountered
> SSL_ERROR_WANT_READ, or is that a misunderstanding on my part?
Not as I understand it. An SSL_ERROR_WANT_READ from SSL_read means that SSL_read can't return any more application data until the socket is readable (and possibly not then, but that's what's obstructing SSL_read at this point).
Go ahead and call SSL_write if you have data to send. What happens then depends on various factors:
- If SSL_read returned SSL_ERROR_WANT_READ because it doesn't have a complete TLS record, or because it's given you all the application data from all the TLS records it's seen so far, then that's irrelevant to SSL_write. TCP is full-duplex, and so is TLS, provided the conversation is in the appropriate state (not negotiating or in error). Nothing says you can't write while you're waiting to read.
SSL_write may still fail at this point, of course. You might get SSL_ERROR_WANT_WRITE from it, if the peer has advertised a zero window (i.e. it's not read to receive at the stack level). Or there might be communications errors.
- If SSL_read returned SSL_ERROR_WANT_READ because your TLS connection is in the middle of renegotiation and you're waiting for data from the peer, then SSL_write will also return SSL_ERROR_WANT READ. However, in that case OpenSSL is NOT waiting for application data; it's waiting for TLS negotiation, which doesn't involve your application (modulo any callbacks you hooked into the negotiation process), so it doesn't matter if the application is waiting for application data.
Your error is the statement "I can't do any more reads or writes". Reading and writing application data are independent of one another. Reading and writing can become coupled at the TLS layer due to renegotiation, but that's independent of application reads and writes.
In short:
- If a function, whether it's SSL_read or SSL_write, returns SSL_ERROR_WANT_READ, then retry it when the socket becomes readable.
- If a function returns SSL_ERROR_WANT_WRITE, then retry it when the socket becomes writable.
- In the meantime, if you know you're in WANT_READ state and you want to try to write something, or vice versa, go ahead. The worst that will happen is that you'll get WANT_* back.
- Note that it's possible to be in both WANT_READ and WANT_WRITE, for example if there's no data available from the peer and the peer has advertised a zero window. So you have two bits of state to keep track of there, plus information about whatever pending operations you have. You might have a structure along these lines:
struct IOState {
int WantRead; /* waiting for socket to become readable */
int WantWrite; /* waiting for it to become writable */
size_t BytesToRead; /* how many bytes to try to read; 0 if we're not looking for data now */
size_t BytesToWrite; /* data waiting to be written */
...
};
plus your socket descriptor/handle, your buffers, your SSL*, etc.
Then when you're setting up your multiplexed I/O lists (select, poll, WaitForMultipleObjects, whatever mechanism you use), you'd use the WantRead and WantWrite fields/members to decide whether to include the socket for that type of I/O. And when it pops, if the socket is available, you can try to read and/or write, depending on the BytesTo* fields/members.
It'd be a bit more optimal to separately track the WANT_* state for both reading and writing, but in practice it's unlikely to make much of a difference unless you're really performance-critical.
There are enough separate states here that it's possible to get the code in quite a mess, so it's probably worth sketching out the state machine on paper first, to help you create a clean, consistent implementation.
--
Michael Wojcik
Technology Specialist, Micro Focus
More information about the openssl-users
mailing list