An Introduction to OpenSSL Programming, Part II of II
The basic problem we're facing is that SSL is a record-oriented protocol. Thus, even if we want to read only one byte from the SSL connection, we still need to read the entire record containing that byte into memory. Without the entire record in hand, OpenSSL can't check the record MAC, so we can't safely deliver the data to the programmer. Unfortunately, this behavior interacts poorly with select(), as shown in Figure 1.

Figure 1. Read Interaction with SSL
The left-hand side of Figure 1 shows the situation when the machine has received a record but it's still waiting in the network buffers. The arrow represents the read pointer that is set at the beginning of the buffer. The bottom row represents data decoded by OpenSSL but not yet read by the program (the SSL buffer). This buffer is currently empty so we haven't shown a box. If the program calls select() at this point, it will return immediately, indicating that a call to read() will succeed. Now, imagine that the programmer calls SSL_read() requesting one byte. This takes us to the situation at the right side of the figure.
As we said earlier, the OpenSSL has to read the entire record in order to deliver even a single byte to the program. In general, the application does not know the size of records, and so its reads will not match the records. Thus, the box in the upper right-hand corner shows that the read pointer has moved to the end of the record. We've read all the data in the network buffer. When the implementation decrypts and verifies the record, it places the data in the SSL buffer. Then it delivers the one byte that the program asked for in SSL_read(). We show the SSL buffer in the lower right-hand corner. The read pointer points somewhere in the buffer, indicating that some of the data is available for reading but some has already been read.
Consider what happens if the programmer calls select() at this point. select() is concerned solely with the contents of the network buffer, and that's empty. Thus, as far as select() is concerned there's no data to read. Depending on the exact arguments it's passed, it will either return and say that there's nothing to read or wait for some more network data to become available. In either case we wouldn't read the data in the SSL buffer. Note that if another record arrived, select() would indicate that the socket was ready to read and, we'd have an opportunity to read more data.
Thus, select() is an unreliable guide to whether there is SSL data ready to read. We need some way to determine the status of the SSL buffer. This can't be provided by the operating system because it has no access to the SSL buffers. It must be provided OpenSSL. OpenSSL provides exactly such a function. The function SSL_pending() tells us whether there is data in the SSL buffer for a given socket. Listing 8 shows SSL_pending() in action.
Listing 8. Reading Data Using SSL_Pending()
49 /* Now check if there's data to read */
50 if((FD_ISSET(sock,&readfds) && !write_blocked_on_read) ||
51 (read_blocked_on_write && FD_ISSET(sock,&writefds))){
52 do {
53 read_blocked_on_write=0;
54 read_blocked=0;
55
56 r=SSL_read(ssl,s2c,BUFSIZZ);
57
58 switch(SSL_get_error(ssl,r)){
59 case SSL_ERROR_NONE:
60 /* Note: this call could block, which blocks the
61 entire application. It's arguable this is the
62 right behavior since this is essentially a terminal
63 client. However, in some other applications you
64 would have to prevent this condition */
65 fwrite(s2c,1,r,stdout);
66 break;
67 case SSL_ERROR_ZERO_RETURN:
68 /* End of data */
69 if(!shutdown_wait)
70 SSL_shutdown(ssl);
71 goto end;
72 break;
73 case SSL_ERROR_WANT_READ:
74 read_blocked=1;
75 break;
76
77 /* We get a WANT_WRITE if we're
78 trying to rehandshake and we block on
79 a write during that rehandshake.
80
81 We need to wait on the socket to be
82 writeable but reinitiate the read
83 when it is */
84 case SSL_ERROR_WANT_WRITE:
85 read_blocked_on_write=1;
86 break;
87 default:
88 berr_exit("SSL read problem");
89 }
90
91 /* We need a check for read_blocked here because
92 SSL_pending() doesn't work properly during the
93 handshake. This check prevents a busy-wait
94 loop around SSL_read() */
95 } while (SSL_pending(ssl) && !read_blocked);
96 }
The logic of this code is fairly straightforward. select() has been called earlier, setting the variable readfds with the sockets that are ready to read. If the SSL socket is ready to read, we go ahead and try to fill our buffer, unless the variable write_blocked_on_read is set (this variable is used when we're rehandshaking and we'll discuss it later). Once we've read some data, we write it to the console. Then we check with SSL_pending() to see if the record was longer than our buffer. If it was, we loop back and read some more data.
Note that we've added a new branch to our switch statement: a check for SSL_ERROR_WANT_READ. What's going on here is that we've set the socket for nonblocking operation. Recall that we said that if you called read() when the network buffers were empty, it would simply block (wait) until they weren't. Setting the socket to nonblocking causes it to return immediately, saying that it would have blocked.
To understand why we've done this, consider what happens if an SSL record arrives in two pieces. When the first piece arrives, select() will signal that we're ready to read. However, we need to read the entire record in order to return any data, so this is a false positive. Attempting to read all that data will block, leading to exactly the deadlock we were trying to avoid. Instead, we set the socket to non-blocking and catch the error, which OpenSSL translates to SSL_ERROR_WANT_READ.
It's worth noting that the call to fwrite() that we use to write to the console can block. This will cause the entire application to stall. This is reasonable behavior in a terminal client--if the user isn't looking at the screen we want the server to wait for him--but in other applications we might have to make this file descriptor non-blocking and select() on it as well. This is left as an exercise for the reader.
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Sponsored by AMD
If you already use virtualized infrastructure, you are well on your way to leveraging the power of the cloud. Virtualization offers the promise of limitless resources, but how do you manage that scalability when your DevOps team doesn’t scale? In today’s hypercompetitive markets, fast results can make a difference between leading the pack vs. obsolescence. Organizations need more benefits from cloud computing than just raw resources. They need agility, flexibility, convenience, ROI, and control.
Stackato private Platform-as-a-Service technology from ActiveState extends your private cloud infrastructure by creating a private PaaS to provide on-demand availability, flexibility, control, and ultimately, faster time-to-market for your enterprise.
Sponsored by ActiveState
| Non-Linux FOSS: libnotify, OS X Style | Jun 18, 2013 |
| Containers—Not Virtual Machines—Are the Future Cloud | Jun 17, 2013 |
| Lock-Free Multi-Producer Multi-Consumer Queue on Ring Buffer | Jun 12, 2013 |
| Weechat, Irssi's Little Brother | Jun 11, 2013 |
| One Tail Just Isn't Enough | Jun 07, 2013 |
| Introduction to MapReduce with Hadoop on Linux | Jun 05, 2013 |
- Containers—Not Virtual Machines—Are the Future Cloud
- Non-Linux FOSS: libnotify, OS X Style
- Linux Systems Administrator
- Validate an E-Mail Address with PHP, the Right Way
- Lock-Free Multi-Producer Multi-Consumer Queue on Ring Buffer
- Senior Perl Developer
- Technical Support Rep
- UX Designer
- RSS Feeds
- Introduction to MapReduce with Hadoop on Linux
Featured Jobs
| Linux Systems Administrator | Houston and Austin, Texas | Host Gator |
| Senior Perl Developer | Austin, Texas | Host Gator |
| Technical Support Rep | Houston and Austin, Texas | Host Gator |
| UX Designer | Austin, Texas | Host Gator |
| Web & UI Developer (JavaScript & j Query) | Austin, Texas | Host Gator |
Free Webinar: Hadoop
How to Build an Optimal Hadoop Cluster to Store and Maintain Unlimited Amounts of Data Using Microservers
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Some of key questions to be discussed are:
- What is the “typical” Hadoop cluster and what should be installed on the different machine types?
- Why should you consider the typical workload patterns when making your hardware decisions?
- Are all microservers created equal for Hadoop deployments?
- How do I plan for expansion if I require more compute, memory, storage or networking?



Comments
Block socket with SSL_MODE_AUTO_RETRY
If I use block socket with SSL_MODE_AUTO_RETRY
The flag SSL_MODE_AUTO_RETRY will cause read/write operations to only return after the handshake and successful completion.
Do I have to handle the retrying in SSL_Read and SSL_Write? Isn't it easier to do this way? I know it hurts throughput a little, but is it a big deal?