c++sslopensslnonblockingsystem-error

SSL_shutdown() returns -1 and errno is 0


In my C++ application I use OpenSSL to connect to a server using nonblocking BIO. I am developing for mac OS X and iOS.

The first call to SSL_shutdown() returns 0. Which means I have to call SSL_shutdown() again:

The following return values can occur:

0 The shutdown is not yet finished. Call SSL_shutdown() for a second time, if a bidirectional shutdown shall be performed. The output of SSL_get_error may be misleading, as an erroneous SSL_ERROR_SYSCALL may be flagged even though no error occurred.

<0 The shutdown was not successful because a fatal error occurred either at the protocol level or a connection failure occurred. It can also occur if action is need to continue the operation for non-blocking BIOs. Call SSL_get_error with the return value ret to find out the reason.

https://www.openssl.org/docs/ssl/SSL_shutdown.html

So far so god. The problem occurs on the second call to SSL_shutdown(). This returns -1 which means an error has occurred (see above). Now if I check with SSL_get_error() I get error SSL_ERROR_SYSCALL which in turn is supposed to mean a system error has occurred. But now the catch. If I check the errno it returns 0 -> unknown error. What I have read so far about the issue is, that it could mean that the server did just "hang up", but to be honest this does not satisfy me.

Here is my implementation of the shutdown:

int result = 0;
int shutdownResult;
while ((shutdownResult = SSL_shutdown(sslHandle)) != 1) { //close connection 1 means everything is shut down ok
  if (shutdownResult == 0) { //we are supposed to call shutdown again
    continue;
  } else if (SSL_get_error(sslHandle, shutdownResult) == SSL_ERROR_WANT_READ) {
                [...] //omitted want read code, in this case the application never reaches this point
  } else if (SSL_get_error(sslHandle, shutdownResult) == SSL_ERROR_WANT_WRITE) {
                [...] //omitted want write code, in this case the application never reaches this point
  } else {
    logError("Error in ssl shutdown, ssl error: " + std::to_string(SSL_get_error(sslHandle, shutdownResult)) + ", system error: " + std::string(strerror(errno))); //something went wrong
    break;
  }
} 

When run the application logs:

ERROR:: Error in ssl shutdown, ssl error: 5, system error: Undefined error: 0

So is here just the server shutting down the connection or is there a more critical issue? Am I just missing something really obvious?


Solution

  • A full SSL shutdown consists of two parts:

    The first SSL_shutdown returned 0 which means that it did send the 'close notify' to the peer but did not receive anything back yet. The second call of SSL_shutdown fails because the peer did not do a proper SSL shutdown and send a 'close notify' back, but instead just closed the underlying TCP connection.

    This behavior is actually very common and you can usually just ignore the error. It does not matter much if the underlying TCP connection should be closed anyway. But a proper SSL shutdown is usually needed when you want to continue in plain text on the same TCP connection, like needed for the CCC command in FTPS connections (but even there various implementation fail to handle this case properly).