From 6f4705cd977932f0bc5f8ce126de0ffec9113dae Mon Sep 17 00:00:00 2001
From: msieben <4319079+msieben@users.noreply.github.com>
Date: Fri, 25 Oct 2024 09:48:39 +0000
Subject: [PATCH 01/76] [WebSocket/WebSocketLink] : Enable unsollicited 'Pong'
and rename enum item
- Rename 'WEBSERVER' to 'WEBSERVICE' as both client and server utilize state information
- Unsollicited 'Pong' may act as a heart beat
---
Source/websocket/WebSocketLink.h | 42 ++++++++++++++++++++++++++------
1 file changed, 35 insertions(+), 7 deletions(-)
diff --git a/Source/websocket/WebSocketLink.h b/Source/websocket/WebSocketLink.h
index c1f3f14ad..3125b3ffe 100644
--- a/Source/websocket/WebSocketLink.h
+++ b/Source/websocket/WebSocketLink.h
@@ -35,6 +35,10 @@ namespace Web {
CLOSE = 0x08,
PING = 0x09,
PONG = 0x0A,
+ // Reserved ranges
+ // 0x3-0x7
+ // 0xB-0xF
+ // Following are outside reseved 4-bit ranges
VIOLATION = 0x10, // e.g. a control package without a FIN flag
TOO_BIG = 0x20, // Protocol max support for 2^16 message per chunk
INCONSISTENT = 0x30 // e.g. Protocol defined as Text, but received a binary.
@@ -182,7 +186,7 @@ namespace Web {
class WebSocketLinkType {
public:
enum EnumlinkState : uint8_t {
- WEBSERVER = 0x01,
+ WEBSERVICE = 0x01,
UPGRADING = 0x02,
WEBSOCKET = 0x04,
SUSPENDED = 0x08,
@@ -373,7 +377,7 @@ PUSH_WARNING(DISABLE_WARNING_THIS_IN_MEMBER_INITIALIZER_LIST)
, _handler(binary, masking)
, _parent(parent)
, _adminLock()
- , _state(WEBSERVER)
+ , _state(WEBSERVICE)
, _serializerImpl(*this, queueSize)
, _deserialiserImpl(*this, queueSize)
, _path()
@@ -390,7 +394,7 @@ PUSH_WARNING(DISABLE_WARNING_THIS_IN_MEMBER_INITIALIZER_LIST)
, _handler(binary, masking)
, _parent(parent)
, _adminLock()
- , _state(WEBSERVER)
+ , _state(WEBSERVICE)
, _serializerImpl(*this, queueSize)
, _deserialiserImpl(*this, allocator)
, _path()
@@ -419,7 +423,7 @@ POP_WARNING()
}
bool IsWebServer() const
{
- return ((State() & WEBSERVER) != 0);
+ return ((State() & WEBSERVICE) != 0);
}
bool IsUpgrading() const
{
@@ -477,6 +481,18 @@ POP_WARNING()
ACTUALLINK::Trigger();
}
+ void Pong()
+ {
+ _pingFireTime = Core::Time::Now().Ticks();
+
+ _adminLock.Lock();
+
+ _handler.Pong();
+
+ _adminLock.Unlock();
+
+ ACTUALLINK::Trigger();
+ }
bool Masking() const
{
return (_handler.Masking());
@@ -775,7 +791,7 @@ POP_WARNING()
// Multiple message might be coming in, protect the state before we make assumptions on it value.
_adminLock.Lock();
- if ((_state & WEBSERVER) == 0) {
+ if ((_state & WEBSERVICE) == 0) {
_webSocketMessage->ErrorCode = Web::STATUS_INTERNAL_SERVER_ERROR;
_webSocketMessage->Message = _T("State of the link can not be upgraded.");
} else {
@@ -794,7 +810,7 @@ POP_WARNING()
_parent.StateChange();
if (_webSocketMessage->ErrorCode != Web::STATUS_SWITCH_PROTOCOL) {
- _state = (_state & 0xF0) | WEBSERVER;
+ _state = (_state & 0xF0) | WEBSERVICE;
_path.clear();
_query.clear();
_protocol.Clear();
@@ -845,7 +861,7 @@ POP_WARNING()
_adminLock.Lock();
- if ((_state & WEBSERVER) != 0) {
+ if ((_state & WEBSERVICE) != 0) {
result = true;
_state = (_state & 0xF0) | UPGRADING;
_origin = (origin.empty() ? ACTUALLINK::LocalId() : origin);
@@ -1076,6 +1092,10 @@ POP_WARNING()
{
_channel.Ping();
}
+ void Pong()
+ {
+ _channel.Pong();
+ }
void Trigger()
{
_channel.Trigger();
@@ -1272,6 +1292,10 @@ POP_WARNING()
{
_channel.Ping();
}
+ void Pong()
+ {
+ _channel.Pong();
+ }
virtual bool IsIdle() const = 0;
virtual void StateChange() = 0;
@@ -1430,6 +1454,10 @@ POP_WARNING()
{
_channel.Ping();
}
+ void Pong()
+ {
+ _channel.Pong();
+ }
virtual bool IsIdle() const = 0;
virtual void StateChange() = 0;
From aa8c25dac888767515c326a0616f5b235d0ce5b4 Mon Sep 17 00:00:00 2001
From: msieben <4319079+msieben@users.noreply.github.com>
Date: Wed, 6 Nov 2024 15:24:54 +0000
Subject: [PATCH 02/76] [WebSocket/WebSocketLink] : METROL-1087
---
Source/websocket/WebSocketLink.h | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/Source/websocket/WebSocketLink.h b/Source/websocket/WebSocketLink.h
index 3125b3ffe..b5ddd7fba 100644
--- a/Source/websocket/WebSocketLink.h
+++ b/Source/websocket/WebSocketLink.h
@@ -193,6 +193,8 @@ namespace Web {
ACTIVITY = 0x10
};
+ DEPRECATED constexpr static EnumlinkState WEBSERVER { EnumlinkState::WEBSERVICE };
+
typedef WebSocketLinkType ParentClass;
private:
@@ -377,7 +379,7 @@ PUSH_WARNING(DISABLE_WARNING_THIS_IN_MEMBER_INITIALIZER_LIST)
, _handler(binary, masking)
, _parent(parent)
, _adminLock()
- , _state(WEBSERVICE)
+ , _state(WEBSERVER)
, _serializerImpl(*this, queueSize)
, _deserialiserImpl(*this, queueSize)
, _path()
From e923fcfdc67d04b8780f4b537ac77d36ceac55fe Mon Sep 17 00:00:00 2001
From: msieben <4319079+msieben@users.noreply.github.com>
Date: Thu, 7 Nov 2024 08:18:58 +0000
Subject: [PATCH 03/76] [WebSocket/WebSocketLink] : rename forgotten
'WEBSERVER' to 'WEBSERVICE'
---
Source/websocket/WebSocketLink.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Source/websocket/WebSocketLink.h b/Source/websocket/WebSocketLink.h
index b5ddd7fba..598f19bbc 100644
--- a/Source/websocket/WebSocketLink.h
+++ b/Source/websocket/WebSocketLink.h
@@ -379,7 +379,7 @@ PUSH_WARNING(DISABLE_WARNING_THIS_IN_MEMBER_INITIALIZER_LIST)
, _handler(binary, masking)
, _parent(parent)
, _adminLock()
- , _state(WEBSERVER)
+ , _state(WEBSERVICE)
, _serializerImpl(*this, queueSize)
, _deserialiserImpl(*this, queueSize)
, _path()
From 47697662e9c3d305054e99716d3c5e766bcb4937 Mon Sep 17 00:00:00 2001
From: msieben <4319079+msieben@users.noreply.github.com>
Date: Thu, 7 Nov 2024 08:29:36 +0000
Subject: [PATCH 04/76] [Tests/unit/core] : add 'test_websocket'
---
Tests/unit/core/test_websocket.cpp | 1006 ++++++++++++++++++++++++++++
1 file changed, 1006 insertions(+)
create mode 100644 Tests/unit/core/test_websocket.cpp
diff --git a/Tests/unit/core/test_websocket.cpp b/Tests/unit/core/test_websocket.cpp
new file mode 100644
index 000000000..2442085b0
--- /dev/null
+++ b/Tests/unit/core/test_websocket.cpp
@@ -0,0 +1,1006 @@
+/*
+ * If not stated otherwise in this file or this component's LICENSE file the
+ * following copyright and licenses apply:
+ *
+ * Copyright 2020 Metrological
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include
+
+#ifndef MODULE_NAME
+#include "../Module.h"
+#endif
+
+#include
+#include
+#include
+
+#include "../IPTestAdministrator.h"
+
+namespace Thunder {
+namespace Tests {
+namespace Core {
+
+ class CustomSocketStream : public ::Thunder::Core::SocketStream {
+ public:
+ CustomSocketStream(
+ const SOCKET& socket
+ , const ::Thunder::Core::NodeId& localNode
+ , const uint16_t sendBufferSize
+ , const uint16_t receiveBufferSize
+ , const std::string& prefix
+ )
+ : SocketStream(false, socket, localNode, sendBufferSize, receiveBufferSize)
+ , _prefix { prefix }
+ {
+ }
+
+ CustomSocketStream(
+ const bool
+ , const ::Thunder::Core::NodeId& localNode
+ , const ::Thunder::Core::NodeId& remoteNode
+ , const uint16_t sendBufferSize
+ , const uint16_t receiveBufferSize
+ , const std::string& prefix
+ )
+ : SocketStream(false, localNode, remoteNode, sendBufferSize, receiveBufferSize, sendBufferSize, receiveBufferSize)
+ , _prefix { prefix }
+ {
+ }
+
+ ~CustomSocketStream()
+ {
+ std::cout.flush();
+ }
+
+ // Raw TCP data
+ int32_t Read(uint8_t buffer[], const uint16_t length) const override
+ {
+#ifdef _VERBOSE
+ std::cout << std::dec <<__LINE__ << " : " << __PRETTY_FUNCTION__ << "\n";
+#endif
+
+ int32_t count = SocketPort::Read(buffer, length);
+
+#ifdef _VERBOSE
+ if (count > 0) {
+ std::cout << " |--> buffer ( " << count << "/" << length << " ) = ";
+ for (int32_t index = 0; index < count; index++) {
+ std::cout << std::hex << static_cast(buffer[index]) << " ";
+ }
+ std::cout << "\n";
+ }
+#endif
+
+ return count;
+ }
+
+ // Raw TCP data
+ int32_t Write(const uint8_t buffer[], const uint16_t length) override
+ {
+#ifdef _VERBOSE
+ std::cout << std::dec <<__LINE__ << " : " << __PRETTY_FUNCTION__ << "\n";
+#endif
+
+ int32_t count = SocketPort::Write(buffer, length);
+
+#ifdef _VERBOSE
+ if (count > 0) {
+ std::cout << " |--> buffer ( " << count << "/"<< length <<" ) = ";
+ for (int32_t index = 0; index < count; index++) {
+ std::cout << std::hex << static_cast(buffer[index]) << " ";
+ }
+ std::cout << "\n";
+ }
+#endif
+
+ return count;
+ }
+
+ private:
+
+ const std::string _prefix;
+ };
+
+ template
+ class WebSocketClient : public ::Thunder::Web::WebSocketClientType {
+ public :
+
+ template
+ WebSocketClient(
+ const string& path
+ , const string& protocol
+ , const string& query
+ , const string& origin
+ , const bool binary
+ , const bool masking
+ , Args&&... args
+ )
+ : ::Thunder::Web::WebSocketClientType(path, protocol, query, origin, binary, masking, /* */ std::forward(args)... /**/)
+ {
+ }
+
+ ~WebSocketClient() override = default;
+
+ // Non-idle then data available to send
+ bool IsIdle() const override { return _post.size() == 0; }
+
+ // Allow for eventfull state updates in this class
+ void StateChange() override
+ {
+#ifdef _VERBOSE
+ std::cout << std::dec << __LINE__ << " : " << __PRETTY_FUNCTION__ << "\n";
+
+ // Socket port open AND upgraded to WebSocket
+ std::cout << " |--> IsOpen() = " << this->IsOpen() << "\n";
+ // Link will not accept new messages as they arrive
+ std::cout << " |--> IsSuspended() = " << this->IsSuspended() << "\n";
+ // Socket has been closed (removed), link cannot receive new TCP data
+ std::cout << " |--> IsClosed() = " << this->IsClosed() << "\n";
+ // Regular HTTP connection, no upgraded connection
+ std::cout << " |--> IsWebServer() = " << this->IsWebServer() << "\n";
+ // Upgrade in progress
+ std::cout << " |--> IsUpgrading() = " << this->IsUpgrading() << "\n";
+ // Upgraded connection
+ std::cout << " |--> IsWebSocket() = " << this->IsWebSocket() << "\n";
+ // Finishing frame received
+ std::cout << " |--> IsCompleted() = " << this->IsCompleted() << "\n";
+#endif
+ }
+
+ // Reflects payload, effective after upgrade
+ uint16_t SendData(uint8_t* dataFrame, const uint16_t maxSendSize) override
+ {
+#ifdef _VERBOSE
+ std::cout << std::dec << __LINE__ << " : " << __PRETTY_FUNCTION__ << "\n";
+#endif
+
+ size_t count = 0;
+
+ if ( dataFrame != nullptr
+ && maxSendSize > 0
+ && !IsIdle()
+ && ::Thunder::Web::WebSocketClientType::IsOpen()
+ && ::Thunder::Web::WebSocketClientType::IsWebSocket() // Redundant, covered by IsOpen
+ && ::Thunder::Web::WebSocketClientType::IsCompleted()
+ ) {
+ std::basic_string& message = _post.front();
+
+ count = std::min(message.size(), static_cast(maxSendSize));
+
+ /* void* */ memcpy(dataFrame, message.data(), count);
+
+#ifdef _VERBOSE
+ std::cout << " |--> dataFrame ( " << count << "/" << maxSendSize << " ) = ";
+ for (int32_t index = 0; index < count; index++) {
+ std::cout << std::hex << static_cast(dataFrame[index]) << " ";
+ }
+ std::cout << "\n";
+#endif
+
+ if (count == message.size()) {
+ /* iterator */ _post.erase(_post.begin());
+ } else {
+ /* this */ message.erase(0, count);
+
+ // Trigger a call to SendData for remaining data
+ ::Thunder::Web::WebSocketClientType::Link().Trigger();
+ }
+ }
+
+ return count;
+ }
+
+ // Reflects payload, effective after upgrade
+ uint16_t ReceiveData(uint8_t* dataFrame, const uint16_t receivedSize) override
+ {
+#ifdef _VERBOSE
+ std::cout << std::dec << __LINE__ << " : " << __PRETTY_FUNCTION__ << "\n";
+
+ if (receivedSize > 0) {
+ std::cout << " |--> dataFrame ( " << receivedSize << " ) = ";
+ for (int32_t index = 0; index < receivedSize; index++) {
+ std::cout << std::hex << static_cast(dataFrame[index]) << " ";
+ }
+ std::cout << "\n";
+ }
+#endif
+
+ // Echo the data in reverse order
+
+ std::basic_string message{ dataFrame, receivedSize };
+
+ std::reverse(message.begin(), message.end());
+
+ return Submit(message) ? message.size() : 0;
+ }
+
+ bool Submit(const std::basic_string& message)
+ {
+ size_t count = _post.size();
+
+ _post.emplace_back(message);
+
+ ::Thunder::Web::WebSocketClientType::Link().Trigger();
+
+ return count < _post.size();
+ }
+
+ private:
+
+ std::vector> _post; // Send message queue
+ };
+
+ template
+ class WebSocketServer : public ::Thunder::Web::WebSocketServerType {
+ public :
+
+ // SocketServerType defines SocketHandler of type SocketListener. SocketListener triggers Accept on StateChange, effectively, calling SocketServerType::Accept(SOCKET, NodeId) which creates a WebSocketServer with these parameters
+ WebSocketServer(const SOCKET& socket, const ::Thunder::Core::NodeId remoteNode, ::Thunder::Core::SocketServerType>*)
+ // Initially this should be defined as a regular TCP socket
+ : ::Thunder::Web::WebSocketServerType(false /* binary*/, false /*masking */, socket, remoteNode, SENDBUFFERSIZE /* send buffer size */, RECEIVEBUFFERSIZE /* receive buffer size */, "WebSocketServerType")
+ {
+ }
+
+ ~WebSocketServer() override = default;
+
+ // Non-idle then data available to send
+ bool IsIdle() const override { return _post.size() == 0; }
+
+ // Allow for eventfull state updates in this class
+ void StateChange() override
+ {
+#ifdef _VERBOSE
+ std::cout << std::dec << __LINE__ << " : " << __PRETTY_FUNCTION__ << "\n";
+
+ // Socket port open AND upgraded to WebSocket
+ std::cout << " |--> IsOpen() = " << this->IsOpen() << "\n";
+ // Link will not accept new messages as they arrive
+ std::cout << " |--> IsSuspended() = " << this->IsSuspended() << "\n";
+ // Socket has been closed (removed), link cannot receive new TCP data
+ std::cout << " |--> IsClosed() = " << this->IsClosed() << "\n";
+ // Regular HTTP connection, no upgraded connection
+ std::cout << " |--> IsWebServer() = " << this->IsWebServer() << "\n";
+ // Upgrade in progress
+ std::cout << " |--> IsUpgrading() = " << this->IsUpgrading() << "\n";
+ // Upgraded connection
+ std::cout << " |--> IsWebSocket() = " << this->IsWebSocket() << "\n";
+ // Finishing frame received
+ std::cout << " |--> IsCompleted() = " << this->IsCompleted() << "\n";
+#endif
+ }
+
+ // Reflects payload, effective after upgrade
+ uint16_t SendData(uint8_t* dataFrame, const uint16_t maxSendSize) override
+ {
+#ifdef _VERBOSE
+ std::cout << std::dec << __LINE__ << " : " << __PRETTY_FUNCTION__ << "\n";
+#endif
+
+ size_t count = 0;
+
+ if ( dataFrame != nullptr
+ && maxSendSize > 0
+ && !IsIdle()
+ && ::Thunder::Web::WebSocketServerType::IsOpen()
+ && ::Thunder::Web::WebSocketServerType::IsWebSocket() // Redundant, covered by IsOpen
+ && ::Thunder::Web::WebSocketServerType::IsCompleted()
+ ) {
+ std::basic_string& message = _post.front();
+
+ count = std::min(message.size(), static_cast(maxSendSize));
+
+ /* void* */ memcpy(dataFrame, message.data(), count);
+
+#ifdef _VERBOSE
+ std::cout << " |--> dataFrame (" << count << " ) = ";
+ for (int32_t index = 0; index < count; index++) {
+ std::cout << std::hex << static_cast(dataFrame[index]) << " ";
+ }
+ std::cout << "\n";
+#endif
+
+ if (count == message.size()) {
+ /* iterator */ _post.erase(_post.begin());
+ } else {
+ /* this */ message.erase(0, count);
+
+ // Trigger a call to SendData for remaining data
+ ::Thunder::Web::WebSocketServerType::Link().Trigger();
+ }
+ }
+
+ return count;
+ }
+
+ // Reflects payload, effective after upgrade
+ uint16_t ReceiveData(uint8_t* dataFrame, const uint16_t receivedSize) override {
+#ifdef _VERBOSE
+ std::cout << std::dec << __LINE__ << " : " << __PRETTY_FUNCTION__ << "\n";
+#endif
+
+ if (receivedSize > 0) {
+ _response.emplace_back(std::basic_string{ dataFrame, receivedSize });
+
+#ifdef _VERBOSE
+ std::cout << " |--> dataFrame ( " << receivedSize << " ) = ";
+ for (int32_t index = 0; index < receivedSize; index++) {
+ std::cout << std::hex << static_cast(dataFrame[index]) << " ";
+ }
+ std::cout << "\n";
+#endif
+ }
+
+ return receivedSize;
+ }
+
+ // Put data in the queue to send (to the (connected) client)
+ bool Submit(const std::basic_string& message)
+ {
+ size_t count = _post.size();
+
+ _post.emplace_back(message);
+
+ // Trigger a call to SendData
+ ::Thunder::Web::WebSocketServerType::Link().Trigger();
+
+ return count < _post.size();
+ }
+
+ std::basic_string Response()
+ {
+#ifdef _VERBOSE
+ std::cout << std::dec << __LINE__ << " : " << __PRETTY_FUNCTION__ << "\n";
+#endif
+
+ std::basic_string message;
+
+ if (_response.size() > 0) {
+ message = _response.front();
+ _response.erase(_response.begin());
+
+#ifdef _VERBOSE
+ std::cout << " |--> message ( " << message.size() << " ) = ";
+ for (int32_t index = 0; index < message.size(); index++) {
+ std::cout << std::hex << static_cast(message[index]) << " ";
+ }
+ std::cout << "\n";
+#endif
+ }
+
+ return message;
+ }
+
+ private:
+
+ std::vector> _post; // Send message queue
+ std::vector> _response; // Receive message queue
+ };
+
+ TEST(WebSocket, DISABLED_OpeningServerPort)
+ {
+ const TCHAR localHostName[] {"127.0.0.1"};
+
+ constexpr uint16_t tcpServerPort {12345}; // TCP, default 80 or 443 (SSL)
+ constexpr uint32_t tcpProtocol {0}; // HTTP or HTTPS but can only be set on raw sockets
+
+ // The minimum size is determined by the HTTP upgrade process. The limit here is above that threshold.
+ constexpr uint16_t sendBufferSize {1024};
+ constexpr uint16_t receiveBufferSize {1024};
+
+ constexpr uint32_t maxWaitTimeMs = 4000;
+
+ const ::Thunder::Core::NodeId localNode {localHostName, tcpServerPort, ::Thunder::Core::NodeId::TYPE_IPV4, tcpProtocol};
+
+ // This is a listening socket as result of using SocketServerType which enables listening
+ ::Thunder::Core::SocketServerType> server(localNode /* listening node*/);
+
+ ASSERT_EQ(server.Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
+
+ SleepMs(maxWaitTimeMs);
+
+ // Obtain the endpoint at the server side for each (remotely) connected client
+ auto it = server.Clients();
+
+ if (it.Next()) {
+ // Unless a client has send an upgrade request we cannot send data out although we might be calling WebSocket functionality
+
+ if (it.Client()->IsOpen()) {
+ // No data should be transferred to the remote client
+ }
+ }
+
+ SleepMs(maxWaitTimeMs);
+
+ EXPECT_EQ(server.Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
+ }
+
+ TEST(WebSocket, DISABLED_OpeningClientPort)
+ {
+ const std::string webSocketURIPath; // HTTP URI part, empty path allowed
+ const std::string webSocketProtocol; // Optional HTTP field, WebSocket SubProtocol, ie, Sec-WebSocket-Protocol
+ const std::string webSocketURIQuery; // HTTP URI part, absent query allowe
+ const std::string webSocketOrigin; // Optional, set by browser clients
+ constexpr bool binary {false}; // Flag to indicate WebSocket opcode 0x1 (test frame) or 0x2 (binary frame)
+ constexpr bool masking {true}; // Flag set by client to enable masking
+
+ const TCHAR remoteHostName[] {"127.0.0.1"};
+
+ constexpr uint16_t tcpServerPort {12345}; // TCP, default 80 or 443 (SSL)
+ constexpr uint32_t tcpProtocol {0}; // HTTP or HTTPS but can only be set on raw sockets
+
+ constexpr bool rawSocket {false};
+
+ // The minimum size is determined by the HTTP upgrade process. The limit here is above that threshold.
+ constexpr uint16_t sendBufferSize {1024};
+ constexpr uint16_t receiveBufferSize {1024};
+
+ constexpr uint32_t maxWaitTimeMs = 4000;
+
+ const ::Thunder::Core::NodeId remoteNode {remoteHostName, tcpServerPort, ::Thunder::Core::NodeId::TYPE_IPV4, tcpProtocol};
+
+ WebSocketClient client(webSocketURIPath, webSocketProtocol, webSocketURIQuery, webSocketOrigin, false, true, rawSocket, remoteNode.AnyInterface(), remoteNode, sendBufferSize, receiveBufferSize, "WebSocketClient");
+
+ SleepMs(maxWaitTimeMs);
+
+ ASSERT_EQ(client.Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
+
+ SleepMs(maxWaitTimeMs);
+
+ EXPECT_EQ(client.Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
+ }
+
+ TEST(WebSocket, DISABLED_UnsecuredSocketUpgrade)
+ {
+ const TCHAR hostName[] {"127.0.0.1"};
+
+ // Some aliases
+ const auto& remoteHostName = hostName;
+ const auto& localHostName = hostName;
+
+ constexpr uint16_t tcpServerPort {12345}; // TCP, default 80 or 443 (SSL)
+ constexpr uint32_t tcpProtocol {0}; // HTTP or HTTPS but can only be set on raw sockets
+
+ // The minimum size is determined by the HTTP upgrade process. The limit here is above that threshold.
+ constexpr uint16_t sendBufferSize {1024};
+ constexpr uint16_t receiveBufferSize {1024};
+
+ constexpr uint32_t initHandshakeValue = 0, maxWaitTime = 8, maxWaitTimeMs = 8000, maxInitTime = 2000;
+ constexpr uint8_t maxRetries = 10;
+
+ IPTestAdministrator::Callback callback_child = [&](IPTestAdministrator& testAdmin) {
+ const std::string webSocketURIPath; // HTTP URI part, empty path allowed
+ const std::string webSocketProtocol; // Optional HTTP field, WebSocket SubProtocol, ie, Sec-WebSocket-Protocol
+ const std::string webSocketURIQuery; // HTTP URI part, absent query allowe
+ const std::string webSocketOrigin; // Optional, set by browser clients
+ constexpr bool binary {false}; // Flag to indicate WebSocket opcode 0x1 (test frame) or 0x2 (binary frame)
+ constexpr bool masking {true}; // Flag set by client to enable masking
+
+ constexpr bool rawSocket {false};
+
+ const ::Thunder::Core::NodeId remoteNode {remoteHostName, tcpServerPort, ::Thunder::Core::NodeId::TYPE_IPV4, tcpProtocol};
+
+ WebSocketClient client(webSocketURIPath, webSocketProtocol, webSocketURIQuery, webSocketOrigin, false, true, rawSocket, remoteNode.AnyInterface(), remoteNode, sendBufferSize, receiveBufferSize, "WebSocketClient");
+
+ EXPECT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE);
+
+ ASSERT_EQ(client.Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
+
+ EXPECT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE);
+
+ EXPECT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE);
+
+ EXPECT_EQ(client.Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
+
+ EXPECT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE);
+ };
+
+ IPTestAdministrator::Callback callback_parent = [&](IPTestAdministrator& testAdmin) {
+ const ::Thunder::Core::NodeId localNode {localHostName, tcpServerPort, ::Thunder::Core::NodeId::TYPE_IPV4, tcpProtocol};
+
+ // This is a listening socket as result of using SocketServerType which enables listening
+ ::Thunder::Core::SocketServerType> server(localNode /* listening node*/);
+
+ ASSERT_EQ(server.Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
+
+ // A small delay so the child can be set up
+// SleepMs(maxInitTime);
+
+ EXPECT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE);
+
+ EXPECT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE);
+
+ EXPECT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE);
+
+ EXPECT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE);
+
+ EXPECT_EQ(server.Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
+ };
+
+ IPTestAdministrator testAdmin(callback_parent, callback_child, initHandshakeValue, maxWaitTime);
+
+ // Code after this line is executed by both parent and child
+
+ ::Thunder::Core::Singleton::Dispose();
+ }
+
+ TEST(WebSocket, DISABLED_UnsecuredSocketServerPingClientPong)
+ {
+ const TCHAR hostName[] {"127.0.0.1"};
+
+ // Some aliases
+ const auto& remoteHostName = hostName;
+ const auto& localHostName = hostName;
+
+ constexpr uint16_t tcpServerPort {12346}; // TCP, default 80 or 443 (SSL)
+ constexpr uint32_t tcpProtocol {0}; // HTTP or HTTPS but can only be set on raw sockets
+
+ // The minimum size is determined by the HTTP upgrade process. The limit here is above that threshold.
+ constexpr uint16_t sendBufferSize {1024};
+ constexpr uint16_t receiveBufferSize {1024};
+
+ constexpr uint32_t initHandshakeValue = 0, maxWaitTime = 8, maxWaitTimeMs = 8000, maxInitTime = 2000;
+ constexpr uint8_t maxRetries = 10;
+
+ IPTestAdministrator::Callback callback_child = [&](IPTestAdministrator& testAdmin) {
+ const std::string webSocketURIPath; // HTTP URI part, empty path allowed
+ const std::string webSocketProtocol; // Optional HTTP field, WebSocket SubProtocol, ie, Sec-WebSocket-Protocol
+ const std::string webSocketURIQuery; // HTTP URI part, absent query allowe
+ const std::string webSocketOrigin; // Optional, set by browser clients
+ constexpr bool binary {false}; // Flag to indicate WebSocket opcode 0x1 (test frame) or 0x2 (binary frame)
+ constexpr bool masking {true}; // Flag set by client to enable masking
+
+ constexpr bool rawSocket {false};
+
+ const ::Thunder::Core::NodeId remoteNode {remoteHostName, tcpServerPort, ::Thunder::Core::NodeId::TYPE_IPV4, tcpProtocol};
+
+ WebSocketClient client(webSocketURIPath, webSocketProtocol, webSocketURIQuery, webSocketOrigin, false, true, rawSocket, remoteNode.AnyInterface(), remoteNode, sendBufferSize, receiveBufferSize, "WebSocketClient");
+
+ EXPECT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE);
+
+ ASSERT_EQ(client.Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
+
+ EXPECT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE);
+
+ // Avoid premature shutdown() at the other side
+ SleepMs(maxWaitTimeMs);
+
+ EXPECT_EQ(client.Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
+ };
+
+ IPTestAdministrator::Callback callback_parent = [&](IPTestAdministrator& testAdmin) {
+ const ::Thunder::Core::NodeId localNode {localHostName, tcpServerPort, ::Thunder::Core::NodeId::TYPE_IPV4, tcpProtocol};
+
+ // This is a listening socket as result of using SocketServerType which enables listening
+ ::Thunder::Core::SocketServerType> server(localNode /* listening node*/);
+
+ ASSERT_EQ(server.Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
+
+ // A small delay so the child can be set up
+// SleepMs(maxInitTime);
+
+ EXPECT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE);
+
+ EXPECT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE);
+
+ // Obtain the endpoint at the server side for each (remotely) connected client
+ auto it = server.Clients();
+
+ if (it.Next()) {
+ // Unless a client has send an upgrade request we cannot send data out although we might be calling WebSocket functionality
+
+ if (it.Client()->IsOpen()) {
+ /* void */ it.Client()->Ping();
+ }
+ }
+
+ // Allow some time to receive the PONG response
+ SleepMs(maxWaitTimeMs);
+
+ EXPECT_EQ(server.Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
+ };
+
+ IPTestAdministrator testAdmin(callback_parent, callback_child, initHandshakeValue, maxWaitTime);
+
+ // Code after this line is executed by both parent and child
+
+ ::Thunder::Core::Singleton::Dispose();
+ }
+
+ TEST(WebSocket, DISABLED_UnsecuredSocketServerUnsollicitedPong)
+ {
+ const TCHAR hostName[] {"127.0.0.1"};
+
+ // Some aliases
+ const auto& remoteHostName = hostName;
+ const auto& localHostName = hostName;
+
+ constexpr uint16_t tcpServerPort {12346}; // TCP, default 80 or 443 (SSL)
+ constexpr uint32_t tcpProtocol {0}; // HTTP or HTTPS but can only be set on raw sockets
+
+ // The minimum size is determined by the HTTP upgrade process. The limit here is above that threshold.
+ constexpr uint16_t sendBufferSize {1024};
+ constexpr uint16_t receiveBufferSize {1024};
+
+ constexpr uint32_t initHandshakeValue = 0, maxWaitTime = 8, maxWaitTimeMs = 8000, maxInitTime = 2000;
+ constexpr uint8_t maxRetries = 10;
+
+ IPTestAdministrator::Callback callback_child = [&](IPTestAdministrator& testAdmin) {
+ const std::string webSocketURIPath; // HTTP URI part, empty path allowed
+ const std::string webSocketProtocol; // Optional HTTP field, WebSocket SubProtocol, ie, Sec-WebSocket-Protocol
+ const std::string webSocketURIQuery; // HTTP URI part, absent query allowe
+ const std::string webSocketOrigin; // Optional, set by browser clients
+ constexpr bool binary {false}; // Flag to indicate WebSocket opcode 0x1 (test frame) or 0x2 (binary frame)
+ constexpr bool masking {true}; // Flag set by client to enable masking
+
+ constexpr bool rawSocket {false};
+
+ const ::Thunder::Core::NodeId remoteNode {remoteHostName, tcpServerPort, ::Thunder::Core::NodeId::TYPE_IPV4, tcpProtocol};
+
+ WebSocketClient client(webSocketURIPath, webSocketProtocol, webSocketURIQuery, webSocketOrigin, false, true, rawSocket, remoteNode.AnyInterface(), remoteNode, sendBufferSize, receiveBufferSize, "WebSocketClient");
+
+ EXPECT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE);
+
+ ASSERT_EQ(client.Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
+
+ EXPECT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE);
+
+ // Allow some time to receive the PONG
+ SleepMs(maxWaitTimeMs);
+
+ EXPECT_EQ(client.Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
+ };
+
+ IPTestAdministrator::Callback callback_parent = [&](IPTestAdministrator& testAdmin) {
+ const ::Thunder::Core::NodeId localNode {localHostName, tcpServerPort, ::Thunder::Core::NodeId::TYPE_IPV4, tcpProtocol};
+
+ // This is a listening socket as result of using SocketServerType which enables listening
+ ::Thunder::Core::SocketServerType> server(localNode /* listening node*/);
+
+ ASSERT_EQ(server.Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
+
+ // A small delay so the child can be set up
+// SleepMs(maxInitTime);
+
+ EXPECT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE);
+
+ EXPECT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE);
+
+ // Obtain the endpoint at the server side for each (remotely) connected client
+ auto it = server.Clients();
+
+ if (it.Next()) {
+ // Unless a client has send an upgrade request we cannot send data out although we might be calling WebSocket functionality
+
+ if (it.Client()->IsOpen()) {
+ /* void */ it.Client()->Pong();
+ }
+ }
+
+ // Avoid premature shutdown() at the receiving side
+ SleepMs(maxWaitTimeMs);
+
+ EXPECT_EQ(server.Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
+ };
+
+ IPTestAdministrator testAdmin(callback_parent, callback_child, initHandshakeValue, maxWaitTime);
+
+ // Code after this line is executed by both parent and child
+
+ ::Thunder::Core::Singleton::Dispose();
+ }
+
+ TEST(WebSocket, DISABLED_UnsecuredSocketClientUnsollicitedPong)
+ {
+ const TCHAR hostName[] {"127.0.0.1"};
+
+ // Some aliases
+ const auto& remoteHostName = hostName;
+ const auto& localHostName = hostName;
+
+ constexpr uint16_t tcpServerPort {12346}; // TCP, default 80 or 443 (SSL)
+ constexpr uint32_t tcpProtocol {0}; // HTTP or HTTPS but can only be set on raw sockets
+
+ // The minimum size is determined by the HTTP upgrade process. The limit here is above that threshold.
+ constexpr uint16_t sendBufferSize {1024};
+ constexpr uint16_t receiveBufferSize {1024};
+
+ constexpr uint32_t initHandshakeValue = 0, maxWaitTime = 8, maxWaitTimeMs = 8000, maxInitTime = 2000;
+ constexpr uint8_t maxRetries = 10;
+
+ IPTestAdministrator::Callback callback_child = [&](IPTestAdministrator& testAdmin) {
+ const std::string webSocketURIPath; // HTTP URI part, empty path allowed
+ const std::string webSocketProtocol; // Optional HTTP field, WebSocket SubProtocol, ie, Sec-WebSocket-Protocol
+ const std::string webSocketURIQuery; // HTTP URI part, absent query allowe
+ const std::string webSocketOrigin; // Optional, set by browser clients
+ constexpr bool binary {false}; // Flag to indicate WebSocket opcode 0x1 (test frame) or 0x2 (binary frame)
+ constexpr bool masking {true}; // Flag set by client to enable masking
+
+ constexpr bool rawSocket {false};
+
+ const ::Thunder::Core::NodeId remoteNode {remoteHostName, tcpServerPort, ::Thunder::Core::NodeId::TYPE_IPV4, tcpProtocol};
+
+ WebSocketClient client(webSocketURIPath, webSocketProtocol, webSocketURIQuery, webSocketOrigin, false, true, rawSocket, remoteNode.AnyInterface(), remoteNode, sendBufferSize, receiveBufferSize, "WebSocketClient");
+
+ EXPECT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE);
+
+ ASSERT_EQ(client.Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
+
+ EXPECT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE);
+
+ client.Pong();
+
+ // Avoid premature shutdown() at the other side
+ SleepMs(maxWaitTimeMs);
+
+ EXPECT_EQ(client.Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
+ };
+
+ IPTestAdministrator::Callback callback_parent = [&](IPTestAdministrator& testAdmin) {
+ const ::Thunder::Core::NodeId localNode {localHostName, tcpServerPort, ::Thunder::Core::NodeId::TYPE_IPV4, tcpProtocol};
+
+ // This is a listening socket as result of using SocketServerType which enables listening
+ ::Thunder::Core::SocketServerType> server(localNode /* listening node*/);
+
+ ASSERT_EQ(server.Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
+
+ // A small delay so the child can be set up
+// SleepMs(maxInitTime);
+
+ EXPECT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE);
+
+ EXPECT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE);
+
+ // Obtain the endpoint at the server side for each (remotely) connected client
+ auto it = server.Clients();
+
+ // Allow some time to receive the PONG
+ SleepMs(maxWaitTimeMs);
+
+ EXPECT_EQ(server.Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
+ };
+
+ IPTestAdministrator testAdmin(callback_parent, callback_child, initHandshakeValue, maxWaitTime);
+
+ // Code after this line is executed by both parent and child
+
+ ::Thunder::Core::Singleton::Dispose();
+ }
+
+ TEST(WebSocket, DISABLED_UnsecuredSocketDataExchange)
+ {
+ const TCHAR hostName[] {"127.0.0.1"};
+
+ // Some aliases
+ const auto& remoteHostName = hostName;
+ const auto& localHostName = hostName;
+
+ constexpr uint16_t tcpServerPort {12346}; // TCP, default 80 or 443 (SSL)
+ constexpr uint32_t tcpProtocol {0}; // HTTP or HTTPS but can only be set on raw sockets
+
+ // The minimum size is determined by the HTTP upgrade process. The limit here is above that threshold.
+ constexpr uint16_t sendBufferSize {1024};
+ constexpr uint16_t receiveBufferSize {1024};
+
+ constexpr uint32_t initHandshakeValue = 0, maxWaitTime = 8, maxWaitTimeMs = 8000, maxInitTime = 2000;
+ constexpr uint8_t maxRetries = 10;
+
+ IPTestAdministrator::Callback callback_child = [&](IPTestAdministrator& testAdmin) {
+ const std::string webSocketURIPath; // HTTP URI part, empty path allowed
+ const std::string webSocketProtocol; // Optional HTTP field, WebSocket SubProtocol, ie, Sec-WebSocket-Protocol
+ const std::string webSocketURIQuery; // HTTP URI part, absent query allowe
+ const std::string webSocketOrigin; // Optional, set by browser clients
+ constexpr bool binary {false}; // Flag to indicate WebSocket opcode 0x1 (test frame) or 0x2 (binary frame)
+ constexpr bool masking {true}; // Flag set by client to enable masking
+
+ constexpr bool rawSocket {false};
+
+ const ::Thunder::Core::NodeId remoteNode {remoteHostName, tcpServerPort, ::Thunder::Core::NodeId::TYPE_IPV4, tcpProtocol};
+
+ WebSocketClient client(webSocketURIPath, webSocketProtocol, webSocketURIQuery, webSocketOrigin, false, true, rawSocket, remoteNode.AnyInterface(), remoteNode, sendBufferSize, receiveBufferSize, "WebSocketClient");
+
+ EXPECT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE);
+
+ ASSERT_EQ(client.Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
+
+ EXPECT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE);
+
+ // Avoid premature shutdown() at the other side
+ SleepMs(maxWaitTimeMs);
+
+ EXPECT_EQ(client.Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
+ };
+
+ IPTestAdministrator::Callback callback_parent = [&](IPTestAdministrator& testAdmin) {
+ const ::Thunder::Core::NodeId localNode {localHostName, tcpServerPort, ::Thunder::Core::NodeId::TYPE_IPV4, tcpProtocol};
+
+ // This is a listening socket as result of using SocketServerType which enables listening
+ ::Thunder::Core::SocketServerType> server(localNode /* listening node*/);
+
+ ASSERT_EQ(server.Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
+
+ // A small delay so the child can be set up
+// SleepMs(maxInitTime);
+
+ EXPECT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE);
+
+ EXPECT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE);
+
+ // Obtain the endpoint at the server side for each (remotely) connected client
+ auto it = server.Clients();
+
+ constexpr uint8_t data[] = { 0xF, 0xE, 0xD, 0xC, 0xB, 0xA, 0x9, 0x8, 0x7, 0x6, 0x5, 0x3, 0x2, 0x1, 0x0 };
+
+ if (it.Next()) {
+ // Unless a client has send an upgrade request we cannot send data out although we might be calling WebSocket functionality
+
+ if (it.Client()->IsOpen()) {
+ /* bool */ it.Client()->Submit(std::basic_string{ data, sizeof(data) });
+ }
+ }
+
+ // Allow some time to receive the response
+ SleepMs(maxWaitTimeMs);
+
+ std::basic_string response{ data, sizeof(data) };
+ std::reverse(response.begin(), response.end());
+
+ // A simple poll to keep it simple
+ EXPECT_TRUE( it.IsValid()
+ && (it.Client()->Response() == response)
+ );
+
+ EXPECT_EQ(server.Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
+ };
+
+ IPTestAdministrator testAdmin(callback_parent, callback_child, initHandshakeValue, maxWaitTime);
+
+ // Code after this line is executed by both parent and child
+
+ ::Thunder::Core::Singleton::Dispose();
+ }
+
+ TEST(WebSocket, UnsecuredSocketMultiFrameDataExchange)
+ {
+ const TCHAR hostName[] {"127.0.0.1"};
+
+ // Some aliases
+ const auto& remoteHostName = hostName;
+ const auto& localHostName = hostName;
+
+ constexpr uint16_t tcpServerPort {12346}; // TCP, default 80 or 443 (SSL)
+ constexpr uint32_t tcpProtocol {0}; // HTTP or HTTPS but can only be set on raw sockets
+
+ // The minimum size is determined by the HTTP upgrade process. The limit here is above that threshold.
+ constexpr uint16_t sendBufferSize {75};
+ constexpr uint16_t receiveBufferSize {75};
+
+ constexpr uint32_t initHandshakeValue = 0, maxWaitTime = 8, maxWaitTimeMs = 8000, maxInitTime = 2000;
+ constexpr uint8_t maxRetries = 10;
+
+ constexpr uint16_t nagglesTimeoutMs = 250; // Typical is 200 milliseconds
+
+ IPTestAdministrator::Callback callback_child = [&](IPTestAdministrator& testAdmin) {
+ const std::string webSocketURIPath; // HTTP URI part, empty path allowed
+ const std::string webSocketProtocol; // Optional HTTP field, WebSocket SubProtocol, ie, Sec-WebSocket-Protocol
+ const std::string webSocketURIQuery; // HTTP URI part, absent query allowe
+ const std::string webSocketOrigin; // Optional, set by browser clients
+ constexpr bool binary {false}; // Flag to indicate WebSocket opcode 0x1 (test frame) or 0x2 (binary frame)
+ constexpr bool masking {true}; // Flag set by client to enable masking
+
+ constexpr bool rawSocket {false};
+
+ const ::Thunder::Core::NodeId remoteNode {remoteHostName, tcpServerPort, ::Thunder::Core::NodeId::TYPE_IPV4, tcpProtocol};
+
+ WebSocketClient client(webSocketURIPath, webSocketProtocol, webSocketURIQuery, webSocketOrigin, false, true, rawSocket, remoteNode.AnyInterface(), remoteNode, sendBufferSize, receiveBufferSize, "WebSocketClient");
+
+ EXPECT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE);
+
+ ASSERT_EQ(client.Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
+
+ EXPECT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE);
+
+#ifdef _VERBOSE
+ std::cout << std::dec <<__LINE__ << " : " << __PRETTY_FUNCTION__ << "\n";
+ std::cout << " |--> SendBufferSize = " << client.Link().SendBufferSize() << "\n";
+ std::cout << " |--> ReceiveBufferSize = " << client.Link().ReceiveBufferSize() << "\n";
+ std::cout << " |--> SocketSendBufferSize = " << client.Link().SocketSendBufferSize() << "\n";
+ std::cout << " |--> SocketReceiveBufferSize = " << client.Link().SocketReceiveBufferSize() << "\n";
+#endif
+
+ // Avoid premature shutdown() at the other side
+ SleepMs(maxWaitTimeMs);
+
+ EXPECT_EQ(client.Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
+ };
+
+ IPTestAdministrator::Callback callback_parent = [&](IPTestAdministrator& testAdmin) {
+ const ::Thunder::Core::NodeId localNode {localHostName, tcpServerPort, ::Thunder::Core::NodeId::TYPE_IPV4, tcpProtocol};
+
+ // This is a listening socket as result of using SocketServerType which enables listening
+ ::Thunder::Core::SocketServerType> server(localNode /* listening node*/);
+
+ ASSERT_EQ(server.Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
+
+ // A small delay so the child can be set up
+// SleepMs(maxInitTime);
+
+ EXPECT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE);
+
+ EXPECT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE);
+
+ // Obtain the endpoint at the server side for each (remotely) connected client
+ auto it = server.Clients();
+
+ // Do not use '\0' as a marker as std::basic_strng<> assumes it is an end of string
+
+ constexpr uint8_t data[] = { 0xF, 0xE, 0xD, 0xC, 0xB, 0xA, 0x9, 0x8, 0x7, 0x6, 0x5, 0x3, 0x2, 0x1, 0x10 };
+
+ std::basic_string message;
+
+ if (it.Next()) {
+ // Unless a client has send an upgrade request we cannot send data out although we might be calling WebSocket functionality
+
+ if (it.Client()->IsOpen()) {
+ // Construct a message larger than the buffer size to force use of continuation frames
+ size_t count = (it.Client()->Link().SendBufferSize() / sizeof(data) + 1 ) * sizeof(data);
+
+ ASSERT_LE(count, message.max_size());
+
+ message.resize(count);
+
+ for (size_t index = 0; index < count; index += sizeof(data) ) {
+ message.replace(index, sizeof(data), data);
+ }
+
+ /* bool */ it.Client()->Submit(std::basic_string{ message.data(), count });
+ }
+ }
+
+ // Allow some time to receive the response
+ SleepMs(maxWaitTimeMs);
+
+ std::reverse(message.begin(), message.end());
+
+ std::basic_string response;
+
+ response.reserve( message.size() );
+
+ // A simple poll to keep it simple
+ for (int8_t retry = 0; retry < maxRetries; ++retry) {
+ SleepMs(nagglesTimeoutMs); // Naggle's typical delay, perhaps a bit more
+
+ if (it.IsValid()) {
+ response = it.Client()->Response() + response;
+ }
+ }
+
+ EXPECT_TRUE( response.size() == message.size()
+ && response == message
+ );
+
+ EXPECT_EQ(server.Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
+ };
+
+ IPTestAdministrator testAdmin(callback_parent, callback_child, initHandshakeValue, maxWaitTime);
+
+ // Code after this line is executed by both parent and child
+
+ ::Thunder::Core::Singleton::Dispose();
+ }
+
+} // Core
+} // Tests
+} // Thunder
From 45bfec56f7bc95c1dd26b676b1d9a7b3dae82d16 Mon Sep 17 00:00:00 2001
From: msieben <4319079+msieben@users.noreply.github.com>
Date: Thu, 21 Nov 2024 08:28:58 +0000
Subject: [PATCH 05/76] [Tests/unit/core] : Add missing VERBOSE preprocessor
directive to 'test_websocket'
---
Tests/unit/core/test_websocket.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Tests/unit/core/test_websocket.cpp b/Tests/unit/core/test_websocket.cpp
index 2442085b0..794875e69 100644
--- a/Tests/unit/core/test_websocket.cpp
+++ b/Tests/unit/core/test_websocket.cpp
@@ -62,7 +62,9 @@ namespace Core {
~CustomSocketStream()
{
+#ifdef _VERBOSE
std::cout.flush();
+#endif
}
// Raw TCP data
From a206e194452e7a098eb937b3865bc2de1314a2c2 Mon Sep 17 00:00:00 2001
From: msieben <4319079+msieben@users.noreply.github.com>
Date: Thu, 21 Nov 2024 08:42:09 +0000
Subject: [PATCH 06/76] [Tests/unit/core] : Add 'SecureSocketPort' tests to
'test_websocket'
---
Tests/unit/core/test_websocket.cpp | 145 ++++++++++++++++++++++++++++-
1 file changed, 144 insertions(+), 1 deletion(-)
diff --git a/Tests/unit/core/test_websocket.cpp b/Tests/unit/core/test_websocket.cpp
index 794875e69..a2cb1f4f7 100644
--- a/Tests/unit/core/test_websocket.cpp
+++ b/Tests/unit/core/test_websocket.cpp
@@ -391,6 +391,75 @@ namespace Core {
std::vector> _response; // Receive message queue
};
+ class CustomSecureSocketStream : public ::Thunder::Crypto::SecureSocketPort {
+ private :
+
+ // Validat eclient certificate
+ class Validator : public ::Thunder::Crypto::SecureSocketPort::IValidator {
+ public:
+
+ Validator() = default;
+ ~Validator() = default;
+
+ bool Validate(const Certificate& certificate) const override {
+ // Print certificate properties
+#ifdef _VERBOSE
+ std::cout << std::dec <<__LINE__ << " : " << __PRETTY_FUNCTION__ << "\n";
+ std::cout << " |--> Issuer = " << certificate.Issuer() << "\n";
+ std::cout << " |--> Subject = " << certificate.Subject() << "\n";
+ std::cout << " |--> Valid from = " << certificate.ValidFrom().ToRFC1123() << "\n";
+ std::cout << " |--> Valid until = " << certificate.ValidTill().ToRFC1123() << "\n";
+#endif
+ return true; // Always accept
+ }
+ };
+
+ public :
+
+ // In essence, all parameters to SecureSocket are passed to a base class SocketPort
+ CustomSecureSocketStream(
+ const SOCKET& socket
+ , const ::Thunder::Core::NodeId& localNode
+ , const uint16_t sendBufferSize
+ , const uint16_t receiveBufferSize
+ , const std::string& prefix
+ )
+ : ::Thunder::Crypto::SecureSocketPort(::Thunder::Core::SocketPort::STREAM, socket, localNode, sendBufferSize, receiveBufferSize)
+ , _prefix{ prefix }
+ , _validator{}
+ {
+ // Validate custom (sefl signed) certificates
+ uint32_t result = Callback(&_validator);
+ }
+
+ CustomSecureSocketStream(
+ const bool
+ , const ::Thunder::Core::NodeId& localNode
+ , const ::Thunder::Core::NodeId& remoteNode
+ , const uint16_t sendBufferSize
+ , const uint16_t receiveBufferSize
+ , const std::string& prefix
+ )
+ : ::Thunder::Crypto::SecureSocketPort(::Thunder::Core::SocketPort::STREAM, localNode, remoteNode, sendBufferSize, receiveBufferSize, sendBufferSize, receiveBufferSize)
+ , _prefix{ prefix }
+ , _validator{}
+ {
+ // Validate custom (self signed) client certificates
+ uint32_t result = Callback(&_validator);
+ }
+
+ ~CustomSecureSocketStream()
+ {
+ #ifdef _VERBOSE
+ std::cout.flush();
+#endif
+ }
+
+ private:
+ const std::string _prefix;
+ Validator _validator;
+ };
+
TEST(WebSocket, DISABLED_OpeningServerPort)
{
const TCHAR localHostName[] {"127.0.0.1"};
@@ -874,7 +943,7 @@ namespace Core {
::Thunder::Core::Singleton::Dispose();
}
- TEST(WebSocket, UnsecuredSocketMultiFrameDataExchange)
+ TEST(WebSocket, DISABLED_UnsecuredSocketMultiFrameDataExchange)
{
const TCHAR hostName[] {"127.0.0.1"};
@@ -1003,6 +1072,80 @@ namespace Core {
::Thunder::Core::Singleton::Dispose();
}
+ TEST(WebSocket, DISABLED_OpeningSecuredServerPort)
+ {
+ const TCHAR localHostName[] {"127.0.0.1"};
+
+ constexpr uint16_t tcpServerPort {12345}; // TCP, default 80 or 443 (SSL)
+ constexpr uint32_t tcpProtocol {0}; // HTTP or HTTPS but can only be set on raw sockets
+
+ // The minimum size is determined by the HTTP upgrade process. The limit here is above that threshold.
+ constexpr uint16_t sendBufferSize {1024};
+ constexpr uint16_t receiveBufferSize {1024};
+
+ constexpr uint32_t maxWaitTimeMs = 4000;
+
+ const ::Thunder::Core::NodeId localNode {localHostName, tcpServerPort, ::Thunder::Core::NodeId::TYPE_IPV4, tcpProtocol};
+
+ // This is a listening socket as result of using SocketServerType which enables listening
+ ::Thunder::Core::SocketServerType> server(localNode /* listening node*/);
+
+ ASSERT_EQ(server.Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
+
+// SleepMs(maxWaitTimeMs);
+
+ // Obtain the endpoint at the server side for each (remotely) connected client
+ auto it = server.Clients();
+
+ if (it.Next()) {
+ // Unless a client has send an upgrade request we cannot send data out although we might be calling WebSocket functionality
+ if (it.Client()->IsOpen()) {
+ // No data should be transferred to the remote client
+ } else {
+ }
+ }
+
+ SleepMs(4*maxWaitTimeMs);
+
+
+ EXPECT_EQ(server.Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
+ }
+
+ TEST(WebSocket, OpeningSecuredClientPort)
+ {
+ const std::string webSocketURIPath; // HTTP URI part, empty path allowed
+ const std::string webSocketProtocol; // Optional HTTP field, WebSocket SubProtocol, ie, Sec-WebSocket-Protocol
+ const std::string webSocketURIQuery; // HTTP URI part, absent query allowe
+ const std::string webSocketOrigin; // Optional, set by browser clients
+ constexpr bool binary {false}; // Flag to indicate WebSocket opcode 0x1 (test frame) or 0x2 (binary frame)
+ constexpr bool masking {true}; // Flag set by client to enable masking
+
+ const TCHAR remoteHostName[] {"127.0.0.1"};
+
+ constexpr uint16_t tcpServerPort {12345}; // TCP, default 80 or 443 (SSL)
+ constexpr uint32_t tcpProtocol {0}; // HTTP or HTTPS but can only be set on raw sockets
+
+ constexpr bool rawSocket {false};
+
+ // The minimum size is determined by the HTTP upgrade process. The limit here is above that threshold.
+ constexpr uint16_t sendBufferSize {1024};
+ constexpr uint16_t receiveBufferSize {1024};
+
+ constexpr uint32_t maxWaitTimeMs = 4000;
+
+ const ::Thunder::Core::NodeId remoteNode {remoteHostName, tcpServerPort, ::Thunder::Core::NodeId::TYPE_IPV4, tcpProtocol};
+
+ WebSocketClient client(webSocketURIPath, webSocketProtocol, webSocketURIQuery, webSocketOrigin, false, true, rawSocket, remoteNode.AnyInterface(), remoteNode, sendBufferSize, receiveBufferSize, "WebSocketClient");
+
+// SleepMs(maxWaitTimeMs);
+
+ ASSERT_EQ(client.Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
+
+ SleepMs(maxWaitTimeMs);
+
+ EXPECT_EQ(client.Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
+ }
+
} // Core
} // Tests
} // Thunder
From 426161696810252a8730c26277f2e14bbde259e2 Mon Sep 17 00:00:00 2001
From: msieben <4319079+msieben@users.noreply.github.com>
Date: Thu, 21 Nov 2024 10:15:08 +0000
Subject: [PATCH 07/76] [Source/crypto/SecureSocketPort] : Initialize when
context and SSL structure are not yet contructed, and, improve SSL read and
write use
- SSL read and write methods should only be used if the handshake is successfull
- Do not return error values for SSL read and write methods as number of read or written bytes
---
Source/cryptalgo/SecureSocketPort.cpp | 17 +++++++++++++----
1 file changed, 13 insertions(+), 4 deletions(-)
diff --git a/Source/cryptalgo/SecureSocketPort.cpp b/Source/cryptalgo/SecureSocketPort.cpp
index 764b94398..6188954aa 100644
--- a/Source/cryptalgo/SecureSocketPort.cpp
+++ b/Source/cryptalgo/SecureSocketPort.cpp
@@ -151,16 +151,25 @@ uint32_t SecureSocketPort::Handler::Initialize() {
}
int32_t SecureSocketPort::Handler::Read(uint8_t buffer[], const uint16_t length) const {
- int32_t result = SSL_read(static_cast(_ssl), buffer, length);
+ int32_t result = 0;
- if (_handShaking != CONNECTED) {
- const_cast(*this).Update();
+ if (_handShaking == CONNECTED) {
+ int value = SSL_read(static_cast(_ssl), buffer, length);
+ result = (value > 0 ? value : 0);
}
+
return (result);
}
int32_t SecureSocketPort::Handler::Write(const uint8_t buffer[], const uint16_t length) {
- return (SSL_write(static_cast(_ssl), buffer, length));
+ int32_t result = 0;
+
+ if (_handShaking == CONNECTED) {
+ int value = (SSL_write(static_cast(_ssl), buffer, length));
+ result = (value > 0 ? value : 0);
+ }
+
+ return (result);
}
From d9d1dd406b4d081a4a417ce32588405b96177abb Mon Sep 17 00:00:00 2001
From: msieben <4319079+msieben@users.noreply.github.com>
Date: Thu, 21 Nov 2024 13:40:17 +0000
Subject: [PATCH 08/76] [Source/cryptalgo/SecureSocketPort] : (Re)initialize if
socket has been opened but context has not been contructed.
---
Source/cryptalgo/SecureSocketPort.h | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/Source/cryptalgo/SecureSocketPort.h b/Source/cryptalgo/SecureSocketPort.h
index 280a44ae3..052333263 100644
--- a/Source/cryptalgo/SecureSocketPort.h
+++ b/Source/cryptalgo/SecureSocketPort.h
@@ -105,10 +105,14 @@ namespace Crypto {
// Signal a state change, Opened, Closed or Accepted
void StateChange() override {
+ if (IsOpen() && _context != nullptr) { // Initialize may not yet have happened
+ Initialize();
+ }
- ASSERT(_context != nullptr);
- Update();
- };
+ if (_ssl != nullptr) {
+ Update();
+ }
+ };
inline uint32_t Callback(IValidator* callback) {
uint32_t result = Core::ERROR_ILLEGAL_STATE;
From 41fde68e40caa418086c94916101e15c1726506b Mon Sep 17 00:00:00 2001
From: msieben <4319079+msieben@users.noreply.github.com>
Date: Thu, 21 Nov 2024 13:43:21 +0000
Subject: [PATCH 09/76] [Source/cryptalgo/SecureSocketPort] : Context settings
do not (necessarily) propagate to SSL structure after construction.
- Context should exist
- Options should be set
---
Source/cryptalgo/SecureSocketPort.cpp | 23 ++++++++++++++---------
1 file changed, 14 insertions(+), 9 deletions(-)
diff --git a/Source/cryptalgo/SecureSocketPort.cpp b/Source/cryptalgo/SecureSocketPort.cpp
index 6188954aa..73f216c74 100644
--- a/Source/cryptalgo/SecureSocketPort.cpp
+++ b/Source/cryptalgo/SecureSocketPort.cpp
@@ -135,16 +135,21 @@ uint32_t SecureSocketPort::Handler::Initialize() {
_context = SSL_CTX_new(TLS_method());
- _ssl = SSL_new(static_cast(_context));
- SSL_set_fd(static_cast(_ssl), static_cast(*this).Descriptor());
- SSL_CTX_set_options(static_cast(_context), SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
+ if (_context != nullptr) {
+ if ( // Returns bit-mask after adding options
+ ((SSL_CTX_set_options(static_cast(_context), SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3) & (SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3)) == (SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3))
+ && // Trust the same certificates as any other application
+ (SSL_CTX_set_default_verify_paths(static_cast(_context)) == 1)
+ ) {
+ _ssl = SSL_new(static_cast(_context));
- // Trust the same certificates as any other application
- if (SSL_CTX_set_default_verify_paths(static_cast(_context)) == 1) {
- success = Core::SocketPort::Initialize();
- } else {
- TRACE_L1("OpenSSL failed to load certificate store");
- success = Core::ERROR_GENERAL;
+ SSL_set_fd(static_cast(_ssl), static_cast(*this).Descriptor());
+
+ success = Core::SocketPort::Initialize();
+ } else {
+ TRACE_L1("OpenSSL failed to set protocol level or load certificate store");
+ success = Core::ERROR_GENERAL;
+ }
}
return success;
From efe82e0ad50b8257603cb8b00212d52f34a52d67 Mon Sep 17 00:00:00 2001
From: msieben <4319079+msieben@users.noreply.github.com>
Date: Thu, 21 Nov 2024 13:59:13 +0000
Subject: [PATCH 10/76] [Source/cryptalgo/SecureSocketPort] : check return
value for setting hostname.
---
Source/cryptalgo/SecureSocketPort.cpp | 21 +++++++++------------
1 file changed, 9 insertions(+), 12 deletions(-)
diff --git a/Source/cryptalgo/SecureSocketPort.cpp b/Source/cryptalgo/SecureSocketPort.cpp
index 73f216c74..9dbabc840 100644
--- a/Source/cryptalgo/SecureSocketPort.cpp
+++ b/Source/cryptalgo/SecureSocketPort.cpp
@@ -226,29 +226,26 @@ void SecureSocketPort::Handler::ValidateHandShake() {
}
void SecureSocketPort::Handler::Update() {
- if (IsOpen() == true) {
- int result;
-
+ if (IsOpen() == true && _ssl != nullptr) {
if (_handShaking == IDLE) {
- SSL_set_tlsext_host_name(static_cast(_ssl), RemoteNode().HostName().c_str());
- result = SSL_connect(static_cast(_ssl));
- if (result == 1) {
+ int result{0};
+
+ if( (result = SSL_set_tlsext_host_name(static_cast(_ssl), RemoteNode().HostName().c_str()) == 1)
+ && (result = SSL_connect(static_cast(_ssl)) == 1)
+ ) {
ValidateHandShake();
- }
- else {
+ } else { // Support non-blocking SocketPorts, result taken from SSL_connect
result = SSL_get_error(static_cast(_ssl), result);
if ((result == SSL_ERROR_WANT_READ) || (result == SSL_ERROR_WANT_WRITE)) {
_handShaking = EXCHANGE;
}
}
- }
- else if (_handShaking == EXCHANGE) {
+ } else if (_handShaking == EXCHANGE) {
if (SSL_do_handshake(static_cast(_ssl)) == 1) {
ValidateHandShake();
}
}
- }
- else if (_ssl != nullptr) {
+ } else if (_ssl != nullptr) {
_handShaking = IDLE;
_parent.StateChange();
}
From 4a8b4b1d21736ecb2ab03ecdebf33af48fa72cee Mon Sep 17 00:00:00 2001
From: msieben <4319079+msieben@users.noreply.github.com>
Date: Thu, 21 Nov 2024 14:22:43 +0000
Subject: [PATCH 11/76] [Source/cryptalgo/SecureSocketPort] : improve return
value checking in 'Handler::Initialize'
---
Source/cryptalgo/SecureSocketPort.cpp | 14 +++++---------
1 file changed, 5 insertions(+), 9 deletions(-)
diff --git a/Source/cryptalgo/SecureSocketPort.cpp b/Source/cryptalgo/SecureSocketPort.cpp
index 9dbabc840..5299331d3 100644
--- a/Source/cryptalgo/SecureSocketPort.cpp
+++ b/Source/cryptalgo/SecureSocketPort.cpp
@@ -131,20 +131,16 @@ SecureSocketPort::Handler::~Handler() {
}
uint32_t SecureSocketPort::Handler::Initialize() {
- uint32_t success = Core::ERROR_NONE;
+ uint32_t success = Core::ERROR_GENERAL;
- _context = SSL_CTX_new(TLS_method());
-
- if (_context != nullptr) {
+ if ((_context = SSL_CTX_new(TLS_method())) != nullptr) {
if ( // Returns bit-mask after adding options
((SSL_CTX_set_options(static_cast(_context), SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3) & (SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3)) == (SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3))
&& // Trust the same certificates as any other application
(SSL_CTX_set_default_verify_paths(static_cast(_context)) == 1)
- ) {
- _ssl = SSL_new(static_cast(_context));
-
- SSL_set_fd(static_cast(_ssl), static_cast(*this).Descriptor());
-
+ && ((_ssl = SSL_new(static_cast(_context))) != nullptr)
+ && (SSL_set_fd(static_cast(_ssl), static_cast(*this).Descriptor()) == 1)
+ ) {
success = Core::SocketPort::Initialize();
} else {
TRACE_L1("OpenSSL failed to set protocol level or load certificate store");
From bdaff2986d2d5b27e0939f93e4e550194db01fe9 Mon Sep 17 00:00:00 2001
From: msieben <4319079+msieben@users.noreply.github.com>
Date: Thu, 21 Nov 2024 14:34:33 +0000
Subject: [PATCH 12/76] [Source/cryptalgo/SecureSocketPort] : correct check for
invalid context
---
Source/cryptalgo/SecureSocketPort.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Source/cryptalgo/SecureSocketPort.h b/Source/cryptalgo/SecureSocketPort.h
index 052333263..49c5d5ed1 100644
--- a/Source/cryptalgo/SecureSocketPort.h
+++ b/Source/cryptalgo/SecureSocketPort.h
@@ -105,7 +105,7 @@ namespace Crypto {
// Signal a state change, Opened, Closed or Accepted
void StateChange() override {
- if (IsOpen() && _context != nullptr) { // Initialize may not yet have happened
+ if (IsOpen() && _context == nullptr) { // Initialize may not yet have happened
Initialize();
}
From 25634c61c665a8629285590690a51b29c91a06c8 Mon Sep 17 00:00:00 2001
From: msieben <4319079+msieben@users.noreply.github.com>
Date: Fri, 22 Nov 2024 09:42:50 +0000
Subject: [PATCH 13/76] [Source/core/SocketPort
Source/cryptalgo/SecureSocketPort Source/websocket/WebSocketLink] : cherry
pick from 'master'
---
Source/core/SocketPort.cpp | 12 ++-
Source/core/SocketPort.h | 8 +-
Source/cryptalgo/SecureSocketPort.cpp | 123 +++++++++++++++-----------
Source/cryptalgo/SecureSocketPort.h | 18 ++--
Source/websocket/WebSocketLink.h | 8 +-
5 files changed, 96 insertions(+), 73 deletions(-)
diff --git a/Source/core/SocketPort.cpp b/Source/core/SocketPort.cpp
index 5a487e84f..c8e953396 100644
--- a/Source/core/SocketPort.cpp
+++ b/Source/core/SocketPort.cpp
@@ -496,9 +496,15 @@ namespace Thunder {
m_SendOffset = 0;
if ((m_State.load(Core::memory_order::memory_order_relaxed) & (SocketPort::LINK | SocketPort::OPEN | SocketPort::MONITOR)) == (SocketPort::LINK | SocketPort::OPEN)) {
- // Open up an accepted socket, but not yet added to the monitor.
- m_State.fetch_or(SocketPort::UPDATE, Core::memory_order::memory_order_relaxed);
- nStatus = Core::ERROR_NONE;
+
+ if (Initialize() != Core::ERROR_NONE) {
+ nStatus = Core::ERROR_ABORTED;
+ }
+ else {
+ // Open up an accepted socket, but not yet added to the monitor.
+ m_State.fetch_or(SocketPort::UPDATE, Core::memory_order::memory_order_relaxed);
+ nStatus = Core::ERROR_INPROGRESS;
+ }
}
else {
ASSERT((m_Socket == INVALID_SOCKET) && (m_State.load(Core::memory_order::memory_order_relaxed) == 0));
diff --git a/Source/core/SocketPort.h b/Source/core/SocketPort.h
index efadf89bb..2f3f51b31 100644
--- a/Source/core/SocketPort.h
+++ b/Source/core/SocketPort.h
@@ -250,6 +250,10 @@ namespace Thunder {
NodeId Accept();
void Listen();
SOCKET Accept(NodeId& remoteId);
+ IResource::handle Descriptor() const override
+ {
+ return (static_cast(m_Socket));
+ }
protected:
virtual uint32_t Initialize();
@@ -267,10 +271,6 @@ namespace Thunder {
private:
string Identifier(const NodeId& node) const;
- IResource::handle Descriptor() const override
- {
- return (static_cast(m_Socket));
- }
inline uint32_t SocketMode() const
{
return (((m_SocketType == LISTEN) || (m_SocketType == STREAM)) ? SOCK_STREAM : ((m_SocketType == DATAGRAM) ? SOCK_DGRAM : (m_SocketType == SEQUENCED ? SOCK_SEQPACKET : SOCK_RAW)));
diff --git a/Source/cryptalgo/SecureSocketPort.cpp b/Source/cryptalgo/SecureSocketPort.cpp
index 5299331d3..073414867 100644
--- a/Source/cryptalgo/SecureSocketPort.cpp
+++ b/Source/cryptalgo/SecureSocketPort.cpp
@@ -122,55 +122,51 @@ bool SecureSocketPort::Certificate::Verify(string& errorMsg) const {
SecureSocketPort::Handler::~Handler() {
- if(_ssl != nullptr) {
- SSL_free(static_cast(_ssl));
- }
- if(_context != nullptr) {
- SSL_CTX_free(static_cast(_context));
- }
+ ASSERT(IsClosed() == true);
+ Close(0);
}
uint32_t SecureSocketPort::Handler::Initialize() {
- uint32_t success = Core::ERROR_GENERAL;
-
- if ((_context = SSL_CTX_new(TLS_method())) != nullptr) {
- if ( // Returns bit-mask after adding options
- ((SSL_CTX_set_options(static_cast(_context), SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3) & (SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3)) == (SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3))
- && // Trust the same certificates as any other application
- (SSL_CTX_set_default_verify_paths(static_cast(_context)) == 1)
- && ((_ssl = SSL_new(static_cast(_context))) != nullptr)
- && (SSL_set_fd(static_cast(_ssl), static_cast(*this).Descriptor()) == 1)
- ) {
- success = Core::SocketPort::Initialize();
- } else {
- TRACE_L1("OpenSSL failed to set protocol level or load certificate store");
- success = Core::ERROR_GENERAL;
+ uint32_t success = Core::ERROR_NONE;
+
+ if (IsOpen() == true) {
+ _context = SSL_CTX_new(TLS_server_method());
+ _handShaking = ACCEPTING;
+ }
+ else {
+ _context = SSL_CTX_new(TLS_method());
+ _handShaking = CONNECTING;
+ }
+
+ _ssl = SSL_new(static_cast(_context));
+ SSL_set_fd(static_cast(_ssl), static_cast(*this).Descriptor());
+ SSL_CTX_set_options(static_cast(_context), SSL_OP_ALL | SSL_OP_NO_SSLv2);
+
+ // Trust the same certificates as any other application
+ if (SSL_CTX_set_default_verify_paths(static_cast(_context)) == 1) {
+ success = Core::SocketPort::Initialize();
+
+ if (success == Core::ERROR_NONE) {
+ SSL_set_tlsext_host_name(static_cast(_ssl), RemoteNode().HostName().c_str());
}
}
+ else {
+ TRACE_L1("OpenSSL failed to load certificate store");
+ success = Core::ERROR_GENERAL;
+ }
return success;
}
int32_t SecureSocketPort::Handler::Read(uint8_t buffer[], const uint16_t length) const {
- int32_t result = 0;
-
- if (_handShaking == CONNECTED) {
- int value = SSL_read(static_cast(_ssl), buffer, length);
- result = (value > 0 ? value : 0);
+ if (_handShaking != CONNECTED) {
+ const_cast(*this).Update();
}
-
- return (result);
+ return (SSL_read(static_cast(_ssl), buffer, length));
}
int32_t SecureSocketPort::Handler::Write(const uint8_t buffer[], const uint16_t length) {
- int32_t result = 0;
-
- if (_handShaking == CONNECTED) {
- int value = (SSL_write(static_cast(_ssl), buffer, length));
- result = (value > 0 ? value : 0);
- }
-
- return (result);
+ return (SSL_write(static_cast(_ssl), buffer, length));
}
@@ -181,8 +177,14 @@ uint32_t SecureSocketPort::Handler::Open(const uint32_t waitTime) {
uint32_t SecureSocketPort::Handler::Close(const uint32_t waitTime) {
if (_ssl != nullptr) {
SSL_shutdown(static_cast(_ssl));
+ SSL_free(static_cast(_ssl));
+ _ssl = nullptr;
+ }
+ if (_context != nullptr) {
+ SSL_CTX_free(static_cast(_context));
+ _context = nullptr;
}
- _handShaking = IDLE;
+
return(Core::SocketPort::Close(waitTime));
}
@@ -209,40 +211,53 @@ void SecureSocketPort::Handler::ValidateHandShake() {
if (!validationError.empty()) {
TRACE_L1("OpenSSL certificate validation error for %s: %s", certificate.Subject().c_str(), validationError.c_str());
}
- _handShaking = IDLE;
+ _handShaking = ERROR;
Core::SocketPort::Unlock();
SetError();
}
X509_free(x509cert);
} else {
- _handShaking = IDLE;
+ _handShaking = ERROR;
SetError();
}
}
void SecureSocketPort::Handler::Update() {
- if (IsOpen() == true && _ssl != nullptr) {
- if (_handShaking == IDLE) {
- int result{0};
- if( (result = SSL_set_tlsext_host_name(static_cast(_ssl), RemoteNode().HostName().c_str()) == 1)
- && (result = SSL_connect(static_cast(_ssl)) == 1)
- ) {
- ValidateHandShake();
- } else { // Support non-blocking SocketPorts, result taken from SSL_connect
- result = SSL_get_error(static_cast(_ssl), result);
- if ((result == SSL_ERROR_WANT_READ) || (result == SSL_ERROR_WANT_WRITE)) {
- _handShaking = EXCHANGE;
- }
+ if (IsOpen() == true) {
+ int result;
+
+ ASSERT(_ssl != nullptr);
+
+ if (_handShaking == CONNECTING) {
+ if ((result = SSL_connect(static_cast(_ssl))) == 1) {
+ _handShaking = EXCHANGE;
}
- } else if (_handShaking == EXCHANGE) {
- if (SSL_do_handshake(static_cast(_ssl)) == 1) {
+ }
+ else if (_handShaking == ACCEPTING) {
+ if ((result = SSL_accept(static_cast(_ssl))) == 1) {
+ _handShaking = EXCHANGE;
+ }
+ }
+
+ if (_handShaking == EXCHANGE) {
+ if ((result = SSL_do_handshake(static_cast(_ssl))) == 1) {
ValidateHandShake();
}
}
- } else if (_ssl != nullptr) {
- _handShaking = IDLE;
+
+ if (result != 1) {
+ result = SSL_get_error(static_cast(_ssl), result);
+ if ((result != SSL_ERROR_WANT_READ) && (result != SSL_ERROR_WANT_WRITE)) {
+ _handShaking = ERROR;
+ }
+ else if (result == SSL_ERROR_WANT_WRITE) {
+ Trigger();
+ }
+ }
+ }
+ else {
_parent.StateChange();
}
}
diff --git a/Source/cryptalgo/SecureSocketPort.h b/Source/cryptalgo/SecureSocketPort.h
index 49c5d5ed1..5fd037b40 100644
--- a/Source/cryptalgo/SecureSocketPort.h
+++ b/Source/cryptalgo/SecureSocketPort.h
@@ -64,9 +64,11 @@ namespace Crypto {
class EXTERNAL Handler : public Core::SocketPort {
private:
enum state : uint8_t {
- IDLE,
+ ACCEPTING,
+ CONNECTING,
EXCHANGE,
- CONNECTED
+ CONNECTED,
+ ERROR
};
public:
@@ -81,7 +83,7 @@ namespace Crypto {
, _context(nullptr)
, _ssl(nullptr)
, _callback(nullptr)
- , _handShaking(IDLE) {
+ , _handShaking(CONNECTING) {
}
~Handler();
@@ -105,14 +107,8 @@ namespace Crypto {
// Signal a state change, Opened, Closed or Accepted
void StateChange() override {
- if (IsOpen() && _context == nullptr) { // Initialize may not yet have happened
- Initialize();
- }
-
- if (_ssl != nullptr) {
- Update();
- }
- };
+ Update();
+ };
inline uint32_t Callback(IValidator* callback) {
uint32_t result = Core::ERROR_ILLEGAL_STATE;
diff --git a/Source/websocket/WebSocketLink.h b/Source/websocket/WebSocketLink.h
index 98e3d2dac..f074ed136 100644
--- a/Source/websocket/WebSocketLink.h
+++ b/Source/websocket/WebSocketLink.h
@@ -408,7 +408,13 @@ PUSH_WARNING(DISABLE_WARNING_THIS_IN_MEMBER_INITIALIZER_LIST)
{
}
POP_WARNING()
- ~HandlerType() override = default;
+ ~HandlerType() override {
+ // If this assert fires, it means the socket was not closed
+ // by the one who opened it. That is unexpected. The creater
+ // of this link, should (besides opening it) also close it.
+ ASSERT(ACTUALLINK::IsClosed() == true);
+ ACTUALLINK::Close(Core::infinite);
+ }
public:
bool IsOpen() const
From f6e297e857a232295b955078d8de77f8f81a93bd Mon Sep 17 00:00:00 2001
From: msieben <4319079+msieben@users.noreply.github.com>
Date: Fri, 22 Nov 2024 09:58:22 +0000
Subject: [PATCH 14/76] [Source/cryptalgo/SecureSocketPort] : use the generic
method
---
Source/cryptalgo/SecureSocketPort.cpp | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/Source/cryptalgo/SecureSocketPort.cpp b/Source/cryptalgo/SecureSocketPort.cpp
index 073414867..bc21d6ece 100644
--- a/Source/cryptalgo/SecureSocketPort.cpp
+++ b/Source/cryptalgo/SecureSocketPort.cpp
@@ -130,14 +130,16 @@ uint32_t SecureSocketPort::Handler::Initialize() {
uint32_t success = Core::ERROR_NONE;
if (IsOpen() == true) {
- _context = SSL_CTX_new(TLS_server_method());
_handShaking = ACCEPTING;
- }
- else {
- _context = SSL_CTX_new(TLS_method());
+ } else {
_handShaking = CONNECTING;
}
+ // Client and server use
+ _context = SSL_CTX_new(TLS_method());
+
+ ASSERT(_context != nullptr)
+
_ssl = SSL_new(static_cast(_context));
SSL_set_fd(static_cast(_ssl), static_cast(*this).Descriptor());
SSL_CTX_set_options(static_cast(_context), SSL_OP_ALL | SSL_OP_NO_SSLv2);
From 980e387eb49a23ee9bb086d0626012085683ea51 Mon Sep 17 00:00:00 2001
From: msieben <4319079+msieben@users.noreply.github.com>
Date: Fri, 22 Nov 2024 12:33:04 +0000
Subject: [PATCH 15/76] [Source/cryptalgo/SecureSocketPort] : Re-order creation
of structures
Context settings do not (necessarily) propagate to the SSL structure
---
Source/cryptalgo/SecureSocketPort.cpp | 25 +++++++++++++------------
1 file changed, 13 insertions(+), 12 deletions(-)
diff --git a/Source/cryptalgo/SecureSocketPort.cpp b/Source/cryptalgo/SecureSocketPort.cpp
index bc21d6ece..18f08ffaf 100644
--- a/Source/cryptalgo/SecureSocketPort.cpp
+++ b/Source/cryptalgo/SecureSocketPort.cpp
@@ -138,22 +138,23 @@ uint32_t SecureSocketPort::Handler::Initialize() {
// Client and server use
_context = SSL_CTX_new(TLS_method());
- ASSERT(_context != nullptr)
+ ASSERT(_context != nullptr);
- _ssl = SSL_new(static_cast(_context));
- SSL_set_fd(static_cast(_ssl), static_cast(*this).Descriptor());
- SSL_CTX_set_options(static_cast(_context), SSL_OP_ALL | SSL_OP_NO_SSLv2);
+ constexpr uint64_t options = SSL_OP_ALL | SSL_OP_NO_SSLv2;
+
+ VARIABLE_IS_NOT_USED uint64_t bitmask = SSL_CTX_set_options(static_cast(_context), options);
+
+ ASSERT((bitmask & options) == options);
// Trust the same certificates as any other application
- if (SSL_CTX_set_default_verify_paths(static_cast(_context)) == 1) {
+ if ( // Trust the same certificates as any other application
+ (SSL_CTX_set_default_verify_paths(static_cast(_context)) == 1)
+ && ((_ssl = SSL_new(static_cast(_context))) != nullptr)
+ && (SSL_set_fd(static_cast(_ssl), static_cast(*this).Descriptor()) == 1)
+ ) {
success = Core::SocketPort::Initialize();
-
- if (success == Core::ERROR_NONE) {
- SSL_set_tlsext_host_name(static_cast(_ssl), RemoteNode().HostName().c_str());
- }
- }
- else {
- TRACE_L1("OpenSSL failed to load certificate store");
+ } else {
+ TRACE_L1("OpenSSL failed to initialize: ssl structure / certificate store");
success = Core::ERROR_GENERAL;
}
From 25a61a33d0cfa313ed16b94d901280b1238a608e Mon Sep 17 00:00:00 2001
From: msieben <4319079+msieben@users.noreply.github.com>
Date: Fri, 22 Nov 2024 12:55:09 +0000
Subject: [PATCH 16/76] [Source/cryptalgo/SecureSocketPort] : do not return
error value for SSL read and write
Only read or written bytes have values >= 0, otherwise it indicates an error and does not reflect the actual amount
---
Source/cryptalgo/SecureSocketPort.cpp | 15 +-
.../cryptalgo/SecureSocketPort.cpp.modified | 284 ++++++++++++++++++
Source/cryptalgo/SecureSocketPort.h.modified | 231 ++++++++++++++
.../cryptalgo/SecureSocketPort.modifications | 249 +++++++++++++++
4 files changed, 774 insertions(+), 5 deletions(-)
create mode 100644 Source/cryptalgo/SecureSocketPort.cpp.modified
create mode 100644 Source/cryptalgo/SecureSocketPort.h.modified
create mode 100644 Source/cryptalgo/SecureSocketPort.modifications
diff --git a/Source/cryptalgo/SecureSocketPort.cpp b/Source/cryptalgo/SecureSocketPort.cpp
index 18f08ffaf..b220711ac 100644
--- a/Source/cryptalgo/SecureSocketPort.cpp
+++ b/Source/cryptalgo/SecureSocketPort.cpp
@@ -162,14 +162,19 @@ uint32_t SecureSocketPort::Handler::Initialize() {
}
int32_t SecureSocketPort::Handler::Read(uint8_t buffer[], const uint16_t length) const {
- if (_handShaking != CONNECTED) {
- const_cast(*this).Update();
- }
- return (SSL_read(static_cast(_ssl), buffer, length));
+ ASSERT(_handShaking != CONNECTED);
+
+ int result = SSL_read(static_cast(_ssl), buffer, length);
+
+ return (result > 0 ? result : 0);
}
int32_t SecureSocketPort::Handler::Write(const uint8_t buffer[], const uint16_t length) {
- return (SSL_write(static_cast(_ssl), buffer, length));
+ ASSERT(_handShaking != CONNECTED);
+
+ int result = SSL_write(static_cast(_ssl), buffer, length);
+
+ return (result > 0 ? result : 0);
}
diff --git a/Source/cryptalgo/SecureSocketPort.cpp.modified b/Source/cryptalgo/SecureSocketPort.cpp.modified
new file mode 100644
index 000000000..e52d22c99
--- /dev/null
+++ b/Source/cryptalgo/SecureSocketPort.cpp.modified
@@ -0,0 +1,284 @@
+/*
+ * If not stated otherwise in this file or this component's LICENSE file the
+ * following copyright and licenses apply:
+ *
+ * Copyright 2020 Metrological
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SecureSocketPort.h"
+
+#include
+#include
+
+#ifndef __WINDOWS__
+namespace {
+
+ class OpenSSL {
+ public:
+ OpenSSL(OpenSSL&&) = delete;
+ OpenSSL(const OpenSSL&) = delete;
+ OpenSSL& operator=(OpenSSL&&) = delete;
+ OpenSSL& operator=(const OpenSSL&) = delete;
+
+ OpenSSL()
+ {
+ SSL_library_init();
+ OpenSSL_add_all_algorithms();
+ SSL_load_error_strings();
+ }
+ ~OpenSSL()
+ {
+ }
+ };
+
+ static OpenSSL _initialization;
+}
+#endif
+
+namespace Thunder {
+
+namespace Crypto {
+
+ static Core::Time ASN1_ToTime(const ASN1_TIME* input)
+ {
+ Core::Time result;
+
+ if (input != nullptr) {
+ uint16_t year = 0;
+ const char* textVersion = reinterpret_cast(input->data);
+
+ if (input->type == V_ASN1_UTCTIME)
+ {
+ year = (textVersion[0] - '0') * 10 + (textVersion[1] - '0');
+ year += (year < 70 ? 2000 : 1900);
+ textVersion = &textVersion[2];
+ }
+ else if (input->type == V_ASN1_GENERALIZEDTIME)
+ {
+ year = (textVersion[0] - '0') * 1000 + (textVersion[1] - '0') * 100 + (textVersion[2] - '0') * 10 + (textVersion[3] - '0');
+ textVersion = &textVersion[4];
+ }
+ uint8_t month = ((textVersion[0] - '0') * 10 + (textVersion[1] - '0')) - 1;
+ uint8_t day = (textVersion[2] - '0') * 10 + (textVersion[3] - '0');
+ uint8_t hour = (textVersion[4] - '0') * 10 + (textVersion[5] - '0');
+ uint8_t minutes = (textVersion[6] - '0') * 10 + (textVersion[7] - '0');
+ uint8_t seconds = (textVersion[8] - '0') * 10 + (textVersion[9] - '0');
+
+ /* Note: we did not adjust the time based on time zone information */
+ result = Core::Time(year, month, day, hour, minutes, seconds, 0, false);
+ }
+ return (result);
+ }
+
+string SecureSocketPort::Certificate::Issuer() const {
+ char buffer[1024];
+ buffer[0] = '\0';
+ X509_NAME_oneline(X509_get_issuer_name(_certificate), buffer, sizeof(buffer));
+
+ return (string(buffer));
+}
+
+string SecureSocketPort::Certificate::Subject() const {
+ char buffer[1024];
+ buffer[0] = '\0';
+ X509_NAME_oneline(X509_get_subject_name(_certificate), buffer, sizeof(buffer));
+
+ return (string(buffer));
+}
+
+Core::Time SecureSocketPort::Certificate::ValidFrom() const {
+ return(ASN1_ToTime(X509_get0_notBefore(_certificate)));
+}
+
+Core::Time SecureSocketPort::Certificate::ValidTill() const {
+ return(ASN1_ToTime(X509_get0_notAfter(_certificate)));
+}
+
+bool SecureSocketPort::Certificate::ValidHostname(const string& expectedHostname) const {
+ return (X509_check_host(_certificate, expectedHostname.data(), expectedHostname.size(), 0, nullptr) == 1);
+}
+
+bool SecureSocketPort::Certificate::Verify(string& errorMsg) const {
+ long error = SSL_get_verify_result(_context);
+
+ if (error != X509_V_OK) {
+ errorMsg = X509_verify_cert_error_string(error);
+ }
+
+ return error == X509_V_OK;
+}
+
+SecureSocketPort::Handler::~Handler() {
+ if(_ssl != nullptr) {
+ SSL_free(static_cast(_ssl));
+ }
+ if(_context != nullptr) {
+ SSL_CTX_free(static_cast(_context));
+ }
+}
+
+uint32_t SecureSocketPort::Handler::Initialize() {
+ uint32_t success = Core::ERROR_NONE;
+//implicitly sets handshake method
+// This may require set state or explicit connect or accept
+ _context = SSL_CTX_new(TLS_method());
+ //
+// _context = SSL_CTX_new(TLS_server_method());
+
+// Load the trusted root
+int result = SSL_CTX_load_verify_locations(static_cast(_context), "RootCA.pem", nullptr);
+// Specify server certificate
+ result = SSL_CTX_use_certificate_chain_file(static_cast(_context), "localhost.pem");
+// Load private key for server certificate
+result = SSL_CTX_use_PrivateKey_file(static_cast(_context), "localhost.key", SSL_FILETYPE_PEM);
+
+
+ // CTX setting have no effect after, then, use SSL setters instead
+ _ssl = SSL_new(static_cast(_context));
+
+
+ SSL_set_fd(static_cast(_ssl), static_cast(*this).Descriptor());
+// SSL_CTX_set_options(static_cast(_context), SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
+
+// Load the trusted root
+// result = SSL_CTX_load_verify_locations(static_cast(_context), "RootCA.pem", nullptr);
+// Specify server certificate
+//int result = SSL_CTX_use_certificate_chain_file(static_cast(_context), "localhost.pem");
+// Load private key for server certificate
+//result = SSL_CTX_use_PrivateKey_file(static_cast(_context), "localhost.key", SSL_FILETYPE_PEM);
+
+ if (result == 1) {
+ // Trust the same certificates as any other application
+ // Actually the wrong place
+// if (SSL_CTX_set_default_verify_paths(static_cast(_context)) == 1) {
+ success = Core::SocketPort::Initialize();
+ } else {
+ TRACE_L1("OpenSSL failed to load certificate store");
+ success = Core::ERROR_GENERAL;
+ }
+
+ return success;
+}
+
+int32_t SecureSocketPort::Handler::Read(uint8_t buffer[], const uint16_t length) const {
+ int32_t result = 0;
+
+ if (_handShaking == CONNECTED) {
+ int value = SSL_read(static_cast(_ssl), buffer, length);
+ result = (value > 0 ? value : 0);
+ }
+
+ return (result);
+}
+
+int32_t SecureSocketPort::Handler::Write(const uint8_t buffer[], const uint16_t length) {
+ int32_t result = 0;
+
+ if (_handShaking == CONNECTED) {
+ int value = (SSL_write(static_cast(_ssl), buffer, length));
+ result = (value > 0 ? value : 0);
+ }
+
+ return (result);
+}
+
+uint32_t SecureSocketPort::Handler::Open(const uint32_t waitTime) {
+ uint32_t result = Core::SocketPort::Open(waitTime);
+
+ if (result == Core::ERROR_NONE) {
+ result = Initialize();
+ }
+
+ return result;
+}
+
+uint32_t SecureSocketPort::Handler::Close(const uint32_t waitTime) {
+ if (_ssl != nullptr) {
+ SSL_shutdown(static_cast(_ssl));
+ }
+ _handShaking = IDLE;
+ return(Core::SocketPort::Close(waitTime));
+}
+
+void SecureSocketPort::Handler::ValidateHandShake() {
+ // Step 1: verify a server certificate was presented during the negotiation
+ X509* x509cert = SSL_get_peer_certificate(static_cast(_ssl));
+ if (x509cert != nullptr) {
+ Core::SocketPort::Lock();
+
+ Certificate certificate(x509cert, static_cast(_ssl));
+
+ // Step 2: Validate certificate - use custom IValidator instance if available or if self signed
+ // certificates are needed :-)
+ string validationError;
+ if (_callback && _callback->Validate(certificate) == true) {
+ _handShaking = CONNECTED;
+ Core::SocketPort::Unlock();
+ _parent.StateChange();
+ } else if (certificate.Verify(validationError) && certificate.ValidHostname(RemoteNode().HostName())) {
+ _handShaking = CONNECTED;
+ Core::SocketPort::Unlock();\
+ _parent.StateChange();
+ } else {
+ if (!validationError.empty()) {
+ TRACE_L1("OpenSSL certificate validation error for %s: %s", certificate.Subject().c_str(), validationError.c_str());
+ }
+ _handShaking = IDLE;
+ Core::SocketPort::Unlock();
+ SetError();
+ }
+
+ X509_free(x509cert);
+ } else {
+ _handShaking = IDLE;
+ SetError();
+ }
+}
+
+void SecureSocketPort::Handler::Update() {
+ if (IsOpen() == true) {
+ int result;
+
+ if (_handShaking == IDLE) {
+// SSL_set_tlsext_host_name(static_cast(_ssl), RemoteNode().HostName().c_str());
+
+//this is the next step, for server ssl_acept is required
+result = SSL_accept(static_cast(_ssl));
+
+// result = SSL_connect(static_cast(_ssl));
+ if (result == 1) {
+// ValidateHandShake();
+ }
+ else {
+ result = SSL_get_error(static_cast(_ssl), result);
+ if ((result == SSL_ERROR_WANT_READ) || (result == SSL_ERROR_WANT_WRITE)) {
+ _handShaking = EXCHANGE;
+ }
+ }
+ }
+ else if (_handShaking == EXCHANGE) {
+// This is alrady triggert by connet and accept, this should be used when setting sttae
+// if (SSL_do_handshake(static_cast(_ssl)) == 1) {
+// ValidateHandShake();
+// }
+ }
+ }
+ else if (_ssl != nullptr) {
+ _handShaking = IDLE;
+ _parent.StateChange();
+ }
+}
+
+} } // namespace Thunder::Crypto
diff --git a/Source/cryptalgo/SecureSocketPort.h.modified b/Source/cryptalgo/SecureSocketPort.h.modified
new file mode 100644
index 000000000..b787845fe
--- /dev/null
+++ b/Source/cryptalgo/SecureSocketPort.h.modified
@@ -0,0 +1,231 @@
+/*
+ * If not stated otherwise in this file or this component's LICENSE file the
+ * following copyright and licenses apply:
+ *
+ * Copyright 2020 Metrological
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "Module.h"
+
+struct x509_store_ctx_st;
+struct x509_st;
+struct ssl_st;
+
+namespace Thunder {
+namespace Crypto {
+
+ class EXTERNAL SecureSocketPort : public Core::IResource {
+ public:
+ class EXTERNAL Certificate {
+ public:
+ Certificate() = delete;
+ Certificate(Certificate&&) = delete;
+ Certificate(const Certificate&) = delete;
+
+ Certificate(x509_st* certificate, const ssl_st* context)
+ : _certificate(certificate)
+ , _context(context) {
+ }
+ ~Certificate() = default;
+
+ public:
+ string Issuer() const;
+ string Subject() const;
+ Core::Time ValidFrom() const;
+ Core::Time ValidTill() const;
+ bool ValidHostname(const string& expectedHostname) const;
+ bool Verify(string& errorMsg) const;
+
+ private:
+ x509_st* _certificate;
+ const ssl_st* _context;
+ };
+ struct IValidator {
+ virtual ~IValidator() = default;
+
+ virtual bool Validate(const Certificate& certificate) const = 0;
+ };
+
+ private:
+ class EXTERNAL Handler : public Core::SocketPort {
+ private:
+ enum state : uint8_t {
+ IDLE,
+ EXCHANGE,
+ CONNECTED
+ };
+
+ Handler(Handler&&) = delete;
+ Handler(const Handler&) = delete;
+ Handler& operator=(const Handler&) = delete;
+
+ template
+ Handler(SecureSocketPort& parent, Args&&... args)
+ : Core::SocketPort(args...)
+ , _parent(parent)
+ , _context(nullptr)
+ , _ssl(nullptr)
+ , _callback(nullptr)
+ , _handShaking(IDLE)
+ , _mode{mode_t::CLIENT}
+ {}
+ ~Handler();
+
+ public:
+ uint32_t Initialize() override;
+
+ int32_t Read(uint8_t buffer[], const uint16_t length) const override;
+ int32_t Write(const uint8_t buffer[], const uint16_t length) override;
+
+ uint32_t Open(const uint32_t waitTime);
+ uint32_t Close(const uint32_t waitTime);
+
+ // Methods to extract and insert data into the socket buffers
+ uint16_t SendData(uint8_t* dataFrame, const uint16_t maxSendSize) override {
+ return (_parent.SendData(dataFrame, maxSendSize));
+ }
+
+ uint16_t ReceiveData(uint8_t* dataFrame, const uint16_t receivedSize) override {
+ return (_parent.ReceiveData(dataFrame, receivedSize));
+ }
+
+ // Signal a state change, Opened, Closed or Accepted
+ void StateChange() override {
+ if (_context != nullptr) { // Initialize may not yet have happened
+ Update();
+ }
+ };
+ inline uint32_t Callback(IValidator* callback) {
+ uint32_t result = Core::ERROR_ILLEGAL_STATE;
+
+ Core::SocketPort::Lock();
+
+ ASSERT((callback == nullptr) || (_callback == nullptr));
+
+ if ((callback == nullptr) || (_callback == nullptr)) {
+ _callback = callback;
+ result = Core::ERROR_NONE;
+ }
+ Core::SocketPort::Unlock();
+
+ return (result);
+ }
+
+ private:
+ void Update();
+ void ValidateHandShake();
+
+ private:
+ SecureSocketPort& _parent;
+ void* _context;
+ void* _ssl;
+ IValidator* _callback;
+ mutable state _handShaking;
+ mode_t _mode;
+ };
+
+ private:
+ public:
+ SecureSocketPort(SecureSocketPort&&) = delete;
+ SecureSocketPort(const SecureSocketPort&) = delete;
+ SecureSocketPort& operator=(const SecureSocketPort&) = delete;
+
+ template
+ SecureSocketPort(Args&&... args)
+ : _handler(*this, args...) {
+ }
+ ~SecureSocketPort() override {
+ }
+
+ public:
+ inline bool IsOpen() const
+ {
+ return (_handler.IsOpen());
+ }
+ inline bool IsClosed() const
+ {
+ return (_handler.IsClosed());
+ }
+ inline bool IsSuspended() const {
+ return (_handler.IsSuspended());
+ }
+ inline bool HasError() const
+ {
+ return (_handler.HasError());
+ }
+ inline string LocalId() const
+ {
+ return (_handler.LocalId());
+ }
+ inline string RemoteId() const
+ {
+ return (_handler.RemoteId());
+ }
+ inline void LocalNode(const Core::NodeId& node)
+ {
+ _handler.LocalNode(node);
+ }
+ inline void RemoteNode(const Core::NodeId& node)
+ {
+ _handler.RemoteNode(node);
+ }
+ inline const Core::NodeId& RemoteNode() const
+ {
+ return (_handler.RemoteNode());
+ }
+
+ inline uint32_t Open(const uint32_t waitTime) {
+ return(_handler.Open(waitTime));
+ }
+ inline uint32_t Close(const uint32_t waitTime) {
+ return(_handler.Close(waitTime));
+ }
+ inline void Trigger() {
+ _handler.Trigger();
+ }
+ inline uint32_t Callback(IValidator* callback) {
+ return (_handler.Callback(callback));
+ }
+public:
+ //
+ // Core::IResource interface
+ // ------------------------------------------------------------------------
+ IResource::handle Descriptor() const override
+ {
+ return (static_cast(_handler).Descriptor());
+ }
+ uint16_t Events() override
+ {
+ return (static_cast(_handler).Events());
+ }
+ void Handle(const uint16_t events) override
+ {
+ static_cast(_handler).Handle(events);
+ }
+
+ // Methods to extract and insert data into the socket buffers
+ virtual uint16_t SendData(uint8_t* dataFrame, const uint16_t maxSendSize) = 0;
+ virtual uint16_t ReceiveData(uint8_t* dataFrame, const uint16_t receivedSize) = 0;
+
+ // Signal a state change, Opened, Closed or Accepted
+ virtual void StateChange() = 0;
+
+ private:
+ Handler _handler;
+ };
+}
+}
diff --git a/Source/cryptalgo/SecureSocketPort.modifications b/Source/cryptalgo/SecureSocketPort.modifications
new file mode 100644
index 000000000..e2f721406
--- /dev/null
+++ b/Source/cryptalgo/SecureSocketPort.modifications
@@ -0,0 +1,249 @@
+commit b392a4b6f175241ff71febaae618150ef3385991
+Merge: feef8577 48054e39
+Author: msieben <4319079+msieben@users.noreply.github.com>
+Date: Fri Nov 22 08:53:08 2024 +0000
+
+ WIP on development/websocket: feef8577 [Source/crypto/SecureSocket] : correct check for invalid context
+
+diff --cc Source/cryptalgo/SecureSocketPort.h
+index 49c5d5ed,49c5d5ed..72fb4f6b
+--- a/Source/cryptalgo/SecureSocketPort.h
++++ b/Source/cryptalgo/SecureSocketPort.h
+@@@ -70,18 -70,18 +70,26 @@@ namespace Crypto
+ };
+
+ public:
+++ // Type of underlying socket
+++ enum class sockettype_t : uint8_t {
+++ CLIENT_SOCKET
+++ , SERVER_SOCKET
+++ };
+++
+ Handler(Handler&&) = delete;
+ Handler(const Handler&) = delete;
+ Handler& operator=(const Handler&) = delete;
+
+ template
+-- Handler(SecureSocketPort& parent, Args&&... args)
+++ Handler(SecureSocketPort& parent, sockettype_t type, Args&&... args)
+ : Core::SocketPort(args...)
+ , _parent(parent)
+ , _context(nullptr)
+ , _ssl(nullptr)
+ , _callback(nullptr)
+-- , _handShaking(IDLE) {
+++ , _handShaking(IDLE)
+++ , _type{type}
+++ {
+ }
+ ~Handler();
+
+@@@ -139,6 -139,6 +147,7 @@@
+ void* _ssl;
+ IValidator* _callback;
+ mutable state _handShaking;
+++ sockettype_t _type;
+ };
+
+ public:
+@@@ -146,10 -146,10 +155,25 @@@
+ SecureSocketPort(const SecureSocketPort&) = delete;
+ SecureSocketPort& operator=(const SecureSocketPort&) = delete;
+
+++ protected:
+++ // Operational context
+++ enum class context_t : uint8_t {
+++ CLIENT_CONTEXT
+++ , SERVER_CONTEXT
+++ };
+++
+++ // For the bravehearted, and, those who derive
+++ template
+++ SecureSocketPort(context_t context, Args&&... args)
+++ : _handler{*this, context == context_t::SERVER_CONTEXT ? Handler::sockettype_t::SERVER_SOCKET : Handler::sockettype_t::CLIENT_SOCKET, args...}
+++ {}
+++
+++ public:
+ template
+ SecureSocketPort(Args&&... args)
+-- : _handler(*this, args...) {
+-- }
+++ : SecureSocketPort{context_t::CLIENT_CONTEXT, args...}
+++ {}
+++
+ ~SecureSocketPort() override {
+ }
+
+diff --cc Tests/unit/core/CMakeLists.txt
+index eb8aa882,eb8aa882..cc4e499d
+--- a/Tests/unit/core/CMakeLists.txt
++++ b/Tests/unit/core/CMakeLists.txt
+@@@ -21,120 -21,120 +21,10 @@@ if(LINUX
+ # IPTestAdministrator only supported on LINUX platform
+ add_executable(${TEST_RUNNER_NAME}
+ ../IPTestAdministrator.cpp
+-- test_cyclicbuffer.cpp
+-- test_cyclicbuffer_dataexchange.cpp
+-- test_databuffer.cpp
+-- test_dataelement.cpp
+-- test_dataelementfile.cpp
+-- test_doorbell.cpp
+-- test_enumerate.cpp
+-- test_event.cpp
+-- test_filesystem.cpp
+-- test_frametype.cpp
+-- test_hash.cpp
+-- test_hex2strserialization.cpp
+-- #test_ipc.cpp
+-- test_ipcclient.cpp
+-- test_iso639.cpp
+-- test_iterator.cpp
+-- test_jsonparser.cpp
+-- test_keyvalue.cpp
+-- test_library.cpp
+-- test_lockablecontainer.cpp
+-- test_measurementtype.cpp
+-- test_memberavailability.cpp
+-- test_message_dispatcher.cpp
+-- test_messageException.cpp
+-- test_networkinfo.cpp
+-- test_nodeid.cpp
+-- test_numbertype.cpp
+-- test_optional.cpp
+-- test_parser.cpp
+-- test_portability.cpp
+-- test_processinfo.cpp
+-- test_queue.cpp
+-- test_rangetype.cpp
+-- test_readwritelock.cpp
+-- test_rectangle.cpp
+-- test_rpc.cpp
+-- test_semaphore.cpp
+-- test_sharedbuffer.cpp
+-- test_singleton.cpp
+-- test_socketstreamjson.cpp
+-- test_socketstreamtext.cpp
+-- test_statetrigger.cpp
+-- test_stopwatch.cpp
+-- test_synchronize.cpp
+-- test_synchronous.cpp
+-- test_systeminfo.cpp
+-- test_textfragment.cpp
+-- test_textreader.cpp
+-- test_thread.cpp
+-- test_threadpool.cpp
+-- test_time.cpp
+-- test_timer.cpp
+-- test_tristate.cpp
+-- #test_valuerecorder.cpp
+-- test_weblinkjson.cpp
+-- test_weblinktext.cpp
+-- test_websocketjson.cpp
+-- test_websockettext.cpp
+-- test_workerpool.cpp
+-- test_xgetopt.cpp
+++ test_websocket.cpp
+ )
+--#[[
+--target_sources(${TEST_RUNNER_NAME} PRIVATE test_message_unit.cpp)
+--target_link_libraries(${TEST_RUNNER_NAME}
+-- ThunderMessaging
+--)
+--]]
+ else()
+ add_executable(${TEST_RUNNER_NAME}
+-- test_databuffer.cpp
+-- test_dataelement.cpp
+-- test_dataelementfile.cpp
+-- test_enumerate.cpp
+-- test_event.cpp
+-- test_filesystem.cpp
+-- test_frametype.cpp
+-- test_hash.cpp
+-- test_hex2strserialization.cpp
+-- test_iso639.cpp
+-- test_iterator.cpp
+-- test_jsonparser.cpp
+-- test_keyvalue.cpp
+-- test_library.cpp
+-- test_lockablecontainer.cpp
+-- test_measurementtype.cpp
+-- test_memberavailability.cpp
+-- test_messageException.cpp
+-- test_networkinfo.cpp
+-- test_nodeid.cpp
+-- test_numbertype.cpp
+-- test_optional.cpp
+-- test_parser.cpp
+-- test_portability.cpp
+-- test_processinfo.cpp
+-- test_queue.cpp
+-- test_rangetype.cpp
+-- test_readwritelock.cpp
+-- test_rectangle.cpp
+-- test_semaphore.cpp
+-- test_singleton.cpp
+-- test_statetrigger.cpp
+-- test_stopwatch.cpp
+-- test_synchronize.cpp
+-- test_systeminfo.cpp
+-- test_textfragment.cpp
+-- test_textreader.cpp
+-- test_thread.cpp
+-- test_threadpool.cpp
+-- test_time.cpp
+-- test_timer.cpp
+-- test_tristate.cpp
+-- #test_valuerecorder.cpp
+-- test_workerpool.cpp
+-- test_xgetopt.cpp
+ )
+ endif()
+
+diff --cc Tests/unit/core/test_websocket.cpp
+index a2cb1f4f,a2cb1f4f..ebe33267
+--- a/Tests/unit/core/test_websocket.cpp
++++ b/Tests/unit/core/test_websocket.cpp
+@@@ -424,7 -424,7 +424,7 @@@ namespace Core
+ , const uint16_t receiveBufferSize
+ , const std::string& prefix
+ )
+-- : ::Thunder::Crypto::SecureSocketPort(::Thunder::Core::SocketPort::STREAM, socket, localNode, sendBufferSize, receiveBufferSize)
+++ : ::Thunder::Crypto::SecureSocketPort(::Thunder::Crypto::SecureSocketPort::context_t::CLIENT_CONTEXT, ::Thunder::Core::SocketPort::STREAM, socket, localNode, sendBufferSize, receiveBufferSize)
+ , _prefix{ prefix }
+ , _validator{}
+ {
+@@@ -440,7 -440,7 +440,7 @@@
+ , const uint16_t receiveBufferSize
+ , const std::string& prefix
+ )
+-- : ::Thunder::Crypto::SecureSocketPort(::Thunder::Core::SocketPort::STREAM, localNode, remoteNode, sendBufferSize, receiveBufferSize, sendBufferSize, receiveBufferSize)
+++ : ::Thunder::Crypto::SecureSocketPort(::Thunder::Crypto::SecureSocketPort::context_t::CLIENT_CONTEXT, ::Thunder::Core::SocketPort::STREAM, localNode, remoteNode, sendBufferSize, receiveBufferSize, sendBufferSize, receiveBufferSize)
+ , _prefix{ prefix }
+ , _validator{}
+ {
+@@@ -1138,8 -1138,8 +1138,7 @@@
+ WebSocketClient client(webSocketURIPath, webSocketProtocol, webSocketURIQuery, webSocketOrigin, false, true, rawSocket, remoteNode.AnyInterface(), remoteNode, sendBufferSize, receiveBufferSize, "WebSocketClient");
+
+ // SleepMs(maxWaitTimeMs);
+--
+-- ASSERT_EQ(client.Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
+++ EXPECT_EQ(client.Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
+
+ SleepMs(maxWaitTimeMs);
+
+diff --cc mkdocs.yml
+index 8c4a46d1,8c4a46d1..18e6890b
+--- a/mkdocs.yml
++++ b/mkdocs.yml
+@@@ -79,7 -79,7 +79,9 @@@ nav
+ - Documentation: docs.md
+ - References: references/references.md
+ - Filing an Issue: issuetemplate/issuetemplate.md
+--
+++ - RFC :
+++ - WebSocket protocol: rfc/websocket.md
+++ - WebSocket implementation: rfc/websocketimpl.md
+
+ markdown_extensions:
+ - admonition
From d351981ebfe5d42cd285ae3493cfa4379be0f67f Mon Sep 17 00:00:00 2001
From: msieben <4319079+msieben@users.noreply.github.com>
Date: Fri, 22 Nov 2024 13:04:36 +0000
Subject: [PATCH 17/76] [Source/cryptalgo/SecureSocketPort] : add assert for
invalid context and sll structure in 'Update'
---
Source/cryptalgo/SecureSocketPort.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Source/cryptalgo/SecureSocketPort.h b/Source/cryptalgo/SecureSocketPort.h
index 5fd037b40..6eeae6ded 100644
--- a/Source/cryptalgo/SecureSocketPort.h
+++ b/Source/cryptalgo/SecureSocketPort.h
@@ -107,6 +107,8 @@ namespace Crypto {
// Signal a state change, Opened, Closed or Accepted
void StateChange() override {
+ ASSERT(_context != nullptr && _ssl != nullptr);
+
Update();
};
inline uint32_t Callback(IValidator* callback) {
From 03a69dcedd40290d017283dc2bf49cd0ae149774 Mon Sep 17 00:00:00 2001
From: msieben <4319079+msieben@users.noreply.github.com>
Date: Fri, 22 Nov 2024 14:37:53 +0100
Subject: [PATCH 18/76] Delete Source/cryptalgo/SecureSocketPort.cpp.modified
Whoops ....
---
.../cryptalgo/SecureSocketPort.cpp.modified | 284 ------------------
1 file changed, 284 deletions(-)
delete mode 100644 Source/cryptalgo/SecureSocketPort.cpp.modified
diff --git a/Source/cryptalgo/SecureSocketPort.cpp.modified b/Source/cryptalgo/SecureSocketPort.cpp.modified
deleted file mode 100644
index e52d22c99..000000000
--- a/Source/cryptalgo/SecureSocketPort.cpp.modified
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
- * If not stated otherwise in this file or this component's LICENSE file the
- * following copyright and licenses apply:
- *
- * Copyright 2020 Metrological
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "SecureSocketPort.h"
-
-#include
-#include
-
-#ifndef __WINDOWS__
-namespace {
-
- class OpenSSL {
- public:
- OpenSSL(OpenSSL&&) = delete;
- OpenSSL(const OpenSSL&) = delete;
- OpenSSL& operator=(OpenSSL&&) = delete;
- OpenSSL& operator=(const OpenSSL&) = delete;
-
- OpenSSL()
- {
- SSL_library_init();
- OpenSSL_add_all_algorithms();
- SSL_load_error_strings();
- }
- ~OpenSSL()
- {
- }
- };
-
- static OpenSSL _initialization;
-}
-#endif
-
-namespace Thunder {
-
-namespace Crypto {
-
- static Core::Time ASN1_ToTime(const ASN1_TIME* input)
- {
- Core::Time result;
-
- if (input != nullptr) {
- uint16_t year = 0;
- const char* textVersion = reinterpret_cast(input->data);
-
- if (input->type == V_ASN1_UTCTIME)
- {
- year = (textVersion[0] - '0') * 10 + (textVersion[1] - '0');
- year += (year < 70 ? 2000 : 1900);
- textVersion = &textVersion[2];
- }
- else if (input->type == V_ASN1_GENERALIZEDTIME)
- {
- year = (textVersion[0] - '0') * 1000 + (textVersion[1] - '0') * 100 + (textVersion[2] - '0') * 10 + (textVersion[3] - '0');
- textVersion = &textVersion[4];
- }
- uint8_t month = ((textVersion[0] - '0') * 10 + (textVersion[1] - '0')) - 1;
- uint8_t day = (textVersion[2] - '0') * 10 + (textVersion[3] - '0');
- uint8_t hour = (textVersion[4] - '0') * 10 + (textVersion[5] - '0');
- uint8_t minutes = (textVersion[6] - '0') * 10 + (textVersion[7] - '0');
- uint8_t seconds = (textVersion[8] - '0') * 10 + (textVersion[9] - '0');
-
- /* Note: we did not adjust the time based on time zone information */
- result = Core::Time(year, month, day, hour, minutes, seconds, 0, false);
- }
- return (result);
- }
-
-string SecureSocketPort::Certificate::Issuer() const {
- char buffer[1024];
- buffer[0] = '\0';
- X509_NAME_oneline(X509_get_issuer_name(_certificate), buffer, sizeof(buffer));
-
- return (string(buffer));
-}
-
-string SecureSocketPort::Certificate::Subject() const {
- char buffer[1024];
- buffer[0] = '\0';
- X509_NAME_oneline(X509_get_subject_name(_certificate), buffer, sizeof(buffer));
-
- return (string(buffer));
-}
-
-Core::Time SecureSocketPort::Certificate::ValidFrom() const {
- return(ASN1_ToTime(X509_get0_notBefore(_certificate)));
-}
-
-Core::Time SecureSocketPort::Certificate::ValidTill() const {
- return(ASN1_ToTime(X509_get0_notAfter(_certificate)));
-}
-
-bool SecureSocketPort::Certificate::ValidHostname(const string& expectedHostname) const {
- return (X509_check_host(_certificate, expectedHostname.data(), expectedHostname.size(), 0, nullptr) == 1);
-}
-
-bool SecureSocketPort::Certificate::Verify(string& errorMsg) const {
- long error = SSL_get_verify_result(_context);
-
- if (error != X509_V_OK) {
- errorMsg = X509_verify_cert_error_string(error);
- }
-
- return error == X509_V_OK;
-}
-
-SecureSocketPort::Handler::~Handler() {
- if(_ssl != nullptr) {
- SSL_free(static_cast(_ssl));
- }
- if(_context != nullptr) {
- SSL_CTX_free(static_cast(_context));
- }
-}
-
-uint32_t SecureSocketPort::Handler::Initialize() {
- uint32_t success = Core::ERROR_NONE;
-//implicitly sets handshake method
-// This may require set state or explicit connect or accept
- _context = SSL_CTX_new(TLS_method());
- //
-// _context = SSL_CTX_new(TLS_server_method());
-
-// Load the trusted root
-int result = SSL_CTX_load_verify_locations(static_cast(_context), "RootCA.pem", nullptr);
-// Specify server certificate
- result = SSL_CTX_use_certificate_chain_file(static_cast(_context), "localhost.pem");
-// Load private key for server certificate
-result = SSL_CTX_use_PrivateKey_file(static_cast(_context), "localhost.key", SSL_FILETYPE_PEM);
-
-
- // CTX setting have no effect after, then, use SSL setters instead
- _ssl = SSL_new(static_cast(_context));
-
-
- SSL_set_fd(static_cast(_ssl), static_cast(*this).Descriptor());
-// SSL_CTX_set_options(static_cast(_context), SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
-
-// Load the trusted root
-// result = SSL_CTX_load_verify_locations(static_cast(_context), "RootCA.pem", nullptr);
-// Specify server certificate
-//int result = SSL_CTX_use_certificate_chain_file(static_cast(_context), "localhost.pem");
-// Load private key for server certificate
-//result = SSL_CTX_use_PrivateKey_file(static_cast(_context), "localhost.key", SSL_FILETYPE_PEM);
-
- if (result == 1) {
- // Trust the same certificates as any other application
- // Actually the wrong place
-// if (SSL_CTX_set_default_verify_paths(static_cast(_context)) == 1) {
- success = Core::SocketPort::Initialize();
- } else {
- TRACE_L1("OpenSSL failed to load certificate store");
- success = Core::ERROR_GENERAL;
- }
-
- return success;
-}
-
-int32_t SecureSocketPort::Handler::Read(uint8_t buffer[], const uint16_t length) const {
- int32_t result = 0;
-
- if (_handShaking == CONNECTED) {
- int value = SSL_read(static_cast(_ssl), buffer, length);
- result = (value > 0 ? value : 0);
- }
-
- return (result);
-}
-
-int32_t SecureSocketPort::Handler::Write(const uint8_t buffer[], const uint16_t length) {
- int32_t result = 0;
-
- if (_handShaking == CONNECTED) {
- int value = (SSL_write(static_cast(_ssl), buffer, length));
- result = (value > 0 ? value : 0);
- }
-
- return (result);
-}
-
-uint32_t SecureSocketPort::Handler::Open(const uint32_t waitTime) {
- uint32_t result = Core::SocketPort::Open(waitTime);
-
- if (result == Core::ERROR_NONE) {
- result = Initialize();
- }
-
- return result;
-}
-
-uint32_t SecureSocketPort::Handler::Close(const uint32_t waitTime) {
- if (_ssl != nullptr) {
- SSL_shutdown(static_cast(_ssl));
- }
- _handShaking = IDLE;
- return(Core::SocketPort::Close(waitTime));
-}
-
-void SecureSocketPort::Handler::ValidateHandShake() {
- // Step 1: verify a server certificate was presented during the negotiation
- X509* x509cert = SSL_get_peer_certificate(static_cast(_ssl));
- if (x509cert != nullptr) {
- Core::SocketPort::Lock();
-
- Certificate certificate(x509cert, static_cast(_ssl));
-
- // Step 2: Validate certificate - use custom IValidator instance if available or if self signed
- // certificates are needed :-)
- string validationError;
- if (_callback && _callback->Validate(certificate) == true) {
- _handShaking = CONNECTED;
- Core::SocketPort::Unlock();
- _parent.StateChange();
- } else if (certificate.Verify(validationError) && certificate.ValidHostname(RemoteNode().HostName())) {
- _handShaking = CONNECTED;
- Core::SocketPort::Unlock();\
- _parent.StateChange();
- } else {
- if (!validationError.empty()) {
- TRACE_L1("OpenSSL certificate validation error for %s: %s", certificate.Subject().c_str(), validationError.c_str());
- }
- _handShaking = IDLE;
- Core::SocketPort::Unlock();
- SetError();
- }
-
- X509_free(x509cert);
- } else {
- _handShaking = IDLE;
- SetError();
- }
-}
-
-void SecureSocketPort::Handler::Update() {
- if (IsOpen() == true) {
- int result;
-
- if (_handShaking == IDLE) {
-// SSL_set_tlsext_host_name(static_cast(_ssl), RemoteNode().HostName().c_str());
-
-//this is the next step, for server ssl_acept is required
-result = SSL_accept(static_cast(_ssl));
-
-// result = SSL_connect(static_cast(_ssl));
- if (result == 1) {
-// ValidateHandShake();
- }
- else {
- result = SSL_get_error(static_cast(_ssl), result);
- if ((result == SSL_ERROR_WANT_READ) || (result == SSL_ERROR_WANT_WRITE)) {
- _handShaking = EXCHANGE;
- }
- }
- }
- else if (_handShaking == EXCHANGE) {
-// This is alrady triggert by connet and accept, this should be used when setting sttae
-// if (SSL_do_handshake(static_cast(_ssl)) == 1) {
-// ValidateHandShake();
-// }
- }
- }
- else if (_ssl != nullptr) {
- _handShaking = IDLE;
- _parent.StateChange();
- }
-}
-
-} } // namespace Thunder::Crypto
From a596018915ee6eb1a77b199f3add41cc47320c3d Mon Sep 17 00:00:00 2001
From: msieben <4319079+msieben@users.noreply.github.com>
Date: Fri, 22 Nov 2024 14:38:14 +0100
Subject: [PATCH 19/76] Delete Source/cryptalgo/SecureSocketPort.h.modified
Whoops
---
Source/cryptalgo/SecureSocketPort.h.modified | 231 -------------------
1 file changed, 231 deletions(-)
delete mode 100644 Source/cryptalgo/SecureSocketPort.h.modified
diff --git a/Source/cryptalgo/SecureSocketPort.h.modified b/Source/cryptalgo/SecureSocketPort.h.modified
deleted file mode 100644
index b787845fe..000000000
--- a/Source/cryptalgo/SecureSocketPort.h.modified
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * If not stated otherwise in this file or this component's LICENSE file the
- * following copyright and licenses apply:
- *
- * Copyright 2020 Metrological
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "Module.h"
-
-struct x509_store_ctx_st;
-struct x509_st;
-struct ssl_st;
-
-namespace Thunder {
-namespace Crypto {
-
- class EXTERNAL SecureSocketPort : public Core::IResource {
- public:
- class EXTERNAL Certificate {
- public:
- Certificate() = delete;
- Certificate(Certificate&&) = delete;
- Certificate(const Certificate&) = delete;
-
- Certificate(x509_st* certificate, const ssl_st* context)
- : _certificate(certificate)
- , _context(context) {
- }
- ~Certificate() = default;
-
- public:
- string Issuer() const;
- string Subject() const;
- Core::Time ValidFrom() const;
- Core::Time ValidTill() const;
- bool ValidHostname(const string& expectedHostname) const;
- bool Verify(string& errorMsg) const;
-
- private:
- x509_st* _certificate;
- const ssl_st* _context;
- };
- struct IValidator {
- virtual ~IValidator() = default;
-
- virtual bool Validate(const Certificate& certificate) const = 0;
- };
-
- private:
- class EXTERNAL Handler : public Core::SocketPort {
- private:
- enum state : uint8_t {
- IDLE,
- EXCHANGE,
- CONNECTED
- };
-
- Handler(Handler&&) = delete;
- Handler(const Handler&) = delete;
- Handler& operator=(const Handler&) = delete;
-
- template
- Handler(SecureSocketPort& parent, Args&&... args)
- : Core::SocketPort(args...)
- , _parent(parent)
- , _context(nullptr)
- , _ssl(nullptr)
- , _callback(nullptr)
- , _handShaking(IDLE)
- , _mode{mode_t::CLIENT}
- {}
- ~Handler();
-
- public:
- uint32_t Initialize() override;
-
- int32_t Read(uint8_t buffer[], const uint16_t length) const override;
- int32_t Write(const uint8_t buffer[], const uint16_t length) override;
-
- uint32_t Open(const uint32_t waitTime);
- uint32_t Close(const uint32_t waitTime);
-
- // Methods to extract and insert data into the socket buffers
- uint16_t SendData(uint8_t* dataFrame, const uint16_t maxSendSize) override {
- return (_parent.SendData(dataFrame, maxSendSize));
- }
-
- uint16_t ReceiveData(uint8_t* dataFrame, const uint16_t receivedSize) override {
- return (_parent.ReceiveData(dataFrame, receivedSize));
- }
-
- // Signal a state change, Opened, Closed or Accepted
- void StateChange() override {
- if (_context != nullptr) { // Initialize may not yet have happened
- Update();
- }
- };
- inline uint32_t Callback(IValidator* callback) {
- uint32_t result = Core::ERROR_ILLEGAL_STATE;
-
- Core::SocketPort::Lock();
-
- ASSERT((callback == nullptr) || (_callback == nullptr));
-
- if ((callback == nullptr) || (_callback == nullptr)) {
- _callback = callback;
- result = Core::ERROR_NONE;
- }
- Core::SocketPort::Unlock();
-
- return (result);
- }
-
- private:
- void Update();
- void ValidateHandShake();
-
- private:
- SecureSocketPort& _parent;
- void* _context;
- void* _ssl;
- IValidator* _callback;
- mutable state _handShaking;
- mode_t _mode;
- };
-
- private:
- public:
- SecureSocketPort(SecureSocketPort&&) = delete;
- SecureSocketPort(const SecureSocketPort&) = delete;
- SecureSocketPort& operator=(const SecureSocketPort&) = delete;
-
- template
- SecureSocketPort(Args&&... args)
- : _handler(*this, args...) {
- }
- ~SecureSocketPort() override {
- }
-
- public:
- inline bool IsOpen() const
- {
- return (_handler.IsOpen());
- }
- inline bool IsClosed() const
- {
- return (_handler.IsClosed());
- }
- inline bool IsSuspended() const {
- return (_handler.IsSuspended());
- }
- inline bool HasError() const
- {
- return (_handler.HasError());
- }
- inline string LocalId() const
- {
- return (_handler.LocalId());
- }
- inline string RemoteId() const
- {
- return (_handler.RemoteId());
- }
- inline void LocalNode(const Core::NodeId& node)
- {
- _handler.LocalNode(node);
- }
- inline void RemoteNode(const Core::NodeId& node)
- {
- _handler.RemoteNode(node);
- }
- inline const Core::NodeId& RemoteNode() const
- {
- return (_handler.RemoteNode());
- }
-
- inline uint32_t Open(const uint32_t waitTime) {
- return(_handler.Open(waitTime));
- }
- inline uint32_t Close(const uint32_t waitTime) {
- return(_handler.Close(waitTime));
- }
- inline void Trigger() {
- _handler.Trigger();
- }
- inline uint32_t Callback(IValidator* callback) {
- return (_handler.Callback(callback));
- }
-public:
- //
- // Core::IResource interface
- // ------------------------------------------------------------------------
- IResource::handle Descriptor() const override
- {
- return (static_cast(_handler).Descriptor());
- }
- uint16_t Events() override
- {
- return (static_cast(_handler).Events());
- }
- void Handle(const uint16_t events) override
- {
- static_cast(_handler).Handle(events);
- }
-
- // Methods to extract and insert data into the socket buffers
- virtual uint16_t SendData(uint8_t* dataFrame, const uint16_t maxSendSize) = 0;
- virtual uint16_t ReceiveData(uint8_t* dataFrame, const uint16_t receivedSize) = 0;
-
- // Signal a state change, Opened, Closed or Accepted
- virtual void StateChange() = 0;
-
- private:
- Handler _handler;
- };
-}
-}
From f7dbccee4c127a3bc5171bcbab9184ea3dfb3b2a Mon Sep 17 00:00:00 2001
From: msieben <4319079+msieben@users.noreply.github.com>
Date: Fri, 22 Nov 2024 14:38:32 +0100
Subject: [PATCH 20/76] Delete Source/cryptalgo/SecureSocketPort.modifications
Whoops
---
.../cryptalgo/SecureSocketPort.modifications | 249 ------------------
1 file changed, 249 deletions(-)
delete mode 100644 Source/cryptalgo/SecureSocketPort.modifications
diff --git a/Source/cryptalgo/SecureSocketPort.modifications b/Source/cryptalgo/SecureSocketPort.modifications
deleted file mode 100644
index e2f721406..000000000
--- a/Source/cryptalgo/SecureSocketPort.modifications
+++ /dev/null
@@ -1,249 +0,0 @@
-commit b392a4b6f175241ff71febaae618150ef3385991
-Merge: feef8577 48054e39
-Author: msieben <4319079+msieben@users.noreply.github.com>
-Date: Fri Nov 22 08:53:08 2024 +0000
-
- WIP on development/websocket: feef8577 [Source/crypto/SecureSocket] : correct check for invalid context
-
-diff --cc Source/cryptalgo/SecureSocketPort.h
-index 49c5d5ed,49c5d5ed..72fb4f6b
---- a/Source/cryptalgo/SecureSocketPort.h
-+++ b/Source/cryptalgo/SecureSocketPort.h
-@@@ -70,18 -70,18 +70,26 @@@ namespace Crypto
- };
-
- public:
-++ // Type of underlying socket
-++ enum class sockettype_t : uint8_t {
-++ CLIENT_SOCKET
-++ , SERVER_SOCKET
-++ };
-++
- Handler(Handler&&) = delete;
- Handler(const Handler&) = delete;
- Handler& operator=(const Handler&) = delete;
-
- template
--- Handler(SecureSocketPort& parent, Args&&... args)
-++ Handler(SecureSocketPort& parent, sockettype_t type, Args&&... args)
- : Core::SocketPort(args...)
- , _parent(parent)
- , _context(nullptr)
- , _ssl(nullptr)
- , _callback(nullptr)
--- , _handShaking(IDLE) {
-++ , _handShaking(IDLE)
-++ , _type{type}
-++ {
- }
- ~Handler();
-
-@@@ -139,6 -139,6 +147,7 @@@
- void* _ssl;
- IValidator* _callback;
- mutable state _handShaking;
-++ sockettype_t _type;
- };
-
- public:
-@@@ -146,10 -146,10 +155,25 @@@
- SecureSocketPort(const SecureSocketPort&) = delete;
- SecureSocketPort& operator=(const SecureSocketPort&) = delete;
-
-++ protected:
-++ // Operational context
-++ enum class context_t : uint8_t {
-++ CLIENT_CONTEXT
-++ , SERVER_CONTEXT
-++ };
-++
-++ // For the bravehearted, and, those who derive
-++ template
-++ SecureSocketPort(context_t context, Args&&... args)
-++ : _handler{*this, context == context_t::SERVER_CONTEXT ? Handler::sockettype_t::SERVER_SOCKET : Handler::sockettype_t::CLIENT_SOCKET, args...}
-++ {}
-++
-++ public:
- template
- SecureSocketPort(Args&&... args)
--- : _handler(*this, args...) {
--- }
-++ : SecureSocketPort{context_t::CLIENT_CONTEXT, args...}
-++ {}
-++
- ~SecureSocketPort() override {
- }
-
-diff --cc Tests/unit/core/CMakeLists.txt
-index eb8aa882,eb8aa882..cc4e499d
---- a/Tests/unit/core/CMakeLists.txt
-+++ b/Tests/unit/core/CMakeLists.txt
-@@@ -21,120 -21,120 +21,10 @@@ if(LINUX
- # IPTestAdministrator only supported on LINUX platform
- add_executable(${TEST_RUNNER_NAME}
- ../IPTestAdministrator.cpp
--- test_cyclicbuffer.cpp
--- test_cyclicbuffer_dataexchange.cpp
--- test_databuffer.cpp
--- test_dataelement.cpp
--- test_dataelementfile.cpp
--- test_doorbell.cpp
--- test_enumerate.cpp
--- test_event.cpp
--- test_filesystem.cpp
--- test_frametype.cpp
--- test_hash.cpp
--- test_hex2strserialization.cpp
--- #test_ipc.cpp
--- test_ipcclient.cpp
--- test_iso639.cpp
--- test_iterator.cpp
--- test_jsonparser.cpp
--- test_keyvalue.cpp
--- test_library.cpp
--- test_lockablecontainer.cpp
--- test_measurementtype.cpp
--- test_memberavailability.cpp
--- test_message_dispatcher.cpp
--- test_messageException.cpp
--- test_networkinfo.cpp
--- test_nodeid.cpp
--- test_numbertype.cpp
--- test_optional.cpp
--- test_parser.cpp
--- test_portability.cpp
--- test_processinfo.cpp
--- test_queue.cpp
--- test_rangetype.cpp
--- test_readwritelock.cpp
--- test_rectangle.cpp
--- test_rpc.cpp
--- test_semaphore.cpp
--- test_sharedbuffer.cpp
--- test_singleton.cpp
--- test_socketstreamjson.cpp
--- test_socketstreamtext.cpp
--- test_statetrigger.cpp
--- test_stopwatch.cpp
--- test_synchronize.cpp
--- test_synchronous.cpp
--- test_systeminfo.cpp
--- test_textfragment.cpp
--- test_textreader.cpp
--- test_thread.cpp
--- test_threadpool.cpp
--- test_time.cpp
--- test_timer.cpp
--- test_tristate.cpp
--- #test_valuerecorder.cpp
--- test_weblinkjson.cpp
--- test_weblinktext.cpp
--- test_websocketjson.cpp
--- test_websockettext.cpp
--- test_workerpool.cpp
--- test_xgetopt.cpp
-++ test_websocket.cpp
- )
---#[[
---target_sources(${TEST_RUNNER_NAME} PRIVATE test_message_unit.cpp)
---target_link_libraries(${TEST_RUNNER_NAME}
--- ThunderMessaging
---)
---]]
- else()
- add_executable(${TEST_RUNNER_NAME}
--- test_databuffer.cpp
--- test_dataelement.cpp
--- test_dataelementfile.cpp
--- test_enumerate.cpp
--- test_event.cpp
--- test_filesystem.cpp
--- test_frametype.cpp
--- test_hash.cpp
--- test_hex2strserialization.cpp
--- test_iso639.cpp
--- test_iterator.cpp
--- test_jsonparser.cpp
--- test_keyvalue.cpp
--- test_library.cpp
--- test_lockablecontainer.cpp
--- test_measurementtype.cpp
--- test_memberavailability.cpp
--- test_messageException.cpp
--- test_networkinfo.cpp
--- test_nodeid.cpp
--- test_numbertype.cpp
--- test_optional.cpp
--- test_parser.cpp
--- test_portability.cpp
--- test_processinfo.cpp
--- test_queue.cpp
--- test_rangetype.cpp
--- test_readwritelock.cpp
--- test_rectangle.cpp
--- test_semaphore.cpp
--- test_singleton.cpp
--- test_statetrigger.cpp
--- test_stopwatch.cpp
--- test_synchronize.cpp
--- test_systeminfo.cpp
--- test_textfragment.cpp
--- test_textreader.cpp
--- test_thread.cpp
--- test_threadpool.cpp
--- test_time.cpp
--- test_timer.cpp
--- test_tristate.cpp
--- #test_valuerecorder.cpp
--- test_workerpool.cpp
--- test_xgetopt.cpp
- )
- endif()
-
-diff --cc Tests/unit/core/test_websocket.cpp
-index a2cb1f4f,a2cb1f4f..ebe33267
---- a/Tests/unit/core/test_websocket.cpp
-+++ b/Tests/unit/core/test_websocket.cpp
-@@@ -424,7 -424,7 +424,7 @@@ namespace Core
- , const uint16_t receiveBufferSize
- , const std::string& prefix
- )
--- : ::Thunder::Crypto::SecureSocketPort(::Thunder::Core::SocketPort::STREAM, socket, localNode, sendBufferSize, receiveBufferSize)
-++ : ::Thunder::Crypto::SecureSocketPort(::Thunder::Crypto::SecureSocketPort::context_t::CLIENT_CONTEXT, ::Thunder::Core::SocketPort::STREAM, socket, localNode, sendBufferSize, receiveBufferSize)
- , _prefix{ prefix }
- , _validator{}
- {
-@@@ -440,7 -440,7 +440,7 @@@
- , const uint16_t receiveBufferSize
- , const std::string& prefix
- )
--- : ::Thunder::Crypto::SecureSocketPort(::Thunder::Core::SocketPort::STREAM, localNode, remoteNode, sendBufferSize, receiveBufferSize, sendBufferSize, receiveBufferSize)
-++ : ::Thunder::Crypto::SecureSocketPort(::Thunder::Crypto::SecureSocketPort::context_t::CLIENT_CONTEXT, ::Thunder::Core::SocketPort::STREAM, localNode, remoteNode, sendBufferSize, receiveBufferSize, sendBufferSize, receiveBufferSize)
- , _prefix{ prefix }
- , _validator{}
- {
-@@@ -1138,8 -1138,8 +1138,7 @@@
- WebSocketClient client(webSocketURIPath, webSocketProtocol, webSocketURIQuery, webSocketOrigin, false, true, rawSocket, remoteNode.AnyInterface(), remoteNode, sendBufferSize, receiveBufferSize, "WebSocketClient");
-
- // SleepMs(maxWaitTimeMs);
---
--- ASSERT_EQ(client.Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
-++ EXPECT_EQ(client.Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
-
- SleepMs(maxWaitTimeMs);
-
-diff --cc mkdocs.yml
-index 8c4a46d1,8c4a46d1..18e6890b
---- a/mkdocs.yml
-+++ b/mkdocs.yml
-@@@ -79,7 -79,7 +79,9 @@@ nav
- - Documentation: docs.md
- - References: references/references.md
- - Filing an Issue: issuetemplate/issuetemplate.md
---
-++ - RFC :
-++ - WebSocket protocol: rfc/websocket.md
-++ - WebSocket implementation: rfc/websocketimpl.md
-
- markdown_extensions:
- - admonition
From d2fcaab57a88a0c66b611f398c136175fc23775b Mon Sep 17 00:00:00 2001
From: msieben <4319079+msieben@users.noreply.github.com>
Date: Fri, 22 Nov 2024 14:37:25 +0000
Subject: [PATCH 21/76] [Source/cryptalgo/SecureSocketPort Tests/unit/core] :
Prepare for server side SSL
Users should explicitly derive from teh base to make it happen as it is not the default (most common) scenario
---
Source/cryptalgo/SecureSocketPort.cpp | 37 +++++++++----
Source/cryptalgo/SecureSocketPort.h | 33 +++++++++---
Tests/unit/core/test_websocket.cpp | 78 +++++++++++++++++++++++++--
3 files changed, 126 insertions(+), 22 deletions(-)
diff --git a/Source/cryptalgo/SecureSocketPort.cpp b/Source/cryptalgo/SecureSocketPort.cpp
index b220711ac..7f5a7a565 100644
--- a/Source/cryptalgo/SecureSocketPort.cpp
+++ b/Source/cryptalgo/SecureSocketPort.cpp
@@ -146,9 +146,16 @@ uint32_t SecureSocketPort::Handler::Initialize() {
ASSERT((bitmask & options) == options);
- // Trust the same certificates as any other application
- if ( // Trust the same certificates as any other application
- (SSL_CTX_set_default_verify_paths(static_cast(_context)) == 1)
+ if ( ( ( (_handShaking == ACCEPTING)
+ // Load server certifiate and private key
+ && (SSL_CTX_load_verify_locations(static_cast(_context), "RootCA.pem", nullptr) == 1)
+ && (SSL_CTX_use_certificate_chain_file(static_cast(_context), "localhost.pem") == 1)
+ && (SSL_CTX_use_PrivateKey_file(static_cast(_context), "localhost.key", SSL_FILETYPE_PEM) == 1)
+ )
+ || (_handShaking == CONNECTING)
+ )
+ // Default location from which CA certificates are loaded
+ && (SSL_CTX_set_default_verify_paths(static_cast(_context)) == 1)
&& ((_ssl = SSL_new(static_cast(_context))) != nullptr)
&& (SSL_set_fd(static_cast(_ssl), static_cast(*this).Descriptor()) == 1)
) {
@@ -162,15 +169,17 @@ uint32_t SecureSocketPort::Handler::Initialize() {
}
int32_t SecureSocketPort::Handler::Read(uint8_t buffer[], const uint16_t length) const {
- ASSERT(_handShaking != CONNECTED);
+ int result = 0;
- int result = SSL_read(static_cast(_ssl), buffer, length);
+ if (_handShaking == CONNECTED) { // Avoid reading while still in CONNECTING or ACCEPTING
+ result = SSL_read(static_cast(_ssl), buffer, length);
+ }
return (result > 0 ? result : 0);
}
int32_t SecureSocketPort::Handler::Write(const uint8_t buffer[], const uint16_t length) {
- ASSERT(_handShaking != CONNECTED);
+ ASSERT(_handShaking == CONNECTED);
int result = SSL_write(static_cast(_ssl), buffer, length);
@@ -238,20 +247,25 @@ void SecureSocketPort::Handler::Update() {
ASSERT(_ssl != nullptr);
+ // Client
if (_handShaking == CONNECTING) {
if ((result = SSL_connect(static_cast(_ssl))) == 1) {
- _handShaking = EXCHANGE;
+ _handShaking = CONNECTED;
+ // If server has sent a certificate, and, we want to do 'our' own check
+ ValidateHandShake();
}
- }
+ } // Server
else if (_handShaking == ACCEPTING) {
if ((result = SSL_accept(static_cast(_ssl))) == 1) {
- _handShaking = EXCHANGE;
+ _handShaking = CONNECTED;
+ // Client has sent a certificate (optional, on request only)
+ ValidateHandShake();
}
- }
-
+ } // Re-initialie a previous session
if (_handShaking == EXCHANGE) {
if ((result = SSL_do_handshake(static_cast(_ssl))) == 1) {
ValidateHandShake();
+ _handShaking = CONNECTED;
}
}
@@ -266,6 +280,7 @@ void SecureSocketPort::Handler::Update() {
}
}
else {
+ /// _handShaking = CLOSED
_parent.StateChange();
}
}
diff --git a/Source/cryptalgo/SecureSocketPort.h b/Source/cryptalgo/SecureSocketPort.h
index 9e9705623..03069e7d9 100644
--- a/Source/cryptalgo/SecureSocketPort.h
+++ b/Source/cryptalgo/SecureSocketPort.h
@@ -72,19 +72,26 @@ namespace Crypto {
};
public:
+ // Type of underlying socket
+ enum class sockettype_t : uint8_t {
+ CLIENT_SOCKET
+ , SERVER_SOCKET
+ };
+
Handler(Handler&&) = delete;
Handler(const Handler&) = delete;
Handler& operator=(const Handler&) = delete;
template
- Handler(SecureSocketPort& parent, Args&&... args)
+ Handler(SecureSocketPort& parent, sockettype_t type, Args&&... args)
: Core::SocketPort(args...)
, _parent(parent)
, _context(nullptr)
, _ssl(nullptr)
, _callback(nullptr)
- , _handShaking(CONNECTING) {
- }
+ , _handShaking(type == sockettype_t::CLIENT_SOCKET ? CONNECTING : ACCEPTING)
+ , _type{type}
+ {}
~Handler();
public:
@@ -107,7 +114,7 @@ namespace Crypto {
// Signal a state change, Opened, Closed or Accepted
void StateChange() override {
- ASSERT(_context != nullptr && _ssl != nullptr);
+// ASSERT(_context != nullptr && _ssl != nullptr);
Update();
};
inline uint32_t Callback(IValidator* callback) {
@@ -136,6 +143,7 @@ namespace Crypto {
void* _ssl;
IValidator* _callback;
mutable state _handShaking;
+ const sockettype_t _type;
};
public:
@@ -143,10 +151,23 @@ namespace Crypto {
SecureSocketPort(const SecureSocketPort&) = delete;
SecureSocketPort& operator=(const SecureSocketPort&) = delete;
+ protected:
+ // Operational context
+ enum class context_t : uint8_t {
+ CLIENT_CONTEXT
+ , SERVER_CONTEXT
+ };
+
+ template
+ SecureSocketPort(context_t context, Args&&... args)
+ : _handler(*this, context == context_t:: SERVER_CONTEXT ? Handler::sockettype_t::SERVER_SOCKET : Handler::sockettype_t::CLIENT_SOCKET, args...)
+ {}
+
+ public:
template
SecureSocketPort(Args&&... args)
- : _handler(*this, args...) {
- }
+ : SecureSocketPort(context_t::CLIENT_CONTEXT, args...)
+ {}
~SecureSocketPort() override {
}
diff --git a/Tests/unit/core/test_websocket.cpp b/Tests/unit/core/test_websocket.cpp
index a2cb1f4f7..771abe55f 100644
--- a/Tests/unit/core/test_websocket.cpp
+++ b/Tests/unit/core/test_websocket.cpp
@@ -424,7 +424,7 @@ namespace Core {
, const uint16_t receiveBufferSize
, const std::string& prefix
)
- : ::Thunder::Crypto::SecureSocketPort(::Thunder::Core::SocketPort::STREAM, socket, localNode, sendBufferSize, receiveBufferSize)
+ : ::Thunder::Crypto::SecureSocketPort(::Thunder::Crypto::SecureSocketPort::context_t::CLIENT_CONTEXT, ::Thunder::Core::SocketPort::STREAM, socket, localNode, sendBufferSize, receiveBufferSize)
, _prefix{ prefix }
, _validator{}
{
@@ -440,7 +440,7 @@ namespace Core {
, const uint16_t receiveBufferSize
, const std::string& prefix
)
- : ::Thunder::Crypto::SecureSocketPort(::Thunder::Core::SocketPort::STREAM, localNode, remoteNode, sendBufferSize, receiveBufferSize, sendBufferSize, receiveBufferSize)
+ : ::Thunder::Crypto::SecureSocketPort(::Thunder::Crypto::SecureSocketPort::context_t::CLIENT_CONTEXT, ::Thunder::Core::SocketPort::STREAM, localNode, remoteNode, sendBufferSize, receiveBufferSize, sendBufferSize, receiveBufferSize)
, _prefix{ prefix }
, _validator{}
{
@@ -460,6 +460,75 @@ namespace Core {
Validator _validator;
};
+ class CustomSecureServerSocketStream : public ::Thunder::Crypto::SecureSocketPort {
+ private :
+
+ // Validat eclient certificate
+ class Validator : public ::Thunder::Crypto::SecureSocketPort::IValidator {
+ public:
+
+ Validator() = default;
+ ~Validator() = default;
+
+ bool Validate(const Certificate& certificate) const override {
+ // Print certificate properties
+#ifdef _VERBOSE
+ std::cout << std::dec <<__LINE__ << " : " << __PRETTY_FUNCTION__ << "\n";
+ std::cout << " |--> Issuer = " << certificate.Issuer() << "\n";
+ std::cout << " |--> Subject = " << certificate.Subject() << "\n";
+ std::cout << " |--> Valid from = " << certificate.ValidFrom().ToRFC1123() << "\n";
+ std::cout << " |--> Valid until = " << certificate.ValidTill().ToRFC1123() << "\n";
+#endif
+ return true; // Always accept
+ }
+ };
+
+ public :
+
+ // In essence, all parameters to SecureSocket are passed to a base class SocketPort
+ CustomSecureServerSocketStream(
+ const SOCKET& socket
+ , const ::Thunder::Core::NodeId& localNode
+ , const uint16_t sendBufferSize
+ , const uint16_t receiveBufferSize
+ , const std::string& prefix
+ )
+ : ::Thunder::Crypto::SecureSocketPort(::Thunder::Crypto::SecureSocketPort::context_t::SERVER_CONTEXT, ::Thunder::Core::SocketPort::STREAM, socket, localNode, sendBufferSize, receiveBufferSize)
+ , _prefix{ prefix }
+ , _validator{}
+ {
+ // Validate custom (sefl signed) certificates
+ uint32_t result = Callback(&_validator);
+ }
+
+ CustomSecureServerSocketStream(
+ const bool
+ , const ::Thunder::Core::NodeId& localNode
+ , const ::Thunder::Core::NodeId& remoteNode
+ , const uint16_t sendBufferSize
+ , const uint16_t receiveBufferSize
+ , const std::string& prefix
+ )
+ : ::Thunder::Crypto::SecureSocketPort(::Thunder::Crypto::SecureSocketPort::context_t::SERVER_CONTEXT, ::Thunder::Core::SocketPort::STREAM, localNode, remoteNode, sendBufferSize, receiveBufferSize, sendBufferSize, receiveBufferSize)
+ , _prefix{ prefix }
+ , _validator{}
+ {
+ // Validate custom (self signed) client certificates
+ uint32_t result = Callback(&_validator);
+ }
+
+ ~CustomSecureServerSocketStream()
+ {
+#ifdef _VERBOSE
+ std::cout.flush();
+#endif
+ }
+
+ private:
+ const std::string _prefix;
+ Validator _validator;
+ };
+
TEST(WebSocket, DISABLED_OpeningServerPort)
{
const TCHAR localHostName[] {"127.0.0.1"};
@@ -1088,7 +1157,7 @@ namespace Core {
const ::Thunder::Core::NodeId localNode {localHostName, tcpServerPort, ::Thunder::Core::NodeId::TYPE_IPV4, tcpProtocol};
// This is a listening socket as result of using SocketServerType which enables listening
- ::Thunder::Core::SocketServerType> server(localNode /* listening node*/);
+ ::Thunder::Core::SocketServerType> server(localNode /* listening node*/);
ASSERT_EQ(server.Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
@@ -1138,8 +1207,7 @@ namespace Core {
WebSocketClient client(webSocketURIPath, webSocketProtocol, webSocketURIQuery, webSocketOrigin, false, true, rawSocket, remoteNode.AnyInterface(), remoteNode, sendBufferSize, receiveBufferSize, "WebSocketClient");
// SleepMs(maxWaitTimeMs);
-
- ASSERT_EQ(client.Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
+ EXPECT_EQ(client.Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
SleepMs(maxWaitTimeMs);
From f53b24847d130722ac2c39c4c72b3f90df59bc06 Mon Sep 17 00:00:00 2001
From: msieben <4319079+msieben@users.noreply.github.com>
Date: Mon, 25 Nov 2024 15:55:49 +0000
Subject: [PATCH 22/76] [Source/cryptalgo/SecureSocketPort] : peer certificate
validation only if no callbacks have registered
'SSL_accept' and 'SSL_connect' implicitly validate. Client certificates can be validated if the server had sent a client certificate request
---
Source/cryptalgo/SecureSocketPort.cpp | 81 +++++++++++++++++----------
1 file changed, 50 insertions(+), 31 deletions(-)
diff --git a/Source/cryptalgo/SecureSocketPort.cpp b/Source/cryptalgo/SecureSocketPort.cpp
index 7f5a7a565..c944f0a17 100644
--- a/Source/cryptalgo/SecureSocketPort.cpp
+++ b/Source/cryptalgo/SecureSocketPort.cpp
@@ -206,37 +206,52 @@ uint32_t SecureSocketPort::Handler::Close(const uint32_t waitTime) {
}
void SecureSocketPort::Handler::ValidateHandShake() {
- // Step 1: verify a server certificate was presented during the negotiation
- X509* x509cert = SSL_get_peer_certificate(static_cast(_ssl));
- if (x509cert != nullptr) {
- Core::SocketPort::Lock();
-
- Certificate certificate(x509cert, static_cast(_ssl));
-
- // Step 2: Validate certificate - use custom IValidator instance if available or if self signed
- // certificates are needed :-)
- string validationError;
- if (_callback && _callback->Validate(certificate) == true) {
- _handShaking = CONNECTED;
- Core::SocketPort::Unlock();
- _parent.StateChange();
- } else if (certificate.Verify(validationError) && certificate.ValidHostname(RemoteNode().HostName())) {
- _handShaking = CONNECTED;
- Core::SocketPort::Unlock();\
- _parent.StateChange();
- } else {
- if (!validationError.empty()) {
- TRACE_L1("OpenSSL certificate validation error for %s: %s", certificate.Subject().c_str(), validationError.c_str());
+ // SSL handshake does an implicit verification, its result is:
+ if (SSL_get_verify_result(static_cast(_ssl)) != X509_V_OK) {
+ _handShaking = ERROR;
+ SetError();
+ } else if ( (SSL_CTX_get_verify_mode(static_cast(_context)) != SSL_VERIFY_NONE)
+ && (SSL_CTX_get_verify_callback(static_cast(_context)) != nullptr)
+ && (SSL_get_verify_mode(static_cast(_ssl)) != SSL_VERIFY_NONE)
+ && (SSL_get_verify_callback(static_cast(_ssl)) != nullptr)
+ )
+ {
+ // Only relevant if the server has sent a client certificate request
+ // AND
+ // No callback has been set to do this job for us
+
+ // Step 1: verify a certificate was presented during the negotiation
+ X509* x509cert = SSL_get_peer_certificate(static_cast(_ssl));
+ if (x509cert != nullptr) {
+ Core::SocketPort::Lock();
+
+ Certificate certificate(x509cert, static_cast(_ssl));
+
+ // Step 2: Validate certificate - use custom IValidator instance if available or if self signed
+ // certificates are needed :-)
+ string validationError;
+ if (_callback && _callback->Validate(certificate) == true) {
+ _handShaking = CONNECTED;
+ Core::SocketPort::Unlock();
+ _parent.StateChange();
+ } else if (certificate.Verify(validationError) && certificate.ValidHostname(RemoteNode().HostName())) {
+ _handShaking = CONNECTED;
+ Core::SocketPort::Unlock();\
+ _parent.StateChange();
+ } else {
+ if (!validationError.empty()) {
+ TRACE_L1("OpenSSL certificate validation error for %s: %s", certificate.Subject().c_str(), validationError.c_str());
+ }
+ _handShaking = ERROR;
+ Core::SocketPort::Unlock();
+ SetError();
}
+
+ X509_free(x509cert);
+ } else {
_handShaking = ERROR;
- Core::SocketPort::Unlock();
SetError();
}
-
- X509_free(x509cert);
- } else {
- _handShaking = ERROR;
- SetError();
}
}
@@ -271,12 +286,16 @@ void SecureSocketPort::Handler::Update() {
if (result != 1) {
result = SSL_get_error(static_cast(_ssl), result);
- if ((result != SSL_ERROR_WANT_READ) && (result != SSL_ERROR_WANT_WRITE)) {
- _handShaking = ERROR;
- }
- else if (result == SSL_ERROR_WANT_WRITE) {
+
+ if ((result == SSL_ERROR_WANT_READ) || (result == SSL_ERROR_WANT_WRITE)) {
+ // Non-blocking I/O: select may be used to check if condition has changed
+ ValidateHandShake();
+ _handShaking = CONNECTED;
Trigger();
}
+ else {
+ _handShaking = ERROR;
+ }
}
}
else {
From 845181550c7133cd7667d276208fef0d96aec28c Mon Sep 17 00:00:00 2001
From: msieben <4319079+msieben@users.noreply.github.com>
Date: Tue, 26 Nov 2024 09:35:48 +0000
Subject: [PATCH 23/76] [Source/cryptalgo/SecureSocketPort] : remove handshake
value from initialize and add ASSERT
---
Source/cryptalgo/SecureSocketPort.cpp | 12 +++---------
Source/cryptalgo/SecureSocketPort.h | 3 +--
2 files changed, 4 insertions(+), 11 deletions(-)
diff --git a/Source/cryptalgo/SecureSocketPort.cpp b/Source/cryptalgo/SecureSocketPort.cpp
index c944f0a17..17a809f12 100644
--- a/Source/cryptalgo/SecureSocketPort.cpp
+++ b/Source/cryptalgo/SecureSocketPort.cpp
@@ -129,12 +129,6 @@ SecureSocketPort::Handler::~Handler() {
uint32_t SecureSocketPort::Handler::Initialize() {
uint32_t success = Core::ERROR_NONE;
- if (IsOpen() == true) {
- _handShaking = ACCEPTING;
- } else {
- _handShaking = CONNECTING;
- }
-
// Client and server use
_context = SSL_CTX_new(TLS_method());
@@ -206,6 +200,8 @@ uint32_t SecureSocketPort::Handler::Close(const uint32_t waitTime) {
}
void SecureSocketPort::Handler::ValidateHandShake() {
+ ASSERT(_ssl != nullptr && _context != nullptr);
+
// SSL handshake does an implicit verification, its result is:
if (SSL_get_verify_result(static_cast(_ssl)) != X509_V_OK) {
_handShaking = ERROR;
@@ -256,7 +252,6 @@ void SecureSocketPort::Handler::ValidateHandShake() {
}
void SecureSocketPort::Handler::Update() {
-
if (IsOpen() == true) {
int result;
@@ -279,8 +274,8 @@ void SecureSocketPort::Handler::Update() {
} // Re-initialie a previous session
if (_handShaking == EXCHANGE) {
if ((result = SSL_do_handshake(static_cast(_ssl))) == 1) {
- ValidateHandShake();
_handShaking = CONNECTED;
+ ValidateHandShake();
}
}
@@ -299,7 +294,6 @@ void SecureSocketPort::Handler::Update() {
}
}
else {
- /// _handShaking = CLOSED
_parent.StateChange();
}
}
diff --git a/Source/cryptalgo/SecureSocketPort.h b/Source/cryptalgo/SecureSocketPort.h
index 03069e7d9..32fa38e80 100644
--- a/Source/cryptalgo/SecureSocketPort.h
+++ b/Source/cryptalgo/SecureSocketPort.h
@@ -114,9 +114,8 @@ namespace Crypto {
// Signal a state change, Opened, Closed or Accepted
void StateChange() override {
-// ASSERT(_context != nullptr && _ssl != nullptr);
Update();
- };
+ }
inline uint32_t Callback(IValidator* callback) {
uint32_t result = Core::ERROR_ILLEGAL_STATE;
From 42a5a0c65829cb772de6cda53f832d268bd07470 Mon Sep 17 00:00:00 2001
From: msieben <4319079+msieben@users.noreply.github.com>
Date: Tue, 26 Nov 2024 11:02:09 +0000
Subject: [PATCH 24/76] [Spurce/cryptalgo/SecuresocketPort] : reverse order
handshake and validation
---
Source/cryptalgo/SecureSocketPort.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Source/cryptalgo/SecureSocketPort.cpp b/Source/cryptalgo/SecureSocketPort.cpp
index 17a809f12..298079014 100644
--- a/Source/cryptalgo/SecureSocketPort.cpp
+++ b/Source/cryptalgo/SecureSocketPort.cpp
@@ -284,8 +284,8 @@ void SecureSocketPort::Handler::Update() {
if ((result == SSL_ERROR_WANT_READ) || (result == SSL_ERROR_WANT_WRITE)) {
// Non-blocking I/O: select may be used to check if condition has changed
- ValidateHandShake();
_handShaking = CONNECTED;
+ ValidateHandShake();
Trigger();
}
else {
From f095be44a9df4e20146c9953ced217ae0a7bf673 Mon Sep 17 00:00:00 2001
From: msieben <4319079+msieben@users.noreply.github.com>
Date: Tue, 26 Nov 2024 12:45:19 +0000
Subject: [PATCH 25/76] [Source/cryptalgo/SecureSocketPort] : add 'Handler's'
move assignment operator
---
Source/cryptalgo/SecureSocketPort.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/Source/cryptalgo/SecureSocketPort.h b/Source/cryptalgo/SecureSocketPort.h
index 32fa38e80..5121aeec2 100644
--- a/Source/cryptalgo/SecureSocketPort.h
+++ b/Source/cryptalgo/SecureSocketPort.h
@@ -81,6 +81,7 @@ namespace Crypto {
Handler(Handler&&) = delete;
Handler(const Handler&) = delete;
Handler& operator=(const Handler&) = delete;
+ Handler& operator=(Handler&&) = delete;
template
Handler(SecureSocketPort& parent, sockettype_t type, Args&&... args)
From 821bdfbe52e285e1c25b5140b27a019d809ca3f8 Mon Sep 17 00:00:00 2001
From: msieben <4319079+msieben@users.noreply.github.com>
Date: Tue, 26 Nov 2024 13:23:57 +0000
Subject: [PATCH 26/76] [Source/cryptalgo/SecureSocketPort] : remove enum class
'sockettype_t'
---
Source/cryptalgo/SecureSocketPort.h | 14 +++-----------
1 file changed, 3 insertions(+), 11 deletions(-)
diff --git a/Source/cryptalgo/SecureSocketPort.h b/Source/cryptalgo/SecureSocketPort.h
index 5121aeec2..97d56d150 100644
--- a/Source/cryptalgo/SecureSocketPort.h
+++ b/Source/cryptalgo/SecureSocketPort.h
@@ -72,26 +72,19 @@ namespace Crypto {
};
public:
- // Type of underlying socket
- enum class sockettype_t : uint8_t {
- CLIENT_SOCKET
- , SERVER_SOCKET
- };
-
Handler(Handler&&) = delete;
Handler(const Handler&) = delete;
Handler& operator=(const Handler&) = delete;
Handler& operator=(Handler&&) = delete;
template
- Handler(SecureSocketPort& parent, sockettype_t type, Args&&... args)
+ Handler(SecureSocketPort& parent, bool clientSocketType, Args&&... args)
: Core::SocketPort(args...)
, _parent(parent)
, _context(nullptr)
, _ssl(nullptr)
, _callback(nullptr)
- , _handShaking(type == sockettype_t::CLIENT_SOCKET ? CONNECTING : ACCEPTING)
- , _type{type}
+ , _handShaking{clientSocketType ? CONNECTING : ACCEPTING}
{}
~Handler();
@@ -143,7 +136,6 @@ namespace Crypto {
void* _ssl;
IValidator* _callback;
mutable state _handShaking;
- const sockettype_t _type;
};
public:
@@ -160,7 +152,7 @@ namespace Crypto {
template
SecureSocketPort(context_t context, Args&&... args)
- : _handler(*this, context == context_t:: SERVER_CONTEXT ? Handler::sockettype_t::SERVER_SOCKET : Handler::sockettype_t::CLIENT_SOCKET, args...)
+ : _handler(*this, context == context_t::CLIENT_CONTEXT, args...)
{}
public:
From a05570f792c7b6e7f8103822ac230ebaf8870ce7 Mon Sep 17 00:00:00 2001
From: msieben <4319079+msieben@users.noreply.github.com>
Date: Wed, 27 Nov 2024 15:06:32 +0000
Subject: [PATCH 27/76] [Source/cryptalgo/SecureSocketPort] : enable setting a
custom (server) certificate
'test_websocket' is updated accordingly
---
Source/cryptalgo/SecureSocketPort.cpp | 7 ++++---
Source/cryptalgo/SecureSocketPort.h | 21 +++++++++++++++------
Tests/unit/core/test_websocket.cpp | 12 ++++++------
3 files changed, 25 insertions(+), 15 deletions(-)
diff --git a/Source/cryptalgo/SecureSocketPort.cpp b/Source/cryptalgo/SecureSocketPort.cpp
index 298079014..bf4e705e3 100644
--- a/Source/cryptalgo/SecureSocketPort.cpp
+++ b/Source/cryptalgo/SecureSocketPort.cpp
@@ -142,9 +142,10 @@ uint32_t SecureSocketPort::Handler::Initialize() {
if ( ( ( (_handShaking == ACCEPTING)
// Load server certifiate and private key
- && (SSL_CTX_load_verify_locations(static_cast(_context), "RootCA.pem", nullptr) == 1)
- && (SSL_CTX_use_certificate_chain_file(static_cast(_context), "localhost.pem") == 1)
- && (SSL_CTX_use_PrivateKey_file(static_cast(_context), "localhost.key", SSL_FILETYPE_PEM) == 1)
+ && !_certificatePath.empty()
+ && (SSL_CTX_use_certificate_chain_file(static_cast(_context), _certificatePath.c_str()) == 1)
+ && !_privateKeyPath.empty()
+ && (SSL_CTX_use_PrivateKey_file(static_cast(_context), _privateKeyPath.c_str(), SSL_FILETYPE_PEM) == 1)
)
|| (_handShaking == CONNECTING)
)
diff --git a/Source/cryptalgo/SecureSocketPort.h b/Source/cryptalgo/SecureSocketPort.h
index 97d56d150..de3272368 100644
--- a/Source/cryptalgo/SecureSocketPort.h
+++ b/Source/cryptalgo/SecureSocketPort.h
@@ -78,13 +78,15 @@ namespace Crypto {
Handler& operator=(Handler&&) = delete;
template