Просмотр исходного кода

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

Guenter Obiltschnig 10 лет назад
Родитель
Сommit
f7ba58c80f

+ 15 - 1
NetSSL_OpenSSL/include/Poco/Net/SecureSocketImpl.h

@@ -196,7 +196,21 @@ protected:
 	static bool isLocalHost(const std::string& hostName);
 		/// Returns true iff the given host name is the local host 
 		/// (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);
 		/// Handles an SSL error by throwing an appropriate exception.
 

+ 40 - 15
NetSSL_OpenSSL/src/SecureSocketImpl.cpp

@@ -270,7 +270,7 @@ int SecureSocketImpl::sendBytes(const void* buffer, int length, int flags)
 	{
 		rc = SSL_write(_pSSL, buffer, length);
 	}
-	while (rc <= 0 && _pSocket->lastError() == POCO_EINTR);
+	while (mustRetry(rc));
 	if (rc <= 0) 
 	{
 		rc = handleError(rc);
@@ -298,7 +298,7 @@ int SecureSocketImpl::receiveBytes(void* buffer, int length, int flags)
 	{
 		rc = SSL_read(_pSSL, buffer, length);
 	}
-	while (rc <= 0 && _pSocket->lastError() == POCO_EINTR);
+	while (mustRetry(rc));
 	if (rc <= 0) 
 	{
 		return handleError(rc);
@@ -325,7 +325,7 @@ int SecureSocketImpl::completeHandshake()
 	{
 		rc = SSL_do_handshake(_pSSL);
 	}
-	while (rc <= 0 && _pSocket->lastError() == POCO_EINTR);
+	while (mustRetry(rc));
 	if (rc <= 0) 
 	{
 		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)
 {
 	if (rc > 0) return rc;
@@ -402,13 +438,6 @@ int SecureSocketImpl::handleError(int rc)
 	case SSL_ERROR_ZERO_RETURN:
 		return 0;
 	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;
 	case SSL_ERROR_WANT_WRITE:
 		return SecureStreamSocket::ERR_SSL_WANT_WRITE;
@@ -421,11 +450,7 @@ int SecureSocketImpl::handleError(int rc)
 	case SSL_ERROR_SYSCALL:
 		if (error != 0)
 		{
-			if (_pSocket->getBlocking() && error == POCO_EAGAIN)
-				throw TimeoutException(error);
-			else
-				SocketImpl::error(error);
-			return rc;
+			SocketImpl::error(error);
 		}
 		// fallthrough
 	default: