fix(NetSSL): shutdown behavior

This commit is contained in:
Günter Obiltschnig 2024-11-23 11:10:53 +01:00
parent 29b2c3a7b7
commit 1811f2f35c
15 changed files with 104 additions and 87 deletions

View File

@ -170,12 +170,22 @@ public:
virtual void shutdownReceive(); virtual void shutdownReceive();
/// Shuts down the receiving part of the socket connection. /// Shuts down the receiving part of the socket connection.
virtual void shutdownSend(); virtual int shutdownSend();
/// Shuts down the sending part of the socket connection. /// Shuts down the sending part of the socket connection.
///
/// Returns 0 for a non-blocking socket. May return
/// a negative value for a non-blocking socket in case
/// of a TLS connection. In that case, the operation should
/// be retried once the underlying socket becomes writable.
virtual void shutdown(); virtual int shutdown();
/// Shuts down both the receiving and the sending part /// Shuts down both the receiving and the sending part
/// of the socket connection. /// of the socket connection.
///
/// Returns 0 for a non-blocking socket. May return
/// a negative value for a non-blocking socket in case
/// of a TLS connection. In that case, the operation should
/// be retried once the underlying socket becomes writable.
virtual int sendBytes(const void* buffer, int length, int flags = 0); virtual int sendBytes(const void* buffer, int length, int flags = 0);
/// Sends the contents of the given buffer through /// Sends the contents of the given buffer through

View File

@ -157,12 +157,22 @@ public:
void shutdownReceive(); void shutdownReceive();
/// Shuts down the receiving part of the socket connection. /// Shuts down the receiving part of the socket connection.
void shutdownSend(); int shutdownSend();
/// Shuts down the sending part of the socket connection. /// Shuts down the sending part of the socket connection.
///
/// Returns 0 for a non-blocking socket. May return
/// a negative value for a non-blocking socket in case
/// of a TLS connection. In that case, the operation should
/// be retried once the underlying socket becomes writable.
void shutdown(); int shutdown();
/// Shuts down both the receiving and the sending part /// Shuts down both the receiving and the sending part
/// of the socket connection. /// of the socket connection.
///
/// Returns 0 for a non-blocking socket. May return
/// a negative value for a non-blocking socket in case
/// of a TLS connection. In that case, the operation should
/// be retried once the underlying socket becomes writable.
int sendBytes(const void* buffer, int length, int flags = 0); int sendBytes(const void* buffer, int length, int flags = 0);
/// Sends the contents of the given buffer through /// Sends the contents of the given buffer through

View File

@ -69,8 +69,8 @@ public:
virtual void listen(int backlog = 64); virtual void listen(int backlog = 64);
virtual void close(); virtual void close();
virtual void shutdownReceive(); virtual void shutdownReceive();
virtual void shutdownSend(); virtual int shutdownSend();
virtual void shutdown(); virtual int shutdown();
virtual int sendTo(const void* buffer, int length, const SocketAddress& address, int flags = 0); virtual int sendTo(const void* buffer, int length, const SocketAddress& address, int flags = 0);
virtual int receiveFrom(void* buffer, int length, SocketAddress& address, int flags = 0); virtual int receiveFrom(void* buffer, int length, SocketAddress& address, int flags = 0);
virtual void sendUrgent(unsigned char data); virtual void sendUrgent(unsigned char data);

View File

@ -67,10 +67,11 @@ public:
response.setChunkedTransferEncoding(true); response.setChunkedTransferEncoding(true);
response.setContentType("text/html"); response.setContentType("text/html");
response.set("Clear-Site-Data", "\"cookies\"");
std::ostream& ostr = response.send(); std::ostream& ostr = response.send();
ostr << "<html><head><title>HTTPTimeServer powered by POCO C++ Libraries</title>"; ostr << "<html><head><title>HTTPTimeServer powered by POCO C++ Libraries</title>";
ostr << "<meta http-equiv=\"refresh\" content=\"1\"></head>"; ostr << "</head>";
ostr << "<body><p style=\"text-align: center; font-size: 48px;\">"; ostr << "<body><p style=\"text-align: center; font-size: 48px;\">";
ostr << dt; ostr << dt;
ostr << "</p></body></html>"; ostr << "</p></body></html>";

View File

@ -327,21 +327,23 @@ void SocketImpl::shutdownReceive()
} }
void SocketImpl::shutdownSend() int SocketImpl::shutdownSend()
{ {
if (_sockfd == POCO_INVALID_SOCKET) throw InvalidSocketException(); if (_sockfd == POCO_INVALID_SOCKET) throw InvalidSocketException();
int rc = ::shutdown(_sockfd, 1); int rc = ::shutdown(_sockfd, 1);
if (rc != 0) error(); if (rc != 0) error();
return 0;
} }
void SocketImpl::shutdown() int SocketImpl::shutdown()
{ {
if (_sockfd == POCO_INVALID_SOCKET) throw InvalidSocketException(); if (_sockfd == POCO_INVALID_SOCKET) throw InvalidSocketException();
int rc = ::shutdown(_sockfd, 2); int rc = ::shutdown(_sockfd, 2);
if (rc != 0) error(); if (rc != 0) error();
return 0;
} }

View File

@ -146,15 +146,15 @@ void StreamSocket::shutdownReceive()
} }
void StreamSocket::shutdownSend() int StreamSocket::shutdownSend()
{ {
impl()->shutdownSend(); return impl()->shutdownSend();
} }
void StreamSocket::shutdown() int StreamSocket::shutdown()
{ {
impl()->shutdown(); return impl()->shutdown();
} }

View File

@ -539,15 +539,15 @@ void WebSocketImpl::shutdownReceive()
} }
void WebSocketImpl::shutdownSend() int WebSocketImpl::shutdownSend()
{ {
_pStreamSocketImpl->shutdownSend(); return _pStreamSocketImpl->shutdownSend();
} }
void WebSocketImpl::shutdown() int WebSocketImpl::shutdown()
{ {
_pStreamSocketImpl->shutdown(); return _pStreamSocketImpl->shutdown();
} }

View File

@ -148,10 +148,11 @@ public:
/// number of connections that can be queued /// number of connections that can be queued
/// for this socket. /// for this socket.
void shutdown(); int shutdown();
/// Shuts down the connection by attempting /// Shuts down the connection by attempting
/// an orderly SSL shutdown, then actually /// an orderly SSL shutdown, then actually
/// shutting down the TCP connection. /// shutting down the TCP connection in the
/// send direction.
void close(); void close();
/// Close the socket. /// Close the socket.
@ -294,7 +295,6 @@ private:
bool _needHandshake; bool _needHandshake;
std::string _peerHostName; std::string _peerHostName;
Session::Ptr _pSession; Session::Ptr _pSession;
bool _bidirectShutdown = true;
mutable MutexT _mutex; mutable MutexT _mutex;
friend class SecureStreamSocketImpl; friend class SecureStreamSocketImpl;

View File

@ -116,14 +116,25 @@ public:
/// Since SSL does not support a half shutdown, this does /// Since SSL does not support a half shutdown, this does
/// nothing. /// nothing.
void shutdownSend() override; int shutdownSend() override;
/// Shuts down the receiving part of the socket connection. /// Shuts down the receiving part of the socket connection.
/// ///
/// Since SSL does not support a half shutdown, this does /// Sends a close notify shutdown alert message to the peer
/// nothing. /// (if not sent yet), then calls shutdownSend() on the
/// underlying socket.
///
/// Returns 0 if the message has been sent.
/// Returns 1 if the message has been sent, but the peer
/// has not yet sent its shutdown alert message.
/// In case of a non-blocking socket, returns < 0 if the
/// message cannot be sent at the moment. In this case,
/// the call to shutdownSend() must be retried after the
/// underlying socket becomes writable again.
void shutdown() override; int shutdown() override;
/// Shuts down the SSL connection. /// Shuts down the SSL connection.
///
/// Same as shutdownSend().
void abort(); void abort();
/// Aborts the connection by closing the underlying /// Aborts the connection by closing the underlying

View File

@ -253,69 +253,33 @@ void SecureSocketImpl::listen(int backlog)
} }
void SecureSocketImpl::shutdown() int SecureSocketImpl::shutdown()
{ {
if (_pSSL) if (_pSSL)
{ {
UnLockT l(_mutex); UnLockT l(_mutex);
// Don't shut down the socket more than once.
int shutdownState = ::SSL_get_shutdown(_pSSL); int shutdownState = ::SSL_get_shutdown(_pSSL);
bool shutdownSent = (shutdownState & SSL_SENT_SHUTDOWN) == SSL_SENT_SHUTDOWN; bool shutdownSent = (shutdownState & SSL_SENT_SHUTDOWN) == SSL_SENT_SHUTDOWN;
if (!shutdownSent) if (!shutdownSent)
{ {
// A proper clean shutdown would require us to
// retry the shutdown if we get a zero return
// value, until SSL_shutdown() returns 1.
// However, this will lead to problems with
// most web browsers, so we just set the shutdown
// flag by calling SSL_shutdown() once and be
// done with it.
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
int rc = 0;
if (!_bidirectShutdown)
rc = ::SSL_shutdown(_pSSL);
else
{
Poco::Timespan recvTimeout = _pSocket->getReceiveTimeout();
Poco::Timespan pollTimeout(0, 100000);
Poco::Timestamp tsNow;
do
{
rc = ::SSL_shutdown(_pSSL);
if (rc == 1) break;
if (rc < 0)
{
int err = ::SSL_get_error(_pSSL, rc);
if (err == SSL_ERROR_WANT_READ)
_pSocket->poll(pollTimeout, Poco::Net::Socket::SELECT_READ);
else if (err == SSL_ERROR_WANT_WRITE)
_pSocket->poll(pollTimeout, Poco::Net::Socket::SELECT_WRITE);
else
{
int socketError = SocketImpl::lastError();
long lastError = ::ERR_get_error();
if ((err == SSL_ERROR_SSL) && (socketError == 0) && (lastError == 0x0A000123))
rc = 0;
break;
}
}
else _pSocket->poll(pollTimeout, Poco::Net::Socket::SELECT_READ);
} while (!tsNow.isElapsed(recvTimeout.totalMicroseconds()));
}
#else
int rc = ::SSL_shutdown(_pSSL); int rc = ::SSL_shutdown(_pSSL);
#endif if (rc < 0) rc = handleError(rc);
if (rc < 0) handleError(rc);
l.unlock(); l.unlock();
if (_pSocket->getBlocking()) if (rc >= 0)
{ {
_pSocket->shutdown(); _pSocket->shutdownSend();
} }
return rc;
}
else
{
return (shutdownState & SSL_RECEIVED_SHUTDOWN) == SSL_RECEIVED_SHUTDOWN;
} }
} }
return 1;
} }
@ -407,7 +371,6 @@ int SecureSocketImpl::receiveBytes(void* buffer, int length, int flags)
if (tsStart.isElapsed(recvTimeout.totalMicroseconds())) if (tsStart.isElapsed(recvTimeout.totalMicroseconds()))
throw Poco::TimeoutException(); throw Poco::TimeoutException();
}; };
_bidirectShutdown = false;
if (rc <= 0) if (rc <= 0)
{ {
return handleError(rc); return handleError(rc);

View File

@ -156,14 +156,15 @@ void SecureStreamSocketImpl::shutdownReceive()
} }
void SecureStreamSocketImpl::shutdownSend() int SecureStreamSocketImpl::shutdownSend()
{ {
return _impl.shutdown();
} }
void SecureStreamSocketImpl::shutdown() int SecureStreamSocketImpl::shutdown()
{ {
_impl.shutdown(); return _impl.shutdown();
} }

View File

@ -154,10 +154,11 @@ public:
/// number of connections that can be queued /// number of connections that can be queued
/// for this socket. /// for this socket.
void shutdown(); int shutdown();
/// Shuts down the connection by attempting /// Shuts down the connection by attempting
/// an orderly SSL shutdown, then actually /// an orderly SSL shutdown, then actually
/// shutting down the TCP connection. /// shutting down the TCP connection in the
/// send direction.
void close(); void close();
/// Close the socket. /// Close the socket.

View File

@ -117,14 +117,25 @@ public:
/// Since SSL does not support a half shutdown, this does /// Since SSL does not support a half shutdown, this does
/// nothing. /// nothing.
void shutdownSend(); int shutdownSend();
/// Shuts down the receiving part of the socket connection. /// Shuts down the receiving part of the socket connection.
/// ///
/// Since SSL does not support a half shutdown, this does /// Sends a close notify shutdown alert message to the peer
/// nothing. /// (if not sent yet), then calls shutdownSend() on the
/// underlying socket.
///
/// Returns 0 if the message has been sent.
/// Returns 1 if the message has been sent, but the peer
/// has not yet sent its shutdown alert message.
/// In case of a non-blocking socket, returns < 0 if the
/// message cannot be sent at the moment. In this case,
/// the call to shutdownSend() must be retried after the
/// underlying socket becomes writable again.
void shutdown(); int shutdown();
/// Shuts down the SSL connection. /// Shuts down the SSL connection.
///
/// Same as shutdownSend().
void abort(); void abort();
/// Aborts the connection by closing the underlying /// Aborts the connection by closing the underlying

View File

@ -265,7 +265,7 @@ void SecureSocketImpl::listen(int backlog)
} }
void SecureSocketImpl::shutdown() int SecureSocketImpl::shutdown()
{ {
if (_mode == MODE_SERVER) if (_mode == MODE_SERVER)
serverDisconnect(&_hCreds, &_hContext); serverDisconnect(&_hCreds, &_hContext);
@ -273,16 +273,22 @@ void SecureSocketImpl::shutdown()
clientDisconnect(&_hCreds, &_hContext); clientDisconnect(&_hCreds, &_hContext);
_pSocket->shutdown(); _pSocket->shutdown();
return 0;
} }
void SecureSocketImpl::close() void SecureSocketImpl::close()
{ {
if (_mode == MODE_SERVER) try
serverDisconnect(&_hCreds, &_hContext); {
else if (_mode == MODE_SERVER)
clientDisconnect(&_hCreds, &_hContext); serverDisconnect(&_hCreds, &_hContext);
else
clientDisconnect(&_hCreds, &_hContext);
}
catch (Poco::Exception&)
{
}
_pSocket->close(); _pSocket->close();
cleanup(); cleanup();
} }

View File

@ -151,12 +151,13 @@ void SecureStreamSocketImpl::shutdownReceive()
void SecureStreamSocketImpl::shutdownSend() void SecureStreamSocketImpl::shutdownSend()
{ {
return _impl.shutdown();
} }
void SecureStreamSocketImpl::shutdown() void SecureStreamSocketImpl::shutdown()
{ {
_impl.shutdown(); return _impl.shutdown();
} }