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
smime.p7s
Description: S/MIME Cryptographic Signature