OpenSSL error handling

And what is the difference between SSL_get_error and ERR_get_error?

There are two logical parts to OpenSSL. First is the SSL library, libssl.a (and libssl.so), and it includes the communication related stuff. Second is the cryptography library, libcrypto.a (and libcrypto.so), and it includes big numbers, configuration, input/output, etc.

libssl.a depends upon libcrypto.a, and its why the link command is ordered as -lssl -lcrypto.

You use SSL_get_error to retrieve most errors from the SSL portion library, and you use ERR_get_error to retrieve errors not in the SSL portion of the library.


Is this the correct way to do error handling in OpenSSL?

The code you showed is closer to "how do you shutdown a SSL socket". Ultimately, the gyrations control two cases. First is a half open connection, when the client shutdowns without sending the close notify message. The second is your program's behavior when sending the close notify message.

Its hard to answer "is it correct" because we don't know the behavior you want. If you don't care if the close notify is sent, then I believe you only need to call SSL_shutdown once, regardless of what the client does.


SSL_get_error:

SSL_get_error() returns a result code (suitable for the C "switch" statement) for a preceding call to SSL_connect(), SSL_accept(), SSL_do_handshake(), SSL_read(), SSL_peek(), or SSL_write() on ssl. The value returned by that TLS/SSL I/O function must be passed to SSL_get_error() in parameter ret.

ERR_get_error:

ERR_get_error() returns the earliest error code from the thread's error queue and removes the entry. This function can be called repeatedly until there are no more error codes to return.

So the latter is for more general use and those shouldn't be used together, because:

The current thread's error queue must be empty before the TLS/SSL I/O operation is attempted, or SSL_get_error() will not work reliably.

So you have to read all of the errors using ERR_get_error and handle them (or ignore them by removal as you did in your code sample with ERR_clear_error) and then perform the IO operation. Your approach seems to be correct, although I can't check all aspects of it by myself at the moment.

Refer to this answer and this post for more information.

EDIT: according to this tutorial, BIO_ routines may generate an error and affect error queue:

The third field is the name of the package that generated the error, such as "BIO routines" or "bignum routines".