On Wed, Oct 14, 2009 at 9:55 AM, Rij <rij.gh...@gmail.com> wrote:
Hello everyone,

I need a deeper understanding of SSL_read()/SSL_write() and was
wondering if someone could please provide some insight.

As far as I understand, OpenSSL has is a record-oriented protocol.
Lets say the record size is 16K. Let's say a client requests data of
size 40K and then waits on epoll.

1) Assuming all is well, the server will package the 40K in 3 SSL
records and send them across. Is this correct?

Unknown and irrelevant.  The server can split its data across as many records 
as necessary, and OpenSSL makes it so you don't need to know only minimal 
information (the stuff available from select()) about the underlying bytestream.

2) The client has now 3 SSL records in its network buffer. epoll
returns and the client app issues as SSL_read(). SSL will now read all
the 3 records, if it has sufficient internal buffer, do all the error
checking and pass it on to the application. If SSL's internal buffer
is not large enough, then the data remains in the network buffer. Is
this correct?

No.  OpenSSL will allocate as much memory as necessary for its buffers, and will -- on 
every SSL_read() and SSL_write() -- make as much "forward progress" in the 
connection as possible.  (SSL/TLS act very much like data-correcting modems did, back in 
the day... it's a layer built atop TCP, on the view that TCP is an unreliable connection 
mechanism.  They specify an encoding of the data that you're trying to send, along with 
enough information to at the very least reliably determine if it's been damaged in 
transit.  The computer that the error-correcting modem got connected to didn't see any of 
this underlying mechanism at work, it only saw what the modem could reliably state was 
error-free data.

The SSL* structure is the same way, except that it doesn't really run 
independently of its client -- it must use cooperative multitasking methods to 
ensure that it has enough traffic through its code paths to be able to manage 
the encoding and decoding of each raw data stream (usually a socket).  (The 
SSL_CTX* structure operates like a bank of those error-correcting modems, which 
has behavior which dynamically provisions new ones and decommissions old ones.)

But here's the part that everyone gets messed up with: *those underlying 
streams of data have absolutely no bearing on what your application is going to 
get*.  The simple fact that there was data that came in -- you could observe 
that much from select() or epoll() -- does not mean that any of that data was 
destined to your application.  A common case of this is when either the server 
or the client requests renegotiation of the encoding parameters -- that's 
information that the SSL layer must act on, and must act on on its own without 
the application getting in the way.  The only thing that the application has to 
do -- and in fact, the only thing it *can* do -- is call into the SSL library 
so that it can figure out what it needs to do and do it.

3) Now, if the client application wants to read all the data, then all
is well. But let's say, the app does not want to read more than 10K.
So after reading 10K, the client is done. But we still have another
30K of data in the SSL buffer. Now, the same client issues another
request for the same 40K of data from the server. What happens when
the client receives the new data? Now the SSL buffer will have 30 + 40
= 70K of data. Will the SSL read pointer still be at the beginning,
meaning the next 10 K to be delivered to the app is really data from
the previous call?

If you want to skip the next 30k of data, keep a counter and keep doing an 
SSL_read() into the same buffer, in perhaps 1024 byte increments.  (Keep track 
of what SSL_read() returns, too -- it can return less than you request, for any 
of a dozen reasons including the ones listed in the man page for read(2).)

-Kyle H

Attachment: smime.p7s
Description: S/MIME Cryptographic Signature

Reply via email to