fix for GH #1160: Poco::Net::NetException "SSL Exception: error:1409F07F:SSL routines:ssl3_write_pending:bad write retry

This commit is contained in:
Guenter Obiltschnig 2016-02-26 20:14:19 +01:00
parent 5076f60a5c
commit f7ba58c80f
2 changed files with 55 additions and 16 deletions

View File

@ -196,7 +196,21 @@ protected:
static bool isLocalHost(const std::string& hostName); static bool isLocalHost(const std::string& hostName);
/// Returns true iff the given host name is the local host /// Returns true iff the given host name is the local host
/// (either "localhost" or "127.0.0.1"). /// (either "localhost" or "127.0.0.1").
bool mustRetry(int rc);
/// Returns true if the last operation should be retried,
/// otherwise false.
///
/// In case of an SSL_ERROR_WANT_READ error, and if the socket is
/// blocking, waits for the underlying socket to become readable.
///
/// In case of an SSL_ERROR_WANT_WRITE error, and if the socket is
/// blocking, waits for the underlying socket to become writable.
///
/// Can also throw a Poco::TimeoutException if the socket does
/// not become readable or writable within the sockets
/// receive or send timeout.
int handleError(int rc); int handleError(int rc);
/// Handles an SSL error by throwing an appropriate exception. /// Handles an SSL error by throwing an appropriate exception.

View File

@ -270,7 +270,7 @@ int SecureSocketImpl::sendBytes(const void* buffer, int length, int flags)
{ {
rc = SSL_write(_pSSL, buffer, length); rc = SSL_write(_pSSL, buffer, length);
} }
while (rc <= 0 && _pSocket->lastError() == POCO_EINTR); while (mustRetry(rc));
if (rc <= 0) if (rc <= 0)
{ {
rc = handleError(rc); rc = handleError(rc);
@ -298,7 +298,7 @@ int SecureSocketImpl::receiveBytes(void* buffer, int length, int flags)
{ {
rc = SSL_read(_pSSL, buffer, length); rc = SSL_read(_pSSL, buffer, length);
} }
while (rc <= 0 && _pSocket->lastError() == POCO_EINTR); while (mustRetry(rc));
if (rc <= 0) if (rc <= 0)
{ {
return handleError(rc); return handleError(rc);
@ -325,7 +325,7 @@ int SecureSocketImpl::completeHandshake()
{ {
rc = SSL_do_handshake(_pSSL); rc = SSL_do_handshake(_pSSL);
} }
while (rc <= 0 && _pSocket->lastError() == POCO_EINTR); while (mustRetry(rc));
if (rc <= 0) if (rc <= 0)
{ {
return handleError(rc); return handleError(rc);
@ -390,6 +390,42 @@ X509* SecureSocketImpl::peerCertificate() const
} }
bool SecureSocketImpl::mustRetry(int rc)
{
if (rc <= 0)
{
int sslError = SSL_get_error(_pSSL, rc);
int socketError = _pSocket->lastError();
switch (sslError)
{
case SSL_ERROR_WANT_READ:
if (_pSocket->getBlocking())
{
if (_pSocket->poll(_pSocket->getReceiveTimeout(), Poco::Net::Socket::SELECT_READ))
return true;
else
throw Poco::TimeoutException();
}
break;
case SSL_ERROR_WANT_WRITE:
if (_pSocket->getBlocking())
{
if (_pSocket->poll(_pSocket->getSendTimeout(), Poco::Net::Socket::SELECT_WRITE))
return true;
else
throw Poco::TimeoutException();
}
break;
case SSL_ERROR_SYSCALL:
return socketError == POCO_EAGAIN || socketError == POCO_EINTR;
default:
return socketError == POCO_EINTR;
}
}
return false;
}
int SecureSocketImpl::handleError(int rc) int SecureSocketImpl::handleError(int rc)
{ {
if (rc > 0) return rc; if (rc > 0) return rc;
@ -402,13 +438,6 @@ int SecureSocketImpl::handleError(int rc)
case SSL_ERROR_ZERO_RETURN: case SSL_ERROR_ZERO_RETURN:
return 0; return 0;
case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_READ:
if (_pSocket->getBlocking() && error != 0)
{
if (error == POCO_EAGAIN)
throw TimeoutException(error);
else
SocketImpl::error(error);
}
return SecureStreamSocket::ERR_SSL_WANT_READ; return SecureStreamSocket::ERR_SSL_WANT_READ;
case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_WRITE:
return SecureStreamSocket::ERR_SSL_WANT_WRITE; return SecureStreamSocket::ERR_SSL_WANT_WRITE;
@ -421,11 +450,7 @@ int SecureSocketImpl::handleError(int rc)
case SSL_ERROR_SYSCALL: case SSL_ERROR_SYSCALL:
if (error != 0) if (error != 0)
{ {
if (_pSocket->getBlocking() && error == POCO_EAGAIN) SocketImpl::error(error);
throw TimeoutException(error);
else
SocketImpl::error(error);
return rc;
} }
// fallthrough // fallthrough
default: default: