> From: openssl-users [mailto:openssl-users-boun...@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

_______________________________________________
openssl-users mailing list
To unsubscribe: https://mta.openssl.org/mailman/listinfo/openssl-users

Reply via email to