Wednesday, December 4, 2013

Busy year doing contracting work and using OpenCV

So 2013 has been a pretty busy year.  Contracting has been going incredibly well and I've had a chance to work on some really cool stuff.  Jumping back into C++ after 2 years away from it was quite fun, although it reminded me how tedious and annoying C++ can be, especially when trying to do small things.  Unfortunately being so busy also meant I neglected this blog for most of the year.

My most recent contracting project is pretty interesting and gave me a chance to learn some new things.  One of my more recent tasks was creating a high-speed image processing tool, which lead me to learn about using the OpenCV library for image processing tasks.  There are some pros and cons to using it, however the pros definitely outweigh the downsides.  OpenCV has a huge amount of functionality built-in for image processing, and the API to use it is quite easy to use.  One of the simplest, yet most powerful, methods I found in the library was imread() which handles reading an image file and loading the pixel data into an object.

There are some gotchas when using OpenCV, one that tripped me up initially was how it stores pixel data.  All game engines I've worked with handle color data as RGB channels, OpenCV uses BGR channels (the Red and Blue channels are switched).  This ended up biting me for a little while during the first round of testing when the tool flagged red images as blue ones.

I should also mention OpenCV assumes you have some level of knowledge of image processing techniques, and is mainly there to provide implementations of those techniques.  Coming into it with little knowledge of the subject like I did will be difficult, expect to do a lot of research to play catch-up for what they talk about.

On a final note, if you're new to OpenCV, I highly recommend checking out these excellent lessons http://opencv-srf.blogspot.com/p/opencv-c-tutorials.html. They provide a great starting point for navigating the OpenCV library and some terrific examples of image filtering operations.

Tuesday, February 5, 2013

Handling OpenSSL's SSL_ERROR_WANT_WRITE to avoid socket error:1409F07F

I spent a good part of last week working with OpenSSL C++ networking code.  One of the issues I ran into was the network SSL socket no longer sending data for some reason.

There are a fair number of error codes OpenSSL will return for certain situations when calling into the API.  Based on the error code returned from an I/O operation you may be required to take certain actions.  In particular, if you write to a SSL socket and get the SSL_ERROR_WANT_WRITE error code, you really need to jump through some hoops.  If you receive SSL_ERROR_WANT_WRITE during a write operation, the OpenSSL documentation states you must call the write method again at a later time, with the same parameters.  Please note I emphasized the last part about the parameters, because there is a huge caveat there.

What the OpenSSL documentation doesn't properly convey is when you retry the OpenSSL write operation at a later point after receiving a SSL_ERROR_WANT_WRITE error, the parameters must literally be the same, DOWN TO THE ADDRESS OF THE BUFFER YOU WISH TO WRITE.  My initial assumption was the buffer contents just needed to be the same, which may be the case, but I found the actual buffer address needed to be the same as well.

It seems OpenSSL internally remembers the address of the buffer passed in when SSL write returns the want write error, and if a different address is used on the next write operation, the socket enters an error state.  In this error state the SSL write call returns SSL_ERROR_SSL, and checking the SSL error queue returns this error:

"error:1409F07F:SSL routines:SSL3_WRITE_PENDING: bad write retry"

I'm not sure if this error state is recoverable.  It may be possible to recover by calling the write with the proper parameters.  However if the original buffer was lost, because you memcpy'ed the data into a new buffer and freed it, then your socket is probably hosed.

In my case, we were trying to send data through the socket first, then queuing a copy of the data if the write failed.  Unfortunately the copy operation memcpy'ed the data we wished to send, so the next time we called SSL write we were sending a pointer to a different data location than the original call.  Our solution was to simply copy and queue the data first, then trying to write to the socket.  If the write succeeded then we just removed it from the queue, and if it failed then we were guaranteed to still have the same data buffer address when we tried to write again.