Reputation: 61
I have an async winsock implementation (server and client) that I would like to add Openssl to for a secure connection. For example, the server would process these messages:
FD_ACCEPT, FD_READ, FD_WRITE, and FD_CLOSE
I found an article that proposed a method for adding Openssl to async programs
http://funcptr.net/2012/04/08/openssl-as-a-filter-%28or-non-blocking-openssl%29/
whose code is shown below:
class SSLFilter {
public:
SSLFilter(SSL_CTX* ctxt,
std::string* nread,
std::string* nwrite,
std::string* aread,
std::string* awrite);
virtual ~SSLFilter();
void update();
private:
bool continue_ssl_(int function_return);
SSL * ssl;
BIO * rbio;
BIO * wbio;
std::string* nread;
std::string* nwrite;
std::string* aread;
std::string* awrite;
};
SSLFilter::SSLFilter(SSL_CTX* ctxt,
std::string* nread,
std::string* nwrite,
std::string* aread,
std::string* awrite)
:
nread(nread),
nwrite(nwrite),
aread(aread),
awrite(awrite)
rbio = BIO_new(BIO_s_mem());
wbio = BIO_new(BIO_s_mem());
ssl = SSL_new(ctxt);
SSL_set_accept_state(ssl);
SSL_set_bio(ssl, rbio, wbio);
}
SSLFilter::~SSLFilter() {
SSL_free(_ssl);
}
void SSLFilter::update(Filter::FilterDirection) {
// If we have data from the network to process, put it the memory BIO for OpenSSL
if (!nread->empty()) {
int written = BIO_write(rbio, nread->c_str(), nread->length());
if (written > 0) nread->erase(0, written);
}
// If the application wants to write data out to the network, process it with SSL_write
if (!awrite->empty()) {
int written = SSL_write(ssl, awrite->c_str(), awrite->length());
if (!continue_ssl_()) {
throw std::runtime_error("An SSL error occured.");
}
if (written > 0) awrite->erase(0, written);
}
// Read data for the application from the encrypted connection and place it in the string for the app to read
while (1) {
char *readto = new char[1024];
int read = SSL_read(ssl, readto, 1024);
if (!continue_ssl_()) {
delete readto;
throw std::runtime_error("An SSL error occured.");
}
if (read > 0) {
size_t cur_size = aread->length();
aread->resize(cur_size + read);
std::copy(readto, readto + read, aread->begin() + cur_size);
}
delete readto;
if (static_cast<size_t>(read) != 1024 || written == 0) break;
}
// Read any data to be written to the network from the memory BIO and copy it to nwrite
while (1) {
char *readto = new char[1024];
int read = BIO_read(wbio, readto, 1024);
if (read > 0) {
size_t cur_size = nwrite->length();
nwrite->resize(cur_size + read);
std::copy(readto, readto + read, nwrite->begin() + cur_size);
}
delete readto;
if (static_cast<size_t>(read) != 1024 || read == 0) break;
}
}
bool SSLFilter::continue_ssl_(int function_return) {
int err = SSL_get_error(ssl, function_return);
if (err == SSL_ERROR_NONE || err == SSL_ERROR_WANT_READ) {
return true;
}
if (err == SSL_ERROR_SYSCALL) {
ERR_print_errors_fp(stderr);
perror("syscall error: ");
return false;
}
if (err == SSL_ERROR_SSL) {
ERR_print_errors_fp(stderr);
return false;
}
return true;
}
Here are the steps to using this filter:
At program startup set up the SSL_CTX structure as required, so that it be used in the SSLFilter. Set up socket/event loop and accept() new socket. Create four new std::string's and create a new SSLFilter, pass in the SSL_CTX you created, and the four buffers. Add the new socket to the event loop to wait for reading.
Now upon receiving a read ready status from the event loop, the following should be done:
Read data into nread buffer. call SSLFilter::update() See if aread is not empty, process the data as you please Write data to awrite as needed, if data is written to awrite, call SSLFilter::update(WRITE) to process the data See if nwrite is not empty, if so add socket to event loop for write readiness.
Once you receive a write ready status from the event loop, you should do the following:
Write data in nwrite to the socket. If nwrite is empty, remove the socket from the event loop for writing (so that you don't have the event loop notifying you of the ability to write, even-though you have nothing to write).
My questions are: * Can I use this method with async winsock? Or is there another implementation I should consider? * Are there any changes that need to be made? * How do I handle WOULDBLOCKS?
Thanks for your thoughts.
Upvotes: 3
Views: 2626
Reputation: 21644
I wrote an article for Windows Developer Magazine back in 2002 about this, it's reprinted here. The article explains how to use OpenSSL with async winsock calls and provides code which is works with standard async winsock calls and which is easy to integrate into IOCP style async calls.
Upvotes: 2