An Introduction to OpenSSL Programming, Part II of II

When we're writing to the network we have to face the same
sort of inconsistency that we had when reading. Again, the problem
is the all-or-nothing nature of SSL record transmission. For
simplicity's sake, let's consider the case where the network
buffers are mostly full and the program attempts to perform a
modest-sized write, say 1K. This is illustrated in Figure 2.

Figure 2. Write Interaction with SSL

Again, the left-hand side of the figure represents our
initial situation. The program has 1K to write in some buffer. The
write pointer is set at the beginning of that buffer. The SSL
buffers are empty. The network buffer is half-full (the shading
indicates the full region). The write pointer is set at the
beginning. We've deliberately obscured the distinction between the
TCP buffers and the size of the TCP window because it's not
relevant here. Suffice to say that the program can safely write 512
bytes without blocking.

Now, the program calls SSL_write() with a 1024-byte block.
OpenSSL has no way of knowing how much data it can write safely, so
it simply formats the buffer as a single record, thus moving the
write pointer in the program buffer to the end of the buffer. We
can ignore the slight data expansion from the SSL header and MAC,
and simply act as if the data to be written to the network was 1024
bytes.

Now, what happens when OpenSSL calls write()? It successfully
writes 512 bytes but gets a would_block error when it attempts to
write to the end of the record. As a consequence, the write pointer
in the SSL buffer is moved halfway across--indicating that half of
the data has been written to the network. The network buffer is
shaded to indicate that it's completely full. The network write
pointer hasn't moved.

We now need to concern ourselves with two questions: first,
how does the toolkit indicate this situation to the application,
and, second, how does the programmer arrange it so the SSL buffer
gets flushed when space is available in the network buffer? The
kernel will automatically flush the network buffer when possible,
so we don't need to worry about arranging for that. We can use
select() to see when there is more space available in the network
buffer, and we should therefore flush the SSL buffer.

Once OpenSSL has received a would_block error from the
network, it aborts and propagates that error all the way up to the
application. Note that this does not mean it throws away the data
in the SSL buffer. This is impossible because part of the record
might already have been sent. In order to flush this buffer, we
must call SSL_write() again with the same buffer that it called the
first time (it's permissible to extend the buffer but the start
must be the same.) OpenSSL automatically remembers where the buffer
write pointer was and only writes the data after the write pointer.
Listing 9 shows this process in action.

The first thing we need to do is have some data to write.
Thus, we check to see if the console is ready to read, and if so,
read whatever's there (up to BUFSIZZ bytes) into the buffer c2s,
placing the length in the variable c2sl.

If c2sl is nonzero and the network buffers are (at least
partially) empty, then we have data to write to the network. As
usual, we call SSL_write() with the buffer c2s. As before, if we
manage to write some but not all of the data, we simply increment
c2s_offset and decrement c2sl.

The new behavior here is that we check for the error
SSL_ERROR_WANT_WRITE. This error indicates that we've got unflushed
data in the SSL buffer. As we described above, we need to call
SSL_write() again with the same buffer, so we simply leave c2sl and
c2s_offset unchanged. Thus, the next time SSL_write() is called it
will automatically be with the same data.

OpenSSL actually provides a flag called
SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER that allows you to call
SSL_write() with a different buffer after a would_block error.
However, this merely allows you to allocate a new buffer with the
same contents. SSL_write() still seeks to the same write pointer
before looking for new data.

Trending Topics

Upcoming Webinar

Getting Started with DevOps - Including New Data on IT Performance from Puppet Labs 2015 State of DevOps Report

August 27, 2015
12:00 PM CDT

DevOps represents a profound change from the way most IT departments have traditionally worked: from siloed teams and high-anxiety releases to everyone collaborating on uneventful and more frequent releases of higher-quality code. It doesn't matter how large or small an organization is, or even whether it's historically slow moving or risk averse — there are ways to adopt DevOps sanely, and get measurable results in just weeks.