From 8ea7899cba6cadfa37f2a44bc676d9d01586e437 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Tue, 31 Jan 2023 13:43:44 -0800 Subject: [PATCH 01/32] adapting to new secure tunnel api --- .../secure_tunnel/build_sample.py | 31 +++++ .../secure_tunneling/secure_tunnel/main.cpp | 34 +++++- .../aws/iotsecuretunneling/SecureTunnel.h | 115 ++++++++++++++++-- secure_tunneling/source/SecureTunnel.cpp | 97 ++++++++++++--- 4 files changed, 247 insertions(+), 30 deletions(-) create mode 100644 samples/secure_tunneling/secure_tunnel/build_sample.py diff --git a/samples/secure_tunneling/secure_tunnel/build_sample.py b/samples/secure_tunneling/secure_tunnel/build_sample.py new file mode 100644 index 000000000..0f097d322 --- /dev/null +++ b/samples/secure_tunneling/secure_tunnel/build_sample.py @@ -0,0 +1,31 @@ +from asyncio import subprocess +import os +import os.path +import shutil +import subprocess + +# Place in the sample folder to be built +# Change DCMAKE_PREFIX_PATH below to the + +root_dir = os.path.abspath(os.path.dirname(__file__)) +build_dir = os.path.join(root_dir, 'build') + +# Delete existing Sample build directory +shutil.rmtree(build_dir, ignore_errors=True) +os.mkdir(build_dir) + +os.chdir(build_dir) + + +def check_call(args): + print('$', subprocess.list2cmdline(args)) + subprocess.check_call(args) + + +# Build +check_call(['cmake', '-DCMAKE_PREFIX_PATH="/Users/sbstevek/workplace/secure-tunnel-multiplexing/installs"', + '-DCMAKE_BUILD_TYPE="Debug"', '..']) +check_call(['cmake', '--build', '.', '--config', 'Debug']) + +cmake - DCMAKE_PREFIX_PATH = "/Users/sbstevek/workplace/secure-tunnel-multiplexing/installs" - DCMAKE_BUILD_TYPE = "Debug" .. +cmake - -build . --config Debug diff --git a/samples/secure_tunneling/secure_tunnel/main.cpp b/samples/secure_tunneling/secure_tunnel/main.cpp index 711130bfd..34383af14 100644 --- a/samples/secure_tunneling/secure_tunnel/main.cpp +++ b/samples/secure_tunneling/secure_tunnel/main.cpp @@ -23,6 +23,7 @@ int main(int argc, char *argv[]) ApiHandle apiHandle; String accessToken; + String clientToken; aws_secure_tunneling_local_proxy_mode localProxyMode; String proxyHost; @@ -44,6 +45,7 @@ int main(int argc, char *argv[]) cmdUtils.RegisterCommand("access_token", "", "Tunneling access token (optional if --access_token_file used)."); cmdUtils.RegisterCommand( "local_proxy_mode_source", "", "Use to set local proxy mode to source (optional, default='destination')."); + cmdUtils.RegisterCommand("client_token", "", "Tunneling access token (optional if --client_token_file used)."); cmdUtils.RegisterCommand("message", "", "Message to send (optional, default='Hello World!')."); cmdUtils.RegisterCommand("test", "", "Used to trigger internal testing (optional, ignore unless testing)."); cmdUtils.RegisterCommand( @@ -89,6 +91,28 @@ int main(int argc, char *argv[]) } } + if (cmdUtils.HasCommand("client_token")) + { + clientToken = cmdUtils.GetCommand("client_token"); + } + + if (cmdUtils.HasCommand("client_token_file")) + { + clientToken = cmdUtils.GetCommand("client_token_file"); + + std::ifstream clientTokenFile(clientToken.c_str()); + if (clientTokenFile.is_open()) + { + getline(clientTokenFile, clientToken); + clientTokenFile.close(); + } + else + { + fprintf(stderr, "Failed to open client token file"); + exit(-1); + } + } + if (cmdUtils.HasCommand("proxy_host") || cmdUtils.HasCommand("proxy_port")) { proxyHost = @@ -152,7 +176,7 @@ int main(int argc, char *argv[]) connectionCompletedPromise.set_value(true); fprintf(stdout, "Connection Complete in Source Mode\n"); fprintf(stdout, "Sending Stream Start request\n"); - secureTunnel->SendStreamStart(); + secureTunnel->SendStreamStart("ssh"); break; } }; @@ -200,7 +224,7 @@ int main(int argc, char *argv[]) case AWS_SECURE_TUNNELING_DESTINATION_MODE: fprintf(stdout, "Data Receive Complete in Destination\n"); fprintf(stdout, "Sending response message:\"%s\"\n", returnMessage.c_str()); - secureTunnel->SendData(ByteCursorFromCString(returnMessage.c_str())); + secureTunnel->SendData("ssh", ByteCursorFromCString(returnMessage.c_str())); if (isTest) { expectedMessageCount--; @@ -290,6 +314,7 @@ int main(int argc, char *argv[]) .WithOnStreamStart(OnStreamStart) .WithOnStreamReset(OnStreamReset) .WithOnSessionReset(OnSessionReset) + .WithClientToken(clientToken.c_str()) .Build(); } @@ -314,11 +339,10 @@ int main(int argc, char *argv[]) if (localProxyMode == AWS_SECURE_TUNNELING_SOURCE_MODE) { - messageCount++; String toSend = (std::to_string(messageCount) + ": " + message.c_str()).c_str(); - - if (!secureTunnel->SendData(ByteCursorFromCString(toSend.c_str()))) + if (messageCount < 5 && !secureTunnel->SendData("ssh", ByteCursorFromCString(toSend.c_str()))) { + messageCount++; fprintf(stdout, "Sending Message:\"%s\"\n", toSend.c_str()); if (messageCount >= 5) { diff --git a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h index 56dde464e..7662f3396 100644 --- a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h +++ b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h @@ -16,6 +16,80 @@ namespace Aws { namespace Iotsecuretunneling { + /** + * Data model for Secure Tunnel messages. + * + */ + class AWS_IOTSECURETUNNELING_API Message + { + public: + Message( + const aws_secure_tunnel_message_view &raw_options, + Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; + Message(Aws::Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; + Message(Crt::String serviceId) noexcept; + Message(Crt::String serviceId, Aws::Crt::ByteCursor payload) noexcept; + + /** + * Sets the service id for the secure tunnel message. + * + * @param serviceId The service id for the secure tunnel message. + * @return The Message Object after setting the payload. + */ + Message &withServiceId(Crt::String serviceId) noexcept; + + /** + * Sets the payload for the secure tunnel message. + * + * @param payload The payload for the secure tunnel message. + * @return The Message Object after setting the payload. + */ + Message &withPayload(Aws::Crt::ByteCursor payload) noexcept; + + /** + * The service id of the secure tunnel message. + * + * @return The service id of the secure tunnel message. + */ + const Crt::String &getServiceId() const noexcept; + + /** + * The payload of the secure tunnel message. + * + * @return The payload of the secure tunnel message. + */ + const Aws::Crt::ByteCursor &getPayload() const noexcept; + + virtual ~Message(); + // Steve todo look into these below + // PublishPacket(const PublishPacket &) = delete; + // PublishPacket(PublishPacket &&) noexcept = delete; + // PublishPacket &operator=(const PublishPacket &) = delete; + // PublishPacket &operator=(PublishPacket &&) noexcept = delete; + + private: + Aws::Crt::Allocator *m_allocator; + + /** + * The service id used for the secure tunnel message with multiplexing. + * + * If left empty, a V1 protocol message is assumed. + */ + Crt::String m_serviceId; + + /** + * The payload of the secure tunnel message. + * + */ + Aws::Crt::ByteCursor m_payload; + + /////////////////////////////////////////////////////////////////////////// + // Underlying data storage for internal use + /////////////////////////////////////////////////////////////////////////// + Aws::Crt::ByteBuf m_payloadStorage; + Aws::Crt::String m_serviceIdString; + }; + class SecureTunnel; // Client callback type definitions @@ -23,6 +97,7 @@ namespace Aws using OnConnectionShutdown = std::function; using OnSendDataComplete = std::function; using OnDataReceive = std::function; + using OnMessageReceived = std::function; using OnStreamStart = std::function; using OnStreamReset = std::function; using OnSessionReset = std::function; @@ -61,9 +136,11 @@ namespace Aws SecureTunnelBuilder &WithOnConnectionShutdown(OnConnectionShutdown onConnectionShutdown); SecureTunnelBuilder &WithOnSendDataComplete(OnSendDataComplete onSendDataComplete); SecureTunnelBuilder &WithOnDataReceive(OnDataReceive onDataReceive); + SecureTunnelBuilder &WithOnMessageReceived(OnMessageReceived onMessageReceived); SecureTunnelBuilder &WithOnStreamStart(OnStreamStart onStreamStart); SecureTunnelBuilder &WithOnStreamReset(OnStreamReset onStreamReset); SecureTunnelBuilder &WithOnSessionReset(OnSessionReset onSessionReset); + SecureTunnelBuilder &WithClientToken(const std::string &clientToken); /** * Will return a shared pointer to a new SecureTunnel that countains a @@ -87,6 +164,7 @@ namespace Aws /** * Optional members */ + std::string m_clientToken; std::string m_rootCa; Crt::Optional m_httpClientConnectionProxyOptions; @@ -97,6 +175,7 @@ namespace Aws OnConnectionShutdown m_OnConnectionShutdown; OnSendDataComplete m_OnSendDataComplete; OnDataReceive m_OnDataReceive; + OnMessageReceived m_OnMessageReceived; OnStreamStart m_OnStreamStart; OnStreamReset m_OnStreamReset; OnSessionReset m_OnSessionReset; @@ -151,16 +230,20 @@ namespace Aws bool IsValid(); + // Steve TODO Deprecate int Connect(); - int Close(); - void Shutdown(); - int SendData(const Crt::ByteCursor &data); - int SendStreamStart(); + int Start(); + int Stop(); + int SendData(std::string serviceId, const Crt::ByteCursor &data); + // Steve TODO Source only + int SendStreamStart(); + int SendStreamStart(std::string serviceId); + // Steve TODO Should not be exposed. Under the hood only operation. int SendStreamReset(); aws_secure_tunnel *GetUnderlyingHandle(); @@ -175,6 +258,7 @@ namespace Aws Aws::Crt::Io::ClientBootstrap *clientBootstrap, const Aws::Crt::Io::SocketOptions &socketOptions, const std::string &accessToken, + const std::string &clientToken, aws_secure_tunneling_local_proxy_mode localProxyMode, const std::string &endpointHost, @@ -190,27 +274,38 @@ namespace Aws OnSessionReset onSessionReset); // aws-c-iot callbacks - static void s_OnConnectionComplete(void *user_data); - static void s_OnConnectionShutdown(void *user_data); + static void s_OnMessageReceived(const struct aws_secure_tunnel_message_view *message, void *user_data); + static void s_OnConnectionComplete(int error_code, void *user_data); + static void s_OnConnectionShutdown(int error_code, void *user_data); static void s_OnSendDataComplete(int error_code, void *user_data); - static void s_OnDataReceive(const struct aws_byte_buf *data, void *user_data); - static void s_OnStreamStart(void *user_data); - static void s_OnStreamReset(void *user_data); + static void s_OnStreamStart( + const struct aws_secure_tunnel_message_view *message, + int error_code, + void *user_data); + static void s_OnStreamReset( + const struct aws_secure_tunnel_message_view *message, + int error_code, + void *user_data); static void s_OnSessionReset(void *user_data); static void s_OnTerminationComplete(void *user_data); + // STEVE TODO remove/adapt the callbacks below + // static void s_OnDataReceive(const struct aws_byte_buf *data, void *user_data); + void OnTerminationComplete(); // Client callbacks + OnMessageReceived m_OnMessageReceived; OnConnectionComplete m_OnConnectionComplete; OnConnectionShutdown m_OnConnectionShutdown; OnSendDataComplete m_OnSendDataComplete; - OnDataReceive m_OnDataReceive; OnStreamStart m_OnStreamStart; OnStreamReset m_OnStreamReset; OnSessionReset m_OnSessionReset; aws_secure_tunnel *m_secure_tunnel; + OnDataReceive m_OnDataReceive; + std::promise m_TerminationComplete; friend class SecureTunnelBuilder; diff --git a/secure_tunneling/source/SecureTunnel.cpp b/secure_tunneling/source/SecureTunnel.cpp index 578bc8c7b..158457015 100644 --- a/secure_tunneling/source/SecureTunnel.cpp +++ b/secure_tunneling/source/SecureTunnel.cpp @@ -10,6 +10,13 @@ namespace Aws { namespace Iotsecuretunneling { + Message::Message(const aws_secure_tunnel_message_view &message, Crt::Allocator *allocator) noexcept + : m_allocator(allocator) + { + } + + Message::~Message() { aws_byte_buf_clean_up(&m_payloadStorage); } + SecureTunnelBuilder::SecureTunnelBuilder( Crt::Allocator *allocator, // Should out live this object Aws::Crt::Io::ClientBootstrap &clientBootstrap, // Should out live this object @@ -93,6 +100,12 @@ namespace Aws return *this; } + SecureTunnelBuilder &SecureTunnelBuilder::WithClientToken(const std::string &clientToken) + { + m_clientToken = clientToken; + return *this; + } + std::shared_ptr SecureTunnelBuilder::Build() noexcept { auto tunnel = std::shared_ptr(new SecureTunnel( @@ -100,6 +113,7 @@ namespace Aws m_clientBootstrap, m_socketOptions, m_accessToken, + m_clientToken, m_localProxyMode, m_endpointHost, m_rootCa, @@ -129,6 +143,7 @@ namespace Aws Aws::Crt::Io::ClientBootstrap *clientBootstrap, const Aws::Crt::Io::SocketOptions &socketOptions, const std::string &accessToken, + const std::string &clientToken, aws_secure_tunneling_local_proxy_mode localProxyMode, const std::string &endpointHost, @@ -169,10 +184,16 @@ namespace Aws config.root_ca = rootCa.c_str(); } + if (clientToken.length() > 0) + { + config.client_token = aws_byte_cursor_from_c_str(clientToken.c_str()); + } + + config.on_message_received = s_OnMessageReceived; config.on_connection_complete = s_OnConnectionComplete; config.on_connection_shutdown = s_OnConnectionShutdown; config.on_send_data_complete = s_OnSendDataComplete; - config.on_data_receive = s_OnDataReceive; + // config.on_data_receive = s_OnDataReceive; config.on_stream_start = s_OnStreamStart; config.on_stream_reset = s_OnStreamReset; config.on_session_reset = s_OnSessionReset; @@ -218,6 +239,7 @@ namespace Aws clientBootstrap, socketOptions, accessToken, + nullptr, localProxyMode, endpointHost, rootCa, @@ -257,6 +279,7 @@ namespace Aws Crt::ApiHandle::GetOrCreateStaticDefaultClientBootstrap(), socketOptions, accessToken, + nullptr, localProxyMode, endpointHost, rootCa, @@ -322,31 +345,52 @@ namespace Aws bool SecureTunnel::IsValid() { return m_secure_tunnel ? true : false; } - int SecureTunnel::Connect() { return aws_secure_tunnel_connect(m_secure_tunnel); } + // Steve TODO Deprecate + int SecureTunnel::Connect() { return aws_secure_tunnel_start(m_secure_tunnel); } + int SecureTunnel::Start() { return aws_secure_tunnel_start(m_secure_tunnel); } - int SecureTunnel::Close() { return aws_secure_tunnel_close(m_secure_tunnel); } + // Steve TODO Deprecate + int SecureTunnel::Close() { return aws_secure_tunnel_stop(m_secure_tunnel); } + int SecureTunnel::Stop() { return aws_secure_tunnel_stop(m_secure_tunnel); } - int SecureTunnel::SendData(const Crt::ByteCursor &data) + int SecureTunnel::SendData(const Crt::ByteCursor &data) { return SendData("", data); } + int SecureTunnel::SendData(std::string serviceId, const Crt::ByteCursor &data) { - return aws_secure_tunnel_send_data(m_secure_tunnel, &data); + struct aws_secure_tunnel_message_view messageView; + AWS_ZERO_STRUCT(messageView); + messageView.service_id = aws_byte_cursor_from_c_str(serviceId.c_str()); + messageView.payload = data; + return aws_secure_tunnel_send_message(m_secure_tunnel, &messageView); } - int SecureTunnel::SendStreamStart() { return aws_secure_tunnel_stream_start(m_secure_tunnel); } + // Steve TODO + int SecureTunnel::SendStreamStart() { return SendStreamStart(""); } + int SecureTunnel::SendStreamStart(std::string serviceId) + { + struct aws_secure_tunnel_message_view messageView; + AWS_ZERO_STRUCT(messageView); + messageView.service_id = aws_byte_cursor_from_c_str(serviceId.c_str()); + return aws_secure_tunnel_stream_start(m_secure_tunnel, &messageView); + } - int SecureTunnel::SendStreamReset() { return aws_secure_tunnel_stream_reset(m_secure_tunnel); } + // Steve TODO + int SecureTunnel::SendStreamReset() { return aws_secure_tunnel_stream_reset(m_secure_tunnel, NULL); } aws_secure_tunnel *SecureTunnel::GetUnderlyingHandle() { return m_secure_tunnel; } - void SecureTunnel::s_OnConnectionComplete(void *user_data) + void SecureTunnel::s_OnConnectionComplete(int error_code, void *user_data) { auto *secureTunnel = static_cast(user_data); - if (secureTunnel->m_OnConnectionComplete) + if (!error_code) { - secureTunnel->m_OnConnectionComplete(); + if (secureTunnel->m_OnConnectionComplete) + { + secureTunnel->m_OnConnectionComplete(); + } } } - void SecureTunnel::s_OnConnectionShutdown(void *user_data) + void SecureTunnel::s_OnConnectionShutdown(int error_code, void *user_data) { auto *secureTunnel = static_cast(user_data); if (secureTunnel->m_OnConnectionShutdown) @@ -364,16 +408,36 @@ namespace Aws } } - void SecureTunnel::s_OnDataReceive(const struct aws_byte_buf *data, void *user_data) + void SecureTunnel::s_OnMessageReceived(const struct aws_secure_tunnel_message_view *message, void *user_data) { auto *secureTunnel = static_cast(user_data); + /* V1 Protocol */ if (secureTunnel->m_OnDataReceive) { - secureTunnel->m_OnDataReceive(*data); + /* + * Old API expects an aws_byte_buf. Temporarily creating one from an aws_byte_cursor. The data will be + * managed syncronous here with the expectation that the user copies what they need as it is cleared as + * soon as this function completes + */ + struct aws_byte_buf payload_buf; + AWS_ZERO_STRUCT(payload_buf); + payload_buf.allocator = NULL; + payload_buf.buffer = message->payload.ptr; + payload_buf.len = message->payload.len; + secureTunnel->m_OnDataReceive(payload_buf); + return; + } + + if (secureTunnel->m_OnMessageReceived) + { + secureTunnel->m_OnMessageReceived(message->service_id, message->payload); } } - void SecureTunnel::s_OnStreamStart(void *user_data) + void SecureTunnel::s_OnStreamStart( + const struct aws_secure_tunnel_message_view *message, + int error_code, + void *user_data) { auto *secureTunnel = static_cast(user_data); if (secureTunnel->m_OnStreamStart) @@ -382,7 +446,10 @@ namespace Aws } } - void SecureTunnel::s_OnStreamReset(void *user_data) + void SecureTunnel::s_OnStreamReset( + const struct aws_secure_tunnel_message_view *message, + int error_code, + void *user_data) { auto *secureTunnel = static_cast(user_data); if (secureTunnel->m_OnStreamReset) From 21809499b1e705efa49c344a57cee16a53535476 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Thu, 9 Feb 2023 11:13:34 -0800 Subject: [PATCH 02/32] new api integration wip --- .../aws/iotsecuretunneling/SecureTunnel.h | 118 ++++++---- secure_tunneling/source/SecureTunnel.cpp | 221 +++++++++++++++--- 2 files changed, 257 insertions(+), 82 deletions(-) diff --git a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h index 7662f3396..c1cf8bb6e 100644 --- a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h +++ b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h @@ -26,9 +26,12 @@ namespace Aws Message( const aws_secure_tunnel_message_view &raw_options, Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; - Message(Aws::Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; - Message(Crt::String serviceId) noexcept; - Message(Crt::String serviceId, Aws::Crt::ByteCursor payload) noexcept; + Message(Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; + Message(Crt::ByteCursor payload, Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; + Message( + Crt::ByteCursor serviceId, + Crt::ByteCursor payload, + Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; /** * Sets the service id for the secure tunnel message. @@ -36,7 +39,7 @@ namespace Aws * @param serviceId The service id for the secure tunnel message. * @return The Message Object after setting the payload. */ - Message &withServiceId(Crt::String serviceId) noexcept; + Message &withServiceId(Crt::ByteCursor serviceId) noexcept; /** * Sets the payload for the secure tunnel message. @@ -44,50 +47,61 @@ namespace Aws * @param payload The payload for the secure tunnel message. * @return The Message Object after setting the payload. */ - Message &withPayload(Aws::Crt::ByteCursor payload) noexcept; + Message &withPayload(Crt::ByteCursor payload) noexcept; + + bool initializeRawOptions(aws_secure_tunnel_message_view &raw_options) noexcept; /** * The service id of the secure tunnel message. * * @return The service id of the secure tunnel message. */ - const Crt::String &getServiceId() const noexcept; + const Crt::Optional &getServiceId() const noexcept; /** * The payload of the secure tunnel message. * * @return The payload of the secure tunnel message. */ - const Aws::Crt::ByteCursor &getPayload() const noexcept; + const Crt::Optional &getPayload() const noexcept; virtual ~Message(); - // Steve todo look into these below - // PublishPacket(const PublishPacket &) = delete; - // PublishPacket(PublishPacket &&) noexcept = delete; - // PublishPacket &operator=(const PublishPacket &) = delete; - // PublishPacket &operator=(PublishPacket &&) noexcept = delete; + /* Do not allow direct copy or move */ + Message(const Message &) = delete; + Message(Message &&) noexcept = delete; + Message &operator=(const Message &) = delete; + Message &operator=(Message &&) noexcept = delete; private: - Aws::Crt::Allocator *m_allocator; + Crt::Allocator *m_allocator; /** - * The service id used for the secure tunnel message with multiplexing. + * The service id used for multiplexing. * * If left empty, a V1 protocol message is assumed. */ - Crt::String m_serviceId; + Crt::Optional m_serviceId; /** * The payload of the secure tunnel message. * */ - Aws::Crt::ByteCursor m_payload; + Crt::Optional m_payload; /////////////////////////////////////////////////////////////////////////// // Underlying data storage for internal use /////////////////////////////////////////////////////////////////////////// - Aws::Crt::ByteBuf m_payloadStorage; - Aws::Crt::String m_serviceIdString; + Crt::ByteBuf m_payloadStorage; + Crt::ByteBuf m_serviceIdStorage; + }; + + /** + * The data returned when a message is received on the secure tunnel. + */ + struct AWS_IOTSECURETUNNELING_API MessageReceivedEventData + { + MessageReceivedEventData() : message(nullptr) {} + std::shared_ptr message; }; class SecureTunnel; @@ -97,7 +111,7 @@ namespace Aws using OnConnectionShutdown = std::function; using OnSendDataComplete = std::function; using OnDataReceive = std::function; - using OnMessageReceived = std::function; + using OnMessageReceived = std::function; using OnStreamStart = std::function; using OnStreamReset = std::function; using OnSessionReset = std::function; @@ -109,10 +123,10 @@ namespace Aws * Constructor arguments are the minimum required to create a secure tunnel */ SecureTunnelBuilder( - Crt::Allocator *allocator, // Should out live this object - Aws::Crt::Io::ClientBootstrap &clientBootstrap, // Should out live this object - const Aws::Crt::Io::SocketOptions &socketOptions, // Make a copy and save in this object - const std::string &accessToken, // Make a copy and save in this object + Crt::Allocator *allocator, // Should out live this object + Crt::Io::ClientBootstrap &clientBootstrap, // Should out live this object + const Crt::Io::SocketOptions &socketOptions, // Make a copy and save in this object + const std::string &accessToken, // Make a copy and save in this object aws_secure_tunneling_local_proxy_mode localProxyMode, const std::string &endpointHost); // Make a copy and save in this object @@ -120,9 +134,9 @@ namespace Aws * Constructor arguments are the minimum required to create a secure tunnel */ SecureTunnelBuilder( - Crt::Allocator *allocator, // Should out live this object - const Aws::Crt::Io::SocketOptions &socketOptions, // Make a copy and save in this object - const std::string &accessToken, // Make a copy and save in this object + Crt::Allocator *allocator, // Should out live this object + const Crt::Io::SocketOptions &socketOptions, // Make a copy and save in this object + const std::string &accessToken, // Make a copy and save in this object aws_secure_tunneling_local_proxy_mode localProxyMode, const std::string &endpointHost); // Make a copy and save in this object @@ -131,7 +145,7 @@ namespace Aws */ SecureTunnelBuilder &WithRootCa(const std::string &rootCa); SecureTunnelBuilder &WithHttpClientConnectionProxyOptions( - const Aws::Crt::Http::HttpClientConnectionProxyOptions &httpClientConnectionProxyOptions); + const Crt::Http::HttpClientConnectionProxyOptions &httpClientConnectionProxyOptions); SecureTunnelBuilder &WithOnConnectionComplete(OnConnectionComplete onConnectionComplete); SecureTunnelBuilder &WithOnConnectionShutdown(OnConnectionShutdown onConnectionShutdown); SecureTunnelBuilder &WithOnSendDataComplete(OnSendDataComplete onSendDataComplete); @@ -145,7 +159,7 @@ namespace Aws /** * Will return a shared pointer to a new SecureTunnel that countains a * new aws_secure_tunnel that is generated using the set members of SecureTunnelBuilder. - * On failure, the shared_ptr will contain a nullptr, call Aws::Crt::LastErrorOrUnknown(); to get the reason + * On failure, the shared_ptr will contain a nullptr, call Crt::LastErrorOrUnknown(); to get the reason * for failure. */ std::shared_ptr Build() noexcept; @@ -155,8 +169,8 @@ namespace Aws * Required members */ Crt::Allocator *m_allocator; - Aws::Crt::Io::ClientBootstrap *m_clientBootstrap; - Aws::Crt::Io::SocketOptions m_socketOptions; + Crt::Io::ClientBootstrap *m_clientBootstrap; + Crt::Io::SocketOptions m_socketOptions; std::string m_accessToken; aws_secure_tunneling_local_proxy_mode m_localProxyMode; std::string m_endpointHost; @@ -187,9 +201,9 @@ namespace Aws { public: SecureTunnel( - Crt::Allocator *allocator, // Should out live this object - Aws::Crt::Io::ClientBootstrap *clientBootstrap, // Should out live this object - const Aws::Crt::Io::SocketOptions &socketOptions, // Make a copy and save in this object + Crt::Allocator *allocator, // Should out live this object + Crt::Io::ClientBootstrap *clientBootstrap, // Should out live this object + const Crt::Io::SocketOptions &socketOptions, // Make a copy and save in this object const std::string &accessToken, // Make a copy and save in this object aws_secure_tunneling_local_proxy_mode localProxyMode, @@ -205,8 +219,8 @@ namespace Aws OnSessionReset onSessionReset); SecureTunnel( - Crt::Allocator *allocator, // Should out live this object - const Aws::Crt::Io::SocketOptions &socketOptions, // Make a copy and save in this object + Crt::Allocator *allocator, // Should out live this object + const Crt::Io::SocketOptions &socketOptions, // Make a copy and save in this object const std::string &accessToken, // Make a copy and save in this object aws_secure_tunneling_local_proxy_mode localProxyMode, @@ -220,34 +234,34 @@ namespace Aws OnStreamStart onStreamStart, OnStreamReset onStreamReset, OnSessionReset onSessionReset); - SecureTunnel(const SecureTunnel &) = delete; - SecureTunnel(SecureTunnel &&) noexcept; virtual ~SecureTunnel(); - + /* Do not allow direct copy or move */ + SecureTunnel(const SecureTunnel &) = delete; + SecureTunnel(SecureTunnel &&) noexcept = delete; SecureTunnel &operator=(const SecureTunnel &) = delete; SecureTunnel &operator=(SecureTunnel &&) noexcept; bool IsValid(); - // Steve TODO Deprecate - int Connect(); - int Close(); - void Shutdown(); - int SendData(const Crt::ByteCursor &data); - int Start(); int Stop(); - int SendData(std::string serviceId, const Crt::ByteCursor &data); + int SendMessage(std::shared_ptr messageOptions) noexcept; - // Steve TODO Source only + /* SOURCE MODE ONLY */ int SendStreamStart(); int SendStreamStart(std::string serviceId); - // Steve TODO Should not be exposed. Under the hood only operation. - int SendStreamReset(); aws_secure_tunnel *GetUnderlyingHandle(); + /* These are to be deprecated */ + int Connect(); + int Close(); + void Shutdown(); + int SendData(const Crt::ByteCursor &data); + /* Should not be exposed. Under the hood only operation. */ + int SendStreamReset(); + private: /** * This constructor is used with SecureTunnelBuilder and should be modified when members are added or @@ -255,15 +269,15 @@ namespace Aws */ SecureTunnel( Crt::Allocator *allocator, - Aws::Crt::Io::ClientBootstrap *clientBootstrap, - const Aws::Crt::Io::SocketOptions &socketOptions, + Crt::Io::ClientBootstrap *clientBootstrap, + const Crt::Io::SocketOptions &socketOptions, const std::string &accessToken, const std::string &clientToken, aws_secure_tunneling_local_proxy_mode localProxyMode, const std::string &endpointHost, const std::string &rootCa, - Aws::Crt::Http::HttpClientConnectionProxyOptions *httpClientConnectionProxyOptions, + Crt::Http::HttpClientConnectionProxyOptions *httpClientConnectionProxyOptions, OnConnectionComplete onConnectionComplete, OnConnectionShutdown onConnectionShutdown, @@ -302,7 +316,9 @@ namespace Aws OnStreamStart m_OnStreamStart; OnStreamReset m_OnStreamReset; OnSessionReset m_OnSessionReset; + aws_secure_tunnel *m_secure_tunnel; + Crt::Allocator *m_allocator; OnDataReceive m_OnDataReceive; diff --git a/secure_tunneling/source/SecureTunnel.cpp b/secure_tunneling/source/SecureTunnel.cpp index 158457015..ecfaacb9d 100644 --- a/secure_tunneling/source/SecureTunnel.cpp +++ b/secure_tunneling/source/SecureTunnel.cpp @@ -10,12 +10,130 @@ namespace Aws { namespace Iotsecuretunneling { + void setPacketByteBufOptional( + Crt::Optional &optional, + Crt::ByteBuf &optionalStorage, + Crt::Allocator *allocator, + const Crt::ByteCursor *value) + { + aws_byte_buf_clean_up(&optionalStorage); + AWS_ZERO_STRUCT(optionalStorage); + if (value != nullptr) + { + aws_byte_buf_init_copy_from_cursor(&optionalStorage, allocator, *value); + optional = aws_byte_cursor_from_buf(&optionalStorage); + } + else + { + optional.reset(); + } + } + + void setPacketStringOptional( + Crt::Optional &optional, + Crt::String &optionalStorage, + const aws_byte_cursor *value) + { + if (value != nullptr) + { + optionalStorage = Crt::String((const char *)value->ptr, value->len); + struct aws_byte_cursor optional_cursor; + optional_cursor.ptr = (uint8_t *)optionalStorage.c_str(); + optional_cursor.len = optionalStorage.size(); + optional = optional_cursor; + } + } + Message::Message(const aws_secure_tunnel_message_view &message, Crt::Allocator *allocator) noexcept : m_allocator(allocator) { + AWS_ZERO_STRUCT(m_payload); + AWS_ZERO_STRUCT(m_payloadStorage); + AWS_ZERO_STRUCT(m_serviceId); + AWS_ZERO_STRUCT(m_serviceIdStorage); + + setPacketByteBufOptional(m_payload, m_payloadStorage, m_allocator, message.payload); + setPacketByteBufOptional(m_serviceId, m_serviceIdStorage, m_allocator, message.service_id); + } + + /* Default constructor */ + Message::Message(Crt::Allocator *allocator) noexcept : m_allocator(allocator) + { + AWS_ZERO_STRUCT(m_payload); + AWS_ZERO_STRUCT(m_payloadStorage); + AWS_ZERO_STRUCT(m_serviceId); + AWS_ZERO_STRUCT(m_serviceIdStorage); + } + + Message::Message(Crt::ByteCursor payload, Crt::Allocator *allocator) noexcept : m_allocator(allocator) + { + AWS_ZERO_STRUCT(m_payload); + AWS_ZERO_STRUCT(m_payloadStorage); + AWS_ZERO_STRUCT(m_serviceId); + AWS_ZERO_STRUCT(m_serviceIdStorage); + + aws_byte_buf_clean_up(&m_payloadStorage); + aws_byte_buf_init_copy_from_cursor(&m_payloadStorage, m_allocator, payload); + m_payload = aws_byte_cursor_from_buf(&m_payloadStorage); + } + + Message::Message(Crt::ByteCursor serviceId, Crt::ByteCursor payload, Crt::Allocator *allocator) noexcept + : m_allocator(allocator) + { + AWS_ZERO_STRUCT(m_payload); + AWS_ZERO_STRUCT(m_payloadStorage); + AWS_ZERO_STRUCT(m_serviceId); + AWS_ZERO_STRUCT(m_serviceIdStorage); + + aws_byte_buf_clean_up(&m_payloadStorage); + aws_byte_buf_init_copy_from_cursor(&m_payloadStorage, m_allocator, payload); + m_payload = aws_byte_cursor_from_buf(&m_payloadStorage); + + aws_byte_buf_clean_up(&m_serviceIdStorage); + aws_byte_buf_init_copy_from_cursor(&m_serviceIdStorage, m_allocator, serviceId); + m_payload = aws_byte_cursor_from_buf(&m_serviceIdStorage); + } + + Message &Message::withPayload(Crt::ByteCursor payload) noexcept + { + aws_byte_buf_clean_up(&m_payloadStorage); + aws_byte_buf_init_copy_from_cursor(&m_payloadStorage, m_allocator, payload); + m_payload = aws_byte_cursor_from_buf(&m_payloadStorage); + return *this; + } + + Message &Message::withServiceId(Crt::ByteCursor serviceId) noexcept + { + aws_byte_buf_clean_up(&m_serviceIdStorage); + aws_byte_buf_init_copy_from_cursor(&m_serviceIdStorage, m_allocator, serviceId); + m_payload = aws_byte_cursor_from_buf(&m_serviceIdStorage); + return *this; + } + + bool Message::initializeRawOptions(aws_secure_tunnel_message_view &raw_options) noexcept + { + AWS_ZERO_STRUCT(raw_options); + if (m_payload.has_value()) + { + raw_options.payload = &m_payload.value(); + } + if (m_serviceId.has_value()) + { + raw_options.service_id = &m_serviceId.value(); + } + + return true; } - Message::~Message() { aws_byte_buf_clean_up(&m_payloadStorage); } + const Crt::Optional &Message::getPayload() const noexcept { return m_payload; } + + const Crt::Optional &Message::getServiceId() const noexcept { return m_serviceId; } + + Message::~Message() + { + aws_byte_buf_clean_up(&m_payloadStorage); + aws_byte_buf_clean_up(&m_serviceIdStorage); + } SecureTunnelBuilder::SecureTunnelBuilder( Crt::Allocator *allocator, // Should out live this object @@ -171,7 +289,6 @@ namespace Aws aws_secure_tunnel_options config; AWS_ZERO_STRUCT(config); - config.allocator = allocator; config.bootstrap = clientBootstrap ? clientBootstrap->GetUnderlyingHandle() : nullptr; config.socket_options = &socketOptions.GetImpl(); @@ -210,7 +327,8 @@ namespace Aws } // Create the secure tunnel - m_secure_tunnel = aws_secure_tunnel_new(&config); + m_secure_tunnel = aws_secure_tunnel_new(allocator, &config); + m_allocator = allocator; } /** @@ -353,23 +471,49 @@ namespace Aws int SecureTunnel::Close() { return aws_secure_tunnel_stop(m_secure_tunnel); } int SecureTunnel::Stop() { return aws_secure_tunnel_stop(m_secure_tunnel); } - int SecureTunnel::SendData(const Crt::ByteCursor &data) { return SendData("", data); } - int SecureTunnel::SendData(std::string serviceId, const Crt::ByteCursor &data) + int SecureTunnel::SendData(const Crt::ByteCursor &data) { - struct aws_secure_tunnel_message_view messageView; - AWS_ZERO_STRUCT(messageView); - messageView.service_id = aws_byte_cursor_from_c_str(serviceId.c_str()); - messageView.payload = data; - return aws_secure_tunnel_send_message(m_secure_tunnel, &messageView); + // return SendData("", data); + std::shared_ptr message = std::make_shared(data); + return SendMessage(message); } + int SecureTunnel::SendMessage(std::shared_ptr messageOptions) noexcept + { + if (messageOptions == nullptr) + { + return AWS_OP_ERR; + } + + aws_secure_tunnel_message_view message; + messageOptions->initializeRawOptions(message); + return aws_secure_tunnel_send_message(m_secure_tunnel, &message); + } + // int SecureTunnel::SendData(std::string serviceId, const Crt::ByteCursor &data) + // { + // struct aws_secure_tunnel_message_view messageView; + // AWS_ZERO_STRUCT(messageView); + // struct aws_byte_cursor service_id_cur = aws_byte_cursor_from_c_str(serviceId.c_str()); + // struct aws_byte_cursor payload_cur = { + // .ptr = data.ptr, + // .len = data.len, + // }; + // messageView.service_id = &service_id_cur; + // messageView.payload = &payload_cur; + // return aws_secure_tunnel_send_message(m_secure_tunnel, &messageView); + // } + // Steve TODO int SecureTunnel::SendStreamStart() { return SendStreamStart(""); } int SecureTunnel::SendStreamStart(std::string serviceId) { struct aws_secure_tunnel_message_view messageView; AWS_ZERO_STRUCT(messageView); - messageView.service_id = aws_byte_cursor_from_c_str(serviceId.c_str()); + if (serviceId.length() > 0) + { + struct aws_byte_cursor service_id_cur = aws_byte_cursor_from_c_str(serviceId.c_str()); + messageView.service_id = &service_id_cur; + } return aws_secure_tunnel_stream_start(m_secure_tunnel, &messageView); } @@ -410,27 +554,42 @@ namespace Aws void SecureTunnel::s_OnMessageReceived(const struct aws_secure_tunnel_message_view *message, void *user_data) { - auto *secureTunnel = static_cast(user_data); - /* V1 Protocol */ - if (secureTunnel->m_OnDataReceive) + AWS_LOGF_INFO(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "on message received callback"); + SecureTunnel *secureTunnel = reinterpret_cast(user_data); + if (secureTunnel != nullptr) { - /* - * Old API expects an aws_byte_buf. Temporarily creating one from an aws_byte_cursor. The data will be - * managed syncronous here with the expectation that the user copies what they need as it is cleared as - * soon as this function completes - */ - struct aws_byte_buf payload_buf; - AWS_ZERO_STRUCT(payload_buf); - payload_buf.allocator = NULL; - payload_buf.buffer = message->payload.ptr; - payload_buf.len = message->payload.len; - secureTunnel->m_OnDataReceive(payload_buf); - return; - } - - if (secureTunnel->m_OnMessageReceived) - { - secureTunnel->m_OnMessageReceived(message->service_id, message->payload); + if (message != NULL) + { + if (secureTunnel->m_OnMessageReceived != nullptr) + { + std::shared_ptr packet = + std::make_shared(*message, secureTunnel->m_allocator); + MessageReceivedEventData eventData; + eventData.message = packet; + secureTunnel->m_OnMessageReceived(*secureTunnel, eventData); + return; + } + + if (secureTunnel->m_OnDataReceive != nullptr) + { + /* + * Old API (V1) expects an aws_byte_buf. Temporarily creating one from an aws_byte_cursor. The + * data will be managed syncronous here with the expectation that the user copies what they + * need as it is cleared as soon as this function completes + */ + struct aws_byte_buf payload_buf; + AWS_ZERO_STRUCT(payload_buf); + payload_buf.allocator = NULL; + payload_buf.buffer = message->payload->ptr; + payload_buf.len = message->payload->len; + secureTunnel->m_OnDataReceive(payload_buf); + return; + } + } + else + { + AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Failed to access message view."); + } } } From 6f3d7a22b32cd3bd9a08299143b06f74b0780674 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Fri, 10 Feb 2023 15:15:46 -0800 Subject: [PATCH 03/32] Secure tunnel expansion of api --- .../aws/iotsecuretunneling/SecureTunnel.h | 208 +++++++++++++-- secure_tunneling/source/SecureTunnel.cpp | 246 +++++++++++++----- 2 files changed, 372 insertions(+), 82 deletions(-) diff --git a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h index c1cf8bb6e..e078a6aff 100644 --- a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h +++ b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h @@ -18,7 +18,6 @@ namespace Aws { /** * Data model for Secure Tunnel messages. - * */ class AWS_IOTSECURETUNNELING_API Message { @@ -84,7 +83,6 @@ namespace Aws /** * The payload of the secure tunnel message. - * */ Crt::Optional m_payload; @@ -104,18 +102,168 @@ namespace Aws std::shared_ptr message; }; + /** + * Data model for Secure Tunnel connection view. + */ + class AWS_IOTSECURETUNNELING_API ConnectionData + { + public: + ConnectionData( + const aws_secure_tunnel_connection_view &raw_options, + Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; + + /** + * Service id 1 of the secure tunnel. + * + * @return Service id 1 of the secure tunnel. + */ + const Crt::Optional &getServiceId1() const noexcept; + + /** + * Service id 2 of the secure tunnel. + * + * @return Service id 2 of the secure tunnel. + */ + const Crt::Optional &getServiceId2() const noexcept; + + /** + * Service id 3 of the secure tunnel. + * + * @return Service id 3 of the secure tunnel. + */ + const Crt::Optional &getServiceId3() const noexcept; + + virtual ~ConnectionData(); + /* Do not allow direct copy or move */ + ConnectionData(const ConnectionData &) = delete; + ConnectionData(ConnectionData &&) noexcept = delete; + ConnectionData &operator=(const ConnectionData &) = delete; + ConnectionData &operator=(ConnectionData &&) noexcept = delete; + + private: + Crt::Allocator *m_allocator; + + /** + * Service id 1 used for multiplexing. + * + * If left empty, a V1 protocol message is assumed. + */ + Crt::Optional m_serviceId1; + + /** + * Service id 2 used for multiplexing. + */ + Crt::Optional m_serviceId2; + + /** + * Service id 2 used for multiplexing. + */ + Crt::Optional m_serviceId3; + + /////////////////////////////////////////////////////////////////////////// + // Underlying data storage for internal use + /////////////////////////////////////////////////////////////////////////// + Crt::ByteBuf m_serviceId1Storage; + Crt::ByteBuf m_serviceId2Storage; + Crt::ByteBuf m_serviceId3Storage; + }; + + /** + * The data returned when a connection with secure tunnel service is established. + */ + struct AWS_IOTSECURETUNNELING_API ConnectionEstablishedEventData + { + ConnectionEstablishedEventData() : connectionData(nullptr) {} + std::shared_ptr connectionData; + }; + + /** + * Data model for started Secure Tunnel streams. + */ + class AWS_IOTSECURETUNNELING_API StreamStartedData + { + public: + StreamStartedData( + const aws_secure_tunnel_message_view &raw_options, + Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; + + /** + * Service id of the started stream. + * + * @return Service id of the started stream. + */ + const Crt::Optional &getServiceId() const noexcept; + + virtual ~StreamStartedData(); + /* Do not allow direct copy or move */ + StreamStartedData(const StreamStartedData &) = delete; + StreamStartedData(StreamStartedData &&) noexcept = delete; + StreamStartedData &operator=(const StreamStartedData &) = delete; + StreamStartedData &operator=(StreamStartedData &&) noexcept = delete; + + private: + Crt::Allocator *m_allocator; + + /** + * Service id of started stream. + * + * If left empty, a V1 protocolstream is assumed. + */ + Crt::Optional m_serviceId; + + /////////////////////////////////////////////////////////////////////////// + // Underlying data storage for internal use + /////////////////////////////////////////////////////////////////////////// + Crt::ByteBuf m_serviceIdStorage; + }; + + /** + * The data returned when a stream is started on the Secure Tunnel. + */ + struct AWS_IOTSECURETUNNELING_API StreamStartedEventData + { + StreamStartedEventData() : streamStartedData(nullptr) {} + std::shared_ptr streamStartedData; + }; + class SecureTunnel; // Client callback type definitions - using OnConnectionComplete = std::function; + + /** + * Type signature of the callback invoked when connection is established with the secure tunnel service and + * available service ids are returned. + */ + using OnConnectionEstablished = + std::function; + /** + * Type signature of the callback invoked when connection is shutdown. + */ using OnConnectionShutdown = std::function; + + /** + * Type signature of the callback invoked when data has been sent through the secure tunnel connection. + */ using OnSendDataComplete = std::function; - using OnDataReceive = std::function; + + /** + * Type signature of the callback invoked when a message is received through the secure tunnel connection. + */ using OnMessageReceived = std::function; - using OnStreamStart = std::function; + + /** + * Type signature of the callback invoked when a stream has been started with a source through the secure tunnel + * connection. + */ + using OnStreamStarted = std::function; using OnStreamReset = std::function; using OnSessionReset = std::function; + /* Deprecate */ + using OnConnectionComplete = std::function; + using OnDataReceive = std::function; + using OnStreamStart = std::function; + class AWS_IOTSECURETUNNELING_API SecureTunnelBuilder final { public: @@ -146,15 +294,18 @@ namespace Aws SecureTunnelBuilder &WithRootCa(const std::string &rootCa); SecureTunnelBuilder &WithHttpClientConnectionProxyOptions( const Crt::Http::HttpClientConnectionProxyOptions &httpClientConnectionProxyOptions); - SecureTunnelBuilder &WithOnConnectionComplete(OnConnectionComplete onConnectionComplete); + SecureTunnelBuilder &WithOnConnectionEstablished(OnConnectionEstablished onConnectionEstablished); SecureTunnelBuilder &WithOnConnectionShutdown(OnConnectionShutdown onConnectionShutdown); SecureTunnelBuilder &WithOnSendDataComplete(OnSendDataComplete onSendDataComplete); - SecureTunnelBuilder &WithOnDataReceive(OnDataReceive onDataReceive); SecureTunnelBuilder &WithOnMessageReceived(OnMessageReceived onMessageReceived); - SecureTunnelBuilder &WithOnStreamStart(OnStreamStart onStreamStart); + SecureTunnelBuilder &WithOnStreamStarted(OnStreamStarted onStreamStarted); SecureTunnelBuilder &WithOnStreamReset(OnStreamReset onStreamReset); SecureTunnelBuilder &WithOnSessionReset(OnSessionReset onSessionReset); SecureTunnelBuilder &WithClientToken(const std::string &clientToken); + /* Deprecate */ + SecureTunnelBuilder &WithOnDataReceive(OnDataReceive onDataReceive); + SecureTunnelBuilder &WithOnConnectionComplete(OnConnectionComplete onConnectionComplete); + SecureTunnelBuilder &WithOnStreamStart(OnStreamStart onStreamStart); /** * Will return a shared pointer to a new SecureTunnel that countains a @@ -185,15 +336,21 @@ namespace Aws /** * Callbacks */ - OnConnectionComplete m_OnConnectionComplete; + OnConnectionEstablished m_OnConnectionEstablished; OnConnectionShutdown m_OnConnectionShutdown; OnSendDataComplete m_OnSendDataComplete; - OnDataReceive m_OnDataReceive; OnMessageReceived m_OnMessageReceived; - OnStreamStart m_OnStreamStart; + OnStreamStarted m_OnStreamStarted; OnStreamReset m_OnStreamReset; OnSessionReset m_OnSessionReset; + /* Depricate */ + OnConnectionComplete m_OnConnectionComplete; + /* Depricate */ + OnDataReceive m_OnDataReceive; + /* Depricate */ + OnStreamStart m_OnStreamStart; + friend class SecureTunnel; }; @@ -238,7 +395,7 @@ namespace Aws virtual ~SecureTunnel(); /* Do not allow direct copy or move */ SecureTunnel(const SecureTunnel &) = delete; - SecureTunnel(SecureTunnel &&) noexcept = delete; + SecureTunnel(SecureTunnel &&) noexcept; SecureTunnel &operator=(const SecureTunnel &) = delete; SecureTunnel &operator=(SecureTunnel &&) noexcept; @@ -251,6 +408,7 @@ namespace Aws /* SOURCE MODE ONLY */ int SendStreamStart(); int SendStreamStart(std::string serviceId); + int SendStreamStart(Crt::ByteCursor serviceId); aws_secure_tunnel *GetUnderlyingHandle(); @@ -279,48 +437,54 @@ namespace Aws const std::string &rootCa, Crt::Http::HttpClientConnectionProxyOptions *httpClientConnectionProxyOptions, + OnConnectionEstablished onConnectionEstablished, OnConnectionComplete onConnectionComplete, OnConnectionShutdown onConnectionShutdown, OnSendDataComplete onSendDataComplete, + OnMessageReceived onMessageReceived, OnDataReceive onDataReceive, + OnStreamStarted onStreamStarted, OnStreamStart onStreamStart, OnStreamReset onStreamReset, OnSessionReset onSessionReset); // aws-c-iot callbacks static void s_OnMessageReceived(const struct aws_secure_tunnel_message_view *message, void *user_data); - static void s_OnConnectionComplete(int error_code, void *user_data); - static void s_OnConnectionShutdown(int error_code, void *user_data); - static void s_OnSendDataComplete(int error_code, void *user_data); - static void s_OnStreamStart( - const struct aws_secure_tunnel_message_view *message, + static void s_OnConnectionEstablished( + const struct aws_secure_tunnel_connection_view *connection, int error_code, void *user_data); + static void s_OnConnectionShutdown(int error_code, void *user_data); + static void s_OnSendDataComplete(int error_code, void *user_data); static void s_OnStreamReset( const struct aws_secure_tunnel_message_view *message, int error_code, void *user_data); static void s_OnSessionReset(void *user_data); static void s_OnTerminationComplete(void *user_data); - - // STEVE TODO remove/adapt the callbacks below - // static void s_OnDataReceive(const struct aws_byte_buf *data, void *user_data); + static void s_OnStreamStarted( + const struct aws_secure_tunnel_message_view *message, + int error_code, + void *user_data); void OnTerminationComplete(); // Client callbacks OnMessageReceived m_OnMessageReceived; - OnConnectionComplete m_OnConnectionComplete; + OnConnectionEstablished m_OnConnectionEstablished; OnConnectionShutdown m_OnConnectionShutdown; OnSendDataComplete m_OnSendDataComplete; - OnStreamStart m_OnStreamStart; + OnStreamStarted m_OnStreamStarted; OnStreamReset m_OnStreamReset; OnSessionReset m_OnSessionReset; aws_secure_tunnel *m_secure_tunnel; Crt::Allocator *m_allocator; + /* Deprecate these callbacks */ OnDataReceive m_OnDataReceive; + OnConnectionComplete m_OnConnectionComplete; + OnStreamStart m_OnStreamStart; std::promise m_TerminationComplete; diff --git a/secure_tunneling/source/SecureTunnel.cpp b/secure_tunneling/source/SecureTunnel.cpp index ecfaacb9d..023c7ddc1 100644 --- a/secure_tunneling/source/SecureTunnel.cpp +++ b/secure_tunneling/source/SecureTunnel.cpp @@ -44,6 +44,10 @@ namespace Aws } } + //*********************************************************************************************************************** + /* Message */ + //*********************************************************************************************************************** + Message::Message(const aws_secure_tunnel_message_view &message, Crt::Allocator *allocator) noexcept : m_allocator(allocator) { @@ -91,7 +95,7 @@ namespace Aws aws_byte_buf_clean_up(&m_serviceIdStorage); aws_byte_buf_init_copy_from_cursor(&m_serviceIdStorage, m_allocator, serviceId); - m_payload = aws_byte_cursor_from_buf(&m_serviceIdStorage); + m_serviceId = aws_byte_cursor_from_buf(&m_serviceIdStorage); } Message &Message::withPayload(Crt::ByteCursor payload) noexcept @@ -106,7 +110,7 @@ namespace Aws { aws_byte_buf_clean_up(&m_serviceIdStorage); aws_byte_buf_init_copy_from_cursor(&m_serviceIdStorage, m_allocator, serviceId); - m_payload = aws_byte_cursor_from_buf(&m_serviceIdStorage); + m_serviceId = aws_byte_cursor_from_buf(&m_serviceIdStorage); return *this; } @@ -135,6 +139,60 @@ namespace Aws aws_byte_buf_clean_up(&m_serviceIdStorage); } + //*********************************************************************************************************************** + /* ConnectionData */ + //*********************************************************************************************************************** + + ConnectionData::ConnectionData( + const aws_secure_tunnel_connection_view &connection, + Crt::Allocator *allocator) noexcept + : m_allocator(allocator) + { + AWS_ZERO_STRUCT(m_serviceId1); + AWS_ZERO_STRUCT(m_serviceId2); + AWS_ZERO_STRUCT(m_serviceId3); + AWS_ZERO_STRUCT(m_serviceId1Storage); + AWS_ZERO_STRUCT(m_serviceId2Storage); + AWS_ZERO_STRUCT(m_serviceId3Storage); + + setPacketByteBufOptional(m_serviceId1, m_serviceId1Storage, m_allocator, connection.service_id_1); + setPacketByteBufOptional(m_serviceId2, m_serviceId2Storage, m_allocator, connection.service_id_2); + setPacketByteBufOptional(m_serviceId3, m_serviceId3Storage, m_allocator, connection.service_id_3); + } + + const Crt::Optional &ConnectionData::getServiceId1() const noexcept { return m_serviceId1; } + const Crt::Optional &ConnectionData::getServiceId2() const noexcept { return m_serviceId2; } + const Crt::Optional &ConnectionData::getServiceId3() const noexcept { return m_serviceId3; } + + ConnectionData::~ConnectionData() + { + aws_byte_buf_clean_up(&m_serviceId1Storage); + aws_byte_buf_clean_up(&m_serviceId2Storage); + aws_byte_buf_clean_up(&m_serviceId3Storage); + } + + //*********************************************************************************************************************** + /* StreamStartData */ + //*********************************************************************************************************************** + + StreamStartedData::StreamStartedData( + const aws_secure_tunnel_message_view &message, + Crt::Allocator *allocator) noexcept + : m_allocator(allocator) + { + AWS_ZERO_STRUCT(m_serviceId); + AWS_ZERO_STRUCT(m_serviceIdStorage); + + setPacketByteBufOptional(m_serviceId, m_serviceIdStorage, m_allocator, message.service_id); + } + + const Crt::Optional &StreamStartedData::getServiceId() const noexcept { return m_serviceId; } + + StreamStartedData::~StreamStartedData() { aws_byte_buf_clean_up(&m_serviceIdStorage); } + + //*********************************************************************************************************************** + /* SecureTunnelBuilder */ + //*********************************************************************************************************************** SecureTunnelBuilder::SecureTunnelBuilder( Crt::Allocator *allocator, // Should out live this object Aws::Crt::Io::ClientBootstrap &clientBootstrap, // Should out live this object @@ -144,8 +202,8 @@ namespace Aws const std::string &endpointHost) // Make a copy and save in this object : m_allocator(allocator), m_clientBootstrap(&clientBootstrap), m_socketOptions(socketOptions), m_accessToken(accessToken), m_localProxyMode(localProxyMode), m_endpointHost(endpointHost), m_rootCa(""), - m_httpClientConnectionProxyOptions(), m_OnConnectionComplete(), m_OnConnectionShutdown(), - m_OnSendDataComplete(), m_OnDataReceive(), m_OnStreamStart(), m_OnStreamReset(), m_OnSessionReset() + m_httpClientConnectionProxyOptions(), m_OnConnectionShutdown(), m_OnSendDataComplete(), m_OnStreamReset(), + m_OnSessionReset(), m_OnConnectionComplete(), m_OnDataReceive(), m_OnStreamStart() { } @@ -158,8 +216,8 @@ namespace Aws : m_allocator(allocator), m_clientBootstrap(Crt::ApiHandle::GetOrCreateStaticDefaultClientBootstrap()), m_socketOptions(socketOptions), m_accessToken(accessToken), m_localProxyMode(localProxyMode), m_endpointHost(endpointHost), m_rootCa(""), m_httpClientConnectionProxyOptions(), - m_OnConnectionComplete(), m_OnConnectionShutdown(), m_OnSendDataComplete(), m_OnDataReceive(), - m_OnStreamStart(), m_OnStreamReset(), m_OnSessionReset() + m_OnConnectionShutdown(), m_OnSendDataComplete(), m_OnStreamReset(), m_OnSessionReset(), + m_OnConnectionComplete(), m_OnDataReceive(), m_OnStreamStart() { } @@ -176,9 +234,10 @@ namespace Aws return *this; } - SecureTunnelBuilder &SecureTunnelBuilder::WithOnConnectionComplete(OnConnectionComplete onConnectionComplete) + SecureTunnelBuilder &SecureTunnelBuilder::WithOnConnectionEstablished( + OnConnectionEstablished onConnectionEstablished) { - m_OnConnectionComplete = std::move(onConnectionComplete); + m_OnConnectionEstablished = std::move(onConnectionEstablished); return *this; } @@ -194,15 +253,15 @@ namespace Aws return *this; } - SecureTunnelBuilder &SecureTunnelBuilder::WithOnDataReceive(OnDataReceive onDataReceive) + SecureTunnelBuilder &SecureTunnelBuilder::WithOnMessageReceived(OnMessageReceived OnMessageReceived) { - m_OnDataReceive = std::move(onDataReceive); + m_OnMessageReceived = std::move(OnMessageReceived); return *this; } - SecureTunnelBuilder &SecureTunnelBuilder::WithOnStreamStart(OnStreamStart onStreamStart) + SecureTunnelBuilder &SecureTunnelBuilder::WithOnStreamStarted(OnStreamStarted onStreamStarted) { - m_OnStreamStart = std::move(onStreamStart); + m_OnStreamStarted = std::move(onStreamStarted); return *this; } @@ -224,6 +283,25 @@ namespace Aws return *this; } + /* Deprecate */ + SecureTunnelBuilder &SecureTunnelBuilder::WithOnConnectionComplete(OnConnectionComplete onConnectionComplete) + { + m_OnConnectionComplete = std::move(onConnectionComplete); + return *this; + } + /* Deprecate */ + SecureTunnelBuilder &SecureTunnelBuilder::WithOnStreamStart(OnStreamStart onStreamStart) + { + m_OnStreamStart = std::move(onStreamStart); + return *this; + } + /* Deprecate */ + SecureTunnelBuilder &SecureTunnelBuilder::WithOnDataReceive(OnDataReceive onDataReceive) + { + m_OnDataReceive = std::move(onDataReceive); + return *this; + } + std::shared_ptr SecureTunnelBuilder::Build() noexcept { auto tunnel = std::shared_ptr(new SecureTunnel( @@ -236,10 +314,13 @@ namespace Aws m_endpointHost, m_rootCa, m_httpClientConnectionProxyOptions.has_value() ? &m_httpClientConnectionProxyOptions.value() : nullptr, + m_OnConnectionEstablished, m_OnConnectionComplete, m_OnConnectionShutdown, m_OnSendDataComplete, + m_OnMessageReceived, m_OnDataReceive, + m_OnStreamStarted, m_OnStreamStart, m_OnStreamReset, m_OnSessionReset)); @@ -252,6 +333,10 @@ namespace Aws return tunnel; } + //*********************************************************************************************************************** + /* SecureTunnel */ + //*********************************************************************************************************************** + /** * Private SecureTunnel constructor used by SecureTunnelBuilder on SecureTunnelBuilder::Build() and by old * SecureTunnel constructor which should be deprecated @@ -268,19 +353,25 @@ namespace Aws const std::string &rootCa, Aws::Crt::Http::HttpClientConnectionProxyOptions *httpClientConnectionProxyOptions, + OnConnectionEstablished onConnectionEstablished, OnConnectionComplete onConnectionComplete, OnConnectionShutdown onConnectionShutdown, OnSendDataComplete onSendDataComplete, + OnMessageReceived onMessageReceived, OnDataReceive onDataReceive, + OnStreamStarted onStreamStarted, OnStreamStart onStreamStart, OnStreamReset onStreamReset, OnSessionReset onSessionReset) { // Client callbacks + m_OnConnectionEstablished = std::move(onConnectionEstablished); m_OnConnectionComplete = std::move(onConnectionComplete); m_OnConnectionShutdown = std::move(onConnectionShutdown); m_OnSendDataComplete = std::move(onSendDataComplete); + m_OnMessageReceived = std::move(onMessageReceived); m_OnDataReceive = std::move(onDataReceive); + m_OnStreamStarted = std::move(onStreamStarted); m_OnStreamStart = std::move(onStreamStart); m_OnStreamReset = std::move(onStreamReset); m_OnSessionReset = std::move(onSessionReset); @@ -306,12 +397,12 @@ namespace Aws config.client_token = aws_byte_cursor_from_c_str(clientToken.c_str()); } + /* callbacks for native secure tunnel */ config.on_message_received = s_OnMessageReceived; - config.on_connection_complete = s_OnConnectionComplete; + config.on_connection_complete = s_OnConnectionEstablished; config.on_connection_shutdown = s_OnConnectionShutdown; config.on_send_data_complete = s_OnSendDataComplete; - // config.on_data_receive = s_OnDataReceive; - config.on_stream_start = s_OnStreamStart; + config.on_stream_start = s_OnStreamStarted; config.on_stream_reset = s_OnStreamReset; config.on_session_reset = s_OnSessionReset; config.on_termination_complete = s_OnTerminationComplete; @@ -362,10 +453,13 @@ namespace Aws endpointHost, rootCa, nullptr, + nullptr, onConnectionComplete, onConnectionShutdown, onSendDataComplete, + nullptr, onDataReceive, + nullptr, onStreamStart, onStreamReset, onSessionReset) @@ -402,25 +496,40 @@ namespace Aws endpointHost, rootCa, nullptr, + nullptr, onConnectionComplete, onConnectionShutdown, onSendDataComplete, + nullptr, onDataReceive, + nullptr, onStreamStart, onStreamReset, onSessionReset) { } + SecureTunnel::~SecureTunnel() + { + if (m_secure_tunnel) + { + aws_secure_tunnel_release(m_secure_tunnel); + } + } + SecureTunnel::SecureTunnel(SecureTunnel &&other) noexcept { - m_OnConnectionComplete = std::move(other.m_OnConnectionComplete); + m_OnConnectionEstablished = std::move(other.m_OnConnectionEstablished); m_OnConnectionShutdown = std::move(other.m_OnConnectionShutdown); m_OnSendDataComplete = std::move(other.m_OnSendDataComplete); - m_OnDataReceive = std::move(other.m_OnDataReceive); - m_OnStreamStart = std::move(other.m_OnStreamStart); + m_OnMessageReceived = std::move(other.m_OnMessageReceived); + m_OnStreamStarted = std::move(other.m_OnStreamStarted); m_OnStreamReset = std::move(other.m_OnStreamReset); m_OnSessionReset = std::move(other.m_OnSessionReset); + /* Deprecate */ + m_OnConnectionComplete = std::move(other.m_OnConnectionComplete); + m_OnDataReceive = std::move(other.m_OnDataReceive); + m_OnStreamStart = std::move(other.m_OnStreamStart); m_TerminationComplete = std::move(other.m_TerminationComplete); @@ -429,27 +538,23 @@ namespace Aws other.m_secure_tunnel = nullptr; } - SecureTunnel::~SecureTunnel() - { - if (m_secure_tunnel) - { - aws_secure_tunnel_release(m_secure_tunnel); - } - } - SecureTunnel &SecureTunnel::operator=(SecureTunnel &&other) noexcept { if (this != &other) { this->~SecureTunnel(); - m_OnConnectionComplete = std::move(other.m_OnConnectionComplete); + m_OnConnectionEstablished = std::move(other.m_OnConnectionEstablished); m_OnConnectionShutdown = std::move(other.m_OnConnectionShutdown); m_OnSendDataComplete = std::move(other.m_OnSendDataComplete); - m_OnDataReceive = std::move(other.m_OnDataReceive); - m_OnStreamStart = std::move(other.m_OnStreamStart); + m_OnMessageReceived = std::move(other.m_OnMessageReceived); + m_OnStreamStarted = std::move(other.m_OnStreamStarted); m_OnStreamReset = std::move(other.m_OnStreamReset); m_OnSessionReset = std::move(other.m_OnSessionReset); + /* Deprecate */ + m_OnConnectionComplete = std::move(other.m_OnConnectionComplete); + m_OnDataReceive = std::move(other.m_OnDataReceive); + m_OnStreamStart = std::move(other.m_OnStreamStart); m_TerminationComplete = std::move(other.m_TerminationComplete); @@ -463,14 +568,14 @@ namespace Aws bool SecureTunnel::IsValid() { return m_secure_tunnel ? true : false; } - // Steve TODO Deprecate - int SecureTunnel::Connect() { return aws_secure_tunnel_start(m_secure_tunnel); } int SecureTunnel::Start() { return aws_secure_tunnel_start(m_secure_tunnel); } - - // Steve TODO Deprecate - int SecureTunnel::Close() { return aws_secure_tunnel_stop(m_secure_tunnel); } int SecureTunnel::Stop() { return aws_secure_tunnel_stop(m_secure_tunnel); } + /* Deprecate */ + int SecureTunnel::Connect() { return aws_secure_tunnel_start(m_secure_tunnel); } + /* Deprecate */ + int SecureTunnel::Close() { return aws_secure_tunnel_stop(m_secure_tunnel); } + /* Deprecate */ int SecureTunnel::SendData(const Crt::ByteCursor &data) { // return SendData("", data); @@ -489,44 +594,50 @@ namespace Aws messageOptions->initializeRawOptions(message); return aws_secure_tunnel_send_message(m_secure_tunnel, &message); } - // int SecureTunnel::SendData(std::string serviceId, const Crt::ByteCursor &data) - // { - // struct aws_secure_tunnel_message_view messageView; - // AWS_ZERO_STRUCT(messageView); - // struct aws_byte_cursor service_id_cur = aws_byte_cursor_from_c_str(serviceId.c_str()); - // struct aws_byte_cursor payload_cur = { - // .ptr = data.ptr, - // .len = data.len, - // }; - // messageView.service_id = &service_id_cur; - // messageView.payload = &payload_cur; - // return aws_secure_tunnel_send_message(m_secure_tunnel, &messageView); - // } - - // Steve TODO + int SecureTunnel::SendStreamStart() { return SendStreamStart(""); } int SecureTunnel::SendStreamStart(std::string serviceId) { - struct aws_secure_tunnel_message_view messageView; - AWS_ZERO_STRUCT(messageView); + struct aws_byte_cursor service_id_cur; + AWS_ZERO_STRUCT(service_id_cur); if (serviceId.length() > 0) { - struct aws_byte_cursor service_id_cur = aws_byte_cursor_from_c_str(serviceId.c_str()); - messageView.service_id = &service_id_cur; + service_id_cur = aws_byte_cursor_from_c_str(serviceId.c_str()); } + return SendStreamStart(service_id_cur); + } + int SecureTunnel::SendStreamStart(Crt::ByteCursor serviceId) + { + struct aws_secure_tunnel_message_view messageView; + AWS_ZERO_STRUCT(messageView); + messageView.service_id = &serviceId; return aws_secure_tunnel_stream_start(m_secure_tunnel, &messageView); } - // Steve TODO int SecureTunnel::SendStreamReset() { return aws_secure_tunnel_stream_reset(m_secure_tunnel, NULL); } aws_secure_tunnel *SecureTunnel::GetUnderlyingHandle() { return m_secure_tunnel; } - void SecureTunnel::s_OnConnectionComplete(int error_code, void *user_data) + void SecureTunnel::s_OnConnectionEstablished( + const struct aws_secure_tunnel_connection_view *connection, + int error_code, + void *user_data) { auto *secureTunnel = static_cast(user_data); if (!error_code) { + /* Check for full callback */ + if (secureTunnel->m_OnConnectionEstablished) + { + std::shared_ptr packet = + std::make_shared(*connection, secureTunnel->m_allocator); + ConnectionEstablishedEventData eventData; + eventData.connectionData = packet; + secureTunnel->m_OnConnectionEstablished(*secureTunnel, error_code, eventData); + return; + } + + /* Fall back on deprecated complete callback */ if (secureTunnel->m_OnConnectionComplete) { secureTunnel->m_OnConnectionComplete(); @@ -554,12 +665,12 @@ namespace Aws void SecureTunnel::s_OnMessageReceived(const struct aws_secure_tunnel_message_view *message, void *user_data) { - AWS_LOGF_INFO(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "on message received callback"); SecureTunnel *secureTunnel = reinterpret_cast(user_data); if (secureTunnel != nullptr) { if (message != NULL) { + /* V2 Protocol API */ if (secureTunnel->m_OnMessageReceived != nullptr) { std::shared_ptr packet = @@ -570,6 +681,7 @@ namespace Aws return; } + /* V1 Protocol API */ if (secureTunnel->m_OnDataReceive != nullptr) { /* @@ -593,15 +705,29 @@ namespace Aws } } - void SecureTunnel::s_OnStreamStart( + void SecureTunnel::s_OnStreamStarted( const struct aws_secure_tunnel_message_view *message, int error_code, void *user_data) { - auto *secureTunnel = static_cast(user_data); - if (secureTunnel->m_OnStreamStart) + SecureTunnel *secureTunnel = reinterpret_cast(user_data); + if (!error_code) { - secureTunnel->m_OnStreamStart(); + if (secureTunnel->m_OnStreamStarted) + { + std::shared_ptr packet = + std::make_shared(*message, secureTunnel->m_allocator); + StreamStartedEventData eventData; + eventData.streamStartedData = packet; + secureTunnel->m_OnStreamStarted(*secureTunnel, error_code, eventData); + return; + } + + /* Fall back on deprecated stream start callback */ + if (secureTunnel->m_OnStreamStart) + { + secureTunnel->m_OnStreamStart(); + } } } From ddea3263c3e57cc2054e8ec33a7e158a95243624 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Fri, 10 Feb 2023 16:15:16 -0800 Subject: [PATCH 04/32] documentation --- .../aws/iotsecuretunneling/SecureTunnel.h | 307 +++++++++++++++++- 1 file changed, 292 insertions(+), 15 deletions(-) diff --git a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h index e078a6aff..d19b6ed9b 100644 --- a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h +++ b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h @@ -256,14 +256,33 @@ namespace Aws * connection. */ using OnStreamStarted = std::function; + + /** + * Type signature of the callback invoked when a stream is reset. + */ using OnStreamReset = std::function; + + /** + * Type signature of the callback invoked when the secure tunnel receives a Session Reset. + */ using OnSessionReset = std::function; - /* Deprecate */ + /** + * Deprecated - OnConnectionEstablished + */ using OnConnectionComplete = std::function; + /** + * Deprecated - Use OnMessageReceived + */ using OnDataReceive = std::function; + /** + * Deprecated - Use OnStreamStarted + */ using OnStreamStart = std::function; + /** + * Represents a unique configuration for a secure tunnel + */ class AWS_IOTSECURETUNNELING_API SecureTunnelBuilder final { public: @@ -288,23 +307,113 @@ namespace Aws aws_secure_tunneling_local_proxy_mode localProxyMode, const std::string &endpointHost); // Make a copy and save in this object + /* Optional members */ /** - * Optional members + * Sets rootCA to be used for this secure tunnel connection overriding the default trust store. + * + * @param rootCa string to use as rootCA for secure tunnel connection + * + * @return this builder object */ SecureTunnelBuilder &WithRootCa(const std::string &rootCa); + + /** + * Sets Client Token to a specified value rather than allowing the secure tunnel to auto-generate one. + * + * @param clientToken string to use as unique client token for secure tunnel connection + * + * @return this builder object + */ + SecureTunnelBuilder &WithClientToken(const std::string &clientToken); + + /** + * Sets http proxy options. + * + * @param httpClientConnectionProxyOptions http proxy configuration for connection establishment + * + * @return this builder object + */ SecureTunnelBuilder &WithHttpClientConnectionProxyOptions( const Crt::Http::HttpClientConnectionProxyOptions &httpClientConnectionProxyOptions); + + /** + * Setup callback handler trigged when an Secure Tunnel establishes a connection and receives available + * service ids. + * + * @param callback + * + * @return this builder object + */ SecureTunnelBuilder &WithOnConnectionEstablished(OnConnectionEstablished onConnectionEstablished); + + /** + * Setup callback handler trigged when an Secure Tunnel shuts down connection to the secure tunnel service. + * + * @param callback + * + * @return this builder object + */ SecureTunnelBuilder &WithOnConnectionShutdown(OnConnectionShutdown onConnectionShutdown); + + /** + * Setup callback handler trigged when an Secure Tunnel completes sending data to the secure tunnel service. + * + * @param callback + * + * @return this builder object + */ SecureTunnelBuilder &WithOnSendDataComplete(OnSendDataComplete onSendDataComplete); + + /** + * Setup callback handler trigged when an Secure Tunnel receives a Message through the secure tunnel + * service. + * + * @param callback + * + * @return this builder object + */ SecureTunnelBuilder &WithOnMessageReceived(OnMessageReceived onMessageReceived); + + /** + * Setup callback handler trigged when an Secure Tunnel starts a stream with a source through the secure + * tunnel service. + * + * @param callback + * + * @return this builder object + */ SecureTunnelBuilder &WithOnStreamStarted(OnStreamStarted onStreamStarted); + + /** + * Setup callback handler trigged when an Secure Tunnel receives a stream reset. + * + * @param callback + * + * @return this builder object + */ SecureTunnelBuilder &WithOnStreamReset(OnStreamReset onStreamReset); + + /** + * Setup callback handler trigged when an Secure Tunnel receives a session reset from the secure tunnel + * service. + * + * @param callback + * + * @return this builder object + */ SecureTunnelBuilder &WithOnSessionReset(OnSessionReset onSessionReset); - SecureTunnelBuilder &WithClientToken(const std::string &clientToken); - /* Deprecate */ + + /** + * Deprecated - Use WithOnMessageReceived() + */ SecureTunnelBuilder &WithOnDataReceive(OnDataReceive onDataReceive); + /** + * Deprecated - Use WithOnConnectionEstablished() + */ SecureTunnelBuilder &WithOnConnectionComplete(OnConnectionComplete onConnectionComplete); + /** + * Deprecated - Use WithOnStreamStarted + */ SecureTunnelBuilder &WithOnStreamStart(OnStreamStart onStreamStart); /** @@ -316,39 +425,111 @@ namespace Aws std::shared_ptr Build() noexcept; private: + /* Required Memebers */ + + Crt::Allocator *m_allocator; + /** - * Required members + * Client bootstrap to use. In almost all cases, this can be left undefined. */ - Crt::Allocator *m_allocator; Crt::Io::ClientBootstrap *m_clientBootstrap; + + /** + * Controls socket properties of the underlying connections made by the secure tunnel. Leave undefined to + * use defaults (no TCP keep alive, 10 second socket timeout). + */ Crt::Io::SocketOptions m_socketOptions; + + /** + * Token used to establish a WebSocket connection with the secure tunnel service. This token is one time use + * and must be rotated to establish a new connection to the secure tunnel unless using a unique client + * token. + */ std::string m_accessToken; + + /** + * Proxy mode to use. + */ aws_secure_tunneling_local_proxy_mode m_localProxyMode; + + /** + * AWS Secure Tunnel endpoint to connect to. + */ std::string m_endpointHost; + /* Optional members */ /** - * Optional members + * Client token is used to reconnect to a secure tunnel after initial connection. If this is not set by the + * user, one will be automatically generated and used to maintain a connection as long as the secure tunnel + * has the desired state of CONNECTED. */ std::string m_clientToken; + + /** + * If set, this will be used to override the default trust store. + */ std::string m_rootCa; + + /** + * If set, http proxy configuration will be used for connection establishment + */ Crt::Optional m_httpClientConnectionProxyOptions; + /* Callbacks */ /** - * Callbacks + * Callback handler trigged when secure tunnel establishes connection with secure tunnel service and + * receives available service ids. */ OnConnectionEstablished m_OnConnectionEstablished; + + /** + * Callback handler trigged when secure tunnel connection to secure tunnel service is closed. + */ OnConnectionShutdown m_OnConnectionShutdown; + + /** + * Callback handler trigged when secure tunnel completes sending data to the secure tunnel service. + */ OnSendDataComplete m_OnSendDataComplete; + + /** + * Callback handler trigged when secure tunnel receives a message from the secure tunnel service. + * + * @param SecureTunnel: The shared secure tunnel + * @param MessageReceivedEventData: Data received + */ OnMessageReceived m_OnMessageReceived; + + /** + * Callback handler trigged when secure tunnel receives a stream start from a source device. + * + * @param SecureTunnel: The shared secure tunnel + * @param int: error code + * @param StreamStartedEventData: Stream Started data + */ OnStreamStarted m_OnStreamStarted; + + /** + * Callback handler trigged when secure tunnel receives a stream reset. + */ OnStreamReset m_OnStreamReset; + + /** + * Callback handler trigged when secure tunnel receives a session reset from the secure tunnel service. + */ OnSessionReset m_OnSessionReset; - /* Depricate */ + /** + * Deprecated - Use m_OnConnectionEstablished + */ OnConnectionComplete m_OnConnectionComplete; - /* Depricate */ + /** + * Deprecated - Use m_OnMessageReceived + */ OnDataReceive m_OnDataReceive; - /* Depricate */ + /** + * Deprecated - Use m_OnStreamStarted + */ OnStreamStart m_OnStreamStart; friend class SecureTunnel; @@ -401,22 +582,82 @@ namespace Aws bool IsValid(); + /** + * Notifies the secure tunnel that you want it to attempt to connect to the configured endpoint. + * The secure tunnel will attempt to stay connected and attempt to reconnect if disconnected. + * + * @return bool: true if operation succeed, otherwise false. + */ int Start(); + + /** + * Notifies the secure tunnel that you want it to transition to the stopped state, disconnecting any + * existing connection and stopping subsequent reconnect attempts. + * + * @return bool: true if operation succeed, otherwise false + */ int Stop(); + + /** + * Tells the secure tunnel to attempt to send a Message + * + * @param messageOptions: Message to send to the secure tunnel service. + * + * @return true if the message operation succeed otherwise false + */ int SendMessage(std::shared_ptr messageOptions) noexcept; /* SOURCE MODE ONLY */ + /** + * Notifies the secure tunnel that you want to start a stream with the Destination device. This will result + * in a V1 stream. + * + * @return bool: true if operation succeed, otherwise false + */ int SendStreamStart(); + + /** + * Notifies the secure tunnel that you want to start a stream with the Destination device on a specific + * service id. This will result in a V2 stream. + * + * @param string: The Service Id to start a stream on. + * + * @return bool: true if operation succeed, otherwise false + */ int SendStreamStart(std::string serviceId); + + /** + * Notifies the secure tunnel that you want to start a stream with the Destination device on a specific + * service id. This will result in a V2 stream. + * + * @param ByteCursor: The Service Id to start a stream on. + * + * @return bool: true if operation succeed, otherwise false + */ int SendStreamStart(Crt::ByteCursor serviceId); aws_secure_tunnel *GetUnderlyingHandle(); - /* These are to be deprecated */ + /** + * Deprecated - use Start() + */ int Connect(); + + /** + * Deprecated - Use Stop() + */ int Close(); + + /** + * Deprecated - Use Stop() + */ void Shutdown(); + + /** + * Deprecated - Use SendMessage() + */ int SendData(const Crt::ByteCursor &data); + /* Should not be exposed. Under the hood only operation. */ int SendStreamReset(); @@ -448,7 +689,7 @@ namespace Aws OnStreamReset onStreamReset, OnSessionReset onSessionReset); - // aws-c-iot callbacks + /* Static Callbacks */ static void s_OnMessageReceived(const struct aws_secure_tunnel_message_view *message, void *user_data); static void s_OnConnectionEstablished( const struct aws_secure_tunnel_connection_view *connection, @@ -469,21 +710,57 @@ namespace Aws void OnTerminationComplete(); - // Client callbacks + /** + * Callback handler trigged when secure tunnel receives a Message. + */ OnMessageReceived m_OnMessageReceived; + + /** + * Callback handler trigged when secure tunnel establishes connection with the secure tunnel service and + * receives service ids. + */ OnConnectionEstablished m_OnConnectionEstablished; + + /** + * Callback handler trigged when secure tunnel shuts down connection. + */ OnConnectionShutdown m_OnConnectionShutdown; + + /** + * Callback handler trigged when secure tunnel completes sending data to the secure tunnel service. + */ OnSendDataComplete m_OnSendDataComplete; + + /** + * Callback handler trigged when secure tunnel starts a stream with a source device through the secure + * tunnel service. + */ OnStreamStarted m_OnStreamStarted; + + /** + * Callback handler trigged when secure tunnel receives a stream reset. + */ OnStreamReset m_OnStreamReset; + + /** + * Callback handler trigged when secure tunnel receives a session reset from the secure tunnel service. + */ OnSessionReset m_OnSessionReset; aws_secure_tunnel *m_secure_tunnel; Crt::Allocator *m_allocator; - /* Deprecate these callbacks */ + /** + * Deprecated - m_OnMessageReceived + */ OnDataReceive m_OnDataReceive; + /** + * Deprecated - Use m_OnConnectionEstablished + */ OnConnectionComplete m_OnConnectionComplete; + /** + * Deprecated - Use m_OnStreamStarted + */ OnStreamStart m_OnStreamStart; std::promise m_TerminationComplete; From 8d96699088b01207f385877fd1fecbd39fbf434b Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Fri, 10 Feb 2023 16:25:44 -0800 Subject: [PATCH 05/32] sample --- .../secure_tunneling/secure_tunnel/main.cpp | 297 +++++++++++------- secure_tunneling/source/SecureTunnel.cpp | 37 ++- 2 files changed, 210 insertions(+), 124 deletions(-) diff --git a/samples/secure_tunneling/secure_tunnel/main.cpp b/samples/secure_tunneling/secure_tunnel/main.cpp index 34383af14..6a5ae7705 100644 --- a/samples/secure_tunneling/secure_tunnel/main.cpp +++ b/samples/secure_tunneling/secure_tunnel/main.cpp @@ -18,10 +18,41 @@ using namespace Aws::Iotsecuretunneling; using namespace Aws::Crt::Io; using namespace std::chrono_literals; +void logMessage(std::shared_ptr message) +{ + if (message->getServiceId().has_value()) + { + if (message->getPayload().has_value()) + { + fprintf( + stdout, + "Message received on service id:'" PRInSTR "' with payload:'" PRInSTR "'\n", + AWS_BYTE_CURSOR_PRI(message->getServiceId().value()), + AWS_BYTE_CURSOR_PRI(message->getPayload().value())); + } + else + { + fprintf( + stdout, + "Message with service id:'" PRInSTR "' with no payload.\n", + AWS_BYTE_CURSOR_PRI(message->getServiceId().value())); + } + } + if (message->getPayload().has_value()) + { + fprintf( + stdout, + "Message received with payload:'" PRInSTR "'\n", + AWS_BYTE_CURSOR_PRI(message->getPayload().value())); + } +}; + int main(int argc, char *argv[]) { ApiHandle apiHandle; + aws_iotdevice_library_init(Aws::Crt::g_allocator); + String accessToken; String clientToken; aws_secure_tunneling_local_proxy_mode localProxyMode; @@ -32,6 +63,7 @@ int main(int argc, char *argv[]) String proxyPassword; std::shared_ptr secureTunnel; + std::shared_ptr connectionData; /*********************** Parse Arguments ***************************/ Utils::CommandLineUtils cmdUtils = Utils::CommandLineUtils(); @@ -47,7 +79,6 @@ int main(int argc, char *argv[]) "local_proxy_mode_source", "", "Use to set local proxy mode to source (optional, default='destination')."); cmdUtils.RegisterCommand("client_token", "", "Tunneling access token (optional if --client_token_file used)."); cmdUtils.RegisterCommand("message", "", "Message to send (optional, default='Hello World!')."); - cmdUtils.RegisterCommand("test", "", "Used to trigger internal testing (optional, ignore unless testing)."); cmdUtils.RegisterCommand( "proxy_user_name", "", "User name passed if proxy server requires a user name (optional)"); cmdUtils.RegisterCommand( @@ -108,7 +139,7 @@ int main(int argc, char *argv[]) } else { - fprintf(stderr, "Failed to open client token file"); + fprintf(stderr, "Failed to open client token file\n"); exit(-1); } } @@ -141,13 +172,7 @@ int main(int argc, char *argv[]) localProxyMode = AWS_SECURE_TUNNELING_DESTINATION_MODE; } - String message = cmdUtils.GetCommandOrDefault("message", "Hello World"); - - /* - * For internal testing - */ - bool isTest = cmdUtils.HasCommand("test"); - int expectedMessageCount = 5; + String payloadMessage = cmdUtils.GetCommandOrDefault("message", "Hello World"); if (apiHandle.GetOrCreateStaticDefaultClientBootstrap()->LastError() != AWS_ERROR_SUCCESS) { @@ -165,19 +190,59 @@ int main(int argc, char *argv[]) std::promise connectionCompletedPromise; std::promise connectionClosedPromise; - /*********************** Callbacks ***************************/ - auto OnConnectionComplete = [&]() { - switch (localProxyMode) + //*********************************************************************************************************************** + /* CALLBACKS */ + //*********************************************************************************************************************** + + auto OnConnectionEstablished = [&](SecureTunnel &, int errorCode, const ConnectionEstablishedEventData &eventData) { + if (!errorCode) { - case AWS_SECURE_TUNNELING_DESTINATION_MODE: - fprintf(stdout, "Connection Complete in Destination Mode\n"); - break; - case AWS_SECURE_TUNNELING_SOURCE_MODE: - connectionCompletedPromise.set_value(true); - fprintf(stdout, "Connection Complete in Source Mode\n"); - fprintf(stdout, "Sending Stream Start request\n"); - secureTunnel->SendStreamStart("ssh"); - break; + fprintf(stdout, "Secure Tunnel connected to Service\n"); + connectionData = eventData.connectionData; + if (connectionData->getServiceId1().has_value()) + { + fprintf( + stdout, + "Secure Tunnel Service ID 1:'" PRInSTR "'\n", + AWS_BYTE_CURSOR_PRI(connectionData->getServiceId1().value())); + if (connectionData->getServiceId2().has_value()) + { + fprintf( + stdout, + "Secure Tunnel Service ID 2:'" PRInSTR "'\n", + AWS_BYTE_CURSOR_PRI(connectionData->getServiceId2().value())); + if (connectionData->getServiceId3().has_value()) + { + fprintf( + stdout, + "Secure Tunnel Service ID 3:'" PRInSTR "'\n", + AWS_BYTE_CURSOR_PRI(connectionData->getServiceId3().value())); + } + } + + /* Stream Start can only be called from Source Mode */ + if (localProxyMode == AWS_SECURE_TUNNELING_SOURCE_MODE) + { + fprintf( + stdout, + "Sending Stream Start request with service id:'" PRInSTR "'\n", + AWS_BYTE_CURSOR_PRI(connectionData->getServiceId1().value())); + secureTunnel->SendStreamStart(connectionData->getServiceId1().value()); + } + } + else + { + fprintf(stdout, "Secure Tunnel is not using Service Ids.\n"); + + /* Stream Start can only be called from Source Mode */ + if (localProxyMode == AWS_SECURE_TUNNELING_SOURCE_MODE) + { + fprintf(stdout, "Sending Stream Start request\n"); + secureTunnel->SendStreamStart(); + } + } + + connectionCompletedPromise.set_value(true); } }; @@ -213,56 +278,81 @@ int main(int argc, char *argv[]) } }; - auto OnDataReceive = [&](const struct aws_byte_buf &data) { - String receivedData = String((char *)data.buffer, data.len); - String returnMessage = "Echo:" + receivedData; + auto OnMessageReceived = [&](SecureTunnel &, const MessageReceivedEventData &eventData) { + { + std::shared_ptr message = eventData.message; - fprintf(stdout, "Received: \"%s\"\n", receivedData.c_str()); + logMessage(message); + std::shared_ptr echoMessage; - switch (localProxyMode) - { - case AWS_SECURE_TUNNELING_DESTINATION_MODE: - fprintf(stdout, "Data Receive Complete in Destination\n"); - fprintf(stdout, "Sending response message:\"%s\"\n", returnMessage.c_str()); - secureTunnel->SendData("ssh", ByteCursorFromCString(returnMessage.c_str())); - if (isTest) - { - expectedMessageCount--; - if (expectedMessageCount <= 0) + switch (localProxyMode) + { + case AWS_SECURE_TUNNELING_DESTINATION_MODE: + + echoMessage = std::make_shared(message->getPayload().value()); + + /* Echo message on same service id received message came on */ + if (message->getServiceId().has_value()) { - exit(0); + echoMessage->withServiceId(message->getServiceId().value()); } - } - break; - case AWS_SECURE_TUNNELING_SOURCE_MODE: - fprintf(stdout, "Data Receive Complete in Source\n"); - break; + + secureTunnel->SendMessage(echoMessage); + + fprintf(stdout, "Sending Echo Message\n"); + + break; + case AWS_SECURE_TUNNELING_SOURCE_MODE: + + break; + } } }; - /* - * This only fires in Destination Mode - */ - auto OnStreamStart = [&]() { fprintf(stdout, "Stream Started in Destination Mode\n"); }; + auto OnStreamStarted = [&](SecureTunnel &, int errorCode, const StreamStartedEventData &eventData) { + if (!errorCode) + { + std::shared_ptr streamStartedData = eventData.streamStartedData; + if (streamStartedData->getServiceId().has_value()) + { + fprintf( + stdout, + "Stream started on service id: '" PRInSTR "'\n", + AWS_BYTE_CURSOR_PRI(streamStartedData->getServiceId().value())); + } + else + { + fprintf(stdout, "Stream started using V1 Protocol"); + } + } + }; auto OnStreamReset = [&]() { fprintf(stdout, "Stream Reset\n"); }; auto OnSessionReset = [&]() { fprintf(stdout, "Session Reset\n"); }; - /*********************** Proxy Connection Setup ***************************/ - /* - * Setup HttpClientCommectionProxyOptions for connecting through a proxy before the Secure Tunnel - */ - + /* Use SecureTunnelBuilder to create a new Secure Tunnel */ + SecureTunnelBuilder builder = + SecureTunnelBuilder( + Aws::Crt::g_allocator, SocketOptions(), accessToken.c_str(), localProxyMode, endpoint.c_str()) + .WithRootCa(caFile.c_str()) + .WithOnConnectionEstablished(OnConnectionEstablished) + .WithOnConnectionShutdown(OnConnectionShutdown) + .WithOnSendDataComplete(OnSendDataComplete) + .WithOnMessageReceived(OnMessageReceived) + .WithOnStreamStarted(OnStreamStarted) + .WithOnStreamReset(OnStreamReset) + .WithOnSessionReset(OnSessionReset) + .WithClientToken(clientToken.c_str()); + + /* Setup HttpClientCommectionProxyOptions if connecting through a proxy */ if (proxyHost.length() > 0) { auto proxyOptions = Aws::Crt::Http::HttpClientConnectionProxyOptions(); proxyOptions.HostName = proxyHost.c_str(); proxyOptions.Port = proxyPort; - /* - * Set up Proxy Strategy if a user name and password is provided - */ + /* Set up Proxy Strategy if a user name and password is provided */ if (proxyUserName.length() > 0 || proxyPassword.length() > 0) { fprintf(stdout, "Creating proxy strategy\n"); @@ -279,88 +369,77 @@ int main(int argc, char *argv[]) proxyOptions.AuthType = Aws::Crt::Http::AwsHttpProxyAuthenticationType::None; } - /*********************** Secure Tunnel Setup ***************************/ - /* - * Create a new SecureTunnel using the SecureTunnelBuilder - */ - secureTunnel = - SecureTunnelBuilder( - Aws::Crt::g_allocator, SocketOptions(), accessToken.c_str(), localProxyMode, endpoint.c_str()) - .WithRootCa(caFile.c_str()) - .WithHttpClientConnectionProxyOptions(proxyOptions) - .WithOnConnectionComplete(OnConnectionComplete) - .WithOnConnectionShutdown(OnConnectionShutdown) - .WithOnSendDataComplete(OnSendDataComplete) - .WithOnDataReceive(OnDataReceive) - .WithOnStreamStart(OnStreamStart) - .WithOnStreamReset(OnStreamReset) - .WithOnSessionReset(OnSessionReset) - .Build(); - } - else - { - /*********************** Secure Tunnel Setup ***************************/ - /* - * Create a new SecureTunnel using the SecureTunnelBuilder - */ - secureTunnel = - SecureTunnelBuilder( - Aws::Crt::g_allocator, SocketOptions(), accessToken.c_str(), localProxyMode, endpoint.c_str()) - .WithRootCa(caFile.c_str()) - .WithOnConnectionComplete(OnConnectionComplete) - .WithOnConnectionShutdown(OnConnectionShutdown) - .WithOnSendDataComplete(OnSendDataComplete) - .WithOnDataReceive(OnDataReceive) - .WithOnStreamStart(OnStreamStart) - .WithOnStreamReset(OnStreamReset) - .WithOnSessionReset(OnSessionReset) - .WithClientToken(clientToken.c_str()) - .Build(); + /* Add proxy options to the builder */ + builder.WithHttpClientConnectionProxyOptions(proxyOptions); } + /* Create Secure Tunnel using the options set with the builder */ + secureTunnel = builder.Build(); + if (!secureTunnel) { fprintf(stderr, "Secure Tunnel Creation failed: %s\n", ErrorDebugString(LastError())); exit(-1); } - if (secureTunnel->Connect() == AWS_OP_ERR) + /* Set the Secure Tunnel to desire a connected state */ + if (secureTunnel->Start()) { fprintf(stderr, "Secure Tunnel Connect call failed: %s\n", ErrorDebugString(LastError())); exit(-1); } + int messageCount = 0; + bool keepRunning = true; if (connectionCompletedPromise.get_future().get()) { - while (true) - { - std::this_thread::sleep_for(3000ms); + std::this_thread::sleep_for(1000ms); + while (keepRunning) + { if (localProxyMode == AWS_SECURE_TUNNELING_SOURCE_MODE) { - String toSend = (std::to_string(messageCount) + ": " + message.c_str()).c_str(); - if (messageCount < 5 && !secureTunnel->SendData("ssh", ByteCursorFromCString(toSend.c_str()))) + messageCount++; + String toSend = (std::to_string(messageCount) + ": " + payloadMessage.c_str()).c_str(); + + if (messageCount < 5) { - messageCount++; - fprintf(stdout, "Sending Message:\"%s\"\n", toSend.c_str()); - if (messageCount >= 5) + std::shared_ptr message = std::make_shared(ByteCursorFromCString(toSend.c_str())); + + if (connectionData->getServiceId1().has_value()) { - fprintf(stdout, "Closing Connection\n"); - if (secureTunnel->Close() == AWS_OP_ERR) - { - fprintf(stderr, "Secure Tunnel Close call failed: %s\n", ErrorDebugString(LastError())); - exit(-1); - } + message->withServiceId(connectionData->getServiceId1().value()); } + + secureTunnel->SendMessage(message); + + fprintf(stdout, "Sending Message:\"%s\"\n", toSend.c_str()); + + std::this_thread::sleep_for(5000ms); } - else if (connectionClosedPromise.get_future().get()) + else { - fprintf(stdout, "Sample Complete"); - - exit(0); + keepRunning = false; } } } } + + std::this_thread::sleep_for(3000ms); + fprintf(stdout, "Closing Connection\n"); + + if (secureTunnel->Close() == AWS_OP_ERR) + { + fprintf(stderr, "Secure Tunnel Close call failed: %s\n", ErrorDebugString(LastError())); + exit(-1); + } + + if (connectionClosedPromise.get_future().get()) + { + fprintf(stdout, "Sample Complete\n"); + exit(0); + } + + exit(-1); } diff --git a/secure_tunneling/source/SecureTunnel.cpp b/secure_tunneling/source/SecureTunnel.cpp index 023c7ddc1..0075389b6 100644 --- a/secure_tunneling/source/SecureTunnel.cpp +++ b/secure_tunneling/source/SecureTunnel.cpp @@ -283,19 +283,19 @@ namespace Aws return *this; } - /* Deprecate */ + /* Deprecated - Use WithOnConnectionEstablished */ SecureTunnelBuilder &SecureTunnelBuilder::WithOnConnectionComplete(OnConnectionComplete onConnectionComplete) { m_OnConnectionComplete = std::move(onConnectionComplete); return *this; } - /* Deprecate */ + /* Deprecated - Use WithOnStreamStarted */ SecureTunnelBuilder &SecureTunnelBuilder::WithOnStreamStart(OnStreamStart onStreamStart) { m_OnStreamStart = std::move(onStreamStart); return *this; } - /* Deprecate */ + /* Deprecated - Use WithOnMessageReceived */ SecureTunnelBuilder &SecureTunnelBuilder::WithOnDataReceive(OnDataReceive onDataReceive) { m_OnDataReceive = std::move(onDataReceive); @@ -423,8 +423,7 @@ namespace Aws } /** - * Should be deprecated when possible. - * SecureTunnelBuilder::Build() should be used to generate new SecureTunnels + * Deprecated - Use SecureTunnelBuilder */ SecureTunnel::SecureTunnel( Crt::Allocator *allocator, @@ -467,8 +466,7 @@ namespace Aws } /** - * Should be deprecated when possible. - * SecureTunnelBuilder::Build() should be used to generate new SecureTunnels + * Deprecated - Use SecureTunnelBuilder */ SecureTunnel::SecureTunnel( Crt::Allocator *allocator, @@ -526,9 +524,12 @@ namespace Aws m_OnStreamStarted = std::move(other.m_OnStreamStarted); m_OnStreamReset = std::move(other.m_OnStreamReset); m_OnSessionReset = std::move(other.m_OnSessionReset); - /* Deprecate */ + + /* Deprecated - Use m_OnConnectionEstablished */ m_OnConnectionComplete = std::move(other.m_OnConnectionComplete); + /* Deprecated - Use m_OnMessageReceived */ m_OnDataReceive = std::move(other.m_OnDataReceive); + /* Deprecated - Use m_OnStreamStarted */ m_OnStreamStart = std::move(other.m_OnStreamStart); m_TerminationComplete = std::move(other.m_TerminationComplete); @@ -551,9 +552,12 @@ namespace Aws m_OnStreamStarted = std::move(other.m_OnStreamStarted); m_OnStreamReset = std::move(other.m_OnStreamReset); m_OnSessionReset = std::move(other.m_OnSessionReset); - /* Deprecate */ + + /* Deprecated - Use m_OnConnectionEstablished */ m_OnConnectionComplete = std::move(other.m_OnConnectionComplete); + /* Deprecated - Use m_OnMessageReceived */ m_OnDataReceive = std::move(other.m_OnDataReceive); + /* Deprecated - Use m_OnStreamStarted */ m_OnStreamStart = std::move(other.m_OnStreamStart); m_TerminationComplete = std::move(other.m_TerminationComplete); @@ -569,13 +573,16 @@ namespace Aws bool SecureTunnel::IsValid() { return m_secure_tunnel ? true : false; } int SecureTunnel::Start() { return aws_secure_tunnel_start(m_secure_tunnel); } + int SecureTunnel::Stop() { return aws_secure_tunnel_stop(m_secure_tunnel); } - /* Deprecate */ - int SecureTunnel::Connect() { return aws_secure_tunnel_start(m_secure_tunnel); } - /* Deprecate */ - int SecureTunnel::Close() { return aws_secure_tunnel_stop(m_secure_tunnel); } - /* Deprecate */ + /* Deprecated - Use Start() */ + int SecureTunnel::Connect() { return Start(); } + + /* Deprecated - Use Stop() */ + int SecureTunnel::Close() { return Stop(); } + + /* Deprecated - Use SendMessage() */ int SecureTunnel::SendData(const Crt::ByteCursor &data) { // return SendData("", data); @@ -762,7 +769,7 @@ namespace Aws void SecureTunnel::Shutdown() { - Close(); + Stop(); aws_secure_tunnel_release(m_secure_tunnel); m_secure_tunnel = nullptr; From ed5f65a97944c498b8f62ea0ad5eaa1c85d9d819 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 13 Feb 2023 09:59:58 -0800 Subject: [PATCH 06/32] Add onConnectionFailure callbacks --- .../secure_tunnel/build_sample.py | 5 +- .../secure_tunneling/secure_tunnel/main.cpp | 73 +++++++++---------- .../aws/iotsecuretunneling/SecureTunnel.h | 55 ++++++++++---- secure_tunneling/source/SecureTunnel.cpp | 52 +++++++++---- 4 files changed, 116 insertions(+), 69 deletions(-) diff --git a/samples/secure_tunneling/secure_tunnel/build_sample.py b/samples/secure_tunneling/secure_tunnel/build_sample.py index 0f097d322..a123c3cc0 100644 --- a/samples/secure_tunneling/secure_tunnel/build_sample.py +++ b/samples/secure_tunneling/secure_tunnel/build_sample.py @@ -27,5 +27,6 @@ def check_call(args): '-DCMAKE_BUILD_TYPE="Debug"', '..']) check_call(['cmake', '--build', '.', '--config', 'Debug']) -cmake - DCMAKE_PREFIX_PATH = "/Users/sbstevek/workplace/secure-tunnel-multiplexing/installs" - DCMAKE_BUILD_TYPE = "Debug" .. -cmake - -build . --config Debug + +# "cmake -DCMAKE_PREFIX_PATH="/Users/sbstevek/workplace/secure-tunnel-multiplexing/installs" -DCMAKE_BUILD_TYPE="Debug" .." +# "cmake --build . --config Debug" diff --git a/samples/secure_tunneling/secure_tunnel/main.cpp b/samples/secure_tunneling/secure_tunnel/main.cpp index 6a5ae7705..704f4d224 100644 --- a/samples/secure_tunneling/secure_tunnel/main.cpp +++ b/samples/secure_tunneling/secure_tunnel/main.cpp @@ -194,56 +194,53 @@ int main(int argc, char *argv[]) /* CALLBACKS */ //*********************************************************************************************************************** - auto OnConnectionEstablished = [&](SecureTunnel &, int errorCode, const ConnectionEstablishedEventData &eventData) { - if (!errorCode) + auto OnConnectionSuccess = [&](SecureTunnel &, const ConnectionSuccessEventData &eventData) { + fprintf(stdout, "Secure Tunnel connected to Service\n"); + connectionData = eventData.connectionData; + if (connectionData->getServiceId1().has_value()) { - fprintf(stdout, "Secure Tunnel connected to Service\n"); - connectionData = eventData.connectionData; - if (connectionData->getServiceId1().has_value()) + fprintf( + stdout, + "Secure Tunnel Service ID 1:'" PRInSTR "'\n", + AWS_BYTE_CURSOR_PRI(connectionData->getServiceId1().value())); + if (connectionData->getServiceId2().has_value()) { fprintf( stdout, - "Secure Tunnel Service ID 1:'" PRInSTR "'\n", - AWS_BYTE_CURSOR_PRI(connectionData->getServiceId1().value())); - if (connectionData->getServiceId2().has_value()) - { - fprintf( - stdout, - "Secure Tunnel Service ID 2:'" PRInSTR "'\n", - AWS_BYTE_CURSOR_PRI(connectionData->getServiceId2().value())); - if (connectionData->getServiceId3().has_value()) - { - fprintf( - stdout, - "Secure Tunnel Service ID 3:'" PRInSTR "'\n", - AWS_BYTE_CURSOR_PRI(connectionData->getServiceId3().value())); - } - } - - /* Stream Start can only be called from Source Mode */ - if (localProxyMode == AWS_SECURE_TUNNELING_SOURCE_MODE) + "Secure Tunnel Service ID 2:'" PRInSTR "'\n", + AWS_BYTE_CURSOR_PRI(connectionData->getServiceId2().value())); + if (connectionData->getServiceId3().has_value()) { fprintf( stdout, - "Sending Stream Start request with service id:'" PRInSTR "'\n", - AWS_BYTE_CURSOR_PRI(connectionData->getServiceId1().value())); - secureTunnel->SendStreamStart(connectionData->getServiceId1().value()); + "Secure Tunnel Service ID 3:'" PRInSTR "'\n", + AWS_BYTE_CURSOR_PRI(connectionData->getServiceId3().value())); } } - else - { - fprintf(stdout, "Secure Tunnel is not using Service Ids.\n"); - /* Stream Start can only be called from Source Mode */ - if (localProxyMode == AWS_SECURE_TUNNELING_SOURCE_MODE) - { - fprintf(stdout, "Sending Stream Start request\n"); - secureTunnel->SendStreamStart(); - } + /* Stream Start can only be called from Source Mode */ + if (localProxyMode == AWS_SECURE_TUNNELING_SOURCE_MODE) + { + fprintf( + stdout, + "Sending Stream Start request with service id:'" PRInSTR "'\n", + AWS_BYTE_CURSOR_PRI(connectionData->getServiceId1().value())); + secureTunnel->SendStreamStart(connectionData->getServiceId1().value()); } + } + else + { + fprintf(stdout, "Secure Tunnel is not using Service Ids.\n"); - connectionCompletedPromise.set_value(true); + /* Stream Start can only be called from Source Mode */ + if (localProxyMode == AWS_SECURE_TUNNELING_SOURCE_MODE) + { + fprintf(stdout, "Sending Stream Start request\n"); + secureTunnel->SendStreamStart(); + } } + + connectionCompletedPromise.set_value(true); }; auto OnConnectionShutdown = [&]() { @@ -336,7 +333,7 @@ int main(int argc, char *argv[]) SecureTunnelBuilder( Aws::Crt::g_allocator, SocketOptions(), accessToken.c_str(), localProxyMode, endpoint.c_str()) .WithRootCa(caFile.c_str()) - .WithOnConnectionEstablished(OnConnectionEstablished) + .WithOnConnectionSuccess(OnConnectionSuccess) .WithOnConnectionShutdown(OnConnectionShutdown) .WithOnSendDataComplete(OnSendDataComplete) .WithOnMessageReceived(OnMessageReceived) diff --git a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h index d19b6ed9b..f35369fd4 100644 --- a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h +++ b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h @@ -171,9 +171,9 @@ namespace Aws /** * The data returned when a connection with secure tunnel service is established. */ - struct AWS_IOTSECURETUNNELING_API ConnectionEstablishedEventData + struct AWS_IOTSECURETUNNELING_API ConnectionSuccessEventData { - ConnectionEstablishedEventData() : connectionData(nullptr) {} + ConnectionSuccessEventData() : connectionData(nullptr) {} std::shared_ptr connectionData; }; @@ -234,8 +234,14 @@ namespace Aws * Type signature of the callback invoked when connection is established with the secure tunnel service and * available service ids are returned. */ - using OnConnectionEstablished = - std::function; + using OnConnectionSuccess = std::function; + + /** + * Type signature of the callback invoked when connection is established with the secure tunnel service and + * available service ids are returned. + */ + using OnConnectionFailure = std::function; + /** * Type signature of the callback invoked when connection is shutdown. */ @@ -268,7 +274,7 @@ namespace Aws using OnSessionReset = std::function; /** - * Deprecated - OnConnectionEstablished + * Deprecated - OnConnectionSuccess and OnConnectionFailure */ using OnConnectionComplete = std::function; /** @@ -344,7 +350,16 @@ namespace Aws * * @return this builder object */ - SecureTunnelBuilder &WithOnConnectionEstablished(OnConnectionEstablished onConnectionEstablished); + SecureTunnelBuilder &WithOnConnectionSuccess(OnConnectionSuccess onConnectionSuccess); + + /** + * Setup callback handler trigged when an Secure Tunnel fails a connection attempt. + * + * @param callback + * + * @return this builder object + */ + SecureTunnelBuilder &WithOnConnectionFailure(OnConnectionFailure onConnectionFailure); /** * Setup callback handler trigged when an Secure Tunnel shuts down connection to the secure tunnel service. @@ -408,7 +423,7 @@ namespace Aws */ SecureTunnelBuilder &WithOnDataReceive(OnDataReceive onDataReceive); /** - * Deprecated - Use WithOnConnectionEstablished() + * Deprecated - Use WithOnConnectionSuccess() and WithOnConnectionFailure() */ SecureTunnelBuilder &WithOnConnectionComplete(OnConnectionComplete onConnectionComplete); /** @@ -480,7 +495,14 @@ namespace Aws * Callback handler trigged when secure tunnel establishes connection with secure tunnel service and * receives available service ids. */ - OnConnectionEstablished m_OnConnectionEstablished; + OnConnectionSuccess m_OnConnectionSuccess; + + /* Callbacks */ + /** + * Callback handler trigged when secure tunnel establishes fails a connection attempt with secure tunnel + * service. + */ + OnConnectionFailure m_OnConnectionFailure; /** * Callback handler trigged when secure tunnel connection to secure tunnel service is closed. @@ -520,7 +542,7 @@ namespace Aws OnSessionReset m_OnSessionReset; /** - * Deprecated - Use m_OnConnectionEstablished + * Deprecated - Use m_OnConnectionSuccess and m_OnConnectionFailure */ OnConnectionComplete m_OnConnectionComplete; /** @@ -678,7 +700,8 @@ namespace Aws const std::string &rootCa, Crt::Http::HttpClientConnectionProxyOptions *httpClientConnectionProxyOptions, - OnConnectionEstablished onConnectionEstablished, + OnConnectionSuccess onConnectionSuccess, + OnConnectionFailure onConnectionFailure, OnConnectionComplete onConnectionComplete, OnConnectionShutdown onConnectionShutdown, OnSendDataComplete onSendDataComplete, @@ -691,10 +714,11 @@ namespace Aws /* Static Callbacks */ static void s_OnMessageReceived(const struct aws_secure_tunnel_message_view *message, void *user_data); - static void s_OnConnectionEstablished( + static void s_OnConnectionComplete( const struct aws_secure_tunnel_connection_view *connection, int error_code, void *user_data); + static void s_OnConnectionFailure(int error_code, void *user_data); static void s_OnConnectionShutdown(int error_code, void *user_data); static void s_OnSendDataComplete(int error_code, void *user_data); static void s_OnStreamReset( @@ -719,7 +743,12 @@ namespace Aws * Callback handler trigged when secure tunnel establishes connection with the secure tunnel service and * receives service ids. */ - OnConnectionEstablished m_OnConnectionEstablished; + OnConnectionSuccess m_OnConnectionSuccess; + + /** + * Callback handler trigged when secure tunnel fails a connection attempt with the secure tunnel service. + */ + OnConnectionFailure m_OnConnectionFailure; /** * Callback handler trigged when secure tunnel shuts down connection. @@ -755,7 +784,7 @@ namespace Aws */ OnDataReceive m_OnDataReceive; /** - * Deprecated - Use m_OnConnectionEstablished + * Deprecated - Use m_OnConnectionSuccess and m_OnConnectionFailure */ OnConnectionComplete m_OnConnectionComplete; /** diff --git a/secure_tunneling/source/SecureTunnel.cpp b/secure_tunneling/source/SecureTunnel.cpp index 0075389b6..eddd67a1e 100644 --- a/secure_tunneling/source/SecureTunnel.cpp +++ b/secure_tunneling/source/SecureTunnel.cpp @@ -234,10 +234,15 @@ namespace Aws return *this; } - SecureTunnelBuilder &SecureTunnelBuilder::WithOnConnectionEstablished( - OnConnectionEstablished onConnectionEstablished) + SecureTunnelBuilder &SecureTunnelBuilder::WithOnConnectionSuccess(OnConnectionSuccess onConnectionSuccess) { - m_OnConnectionEstablished = std::move(onConnectionEstablished); + m_OnConnectionSuccess = std::move(onConnectionSuccess); + return *this; + } + + SecureTunnelBuilder &SecureTunnelBuilder::WithOnConnectionFailure(OnConnectionFailure onConnectionFailure) + { + m_OnConnectionFailure = std::move(onConnectionFailure); return *this; } @@ -283,7 +288,7 @@ namespace Aws return *this; } - /* Deprecated - Use WithOnConnectionEstablished */ + /* Deprecated - Use WithOnConnectionSuccess and WithOnConnectionFailure */ SecureTunnelBuilder &SecureTunnelBuilder::WithOnConnectionComplete(OnConnectionComplete onConnectionComplete) { m_OnConnectionComplete = std::move(onConnectionComplete); @@ -314,7 +319,8 @@ namespace Aws m_endpointHost, m_rootCa, m_httpClientConnectionProxyOptions.has_value() ? &m_httpClientConnectionProxyOptions.value() : nullptr, - m_OnConnectionEstablished, + m_OnConnectionSuccess, + m_OnConnectionFailure, m_OnConnectionComplete, m_OnConnectionShutdown, m_OnSendDataComplete, @@ -353,7 +359,8 @@ namespace Aws const std::string &rootCa, Aws::Crt::Http::HttpClientConnectionProxyOptions *httpClientConnectionProxyOptions, - OnConnectionEstablished onConnectionEstablished, + OnConnectionSuccess onConnectionSuccess, + OnConnectionFailure onConnectionFailure, OnConnectionComplete onConnectionComplete, OnConnectionShutdown onConnectionShutdown, OnSendDataComplete onSendDataComplete, @@ -365,7 +372,8 @@ namespace Aws OnSessionReset onSessionReset) { // Client callbacks - m_OnConnectionEstablished = std::move(onConnectionEstablished); + m_OnConnectionSuccess = std::move(onConnectionSuccess); + m_OnConnectionFailure = std::move(onConnectionFailure); m_OnConnectionComplete = std::move(onConnectionComplete); m_OnConnectionShutdown = std::move(onConnectionShutdown); m_OnSendDataComplete = std::move(onSendDataComplete); @@ -399,7 +407,7 @@ namespace Aws /* callbacks for native secure tunnel */ config.on_message_received = s_OnMessageReceived; - config.on_connection_complete = s_OnConnectionEstablished; + config.on_connection_complete = s_OnConnectionComplete; config.on_connection_shutdown = s_OnConnectionShutdown; config.on_send_data_complete = s_OnSendDataComplete; config.on_stream_start = s_OnStreamStarted; @@ -453,6 +461,7 @@ namespace Aws rootCa, nullptr, nullptr, + nullptr, onConnectionComplete, onConnectionShutdown, onSendDataComplete, @@ -495,6 +504,7 @@ namespace Aws rootCa, nullptr, nullptr, + nullptr, onConnectionComplete, onConnectionShutdown, onSendDataComplete, @@ -517,7 +527,8 @@ namespace Aws SecureTunnel::SecureTunnel(SecureTunnel &&other) noexcept { - m_OnConnectionEstablished = std::move(other.m_OnConnectionEstablished); + m_OnConnectionSuccess = std::move(other.m_OnConnectionSuccess); + m_OnConnectionFailure = std::move(other.m_OnConnectionFailure); m_OnConnectionShutdown = std::move(other.m_OnConnectionShutdown); m_OnSendDataComplete = std::move(other.m_OnSendDataComplete); m_OnMessageReceived = std::move(other.m_OnMessageReceived); @@ -525,7 +536,7 @@ namespace Aws m_OnStreamReset = std::move(other.m_OnStreamReset); m_OnSessionReset = std::move(other.m_OnSessionReset); - /* Deprecated - Use m_OnConnectionEstablished */ + /* Deprecated - Use m_OnConnectionSuccess and m_OnConnectionFailure */ m_OnConnectionComplete = std::move(other.m_OnConnectionComplete); /* Deprecated - Use m_OnMessageReceived */ m_OnDataReceive = std::move(other.m_OnDataReceive); @@ -545,7 +556,8 @@ namespace Aws { this->~SecureTunnel(); - m_OnConnectionEstablished = std::move(other.m_OnConnectionEstablished); + m_OnConnectionSuccess = std::move(other.m_OnConnectionSuccess); + m_OnConnectionFailure = std::move(other.m_OnConnectionFailure); m_OnConnectionShutdown = std::move(other.m_OnConnectionShutdown); m_OnSendDataComplete = std::move(other.m_OnSendDataComplete); m_OnMessageReceived = std::move(other.m_OnMessageReceived); @@ -553,7 +565,7 @@ namespace Aws m_OnStreamReset = std::move(other.m_OnStreamReset); m_OnSessionReset = std::move(other.m_OnSessionReset); - /* Deprecated - Use m_OnConnectionEstablished */ + /* Deprecated - Use m_OnConnectionSuccess and m_OnConnectionFailure */ m_OnConnectionComplete = std::move(other.m_OnConnectionComplete); /* Deprecated - Use m_OnMessageReceived */ m_OnDataReceive = std::move(other.m_OnDataReceive); @@ -625,22 +637,23 @@ namespace Aws aws_secure_tunnel *SecureTunnel::GetUnderlyingHandle() { return m_secure_tunnel; } - void SecureTunnel::s_OnConnectionEstablished( + void SecureTunnel::s_OnConnectionComplete( const struct aws_secure_tunnel_connection_view *connection, int error_code, void *user_data) { auto *secureTunnel = static_cast(user_data); + if (!error_code) { /* Check for full callback */ - if (secureTunnel->m_OnConnectionEstablished) + if (secureTunnel->m_OnConnectionSuccess) { std::shared_ptr packet = std::make_shared(*connection, secureTunnel->m_allocator); - ConnectionEstablishedEventData eventData; + ConnectionSuccessEventData eventData; eventData.connectionData = packet; - secureTunnel->m_OnConnectionEstablished(*secureTunnel, error_code, eventData); + secureTunnel->m_OnConnectionSuccess(*secureTunnel, eventData); return; } @@ -650,6 +663,13 @@ namespace Aws secureTunnel->m_OnConnectionComplete(); } } + else + { + if (secureTunnel->m_OnConnectionFailure) + { + secureTunnel->m_OnConnectionFailure(*secureTunnel, error_code); + } + } } void SecureTunnel::s_OnConnectionShutdown(int error_code, void *user_data) From d17dbff6aa541e6416a05dc61ae23cf88c536ec0 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Tue, 14 Feb 2023 15:20:32 -0800 Subject: [PATCH 07/32] added stopped callback --- .../secure_tunneling/secure_tunnel/main.cpp | 166 ++++++++---------- .../aws/iotsecuretunneling/SecureTunnel.h | 57 +++++- secure_tunneling/source/SecureTunnel.cpp | 74 ++++---- 3 files changed, 163 insertions(+), 134 deletions(-) diff --git a/samples/secure_tunneling/secure_tunnel/main.cpp b/samples/secure_tunneling/secure_tunnel/main.cpp index 704f4d224..3d2305747 100644 --- a/samples/secure_tunneling/secure_tunnel/main.cpp +++ b/samples/secure_tunneling/secure_tunnel/main.cpp @@ -49,6 +49,10 @@ void logMessage(std::shared_ptr message) int main(int argc, char *argv[]) { + /************************ Setup the Lib ****************************/ + /* + * Do the global initialization for the API. + */ ApiHandle apiHandle; aws_iotdevice_library_init(Aws::Crt::g_allocator); @@ -62,7 +66,14 @@ int main(int argc, char *argv[]) String proxyUserName; String proxyPassword; - std::shared_ptr secureTunnel; + /* + * In a real world application you probably don't want to enforce synchronous behavior + * but this is a sample console application, so we'll just do that with a condition variable. + */ + std::promise connectionCompletedPromise; + std::promise connectionClosedPromise; + std::promise clientStoppedPromise; + std::shared_ptr connectionData; /*********************** Parse Arguments ***************************/ @@ -183,40 +194,34 @@ int main(int argc, char *argv[]) exit(-1); } - /* - * In a real world application you probably don't want to enforce synchronous behavior - * but this is a sample console application, so we'll just do that with a condition variable. - */ - std::promise connectionCompletedPromise; - std::promise connectionClosedPromise; + SecureTunnelBuilder builder = SecureTunnelBuilder( + Aws::Crt::g_allocator, SocketOptions(), accessToken.c_str(), localProxyMode, endpoint.c_str()); - //*********************************************************************************************************************** - /* CALLBACKS */ - //*********************************************************************************************************************** + builder.WithRootCa(caFile.c_str()); + + /* The client token is optional and will be generated automatically for reconnects. */ + builder.WithClientToken(clientToken.c_str()); + + /* Add callbacks using the builder */ - auto OnConnectionSuccess = [&](SecureTunnel &, const ConnectionSuccessEventData &eventData) { - fprintf(stdout, "Secure Tunnel connected to Service\n"); + builder.WithOnConnectionSuccess([&](SecureTunnel *secureTunnel, const ConnectionSuccessEventData &eventData) { connectionData = eventData.connectionData; + if (connectionData->getServiceId1().has_value()) { fprintf( stdout, - "Secure Tunnel Service ID 1:'" PRInSTR "'\n", + "Secure Tunnel connected with Service IDs '" PRInSTR "'", AWS_BYTE_CURSOR_PRI(connectionData->getServiceId1().value())); if (connectionData->getServiceId2().has_value()) { - fprintf( - stdout, - "Secure Tunnel Service ID 2:'" PRInSTR "'\n", - AWS_BYTE_CURSOR_PRI(connectionData->getServiceId2().value())); + fprintf(stdout, ", '" PRInSTR "'", AWS_BYTE_CURSOR_PRI(connectionData->getServiceId2().value())); if (connectionData->getServiceId3().has_value()) { - fprintf( - stdout, - "Secure Tunnel Service ID 3:'" PRInSTR "'\n", - AWS_BYTE_CURSOR_PRI(connectionData->getServiceId3().value())); + fprintf(stdout, ", '" PRInSTR "'", AWS_BYTE_CURSOR_PRI(connectionData->getServiceId3().value())); } } + fprintf(stdout, "\n"); /* Stream Start can only be called from Source Mode */ if (localProxyMode == AWS_SECURE_TUNNELING_SOURCE_MODE) @@ -241,41 +246,18 @@ int main(int argc, char *argv[]) } connectionCompletedPromise.set_value(true); - }; + }); + + builder.WithOnConnectionFailure([&](SecureTunnel *secureTunnel, int errorCode) { + fprintf(stdout, "Connection attempt failed with error code %d(%s)\n", errorCode, ErrorDebugString(errorCode)); + }); - auto OnConnectionShutdown = [&]() { + builder.WithOnConnectionShutdown([&]() { fprintf(stdout, "Connection Shutdown\n"); connectionClosedPromise.set_value(true); - }; - - auto OnSendDataComplete = [&](int error_code) { - switch (localProxyMode) - { - case AWS_SECURE_TUNNELING_DESTINATION_MODE: - if (!error_code) - { - fprintf(stdout, "Send Data Complete in Destination Mode\n"); - } - else - { - fprintf(stderr, "Send Data Failed: %s\n", ErrorDebugString(error_code)); - } + }); - break; - case AWS_SECURE_TUNNELING_SOURCE_MODE: - if (!error_code) - { - fprintf(stdout, "Send Data Complete in Source Mode\n"); - } - else - { - fprintf(stderr, "Send Data Failed: %s\n", ErrorDebugString(error_code)); - } - break; - } - }; - - auto OnMessageReceived = [&](SecureTunnel &, const MessageReceivedEventData &eventData) { + builder.WithOnMessageReceived([&](SecureTunnel *secureTunnel, const MessageReceivedEventData &eventData) { { std::shared_ptr message = eventData.message; @@ -304,45 +286,35 @@ int main(int argc, char *argv[]) break; } } - }; + }); - auto OnStreamStarted = [&](SecureTunnel &, int errorCode, const StreamStartedEventData &eventData) { - if (!errorCode) - { - std::shared_ptr streamStartedData = eventData.streamStartedData; - if (streamStartedData->getServiceId().has_value()) + builder.WithOnStreamStarted( + [&](SecureTunnel *secureTunnel, int errorCode, const StreamStartedEventData &eventData) { + if (!errorCode) { - fprintf( - stdout, - "Stream started on service id: '" PRInSTR "'\n", - AWS_BYTE_CURSOR_PRI(streamStartedData->getServiceId().value())); - } - else - { - fprintf(stdout, "Stream started using V1 Protocol"); + std::shared_ptr streamStartedData = eventData.streamStartedData; + if (streamStartedData->getServiceId().has_value()) + { + fprintf( + stdout, + "Stream started on service id: '" PRInSTR "'\n", + AWS_BYTE_CURSOR_PRI(streamStartedData->getServiceId().value())); + } + else + { + fprintf(stdout, "Stream started using V1 Protocol"); + } } - } - }; - - auto OnStreamReset = [&]() { fprintf(stdout, "Stream Reset\n"); }; - - auto OnSessionReset = [&]() { fprintf(stdout, "Session Reset\n"); }; - - /* Use SecureTunnelBuilder to create a new Secure Tunnel */ - SecureTunnelBuilder builder = - SecureTunnelBuilder( - Aws::Crt::g_allocator, SocketOptions(), accessToken.c_str(), localProxyMode, endpoint.c_str()) - .WithRootCa(caFile.c_str()) - .WithOnConnectionSuccess(OnConnectionSuccess) - .WithOnConnectionShutdown(OnConnectionShutdown) - .WithOnSendDataComplete(OnSendDataComplete) - .WithOnMessageReceived(OnMessageReceived) - .WithOnStreamStarted(OnStreamStarted) - .WithOnStreamReset(OnStreamReset) - .WithOnSessionReset(OnSessionReset) - .WithClientToken(clientToken.c_str()); - - /* Setup HttpClientCommectionProxyOptions if connecting through a proxy */ + }); + + builder.WithOnStopped([&](SecureTunnel *secureTunnel) { + fprintf(stdout, "Secure Tunnel has entered Stopped State\n"); + clientStoppedPromise.set_value(true); + }); + + //*********************************************************************************************************************** + /* Proxy Options */ + //*********************************************************************************************************************** if (proxyHost.length() > 0) { auto proxyOptions = Aws::Crt::Http::HttpClientConnectionProxyOptions(); @@ -371,7 +343,7 @@ int main(int argc, char *argv[]) } /* Create Secure Tunnel using the options set with the builder */ - secureTunnel = builder.Build(); + std::shared_ptr secureTunnel = builder.Build(); if (!secureTunnel) { @@ -413,7 +385,7 @@ int main(int argc, char *argv[]) fprintf(stdout, "Sending Message:\"%s\"\n", toSend.c_str()); - std::this_thread::sleep_for(5000ms); + std::this_thread::sleep_for(2000ms); } else { @@ -426,7 +398,7 @@ int main(int argc, char *argv[]) std::this_thread::sleep_for(3000ms); fprintf(stdout, "Closing Connection\n"); - if (secureTunnel->Close() == AWS_OP_ERR) + if (secureTunnel->Stop() == AWS_OP_ERR) { fprintf(stderr, "Secure Tunnel Close call failed: %s\n", ErrorDebugString(LastError())); exit(-1); @@ -434,9 +406,15 @@ int main(int argc, char *argv[]) if (connectionClosedPromise.get_future().get()) { - fprintf(stdout, "Sample Complete\n"); - exit(0); + fprintf(stdout, "Secure Tunnel Connection Closed\n"); } - exit(-1); + if (clientStoppedPromise.get_future().get()) + { + secureTunnel = nullptr; + } + + fprintf(stdout, "Secure Tunnel Sample Completed\n"); + + exit(0); } diff --git a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h index f35369fd4..d5fb2a2f0 100644 --- a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h +++ b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h @@ -234,13 +234,13 @@ namespace Aws * Type signature of the callback invoked when connection is established with the secure tunnel service and * available service ids are returned. */ - using OnConnectionSuccess = std::function; + using OnConnectionSuccess = std::function; /** * Type signature of the callback invoked when connection is established with the secure tunnel service and * available service ids are returned. */ - using OnConnectionFailure = std::function; + using OnConnectionFailure = std::function; /** * Type signature of the callback invoked when connection is shutdown. @@ -255,13 +255,14 @@ namespace Aws /** * Type signature of the callback invoked when a message is received through the secure tunnel connection. */ - using OnMessageReceived = std::function; + using OnMessageReceived = std::function; /** * Type signature of the callback invoked when a stream has been started with a source through the secure tunnel * connection. */ - using OnStreamStarted = std::function; + using OnStreamStarted = + std::function; /** * Type signature of the callback invoked when a stream is reset. @@ -273,6 +274,11 @@ namespace Aws */ using OnSessionReset = std::function; + /** + * Type signature of the callback invoked when the secure tunnel completes transitioning to a stopped state. + */ + using OnStopped = std::function; + /** * Deprecated - OnConnectionSuccess and OnConnectionFailure */ @@ -418,6 +424,15 @@ namespace Aws */ SecureTunnelBuilder &WithOnSessionReset(OnSessionReset onSessionReset); + /** + * Setup callback handler trigged when an Secure Tunnel completes entering a stopped state + * + * @param callback + * + * @return this builder object + */ + SecureTunnelBuilder &WithOnStopped(OnStopped onStopped); + /** * Deprecated - Use WithOnMessageReceived() */ @@ -541,6 +556,11 @@ namespace Aws */ OnSessionReset m_OnSessionReset; + /** + * Callback handler trigged when secure tunnel completes transition to stopped state. + */ + OnStopped m_OnStopped; + /** * Deprecated - Use m_OnConnectionSuccess and m_OnConnectionFailure */ @@ -557,7 +577,7 @@ namespace Aws friend class SecureTunnel; }; - class AWS_IOTSECURETUNNELING_API SecureTunnel final + class AWS_IOTSECURETUNNELING_API SecureTunnel final : std::enable_shared_from_this { public: SecureTunnel( @@ -596,14 +616,22 @@ namespace Aws OnSessionReset onSessionReset); virtual ~SecureTunnel(); - /* Do not allow direct copy or move */ SecureTunnel(const SecureTunnel &) = delete; - SecureTunnel(SecureTunnel &&) noexcept; SecureTunnel &operator=(const SecureTunnel &) = delete; + + SecureTunnel(SecureTunnel &&) noexcept; SecureTunnel &operator=(SecureTunnel &&) noexcept; bool IsValid(); + /** + * Get shared poitner of the SecureTunnel. SecureTunnel is inherited to enable_shared_from_this to help + * with memory safety. + * + * @return shared_ptr for the SecureTunnel + */ + std::shared_ptr getptr() { return shared_from_this(); } + /** * Notifies the secure tunnel that you want it to attempt to connect to the configured endpoint. * The secure tunnel will attempt to stay connected and attempt to reconnect if disconnected. @@ -710,7 +738,8 @@ namespace Aws OnStreamStarted onStreamStarted, OnStreamStart onStreamStart, OnStreamReset onStreamReset, - OnSessionReset onSessionReset); + OnSessionReset onSessionReset, + OnStopped onStopped); /* Static Callbacks */ static void s_OnMessageReceived(const struct aws_secure_tunnel_message_view *message, void *user_data); @@ -726,6 +755,7 @@ namespace Aws int error_code, void *user_data); static void s_OnSessionReset(void *user_data); + static void s_OnStopped(void *user_data); static void s_OnTerminationComplete(void *user_data); static void s_OnStreamStarted( const struct aws_secure_tunnel_message_view *message, @@ -776,6 +806,11 @@ namespace Aws */ OnSessionReset m_OnSessionReset; + /** + * Callback handler trigged when secure tunnel finishes entering a stopped state. + */ + OnStopped m_OnStopped; + aws_secure_tunnel *m_secure_tunnel; Crt::Allocator *m_allocator; @@ -792,7 +827,11 @@ namespace Aws */ OnStreamStart m_OnStreamStart; - std::promise m_TerminationComplete; + std::shared_ptr m_selfRef; + // std::promise m_TerminationComplete; + // std::condition_variable m_terminationCondition; + // std::mutex m_terminationMutex; + // bool m_terminationPredicate = false; friend class SecureTunnelBuilder; }; diff --git a/secure_tunneling/source/SecureTunnel.cpp b/secure_tunneling/source/SecureTunnel.cpp index eddd67a1e..94ca1f993 100644 --- a/secure_tunneling/source/SecureTunnel.cpp +++ b/secure_tunneling/source/SecureTunnel.cpp @@ -282,6 +282,12 @@ namespace Aws return *this; } + SecureTunnelBuilder &SecureTunnelBuilder::WithOnStopped(OnStopped onStopped) + { + m_OnStopped = std::move(onStopped); + return *this; + } + SecureTunnelBuilder &SecureTunnelBuilder::WithClientToken(const std::string &clientToken) { m_clientToken = clientToken; @@ -329,7 +335,8 @@ namespace Aws m_OnStreamStarted, m_OnStreamStart, m_OnStreamReset, - m_OnSessionReset)); + m_OnSessionReset, + m_OnStopped)); if (tunnel->m_secure_tunnel == nullptr) { @@ -369,7 +376,8 @@ namespace Aws OnStreamStarted onStreamStarted, OnStreamStart onStreamStart, OnStreamReset onStreamReset, - OnSessionReset onSessionReset) + OnSessionReset onSessionReset, + OnStopped onStopped) { // Client callbacks m_OnConnectionSuccess = std::move(onConnectionSuccess); @@ -383,6 +391,7 @@ namespace Aws m_OnStreamStart = std::move(onStreamStart); m_OnStreamReset = std::move(onStreamReset); m_OnSessionReset = std::move(onSessionReset); + m_OnStopped = std::move(onStopped); // Initialize aws_secure_tunnel_options aws_secure_tunnel_options config; @@ -413,7 +422,7 @@ namespace Aws config.on_stream_start = s_OnStreamStarted; config.on_stream_reset = s_OnStreamReset; config.on_session_reset = s_OnSessionReset; - config.on_termination_complete = s_OnTerminationComplete; + config.on_stopped = s_OnStopped; config.user_data = this; @@ -470,7 +479,8 @@ namespace Aws nullptr, onStreamStart, onStreamReset, - onSessionReset) + onSessionReset, + nullptr) { } @@ -513,7 +523,8 @@ namespace Aws nullptr, onStreamStart, onStreamReset, - onSessionReset) + onSessionReset, + nullptr) { } @@ -522,6 +533,7 @@ namespace Aws if (m_secure_tunnel) { aws_secure_tunnel_release(m_secure_tunnel); + m_secure_tunnel = nullptr; } } @@ -535,6 +547,7 @@ namespace Aws m_OnStreamStarted = std::move(other.m_OnStreamStarted); m_OnStreamReset = std::move(other.m_OnStreamReset); m_OnSessionReset = std::move(other.m_OnSessionReset); + m_OnStopped = std::move(other.m_OnStopped); /* Deprecated - Use m_OnConnectionSuccess and m_OnConnectionFailure */ m_OnConnectionComplete = std::move(other.m_OnConnectionComplete); @@ -543,8 +556,6 @@ namespace Aws /* Deprecated - Use m_OnStreamStarted */ m_OnStreamStart = std::move(other.m_OnStreamStart); - m_TerminationComplete = std::move(other.m_TerminationComplete); - m_secure_tunnel = other.m_secure_tunnel; other.m_secure_tunnel = nullptr; @@ -564,6 +575,7 @@ namespace Aws m_OnStreamStarted = std::move(other.m_OnStreamStarted); m_OnStreamReset = std::move(other.m_OnStreamReset); m_OnSessionReset = std::move(other.m_OnSessionReset); + m_OnStopped = std::move(other.m_OnStopped); /* Deprecated - Use m_OnConnectionSuccess and m_OnConnectionFailure */ m_OnConnectionComplete = std::move(other.m_OnConnectionComplete); @@ -572,8 +584,6 @@ namespace Aws /* Deprecated - Use m_OnStreamStarted */ m_OnStreamStart = std::move(other.m_OnStreamStart); - m_TerminationComplete = std::move(other.m_TerminationComplete); - m_secure_tunnel = other.m_secure_tunnel; other.m_secure_tunnel = nullptr; @@ -584,7 +594,14 @@ namespace Aws bool SecureTunnel::IsValid() { return m_secure_tunnel ? true : false; } - int SecureTunnel::Start() { return aws_secure_tunnel_start(m_secure_tunnel); } + int SecureTunnel::Start() + { + // if (m_selfRef == nullptr) + // { + // m_selfRef = this->getptr(); + // } + return aws_secure_tunnel_start(m_secure_tunnel); + } int SecureTunnel::Stop() { return aws_secure_tunnel_stop(m_secure_tunnel); } @@ -653,7 +670,7 @@ namespace Aws std::make_shared(*connection, secureTunnel->m_allocator); ConnectionSuccessEventData eventData; eventData.connectionData = packet; - secureTunnel->m_OnConnectionSuccess(*secureTunnel, eventData); + secureTunnel->m_OnConnectionSuccess(secureTunnel, eventData); return; } @@ -667,14 +684,14 @@ namespace Aws { if (secureTunnel->m_OnConnectionFailure) { - secureTunnel->m_OnConnectionFailure(*secureTunnel, error_code); + secureTunnel->m_OnConnectionFailure(secureTunnel, error_code); } } } void SecureTunnel::s_OnConnectionShutdown(int error_code, void *user_data) { - auto *secureTunnel = static_cast(user_data); + SecureTunnel *secureTunnel = reinterpret_cast(user_data); if (secureTunnel->m_OnConnectionShutdown) { secureTunnel->m_OnConnectionShutdown(); @@ -683,7 +700,7 @@ namespace Aws void SecureTunnel::s_OnSendDataComplete(int error_code, void *user_data) { - auto *secureTunnel = static_cast(user_data); + SecureTunnel *secureTunnel = reinterpret_cast(user_data); if (secureTunnel->m_OnSendDataComplete) { secureTunnel->m_OnSendDataComplete(error_code); @@ -704,7 +721,7 @@ namespace Aws std::make_shared(*message, secureTunnel->m_allocator); MessageReceivedEventData eventData; eventData.message = packet; - secureTunnel->m_OnMessageReceived(*secureTunnel, eventData); + secureTunnel->m_OnMessageReceived(secureTunnel, eventData); return; } @@ -746,7 +763,7 @@ namespace Aws std::make_shared(*message, secureTunnel->m_allocator); StreamStartedEventData eventData; eventData.streamStartedData = packet; - secureTunnel->m_OnStreamStarted(*secureTunnel, error_code, eventData); + secureTunnel->m_OnStreamStarted(secureTunnel, error_code, eventData); return; } @@ -763,7 +780,7 @@ namespace Aws int error_code, void *user_data) { - auto *secureTunnel = static_cast(user_data); + SecureTunnel *secureTunnel = reinterpret_cast(user_data); if (secureTunnel->m_OnStreamReset) { secureTunnel->m_OnStreamReset(); @@ -772,28 +789,23 @@ namespace Aws void SecureTunnel::s_OnSessionReset(void *user_data) { - auto *secureTunnel = static_cast(user_data); + SecureTunnel *secureTunnel = reinterpret_cast(user_data); if (secureTunnel->m_OnSessionReset) { secureTunnel->m_OnSessionReset(); } } - void SecureTunnel::s_OnTerminationComplete(void *user_data) + void SecureTunnel::s_OnStopped(void *user_data) { - auto *secureTunnel = static_cast(user_data); - secureTunnel->OnTerminationComplete(); + SecureTunnel *secureTunnel = reinterpret_cast(user_data); + secureTunnel->m_selfRef = nullptr; + if (secureTunnel->m_OnStopped) + { + secureTunnel->m_OnStopped(secureTunnel); + } } - void SecureTunnel::OnTerminationComplete() { m_TerminationComplete.set_value(); } - - void SecureTunnel::Shutdown() - { - Stop(); - aws_secure_tunnel_release(m_secure_tunnel); - m_secure_tunnel = nullptr; - - m_TerminationComplete.get_future().wait(); - } + void SecureTunnel::Shutdown() { Stop(); } } // namespace Iotsecuretunneling } // namespace Aws From 05fe2073ccddfabb37bedec16cc3e966835fae3f Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Tue, 14 Feb 2023 15:27:09 -0800 Subject: [PATCH 08/32] Added actual minimal securetunnelbuilder constructor --- samples/secure_tunneling/secure_tunnel/main.cpp | 4 ++-- .../include/aws/iotsecuretunneling/SecureTunnel.h | 9 +++++++++ secure_tunneling/source/SecureTunnel.cpp | 13 +++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/samples/secure_tunneling/secure_tunnel/main.cpp b/samples/secure_tunneling/secure_tunnel/main.cpp index 3d2305747..5cc2f18c6 100644 --- a/samples/secure_tunneling/secure_tunnel/main.cpp +++ b/samples/secure_tunneling/secure_tunnel/main.cpp @@ -194,8 +194,8 @@ int main(int argc, char *argv[]) exit(-1); } - SecureTunnelBuilder builder = SecureTunnelBuilder( - Aws::Crt::g_allocator, SocketOptions(), accessToken.c_str(), localProxyMode, endpoint.c_str()); + SecureTunnelBuilder builder = + SecureTunnelBuilder(Aws::Crt::g_allocator, accessToken.c_str(), localProxyMode, endpoint.c_str()); builder.WithRootCa(caFile.c_str()); diff --git a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h index d5fb2a2f0..e97b09bfd 100644 --- a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h +++ b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h @@ -319,6 +319,15 @@ namespace Aws aws_secure_tunneling_local_proxy_mode localProxyMode, const std::string &endpointHost); // Make a copy and save in this object + /** + * Constructor arguments are the minimum required to create a secure tunnel + */ + SecureTunnelBuilder( + Crt::Allocator *allocator, // Should out live this object + const std::string &accessToken, // Make a copy and save in this object + aws_secure_tunneling_local_proxy_mode localProxyMode, + const std::string &endpointHost); // Make a copy and save in this object + /* Optional members */ /** * Sets rootCA to be used for this secure tunnel connection overriding the default trust store. diff --git a/secure_tunneling/source/SecureTunnel.cpp b/secure_tunneling/source/SecureTunnel.cpp index 94ca1f993..60cea1608 100644 --- a/secure_tunneling/source/SecureTunnel.cpp +++ b/secure_tunneling/source/SecureTunnel.cpp @@ -221,6 +221,19 @@ namespace Aws { } + SecureTunnelBuilder::SecureTunnelBuilder( + Crt::Allocator *allocator, // Should out live this object + const std::string &accessToken, // Make a copy and save in this object + aws_secure_tunneling_local_proxy_mode localProxyMode, + const std::string &endpointHost) // Make a copy and save in this object + : m_allocator(allocator), m_clientBootstrap(Crt::ApiHandle::GetOrCreateStaticDefaultClientBootstrap()), + m_socketOptions(Crt::Io::SocketOptions()), m_accessToken(accessToken), m_localProxyMode(localProxyMode), + m_endpointHost(endpointHost), m_rootCa(""), m_httpClientConnectionProxyOptions(), + m_OnConnectionShutdown(), m_OnSendDataComplete(), m_OnStreamReset(), m_OnSessionReset(), + m_OnConnectionComplete(), m_OnDataReceive(), m_OnStreamStart() + { + } + SecureTunnelBuilder &SecureTunnelBuilder::WithRootCa(const std::string &rootCa) { m_rootCa = rootCa; From 6039bfb8eb287119e4d4452ddc8552dd02dd63e8 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Tue, 14 Feb 2023 16:23:39 -0800 Subject: [PATCH 09/32] Secure Tunnel Sample --- .../secure_tunneling/secure_tunnel/main.cpp | 43 ++++++++++++------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/samples/secure_tunneling/secure_tunnel/main.cpp b/samples/secure_tunneling/secure_tunnel/main.cpp index 5cc2f18c6..5798ee018 100644 --- a/samples/secure_tunneling/secure_tunnel/main.cpp +++ b/samples/secure_tunneling/secure_tunnel/main.cpp @@ -51,10 +51,9 @@ int main(int argc, char *argv[]) { /************************ Setup the Lib ****************************/ /* - * Do the global initialization for the API. + * Do the global initialization for the API and aws-c-iot. */ ApiHandle apiHandle; - aws_iotdevice_library_init(Aws::Crt::g_allocator); String accessToken; @@ -74,6 +73,7 @@ int main(int argc, char *argv[]) std::promise connectionClosedPromise; std::promise clientStoppedPromise; + /* Store to use service id */ std::shared_ptr connectionData; /*********************** Parse Arguments ***************************/ @@ -99,24 +99,16 @@ int main(int argc, char *argv[]) cmdUtils.SendArguments(const_argv, const_argv + argc); cmdUtils.StartLoggingBasedOnCommand(&apiHandle); - /* - * Generate secure tunneling endpoint using region - */ + /* Generate secure tunneling endpoint using region */ String region = cmdUtils.GetCommandRequired("region"); String endpoint = "data.tunneling.iot." + region + ".amazonaws.com"; - if (!(cmdUtils.HasCommand("access_token_file") || cmdUtils.HasCommand("access_token"))) - { - cmdUtils.PrintHelp(); - fprintf(stderr, "--access_token_file or --access_token must be set to connect through a secure tunnel"); - exit(-1); - } - + /* An access token is required to connect to the secure tunnel service */ if (cmdUtils.HasCommand("access_token")) { accessToken = cmdUtils.GetCommand("access_token"); } - else + else if (cmdUtils.HasCommand("access_token_file")) { accessToken = cmdUtils.GetCommand("access_token_file"); @@ -132,7 +124,18 @@ int main(int argc, char *argv[]) exit(-1); } } + else + { + cmdUtils.PrintHelp(); + fprintf(stderr, "--access_token_file or --access_token must be set to connect through a secure tunnel"); + exit(-1); + } + /* + * A client token is optional as one will be automatically generated if it is absent but it is recommended the + * customer provides their own so they can reuse it with other secure tunnel clients after the secure tunnel client + * is terminated. + * */ if (cmdUtils.HasCommand("client_token")) { clientToken = cmdUtils.GetCommand("client_token"); @@ -194,12 +197,12 @@ int main(int argc, char *argv[]) exit(-1); } + /* Use a SecureTunnelBuilder to set up and build the secure tunnel client */ SecureTunnelBuilder builder = SecureTunnelBuilder(Aws::Crt::g_allocator, accessToken.c_str(), localProxyMode, endpoint.c_str()); builder.WithRootCa(caFile.c_str()); - /* The client token is optional and will be generated automatically for reconnects. */ builder.WithClientToken(clientToken.c_str()); /* Add callbacks using the builder */ @@ -293,6 +296,7 @@ int main(int argc, char *argv[]) if (!errorCode) { std::shared_ptr streamStartedData = eventData.streamStartedData; + if (streamStartedData->getServiceId().has_value()) { fprintf( @@ -351,7 +355,7 @@ int main(int argc, char *argv[]) exit(-1); } - /* Set the Secure Tunnel to desire a connected state */ + /* Set the Secure Tunnel Client to desire a connected state */ if (secureTunnel->Start()) { fprintf(stderr, "Secure Tunnel Connect call failed: %s\n", ErrorDebugString(LastError())); @@ -365,6 +369,10 @@ int main(int argc, char *argv[]) { std::this_thread::sleep_for(1000ms); + /* + * In Destination mode the Secure Tunnel Client will remain open and echo messages that come in. + * In Source mode the Secure Tunnel Client will send 4 messages and then disconnect and terminate. + */ while (keepRunning) { if (localProxyMode == AWS_SECURE_TUNNELING_SOURCE_MODE) @@ -376,6 +384,7 @@ int main(int argc, char *argv[]) { std::shared_ptr message = std::make_shared(ByteCursorFromCString(toSend.c_str())); + /* If the secure tunnel has service ids, we will use one for our messages. */ if (connectionData->getServiceId1().has_value()) { message->withServiceId(connectionData->getServiceId1().value()); @@ -396,8 +405,9 @@ int main(int argc, char *argv[]) } std::this_thread::sleep_for(3000ms); - fprintf(stdout, "Closing Connection\n"); + fprintf(stdout, "Closing Connection\n"); + /* Set the Secure Tunnel Client to desire a stopped state */ if (secureTunnel->Stop() == AWS_OP_ERR) { fprintf(stderr, "Secure Tunnel Close call failed: %s\n", ErrorDebugString(LastError())); @@ -409,6 +419,7 @@ int main(int argc, char *argv[]) fprintf(stdout, "Secure Tunnel Connection Closed\n"); } + /* The Secure Tunnel Client at this point will report they are stopped and can be safely removed. */ if (clientStoppedPromise.get_future().get()) { secureTunnel = nullptr; From 4a8ca9a102e5aaafe54f3290e95902e5f5c17d1c Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 15 Feb 2023 11:04:37 -0800 Subject: [PATCH 10/32] Clean up cmdUtils in sample --- .../secure_tunneling/secure_tunnel/main.cpp | 183 +++++++++++------- 1 file changed, 113 insertions(+), 70 deletions(-) diff --git a/samples/secure_tunneling/secure_tunnel/main.cpp b/samples/secure_tunneling/secure_tunnel/main.cpp index 5798ee018..ba67c1c48 100644 --- a/samples/secure_tunneling/secure_tunnel/main.cpp +++ b/samples/secure_tunneling/secure_tunnel/main.cpp @@ -47,75 +47,62 @@ void logMessage(std::shared_ptr message) } }; -int main(int argc, char *argv[]) +void setupCommandLineUtils(Utils::CommandLineUtils *cmdUtils, int argc, char *argv[]) { - /************************ Setup the Lib ****************************/ - /* - * Do the global initialization for the API and aws-c-iot. - */ - ApiHandle apiHandle; - aws_iotdevice_library_init(Aws::Crt::g_allocator); - - String accessToken; - String clientToken; - aws_secure_tunneling_local_proxy_mode localProxyMode; - - String proxyHost; - uint16_t proxyPort(8080); - String proxyUserName; - String proxyPassword; - - /* - * In a real world application you probably don't want to enforce synchronous behavior - * but this is a sample console application, so we'll just do that with a condition variable. - */ - std::promise connectionCompletedPromise; - std::promise connectionClosedPromise; - std::promise clientStoppedPromise; - - /* Store to use service id */ - std::shared_ptr connectionData; - - /*********************** Parse Arguments ***************************/ - Utils::CommandLineUtils cmdUtils = Utils::CommandLineUtils(); - cmdUtils.AddCommonProxyCommands(); - cmdUtils.RegisterProgramName("secure_tunnel"); - cmdUtils.RegisterCommand("region", "", "The region of your secure tunnel"); - cmdUtils.RegisterCommand( + cmdUtils->AddCommonProxyCommands(); + cmdUtils->RegisterProgramName("secure_tunnel"); + cmdUtils->RegisterCommand("region", "", "The region of your secure tunnel"); + cmdUtils->RegisterCommand( "ca_file", "", "Path to AmazonRootCA1.pem (optional, system trust store used by default)."); - cmdUtils.RegisterCommand( + cmdUtils->RegisterCommand( "access_token_file", "", "Path to the tunneling access token file (optional if --access_token used)."); - cmdUtils.RegisterCommand("access_token", "", "Tunneling access token (optional if --access_token_file used)."); - cmdUtils.RegisterCommand( + cmdUtils->RegisterCommand( + "access_token", "", "Tunneling access token (optional if --access_token_file used)."); + cmdUtils->RegisterCommand( "local_proxy_mode_source", "", "Use to set local proxy mode to source (optional, default='destination')."); - cmdUtils.RegisterCommand("client_token", "", "Tunneling access token (optional if --client_token_file used)."); - cmdUtils.RegisterCommand("message", "", "Message to send (optional, default='Hello World!')."); - cmdUtils.RegisterCommand( + cmdUtils->RegisterCommand( + "client_token", "", "Tunneling access token (optional if --client_token_file used)."); + cmdUtils->RegisterCommand("message", "", "Message to send (optional, default='Hello World!')."); + cmdUtils->RegisterCommand( "proxy_user_name", "", "User name passed if proxy server requires a user name (optional)"); - cmdUtils.RegisterCommand( + cmdUtils->RegisterCommand( "proxy_password", "", "Password passed if proxy server requires a password (optional)"); - cmdUtils.AddLoggingCommands(); + cmdUtils->AddLoggingCommands(); const char **const_argv = (const char **)argv; - cmdUtils.SendArguments(const_argv, const_argv + argc); - cmdUtils.StartLoggingBasedOnCommand(&apiHandle); + cmdUtils->SendArguments(const_argv, const_argv + argc); +} +void setupCommandLineValues( + Utils::CommandLineUtils *cmdUtils, + String *endpoint, + String *accessToken, + String *clientToken, + String *caFile, + String *proxyHost, + String *proxyUserName, + String *proxyPassword, + uint16_t &proxyPort, + aws_secure_tunneling_local_proxy_mode &localProxyMode, + String *payloadMessage) +{ /* Generate secure tunneling endpoint using region */ - String region = cmdUtils.GetCommandRequired("region"); - String endpoint = "data.tunneling.iot." + region + ".amazonaws.com"; + String region = cmdUtils->GetCommandRequired("region"); + endpoint->assign("data.tunneling.iot." + region + ".amazonaws.com"); + String tempAccessToken; /* An access token is required to connect to the secure tunnel service */ - if (cmdUtils.HasCommand("access_token")) + if (cmdUtils->HasCommand("access_token")) { - accessToken = cmdUtils.GetCommand("access_token"); + tempAccessToken = cmdUtils->GetCommand("access_token"); } - else if (cmdUtils.HasCommand("access_token_file")) + else if (cmdUtils->HasCommand("access_token_file")) { - accessToken = cmdUtils.GetCommand("access_token_file"); + tempAccessToken = cmdUtils->GetCommand("access_token_file"); - std::ifstream accessTokenFile(accessToken.c_str()); + std::ifstream accessTokenFile(tempAccessToken.c_str()); if (accessTokenFile.is_open()) { - getline(accessTokenFile, accessToken); + getline(accessTokenFile, tempAccessToken); accessTokenFile.close(); } else @@ -126,29 +113,31 @@ int main(int argc, char *argv[]) } else { - cmdUtils.PrintHelp(); + cmdUtils->PrintHelp(); fprintf(stderr, "--access_token_file or --access_token must be set to connect through a secure tunnel"); exit(-1); } + accessToken->assign(tempAccessToken); + String tempClientToken; /* * A client token is optional as one will be automatically generated if it is absent but it is recommended the * customer provides their own so they can reuse it with other secure tunnel clients after the secure tunnel client * is terminated. * */ - if (cmdUtils.HasCommand("client_token")) + if (cmdUtils->HasCommand("client_token")) { - clientToken = cmdUtils.GetCommand("client_token"); + tempClientToken = cmdUtils->GetCommand("client_token"); } - if (cmdUtils.HasCommand("client_token_file")) + if (cmdUtils->HasCommand("client_token_file")) { - clientToken = cmdUtils.GetCommand("client_token_file"); + tempClientToken = cmdUtils->GetCommand("client_token_file"); - std::ifstream clientTokenFile(clientToken.c_str()); + std::ifstream clientTokenFile(tempClientToken.c_str()); if (clientTokenFile.is_open()) { - getline(clientTokenFile, clientToken); + getline(clientTokenFile, tempClientToken); clientTokenFile.close(); } else @@ -158,26 +147,28 @@ int main(int argc, char *argv[]) } } - if (cmdUtils.HasCommand("proxy_host") || cmdUtils.HasCommand("proxy_port")) + clientToken->assign(tempClientToken); + + caFile->assign(cmdUtils->GetCommandOrDefault("ca_file", "")); + + if (cmdUtils->HasCommand("proxy_host") || cmdUtils->HasCommand("proxy_port")) { - proxyHost = - cmdUtils.GetCommandRequired("proxy_host", "--proxy_host must be set to connect through a proxy.").c_str(); + proxyHost->assign( + cmdUtils->GetCommandRequired("proxy_host", "--proxy_host must be set to connect through a proxy.").c_str()); int port = atoi( - cmdUtils.GetCommandRequired("proxy_port", "--proxy_port must be set to connect through a proxy.").c_str()); + cmdUtils->GetCommandRequired("proxy_port", "--proxy_port must be set to connect through a proxy.").c_str()); if (port > 0 && port <= UINT16_MAX) { proxyPort = static_cast(port); } - proxyUserName = cmdUtils.GetCommandOrDefault("proxy_user_name", ""); - proxyPassword = cmdUtils.GetCommandOrDefault("proxy_password", ""); + proxyUserName->assign(cmdUtils->GetCommandOrDefault("proxy_user_name", "")); + proxyPassword->assign(cmdUtils->GetCommandOrDefault("proxy_password", "")); } - String caFile = cmdUtils.GetCommandOrDefault("ca_file", ""); - /* * localProxyMode is set to destination by default unless flag is set to source */ - if (cmdUtils.HasCommand("local_proxy_mode_source")) + if (cmdUtils->HasCommand("local_proxy_mode_source")) { localProxyMode = AWS_SECURE_TUNNELING_SOURCE_MODE; } @@ -186,7 +177,56 @@ int main(int argc, char *argv[]) localProxyMode = AWS_SECURE_TUNNELING_DESTINATION_MODE; } - String payloadMessage = cmdUtils.GetCommandOrDefault("message", "Hello World"); + payloadMessage->assign(cmdUtils->GetCommandOrDefault("message", "Hello World")); +} + +int main(int argc, char *argv[]) +{ + /************************ Setup the Lib ****************************/ + /* + * Do the global initialization for the API and aws-c-iot. + */ + ApiHandle apiHandle; + aws_iotdevice_library_init(Aws::Crt::g_allocator); + + /* + * In a real world application you probably don't want to enforce synchronous behavior + * but this is a sample console application, so we'll just do that with a condition variable. + */ + std::promise connectionCompletedPromise; + std::promise connectionClosedPromise; + std::promise clientStoppedPromise; + + /* Store to use service id */ + std::shared_ptr connectionData; + + String endpoint; + String accessToken; + String clientToken; + String caFile; + String proxyHost; + uint16_t proxyPort(8080); + String proxyUserName; + String proxyPassword; + aws_secure_tunneling_local_proxy_mode localProxyMode; + String payloadMessage; + + /*********************** Parse Arguments ***************************/ + Utils::CommandLineUtils cmdUtils = Utils::CommandLineUtils(); + setupCommandLineUtils(&cmdUtils, argc, argv); + cmdUtils.StartLoggingBasedOnCommand(&apiHandle); + setupCommandLineValues( + &cmdUtils, + &endpoint, + &accessToken, + &clientToken, + &caFile, + &proxyHost, + &proxyUserName, + &proxyPassword, + proxyPort, + localProxyMode, + &payloadMessage); if (apiHandle.GetOrCreateStaticDefaultClientBootstrap()->LastError() != AWS_ERROR_SUCCESS) { @@ -201,7 +241,10 @@ int main(int argc, char *argv[]) SecureTunnelBuilder builder = SecureTunnelBuilder(Aws::Crt::g_allocator, accessToken.c_str(), localProxyMode, endpoint.c_str()); - builder.WithRootCa(caFile.c_str()); + if (caFile.length() > 0) + { + builder.WithRootCa(caFile.c_str()); + } builder.WithClientToken(clientToken.c_str()); From 40fae038d346fe8ef09fc06046b2a7bfa0f8d8a7 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Tue, 21 Feb 2023 11:44:26 -0800 Subject: [PATCH 11/32] pr fixes --- secure_tunneling/source/SecureTunnel.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/secure_tunneling/source/SecureTunnel.cpp b/secure_tunneling/source/SecureTunnel.cpp index 60cea1608..a67b2c3fe 100644 --- a/secure_tunneling/source/SecureTunnel.cpp +++ b/secure_tunneling/source/SecureTunnel.cpp @@ -51,9 +51,7 @@ namespace Aws Message::Message(const aws_secure_tunnel_message_view &message, Crt::Allocator *allocator) noexcept : m_allocator(allocator) { - AWS_ZERO_STRUCT(m_payload); AWS_ZERO_STRUCT(m_payloadStorage); - AWS_ZERO_STRUCT(m_serviceId); AWS_ZERO_STRUCT(m_serviceIdStorage); setPacketByteBufOptional(m_payload, m_payloadStorage, m_allocator, message.payload); From 33094c18456efa18e45dab90cb7361330c0fb676 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 22 Feb 2023 13:45:54 -0800 Subject: [PATCH 12/32] sample update --- .../secure_tunneling/secure_tunnel/main.cpp | 35 +++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/samples/secure_tunneling/secure_tunnel/main.cpp b/samples/secure_tunneling/secure_tunnel/main.cpp index ba67c1c48..a4592f25e 100644 --- a/samples/secure_tunneling/secure_tunnel/main.cpp +++ b/samples/secure_tunneling/secure_tunnel/main.cpp @@ -182,12 +182,13 @@ void setupCommandLineValues( int main(int argc, char *argv[]) { + struct aws_allocator *allocator = aws_mem_tracer_new(aws_default_allocator(), NULL, AWS_MEMTRACE_STACKS, 15); /************************ Setup the Lib ****************************/ /* * Do the global initialization for the API and aws-c-iot. */ ApiHandle apiHandle; - aws_iotdevice_library_init(Aws::Crt::g_allocator); + aws_iotdevice_library_init(allocator); /* * In a real world application you probably don't want to enforce synchronous behavior @@ -238,8 +239,7 @@ int main(int argc, char *argv[]) } /* Use a SecureTunnelBuilder to set up and build the secure tunnel client */ - SecureTunnelBuilder builder = - SecureTunnelBuilder(Aws::Crt::g_allocator, accessToken.c_str(), localProxyMode, endpoint.c_str()); + SecureTunnelBuilder builder = SecureTunnelBuilder(allocator, accessToken.c_str(), localProxyMode, endpoint.c_str()); if (caFile.length() > 0) { @@ -276,7 +276,7 @@ int main(int argc, char *argv[]) stdout, "Sending Stream Start request with service id:'" PRInSTR "'\n", AWS_BYTE_CURSOR_PRI(connectionData->getServiceId1().value())); - secureTunnel->SendStreamStart(connectionData->getServiceId1().value()); + // secureTunnel->SendStreamStart(connectionData->getServiceId1().value()); } } else @@ -377,7 +377,7 @@ int main(int argc, char *argv[]) basicAuthConfig.Username = proxyUserName.c_str(); basicAuthConfig.Password = proxyPassword.c_str(); proxyOptions.ProxyStrategy = - Aws::Crt::Http::HttpProxyStrategy::CreateBasicHttpProxyStrategy(basicAuthConfig, Aws::Crt::g_allocator); + Aws::Crt::Http::HttpProxyStrategy::CreateBasicHttpProxyStrategy(basicAuthConfig, allocator); proxyOptions.AuthType = Aws::Crt::Http::AwsHttpProxyAuthenticationType::Basic; } else @@ -469,6 +469,29 @@ int main(int argc, char *argv[]) } fprintf(stdout, "Secure Tunnel Sample Completed\n"); + std::this_thread::sleep_for(5000ms); - exit(0); + const size_t leaked_bytes = aws_mem_tracer_bytes(allocator); + if (leaked_bytes) + { + struct aws_logger memory_logger; + AWS_ZERO_STRUCT(memory_logger); + struct aws_logger_standard_options options = { + .level = AWS_LL_TRACE, + }; + + aws_logger_init_noalloc(&memory_logger, aws_default_allocator(), &options); + aws_logger_set(&memory_logger); + + fprintf(stdout, "Writing memory leaks to log.\n"); + aws_mem_tracer_dump(allocator); + + aws_logger_set(NULL); + aws_logger_clean_up(&memory_logger); + } + + // apiHandle.~ApiHandle(); + // aws_iotdevice_library_clean_up(); + + return 0; } From c8c3f50bd2d6c10c1c3f7c417fc9cf71656a127e Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 22 Feb 2023 14:24:27 -0800 Subject: [PATCH 13/32] fix memory leak in sample --- .../secure_tunneling/secure_tunnel/main.cpp | 66 ++++++++----------- 1 file changed, 29 insertions(+), 37 deletions(-) diff --git a/samples/secure_tunneling/secure_tunnel/main.cpp b/samples/secure_tunneling/secure_tunnel/main.cpp index a4592f25e..2d0d01f84 100644 --- a/samples/secure_tunneling/secure_tunnel/main.cpp +++ b/samples/secure_tunneling/secure_tunnel/main.cpp @@ -2,6 +2,7 @@ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0. */ +#include #include #include #include @@ -182,7 +183,7 @@ void setupCommandLineValues( int main(int argc, char *argv[]) { - struct aws_allocator *allocator = aws_mem_tracer_new(aws_default_allocator(), NULL, AWS_MEMTRACE_STACKS, 15); + struct aws_allocator *allocator = aws_default_allocator(); /************************ Setup the Lib ****************************/ /* * Do the global initialization for the API and aws-c-iot. @@ -198,8 +199,10 @@ int main(int argc, char *argv[]) std::promise connectionClosedPromise; std::promise clientStoppedPromise; - /* Store to use service id */ - std::shared_ptr connectionData; + /* service id storage for use in sample */ + Aws::Crt::ByteBuf m_serviceIdStorage; + AWS_ZERO_STRUCT(m_serviceIdStorage); + Aws::Crt::Optional m_serviceId; String endpoint; String accessToken; @@ -251,20 +254,29 @@ int main(int argc, char *argv[]) /* Add callbacks using the builder */ builder.WithOnConnectionSuccess([&](SecureTunnel *secureTunnel, const ConnectionSuccessEventData &eventData) { - connectionData = eventData.connectionData; - - if (connectionData->getServiceId1().has_value()) + if (eventData.connectionData->getServiceId1().has_value()) { + /* If secure tunnel is using service ids, store one for future use */ + aws_byte_buf_clean_up(&m_serviceIdStorage); + AWS_ZERO_STRUCT(m_serviceIdStorage); + aws_byte_buf_init_copy_from_cursor( + &m_serviceIdStorage, allocator, eventData.connectionData->getServiceId1().value()); + m_serviceId = aws_byte_cursor_from_buf(&m_serviceIdStorage); + fprintf( stdout, "Secure Tunnel connected with Service IDs '" PRInSTR "'", - AWS_BYTE_CURSOR_PRI(connectionData->getServiceId1().value())); - if (connectionData->getServiceId2().has_value()) + AWS_BYTE_CURSOR_PRI(eventData.connectionData->getServiceId1().value())); + if (eventData.connectionData->getServiceId2().has_value()) { - fprintf(stdout, ", '" PRInSTR "'", AWS_BYTE_CURSOR_PRI(connectionData->getServiceId2().value())); - if (connectionData->getServiceId3().has_value()) + fprintf( + stdout, ", '" PRInSTR "'", AWS_BYTE_CURSOR_PRI(eventData.connectionData->getServiceId2().value())); + if (eventData.connectionData->getServiceId3().has_value()) { - fprintf(stdout, ", '" PRInSTR "'", AWS_BYTE_CURSOR_PRI(connectionData->getServiceId3().value())); + fprintf( + stdout, + ", '" PRInSTR "'", + AWS_BYTE_CURSOR_PRI(eventData.connectionData->getServiceId3().value())); } } fprintf(stdout, "\n"); @@ -275,8 +287,8 @@ int main(int argc, char *argv[]) fprintf( stdout, "Sending Stream Start request with service id:'" PRInSTR "'\n", - AWS_BYTE_CURSOR_PRI(connectionData->getServiceId1().value())); - // secureTunnel->SendStreamStart(connectionData->getServiceId1().value()); + AWS_BYTE_CURSOR_PRI(eventData.connectionData->getServiceId1().value())); + secureTunnel->SendStreamStart(eventData.connectionData->getServiceId1().value()); } } else @@ -428,9 +440,9 @@ int main(int argc, char *argv[]) std::shared_ptr message = std::make_shared(ByteCursorFromCString(toSend.c_str())); /* If the secure tunnel has service ids, we will use one for our messages. */ - if (connectionData->getServiceId1().has_value()) + if (m_serviceId.has_value()) { - message->withServiceId(connectionData->getServiceId1().value()); + message->withServiceId(m_serviceId.value()); } secureTunnel->SendMessage(message); @@ -469,29 +481,9 @@ int main(int argc, char *argv[]) } fprintf(stdout, "Secure Tunnel Sample Completed\n"); - std::this_thread::sleep_for(5000ms); - - const size_t leaked_bytes = aws_mem_tracer_bytes(allocator); - if (leaked_bytes) - { - struct aws_logger memory_logger; - AWS_ZERO_STRUCT(memory_logger); - struct aws_logger_standard_options options = { - .level = AWS_LL_TRACE, - }; - - aws_logger_init_noalloc(&memory_logger, aws_default_allocator(), &options); - aws_logger_set(&memory_logger); - - fprintf(stdout, "Writing memory leaks to log.\n"); - aws_mem_tracer_dump(allocator); - - aws_logger_set(NULL); - aws_logger_clean_up(&memory_logger); - } - // apiHandle.~ApiHandle(); - // aws_iotdevice_library_clean_up(); + /* Clean Up */ + aws_byte_buf_clean_up(&m_serviceIdStorage); return 0; } From 8ae4a2b5452e6c9c2fe57d7a66d9eaa47024f26d Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 22 Feb 2023 15:34:50 -0800 Subject: [PATCH 14/32] pr fixes/changes --- .../include/aws/iotsecuretunneling/SecureTunnel.h | 10 +--------- secure_tunneling/source/SecureTunnel.cpp | 14 +++++++------- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h index e97b09bfd..d62b7263b 100644 --- a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h +++ b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h @@ -586,7 +586,7 @@ namespace Aws friend class SecureTunnel; }; - class AWS_IOTSECURETUNNELING_API SecureTunnel final : std::enable_shared_from_this + class AWS_IOTSECURETUNNELING_API SecureTunnel final { public: SecureTunnel( @@ -633,14 +633,6 @@ namespace Aws bool IsValid(); - /** - * Get shared poitner of the SecureTunnel. SecureTunnel is inherited to enable_shared_from_this to help - * with memory safety. - * - * @return shared_ptr for the SecureTunnel - */ - std::shared_ptr getptr() { return shared_from_this(); } - /** * Notifies the secure tunnel that you want it to attempt to connect to the configured endpoint. * The secure tunnel will attempt to stay connected and attempt to reconnect if disconnected. diff --git a/secure_tunneling/source/SecureTunnel.cpp b/secure_tunneling/source/SecureTunnel.cpp index a67b2c3fe..9ec883b26 100644 --- a/secure_tunneling/source/SecureTunnel.cpp +++ b/secure_tunneling/source/SecureTunnel.cpp @@ -702,7 +702,7 @@ namespace Aws void SecureTunnel::s_OnConnectionShutdown(int error_code, void *user_data) { - SecureTunnel *secureTunnel = reinterpret_cast(user_data); + SecureTunnel *secureTunnel = static_cast(user_data); if (secureTunnel->m_OnConnectionShutdown) { secureTunnel->m_OnConnectionShutdown(); @@ -711,7 +711,7 @@ namespace Aws void SecureTunnel::s_OnSendDataComplete(int error_code, void *user_data) { - SecureTunnel *secureTunnel = reinterpret_cast(user_data); + SecureTunnel *secureTunnel = static_cast(user_data); if (secureTunnel->m_OnSendDataComplete) { secureTunnel->m_OnSendDataComplete(error_code); @@ -720,7 +720,7 @@ namespace Aws void SecureTunnel::s_OnMessageReceived(const struct aws_secure_tunnel_message_view *message, void *user_data) { - SecureTunnel *secureTunnel = reinterpret_cast(user_data); + SecureTunnel *secureTunnel = static_cast(user_data); if (secureTunnel != nullptr) { if (message != NULL) @@ -765,7 +765,7 @@ namespace Aws int error_code, void *user_data) { - SecureTunnel *secureTunnel = reinterpret_cast(user_data); + SecureTunnel *secureTunnel = static_cast(user_data); if (!error_code) { if (secureTunnel->m_OnStreamStarted) @@ -791,7 +791,7 @@ namespace Aws int error_code, void *user_data) { - SecureTunnel *secureTunnel = reinterpret_cast(user_data); + SecureTunnel *secureTunnel = static_cast(user_data); if (secureTunnel->m_OnStreamReset) { secureTunnel->m_OnStreamReset(); @@ -800,7 +800,7 @@ namespace Aws void SecureTunnel::s_OnSessionReset(void *user_data) { - SecureTunnel *secureTunnel = reinterpret_cast(user_data); + SecureTunnel *secureTunnel = static_cast(user_data); if (secureTunnel->m_OnSessionReset) { secureTunnel->m_OnSessionReset(); @@ -809,7 +809,7 @@ namespace Aws void SecureTunnel::s_OnStopped(void *user_data) { - SecureTunnel *secureTunnel = reinterpret_cast(user_data); + SecureTunnel *secureTunnel = static_cast(user_data); secureTunnel->m_selfRef = nullptr; if (secureTunnel->m_OnStopped) { From 0fecccfcabc882cdf9c33c427fa5ac5507ea4fd2 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 22 Feb 2023 15:37:18 -0800 Subject: [PATCH 15/32] merge with main --- crt/aws-c-iot | 2 +- crt/aws-crt-cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crt/aws-c-iot b/crt/aws-c-iot index fc21ca507..0c3cd3627 160000 --- a/crt/aws-c-iot +++ b/crt/aws-c-iot @@ -1 +1 @@ -Subproject commit fc21ca50727548626969a51bc18cf07eca35061b +Subproject commit 0c3cd36278b4927373d87eecce8aa68106a8bf12 diff --git a/crt/aws-crt-cpp b/crt/aws-crt-cpp index f81587db3..f2adef31d 160000 --- a/crt/aws-crt-cpp +++ b/crt/aws-crt-cpp @@ -1 +1 @@ -Subproject commit f81587db32a2f13764b0378fb069161f6742e376 +Subproject commit f2adef31d778cfe90b8a5bb377425f825ebf92f0 From 794869f7054544b836411f3e9a7231125d7230b9 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 22 Feb 2023 15:44:42 -0800 Subject: [PATCH 16/32] don't zero optional members --- secure_tunneling/source/SecureTunnel.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/secure_tunneling/source/SecureTunnel.cpp b/secure_tunneling/source/SecureTunnel.cpp index 9ec883b26..fad2a4170 100644 --- a/secure_tunneling/source/SecureTunnel.cpp +++ b/secure_tunneling/source/SecureTunnel.cpp @@ -61,17 +61,13 @@ namespace Aws /* Default constructor */ Message::Message(Crt::Allocator *allocator) noexcept : m_allocator(allocator) { - AWS_ZERO_STRUCT(m_payload); AWS_ZERO_STRUCT(m_payloadStorage); - AWS_ZERO_STRUCT(m_serviceId); AWS_ZERO_STRUCT(m_serviceIdStorage); } Message::Message(Crt::ByteCursor payload, Crt::Allocator *allocator) noexcept : m_allocator(allocator) { - AWS_ZERO_STRUCT(m_payload); AWS_ZERO_STRUCT(m_payloadStorage); - AWS_ZERO_STRUCT(m_serviceId); AWS_ZERO_STRUCT(m_serviceIdStorage); aws_byte_buf_clean_up(&m_payloadStorage); @@ -82,9 +78,7 @@ namespace Aws Message::Message(Crt::ByteCursor serviceId, Crt::ByteCursor payload, Crt::Allocator *allocator) noexcept : m_allocator(allocator) { - AWS_ZERO_STRUCT(m_payload); AWS_ZERO_STRUCT(m_payloadStorage); - AWS_ZERO_STRUCT(m_serviceId); AWS_ZERO_STRUCT(m_serviceIdStorage); aws_byte_buf_clean_up(&m_payloadStorage); @@ -146,9 +140,6 @@ namespace Aws Crt::Allocator *allocator) noexcept : m_allocator(allocator) { - AWS_ZERO_STRUCT(m_serviceId1); - AWS_ZERO_STRUCT(m_serviceId2); - AWS_ZERO_STRUCT(m_serviceId3); AWS_ZERO_STRUCT(m_serviceId1Storage); AWS_ZERO_STRUCT(m_serviceId2Storage); AWS_ZERO_STRUCT(m_serviceId3Storage); From 87890ced3a6731e1398b8c3a771d99108af535d6 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 22 Feb 2023 15:55:31 -0800 Subject: [PATCH 17/32] disable existing secure tunnel tests --- secure_tunneling/tests/CMakeLists.txt | 8 +- secure_tunneling/tests/SecureTunnelTest.cpp | 516 ++++++++++---------- 2 files changed, 262 insertions(+), 262 deletions(-) diff --git a/secure_tunneling/tests/CMakeLists.txt b/secure_tunneling/tests/CMakeLists.txt index fd4050245..044e52589 100644 --- a/secure_tunneling/tests/CMakeLists.txt +++ b/secure_tunneling/tests/CMakeLists.txt @@ -12,9 +12,9 @@ aws_use_package(aws-crt-cpp) aws_use_package(IotSecureTunneling-cpp) if (UNIX AND NOT APPLE) - add_test_case(SecureTunnelingHandleStreamStartTest) - add_test_case(SecureTunnelingHandleDataReceiveTest) - add_test_case(SecureTunnelingHandleStreamResetTest) - add_test_case(SecureTunnelingHandleSessionResetTest) + # add_test_case(SecureTunnelingHandleStreamStartTest) + # add_test_case(SecureTunnelingHandleDataReceiveTest) + # add_test_case(SecureTunnelingHandleStreamResetTest) + # add_test_case(SecureTunnelingHandleSessionResetTest) generate_cpp_test_driver(${TEST_BINARY_NAME}) endif() diff --git a/secure_tunneling/tests/SecureTunnelTest.cpp b/secure_tunneling/tests/SecureTunnelTest.cpp index fd2d6cc9c..d1d02c110 100644 --- a/secure_tunneling/tests/SecureTunnelTest.cpp +++ b/secure_tunneling/tests/SecureTunnelTest.cpp @@ -3,261 +3,261 @@ * SPDX-License-Identifier: Apache-2.0. */ -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace std; -using namespace Aws::Crt::Io; -using namespace Aws::Iotdevicecommon; -using namespace Aws::Iotsecuretunneling; - -#define INVALID_STREAM_ID 0 -#define STREAM_ID 10 -#define PAYLOAD "secure tunneling data payload" - -extern "C" -{ - struct aws_websocket_incoming_frame; - extern bool on_websocket_incoming_frame_payload( - struct aws_websocket *websocket, - const struct aws_websocket_incoming_frame *frame, - struct aws_byte_cursor data, - void *user_data); -} - -struct SecureTunnelingTestContext -{ - unique_ptr deviceApiHandle; - unique_ptr elGroup; - unique_ptr resolver; - unique_ptr clientBootstrap; - shared_ptr secureTunnel; - - aws_secure_tunneling_local_proxy_mode localProxyMode; - - SecureTunnelingTestContext() { localProxyMode = AWS_SECURE_TUNNELING_DESTINATION_MODE; } -}; -static SecureTunnelingTestContext s_testContext; - -// Client callbacks implementation -static void s_OnConnectionComplete() {} -static void s_OnConnectionShutdown() {} - -static void s_OnSendDataComplete(int errorCode) {} - -static bool s_OnDataReceiveCorrectPayload = false; -static void s_OnDataReceive(const Aws::Crt::ByteBuf &data) -{ - s_OnDataReceiveCorrectPayload = aws_byte_buf_eq_c_str(&data, PAYLOAD); -} - -static bool s_OnStreamStartCalled = false; -static void s_OnStreamStart() -{ - s_OnStreamStartCalled = true; -} - -static bool s_OnStreamResetCalled = false; -static void s_OnStreamReset() -{ - s_OnStreamResetCalled = true; -} - -static bool s_OnSessionResetCalled = false; -static void s_OnSessionReset() -{ - s_OnSessionResetCalled = true; -} - -static int before(struct aws_allocator *allocator, void *ctx) -{ - auto *testContext = static_cast(ctx); - - aws_http_library_init(allocator); - - testContext->deviceApiHandle = unique_ptr(new DeviceApiHandle(allocator)); - testContext->elGroup = unique_ptr(new EventLoopGroup(1, allocator)); - testContext->resolver = unique_ptr(new DefaultHostResolver(*testContext->elGroup, 8, 30, allocator)); - testContext->clientBootstrap = - unique_ptr(new ClientBootstrap(*testContext->elGroup, *testContext->resolver, allocator)); - testContext->secureTunnel = SecureTunnelBuilder( - allocator, - *testContext->clientBootstrap, - SocketOptions(), - "access_token", - testContext->localProxyMode, - "endpoint") - .WithRootCa("") - .WithOnConnectionComplete(s_OnConnectionComplete) - .WithOnConnectionShutdown(s_OnConnectionShutdown) - .WithOnSendDataComplete(s_OnSendDataComplete) - .WithOnDataReceive(s_OnDataReceive) - .WithOnStreamStart(s_OnStreamStart) - .WithOnStreamReset(s_OnStreamReset) - .WithOnSessionReset(s_OnSessionReset) - .Build(); - return AWS_ERROR_SUCCESS; -} - -static int after(struct aws_allocator *allocator, int setup_result, void *ctx) -{ - auto *testContext = static_cast(ctx); - - testContext->secureTunnel->Shutdown(); - - testContext->secureTunnel.reset(); - testContext->clientBootstrap.reset(); - testContext->resolver.reset(); - testContext->elGroup.reset(); - testContext->deviceApiHandle.reset(); - - aws_http_library_clean_up(); - - return AWS_ERROR_SUCCESS; -} - -static void s_send_secure_tunneling_frame_to_websocket( - const struct aws_iot_st_msg *st_msg, - struct aws_allocator *allocator, - struct aws_secure_tunnel *secure_tunnel) -{ - struct aws_byte_buf serialized_st_msg; - aws_iot_st_msg_serialize_from_struct(&serialized_st_msg, allocator, *st_msg); - - /* Prepend 2 bytes length */ - struct aws_byte_buf websocket_frame; - aws_byte_buf_init(&websocket_frame, allocator, serialized_st_msg.len + 2); - aws_byte_buf_write_be16(&websocket_frame, (uint16_t)serialized_st_msg.len); - struct aws_byte_cursor c = aws_byte_cursor_from_buf(&serialized_st_msg); - aws_byte_buf_append(&websocket_frame, &c); - c = aws_byte_cursor_from_buf(&websocket_frame); - - on_websocket_incoming_frame_payload(NULL, NULL, c, secure_tunnel); - - aws_byte_buf_clean_up(&serialized_st_msg); - aws_byte_buf_clean_up(&websocket_frame); -} - -AWS_TEST_CASE_FIXTURE( - SecureTunnelingHandleStreamStartTest, - before, - s_SecureTunnelingHandleStreamStartTest, - after, - &s_testContext); -static int s_SecureTunnelingHandleStreamStartTest(Aws::Crt::Allocator *allocator, void *ctx) -{ - auto *testContext = static_cast(ctx); - - struct aws_iot_st_msg st_msg; - AWS_ZERO_STRUCT(st_msg); - st_msg.type = STREAM_START; - st_msg.stream_id = STREAM_ID; - s_OnStreamStartCalled = false; - s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, testContext->secureTunnel->GetUnderlyingHandle()); - - ASSERT_TRUE(s_OnStreamStartCalled); - ASSERT_INT_EQUALS(STREAM_ID, testContext->secureTunnel->GetUnderlyingHandle()->stream_id); - ASSERT_UINT_EQUALS(0, testContext->secureTunnel->GetUnderlyingHandle()->received_data.len); - - return AWS_ERROR_SUCCESS; -} - -AWS_TEST_CASE_FIXTURE( - SecureTunnelingHandleDataReceiveTest, - before, - s_SecureTunnelingHandleDataReceiveTest, - after, - &s_testContext); -static int s_SecureTunnelingHandleDataReceiveTest(Aws::Crt::Allocator *allocator, void *ctx) -{ - auto *testContext = static_cast(ctx); - - /* Send StreamStart first */ - struct aws_iot_st_msg st_msg; - AWS_ZERO_STRUCT(st_msg); - st_msg.type = STREAM_START; - st_msg.stream_id = STREAM_ID; - s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, testContext->secureTunnel->GetUnderlyingHandle()); - - /* Send data */ - AWS_ZERO_STRUCT(st_msg); - st_msg.type = DATA; - st_msg.stream_id = STREAM_ID; - st_msg.payload = aws_byte_buf_from_c_str(PAYLOAD); - s_OnDataReceiveCorrectPayload = false; - s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, testContext->secureTunnel->GetUnderlyingHandle()); - - ASSERT_TRUE(s_OnDataReceiveCorrectPayload); - ASSERT_INT_EQUALS(STREAM_ID, testContext->secureTunnel->GetUnderlyingHandle()->stream_id); - ASSERT_UINT_EQUALS(0, testContext->secureTunnel->GetUnderlyingHandle()->received_data.len); - - return AWS_OP_SUCCESS; -} - -AWS_TEST_CASE_FIXTURE( - SecureTunnelingHandleStreamResetTest, - before, - SecureTunnelingHandleStreamResetTest, - after, - &s_testContext); -static int SecureTunnelingHandleStreamResetTest(Aws::Crt::Allocator *allocator, void *ctx) -{ - auto *testContext = static_cast(ctx); - - /* Send StreamStart first */ - struct aws_iot_st_msg st_msg; - AWS_ZERO_STRUCT(st_msg); - st_msg.type = STREAM_START; - st_msg.stream_id = STREAM_ID; - s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, testContext->secureTunnel->GetUnderlyingHandle()); - - /* Send StreamReset */ - AWS_ZERO_STRUCT(st_msg); - st_msg.type = STREAM_RESET; - st_msg.stream_id = STREAM_ID; - s_OnStreamResetCalled = false; - s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, testContext->secureTunnel->GetUnderlyingHandle()); - - ASSERT_TRUE(s_OnStreamResetCalled); - ASSERT_INT_EQUALS(INVALID_STREAM_ID, testContext->secureTunnel->GetUnderlyingHandle()->stream_id); - ASSERT_UINT_EQUALS(0, testContext->secureTunnel->GetUnderlyingHandle()->received_data.len); - - return AWS_OP_SUCCESS; -} - -AWS_TEST_CASE_FIXTURE( - SecureTunnelingHandleSessionResetTest, - before, - s_SecureTunnelingHandleSessionResetTest, - after, - &s_testContext); -static int s_SecureTunnelingHandleSessionResetTest(struct aws_allocator *allocator, void *ctx) -{ - auto *testContext = static_cast(ctx); - - /* Send StreamStart first */ - struct aws_iot_st_msg st_msg; - AWS_ZERO_STRUCT(st_msg); - st_msg.type = STREAM_START; - st_msg.stream_id = STREAM_ID; - s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, testContext->secureTunnel->GetUnderlyingHandle()); - - /* Send StreamReset */ - AWS_ZERO_STRUCT(st_msg); - st_msg.type = SESSION_RESET; - st_msg.stream_id = STREAM_ID; - s_OnSessionResetCalled = false; - s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, testContext->secureTunnel->GetUnderlyingHandle()); - - ASSERT_TRUE(s_OnSessionResetCalled); - ASSERT_INT_EQUALS(INVALID_STREAM_ID, testContext->secureTunnel->GetUnderlyingHandle()->stream_id); - ASSERT_UINT_EQUALS(0, testContext->secureTunnel->GetUnderlyingHandle()->received_data.len); - - return AWS_OP_SUCCESS; -} +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include + +// using namespace std; +// using namespace Aws::Crt::Io; +// using namespace Aws::Iotdevicecommon; +// using namespace Aws::Iotsecuretunneling; + +// #define INVALID_STREAM_ID 0 +// #define STREAM_ID 10 +// #define PAYLOAD "secure tunneling data payload" + +// extern "C" +// { +// struct aws_websocket_incoming_frame; +// extern bool on_websocket_incoming_frame_payload( +// struct aws_websocket *websocket, +// const struct aws_websocket_incoming_frame *frame, +// struct aws_byte_cursor data, +// void *user_data); +// } + +// struct SecureTunnelingTestContext +// { +// unique_ptr deviceApiHandle; +// unique_ptr elGroup; +// unique_ptr resolver; +// unique_ptr clientBootstrap; +// shared_ptr secureTunnel; + +// aws_secure_tunneling_local_proxy_mode localProxyMode; + +// SecureTunnelingTestContext() { localProxyMode = AWS_SECURE_TUNNELING_DESTINATION_MODE; } +// }; +// static SecureTunnelingTestContext s_testContext; + +// // Client callbacks implementation +// static void s_OnConnectionComplete() {} +// static void s_OnConnectionShutdown() {} + +// static void s_OnSendDataComplete(int errorCode) {} + +// static bool s_OnDataReceiveCorrectPayload = false; +// static void s_OnDataReceive(const Aws::Crt::ByteBuf &data) +// { +// s_OnDataReceiveCorrectPayload = aws_byte_buf_eq_c_str(&data, PAYLOAD); +// } + +// static bool s_OnStreamStartCalled = false; +// static void s_OnStreamStart() +// { +// s_OnStreamStartCalled = true; +// } + +// static bool s_OnStreamResetCalled = false; +// static void s_OnStreamReset() +// { +// s_OnStreamResetCalled = true; +// } + +// static bool s_OnSessionResetCalled = false; +// static void s_OnSessionReset() +// { +// s_OnSessionResetCalled = true; +// } + +// static int before(struct aws_allocator *allocator, void *ctx) +// { +// auto *testContext = static_cast(ctx); + +// aws_http_library_init(allocator); + +// testContext->deviceApiHandle = unique_ptr(new DeviceApiHandle(allocator)); +// testContext->elGroup = unique_ptr(new EventLoopGroup(1, allocator)); +// testContext->resolver = unique_ptr(new DefaultHostResolver(*testContext->elGroup, 8, 30, +// allocator)); testContext->clientBootstrap = +// unique_ptr(new ClientBootstrap(*testContext->elGroup, *testContext->resolver, allocator)); +// testContext->secureTunnel = SecureTunnelBuilder( +// allocator, +// *testContext->clientBootstrap, +// SocketOptions(), +// "access_token", +// testContext->localProxyMode, +// "endpoint") +// .WithRootCa("") +// .WithOnConnectionComplete(s_OnConnectionComplete) +// .WithOnConnectionShutdown(s_OnConnectionShutdown) +// .WithOnSendDataComplete(s_OnSendDataComplete) +// .WithOnDataReceive(s_OnDataReceive) +// .WithOnStreamStart(s_OnStreamStart) +// .WithOnStreamReset(s_OnStreamReset) +// .WithOnSessionReset(s_OnSessionReset) +// .Build(); +// return AWS_ERROR_SUCCESS; +// } + +// static int after(struct aws_allocator *allocator, int setup_result, void *ctx) +// { +// auto *testContext = static_cast(ctx); + +// testContext->secureTunnel->Shutdown(); + +// testContext->secureTunnel.reset(); +// testContext->clientBootstrap.reset(); +// testContext->resolver.reset(); +// testContext->elGroup.reset(); +// testContext->deviceApiHandle.reset(); + +// aws_http_library_clean_up(); + +// return AWS_ERROR_SUCCESS; +// } + +// static void s_send_secure_tunneling_frame_to_websocket( +// const struct aws_iot_st_msg *st_msg, +// struct aws_allocator *allocator, +// struct aws_secure_tunnel *secure_tunnel) +// { +// struct aws_byte_buf serialized_st_msg; +// aws_iot_st_msg_serialize_from_struct(&serialized_st_msg, allocator, *st_msg); + +// /* Prepend 2 bytes length */ +// struct aws_byte_buf websocket_frame; +// aws_byte_buf_init(&websocket_frame, allocator, serialized_st_msg.len + 2); +// aws_byte_buf_write_be16(&websocket_frame, (uint16_t)serialized_st_msg.len); +// struct aws_byte_cursor c = aws_byte_cursor_from_buf(&serialized_st_msg); +// aws_byte_buf_append(&websocket_frame, &c); +// c = aws_byte_cursor_from_buf(&websocket_frame); + +// on_websocket_incoming_frame_payload(NULL, NULL, c, secure_tunnel); + +// aws_byte_buf_clean_up(&serialized_st_msg); +// aws_byte_buf_clean_up(&websocket_frame); +// } + +// AWS_TEST_CASE_FIXTURE( +// SecureTunnelingHandleStreamStartTest, +// before, +// s_SecureTunnelingHandleStreamStartTest, +// after, +// &s_testContext); +// static int s_SecureTunnelingHandleStreamStartTest(Aws::Crt::Allocator *allocator, void *ctx) +// { +// auto *testContext = static_cast(ctx); + +// struct aws_iot_st_msg st_msg; +// AWS_ZERO_STRUCT(st_msg); +// st_msg.type = STREAM_START; +// st_msg.stream_id = STREAM_ID; +// s_OnStreamStartCalled = false; +// s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, testContext->secureTunnel->GetUnderlyingHandle()); + +// ASSERT_TRUE(s_OnStreamStartCalled); +// ASSERT_INT_EQUALS(STREAM_ID, testContext->secureTunnel->GetUnderlyingHandle()->stream_id); +// ASSERT_UINT_EQUALS(0, testContext->secureTunnel->GetUnderlyingHandle()->received_data.len); + +// return AWS_ERROR_SUCCESS; +// } + +// AWS_TEST_CASE_FIXTURE( +// SecureTunnelingHandleDataReceiveTest, +// before, +// s_SecureTunnelingHandleDataReceiveTest, +// after, +// &s_testContext); +// static int s_SecureTunnelingHandleDataReceiveTest(Aws::Crt::Allocator *allocator, void *ctx) +// { +// auto *testContext = static_cast(ctx); + +// /* Send StreamStart first */ +// struct aws_iot_st_msg st_msg; +// AWS_ZERO_STRUCT(st_msg); +// st_msg.type = STREAM_START; +// st_msg.stream_id = STREAM_ID; +// s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, testContext->secureTunnel->GetUnderlyingHandle()); + +// /* Send data */ +// AWS_ZERO_STRUCT(st_msg); +// st_msg.type = DATA; +// st_msg.stream_id = STREAM_ID; +// st_msg.payload = aws_byte_buf_from_c_str(PAYLOAD); +// s_OnDataReceiveCorrectPayload = false; +// s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, testContext->secureTunnel->GetUnderlyingHandle()); + +// ASSERT_TRUE(s_OnDataReceiveCorrectPayload); +// ASSERT_INT_EQUALS(STREAM_ID, testContext->secureTunnel->GetUnderlyingHandle()->stream_id); +// ASSERT_UINT_EQUALS(0, testContext->secureTunnel->GetUnderlyingHandle()->received_data.len); + +// return AWS_OP_SUCCESS; +// } + +// AWS_TEST_CASE_FIXTURE( +// SecureTunnelingHandleStreamResetTest, +// before, +// SecureTunnelingHandleStreamResetTest, +// after, +// &s_testContext); +// static int SecureTunnelingHandleStreamResetTest(Aws::Crt::Allocator *allocator, void *ctx) +// { +// auto *testContext = static_cast(ctx); + +// /* Send StreamStart first */ +// struct aws_iot_st_msg st_msg; +// AWS_ZERO_STRUCT(st_msg); +// st_msg.type = STREAM_START; +// st_msg.stream_id = STREAM_ID; +// s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, testContext->secureTunnel->GetUnderlyingHandle()); + +// /* Send StreamReset */ +// AWS_ZERO_STRUCT(st_msg); +// st_msg.type = STREAM_RESET; +// st_msg.stream_id = STREAM_ID; +// s_OnStreamResetCalled = false; +// s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, testContext->secureTunnel->GetUnderlyingHandle()); + +// ASSERT_TRUE(s_OnStreamResetCalled); +// ASSERT_INT_EQUALS(INVALID_STREAM_ID, testContext->secureTunnel->GetUnderlyingHandle()->stream_id); +// ASSERT_UINT_EQUALS(0, testContext->secureTunnel->GetUnderlyingHandle()->received_data.len); + +// return AWS_OP_SUCCESS; +// } + +// AWS_TEST_CASE_FIXTURE( +// SecureTunnelingHandleSessionResetTest, +// before, +// s_SecureTunnelingHandleSessionResetTest, +// after, +// &s_testContext); +// static int s_SecureTunnelingHandleSessionResetTest(struct aws_allocator *allocator, void *ctx) +// { +// auto *testContext = static_cast(ctx); + +// /* Send StreamStart first */ +// struct aws_iot_st_msg st_msg; +// AWS_ZERO_STRUCT(st_msg); +// st_msg.type = STREAM_START; +// st_msg.stream_id = STREAM_ID; +// s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, testContext->secureTunnel->GetUnderlyingHandle()); + +// /* Send StreamReset */ +// AWS_ZERO_STRUCT(st_msg); +// st_msg.type = SESSION_RESET; +// st_msg.stream_id = STREAM_ID; +// s_OnSessionResetCalled = false; +// s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, testContext->secureTunnel->GetUnderlyingHandle()); + +// ASSERT_TRUE(s_OnSessionResetCalled); +// ASSERT_INT_EQUALS(INVALID_STREAM_ID, testContext->secureTunnel->GetUnderlyingHandle()->stream_id); +// ASSERT_UINT_EQUALS(0, testContext->secureTunnel->GetUnderlyingHandle()->received_data.len); + +// return AWS_OP_SUCCESS; +// } From d1d893e990df451002aabf48c98c83958010bcbd Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 22 Feb 2023 16:11:56 -0800 Subject: [PATCH 18/32] actually remove old tests --- secure_tunneling/tests/CMakeLists.txt | 20 -- secure_tunneling/tests/SecureTunnelTest.cpp | 263 -------------------- 2 files changed, 283 deletions(-) delete mode 100644 secure_tunneling/tests/CMakeLists.txt delete mode 100644 secure_tunneling/tests/SecureTunnelTest.cpp diff --git a/secure_tunneling/tests/CMakeLists.txt b/secure_tunneling/tests/CMakeLists.txt deleted file mode 100644 index 044e52589..000000000 --- a/secure_tunneling/tests/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -include(AwsTestHarness) -enable_testing() -include(CTest) - -file(GLOB TEST_SRC "*.cpp") -file(GLOB TEST_HDRS "*.h") -file(GLOB TESTS ${TEST_HDRS} ${TEST_SRC}) - -set(TEST_BINARY_NAME ${PROJECT_NAME}-tests) - -aws_use_package(aws-crt-cpp) -aws_use_package(IotSecureTunneling-cpp) - -if (UNIX AND NOT APPLE) - # add_test_case(SecureTunnelingHandleStreamStartTest) - # add_test_case(SecureTunnelingHandleDataReceiveTest) - # add_test_case(SecureTunnelingHandleStreamResetTest) - # add_test_case(SecureTunnelingHandleSessionResetTest) - generate_cpp_test_driver(${TEST_BINARY_NAME}) -endif() diff --git a/secure_tunneling/tests/SecureTunnelTest.cpp b/secure_tunneling/tests/SecureTunnelTest.cpp deleted file mode 100644 index d1d02c110..000000000 --- a/secure_tunneling/tests/SecureTunnelTest.cpp +++ /dev/null @@ -1,263 +0,0 @@ -/** - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include - -// using namespace std; -// using namespace Aws::Crt::Io; -// using namespace Aws::Iotdevicecommon; -// using namespace Aws::Iotsecuretunneling; - -// #define INVALID_STREAM_ID 0 -// #define STREAM_ID 10 -// #define PAYLOAD "secure tunneling data payload" - -// extern "C" -// { -// struct aws_websocket_incoming_frame; -// extern bool on_websocket_incoming_frame_payload( -// struct aws_websocket *websocket, -// const struct aws_websocket_incoming_frame *frame, -// struct aws_byte_cursor data, -// void *user_data); -// } - -// struct SecureTunnelingTestContext -// { -// unique_ptr deviceApiHandle; -// unique_ptr elGroup; -// unique_ptr resolver; -// unique_ptr clientBootstrap; -// shared_ptr secureTunnel; - -// aws_secure_tunneling_local_proxy_mode localProxyMode; - -// SecureTunnelingTestContext() { localProxyMode = AWS_SECURE_TUNNELING_DESTINATION_MODE; } -// }; -// static SecureTunnelingTestContext s_testContext; - -// // Client callbacks implementation -// static void s_OnConnectionComplete() {} -// static void s_OnConnectionShutdown() {} - -// static void s_OnSendDataComplete(int errorCode) {} - -// static bool s_OnDataReceiveCorrectPayload = false; -// static void s_OnDataReceive(const Aws::Crt::ByteBuf &data) -// { -// s_OnDataReceiveCorrectPayload = aws_byte_buf_eq_c_str(&data, PAYLOAD); -// } - -// static bool s_OnStreamStartCalled = false; -// static void s_OnStreamStart() -// { -// s_OnStreamStartCalled = true; -// } - -// static bool s_OnStreamResetCalled = false; -// static void s_OnStreamReset() -// { -// s_OnStreamResetCalled = true; -// } - -// static bool s_OnSessionResetCalled = false; -// static void s_OnSessionReset() -// { -// s_OnSessionResetCalled = true; -// } - -// static int before(struct aws_allocator *allocator, void *ctx) -// { -// auto *testContext = static_cast(ctx); - -// aws_http_library_init(allocator); - -// testContext->deviceApiHandle = unique_ptr(new DeviceApiHandle(allocator)); -// testContext->elGroup = unique_ptr(new EventLoopGroup(1, allocator)); -// testContext->resolver = unique_ptr(new DefaultHostResolver(*testContext->elGroup, 8, 30, -// allocator)); testContext->clientBootstrap = -// unique_ptr(new ClientBootstrap(*testContext->elGroup, *testContext->resolver, allocator)); -// testContext->secureTunnel = SecureTunnelBuilder( -// allocator, -// *testContext->clientBootstrap, -// SocketOptions(), -// "access_token", -// testContext->localProxyMode, -// "endpoint") -// .WithRootCa("") -// .WithOnConnectionComplete(s_OnConnectionComplete) -// .WithOnConnectionShutdown(s_OnConnectionShutdown) -// .WithOnSendDataComplete(s_OnSendDataComplete) -// .WithOnDataReceive(s_OnDataReceive) -// .WithOnStreamStart(s_OnStreamStart) -// .WithOnStreamReset(s_OnStreamReset) -// .WithOnSessionReset(s_OnSessionReset) -// .Build(); -// return AWS_ERROR_SUCCESS; -// } - -// static int after(struct aws_allocator *allocator, int setup_result, void *ctx) -// { -// auto *testContext = static_cast(ctx); - -// testContext->secureTunnel->Shutdown(); - -// testContext->secureTunnel.reset(); -// testContext->clientBootstrap.reset(); -// testContext->resolver.reset(); -// testContext->elGroup.reset(); -// testContext->deviceApiHandle.reset(); - -// aws_http_library_clean_up(); - -// return AWS_ERROR_SUCCESS; -// } - -// static void s_send_secure_tunneling_frame_to_websocket( -// const struct aws_iot_st_msg *st_msg, -// struct aws_allocator *allocator, -// struct aws_secure_tunnel *secure_tunnel) -// { -// struct aws_byte_buf serialized_st_msg; -// aws_iot_st_msg_serialize_from_struct(&serialized_st_msg, allocator, *st_msg); - -// /* Prepend 2 bytes length */ -// struct aws_byte_buf websocket_frame; -// aws_byte_buf_init(&websocket_frame, allocator, serialized_st_msg.len + 2); -// aws_byte_buf_write_be16(&websocket_frame, (uint16_t)serialized_st_msg.len); -// struct aws_byte_cursor c = aws_byte_cursor_from_buf(&serialized_st_msg); -// aws_byte_buf_append(&websocket_frame, &c); -// c = aws_byte_cursor_from_buf(&websocket_frame); - -// on_websocket_incoming_frame_payload(NULL, NULL, c, secure_tunnel); - -// aws_byte_buf_clean_up(&serialized_st_msg); -// aws_byte_buf_clean_up(&websocket_frame); -// } - -// AWS_TEST_CASE_FIXTURE( -// SecureTunnelingHandleStreamStartTest, -// before, -// s_SecureTunnelingHandleStreamStartTest, -// after, -// &s_testContext); -// static int s_SecureTunnelingHandleStreamStartTest(Aws::Crt::Allocator *allocator, void *ctx) -// { -// auto *testContext = static_cast(ctx); - -// struct aws_iot_st_msg st_msg; -// AWS_ZERO_STRUCT(st_msg); -// st_msg.type = STREAM_START; -// st_msg.stream_id = STREAM_ID; -// s_OnStreamStartCalled = false; -// s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, testContext->secureTunnel->GetUnderlyingHandle()); - -// ASSERT_TRUE(s_OnStreamStartCalled); -// ASSERT_INT_EQUALS(STREAM_ID, testContext->secureTunnel->GetUnderlyingHandle()->stream_id); -// ASSERT_UINT_EQUALS(0, testContext->secureTunnel->GetUnderlyingHandle()->received_data.len); - -// return AWS_ERROR_SUCCESS; -// } - -// AWS_TEST_CASE_FIXTURE( -// SecureTunnelingHandleDataReceiveTest, -// before, -// s_SecureTunnelingHandleDataReceiveTest, -// after, -// &s_testContext); -// static int s_SecureTunnelingHandleDataReceiveTest(Aws::Crt::Allocator *allocator, void *ctx) -// { -// auto *testContext = static_cast(ctx); - -// /* Send StreamStart first */ -// struct aws_iot_st_msg st_msg; -// AWS_ZERO_STRUCT(st_msg); -// st_msg.type = STREAM_START; -// st_msg.stream_id = STREAM_ID; -// s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, testContext->secureTunnel->GetUnderlyingHandle()); - -// /* Send data */ -// AWS_ZERO_STRUCT(st_msg); -// st_msg.type = DATA; -// st_msg.stream_id = STREAM_ID; -// st_msg.payload = aws_byte_buf_from_c_str(PAYLOAD); -// s_OnDataReceiveCorrectPayload = false; -// s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, testContext->secureTunnel->GetUnderlyingHandle()); - -// ASSERT_TRUE(s_OnDataReceiveCorrectPayload); -// ASSERT_INT_EQUALS(STREAM_ID, testContext->secureTunnel->GetUnderlyingHandle()->stream_id); -// ASSERT_UINT_EQUALS(0, testContext->secureTunnel->GetUnderlyingHandle()->received_data.len); - -// return AWS_OP_SUCCESS; -// } - -// AWS_TEST_CASE_FIXTURE( -// SecureTunnelingHandleStreamResetTest, -// before, -// SecureTunnelingHandleStreamResetTest, -// after, -// &s_testContext); -// static int SecureTunnelingHandleStreamResetTest(Aws::Crt::Allocator *allocator, void *ctx) -// { -// auto *testContext = static_cast(ctx); - -// /* Send StreamStart first */ -// struct aws_iot_st_msg st_msg; -// AWS_ZERO_STRUCT(st_msg); -// st_msg.type = STREAM_START; -// st_msg.stream_id = STREAM_ID; -// s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, testContext->secureTunnel->GetUnderlyingHandle()); - -// /* Send StreamReset */ -// AWS_ZERO_STRUCT(st_msg); -// st_msg.type = STREAM_RESET; -// st_msg.stream_id = STREAM_ID; -// s_OnStreamResetCalled = false; -// s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, testContext->secureTunnel->GetUnderlyingHandle()); - -// ASSERT_TRUE(s_OnStreamResetCalled); -// ASSERT_INT_EQUALS(INVALID_STREAM_ID, testContext->secureTunnel->GetUnderlyingHandle()->stream_id); -// ASSERT_UINT_EQUALS(0, testContext->secureTunnel->GetUnderlyingHandle()->received_data.len); - -// return AWS_OP_SUCCESS; -// } - -// AWS_TEST_CASE_FIXTURE( -// SecureTunnelingHandleSessionResetTest, -// before, -// s_SecureTunnelingHandleSessionResetTest, -// after, -// &s_testContext); -// static int s_SecureTunnelingHandleSessionResetTest(struct aws_allocator *allocator, void *ctx) -// { -// auto *testContext = static_cast(ctx); - -// /* Send StreamStart first */ -// struct aws_iot_st_msg st_msg; -// AWS_ZERO_STRUCT(st_msg); -// st_msg.type = STREAM_START; -// st_msg.stream_id = STREAM_ID; -// s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, testContext->secureTunnel->GetUnderlyingHandle()); - -// /* Send StreamReset */ -// AWS_ZERO_STRUCT(st_msg); -// st_msg.type = SESSION_RESET; -// st_msg.stream_id = STREAM_ID; -// s_OnSessionResetCalled = false; -// s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, testContext->secureTunnel->GetUnderlyingHandle()); - -// ASSERT_TRUE(s_OnSessionResetCalled); -// ASSERT_INT_EQUALS(INVALID_STREAM_ID, testContext->secureTunnel->GetUnderlyingHandle()->stream_id); -// ASSERT_UINT_EQUALS(0, testContext->secureTunnel->GetUnderlyingHandle()->received_data.len); - -// return AWS_OP_SUCCESS; -// } From 4c0715d8dcc3c8c561c7c65b358d93648f4c311e Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 22 Feb 2023 16:15:11 -0800 Subject: [PATCH 19/32] fix CMakeLists.txt --- secure_tunneling/CMakeLists.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/secure_tunneling/CMakeLists.txt b/secure_tunneling/CMakeLists.txt index eaa9d7dc7..e913c7316 100644 --- a/secure_tunneling/CMakeLists.txt +++ b/secure_tunneling/CMakeLists.txt @@ -137,6 +137,3 @@ install(FILES "${CMAKE_CURRENT_BINARY_DIR}/iotsecuretunneling-cpp-config.cmake" DESTINATION "${CMAKE_INSTALL_LIBDIR}/IotSecureTunneling-cpp/cmake/" COMPONENT Development) -if (BUILD_TESTING) - add_subdirectory(tests) -endif() From 3b35141ce0e6109927344a9a087af4bfdfcda9ab Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 22 Feb 2023 16:22:28 -0800 Subject: [PATCH 20/32] don't zero an optional --- secure_tunneling/source/SecureTunnel.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/secure_tunneling/source/SecureTunnel.cpp b/secure_tunneling/source/SecureTunnel.cpp index fad2a4170..1d69a68ae 100644 --- a/secure_tunneling/source/SecureTunnel.cpp +++ b/secure_tunneling/source/SecureTunnel.cpp @@ -169,7 +169,6 @@ namespace Aws Crt::Allocator *allocator) noexcept : m_allocator(allocator) { - AWS_ZERO_STRUCT(m_serviceId); AWS_ZERO_STRUCT(m_serviceIdStorage); setPacketByteBufOptional(m_serviceId, m_serviceIdStorage, m_allocator, message.service_id); From 25ddd1baea4f0c94b24b36c1ff36ceb0389fe5b9 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 22 Feb 2023 16:33:12 -0800 Subject: [PATCH 21/32] typo --- samples/secure_tunneling/secure_tunnel/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/secure_tunneling/secure_tunnel/main.cpp b/samples/secure_tunneling/secure_tunnel/main.cpp index 2d0d01f84..782739d0e 100644 --- a/samples/secure_tunneling/secure_tunnel/main.cpp +++ b/samples/secure_tunneling/secure_tunnel/main.cpp @@ -46,7 +46,7 @@ void logMessage(std::shared_ptr message) "Message received with payload:'" PRInSTR "'\n", AWS_BYTE_CURSOR_PRI(message->getPayload().value())); } -}; +} void setupCommandLineUtils(Utils::CommandLineUtils *cmdUtils, int argc, char *argv[]) { From 24f33c8e1ce9c4b14062698f580b31ec17bd3a30 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 22 Feb 2023 16:46:53 -0800 Subject: [PATCH 22/32] windows warnings as errors --- secure_tunneling/source/SecureTunnel.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/secure_tunneling/source/SecureTunnel.cpp b/secure_tunneling/source/SecureTunnel.cpp index 1d69a68ae..fa0d644fb 100644 --- a/secure_tunneling/source/SecureTunnel.cpp +++ b/secure_tunneling/source/SecureTunnel.cpp @@ -692,6 +692,7 @@ namespace Aws void SecureTunnel::s_OnConnectionShutdown(int error_code, void *user_data) { + (void)error_code; SecureTunnel *secureTunnel = static_cast(user_data); if (secureTunnel->m_OnConnectionShutdown) { @@ -781,6 +782,8 @@ namespace Aws int error_code, void *user_data) { + (void)message; + (void)error_code; SecureTunnel *secureTunnel = static_cast(user_data); if (secureTunnel->m_OnStreamReset) { From c1e7602fd24e84b4010d57adee2c4004c579bb19 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 22 Feb 2023 17:10:21 -0800 Subject: [PATCH 23/32] expand stream stopped callback --- .../aws/iotsecuretunneling/SecureTunnel.h | 79 ++++++++++++++++++- secure_tunneling/source/SecureTunnel.cpp | 32 ++++++-- 2 files changed, 99 insertions(+), 12 deletions(-) diff --git a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h index d62b7263b..d51b80c23 100644 --- a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h +++ b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h @@ -226,6 +226,59 @@ namespace Aws std::shared_ptr streamStartedData; }; + /** + * Data model for started Secure Tunnel streams. + */ + class AWS_IOTSECURETUNNELING_API StreamStoppedData + { + public: + StreamStoppedData( + const aws_secure_tunnel_message_view &raw_options, + Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; + + /** + * Service id of the stopped stream. + * + * @return Service id of the stopped stream. + */ + const Crt::Optional &getServiceId() const noexcept; + + /** + * Stream id of the stopped stream. + */ + + virtual ~StreamStoppedData(); + /* Do not allow direct copy or move */ + StreamStoppedData(const StreamStoppedData &) = delete; + StreamStoppedData(StreamStoppedData &&) noexcept = delete; + StreamStoppedData &operator=(const StreamStoppedData &) = delete; + StreamStoppedData &operator=(StreamStoppedData &&) noexcept = delete; + + private: + Crt::Allocator *m_allocator; + + /** + * Service id of started stream. + * + * If left empty, a V1 protocolstream is assumed. + */ + Crt::Optional m_serviceId; + + /////////////////////////////////////////////////////////////////////////// + // Underlying data storage for internal use + /////////////////////////////////////////////////////////////////////////// + Crt::ByteBuf m_serviceIdStorage; + }; + + /** + * The data returned when a stream is closed on the Secure Tunnel. + */ + struct AWS_IOTSECURETUNNELING_API StreamStoppedEventData + { + StreamStoppedEventData() : streamStoppedData(nullptr) {} + std::shared_ptr streamStoppedData; + }; + class SecureTunnel; // Client callback type definitions @@ -264,6 +317,12 @@ namespace Aws using OnStreamStarted = std::function; + /** + * Type signature of the callback invoked when a stream has been closed + */ + + using OnStreamStopped = std::function; + /** * Type signature of the callback invoked when a stream is reset. */ @@ -557,8 +616,11 @@ namespace Aws /** * Callback handler trigged when secure tunnel receives a stream reset. + * + * @param SecureTunnel: The shared secure tunnel + * @param StreamStoppedEventData: Stream Started data */ - OnStreamReset m_OnStreamReset; + OnStreamStopped m_OnStreamStopped; /** * Callback handler trigged when secure tunnel receives a session reset from the secure tunnel service. @@ -582,6 +644,10 @@ namespace Aws * Deprecated - Use m_OnStreamStarted */ OnStreamStart m_OnStreamStart; + /** + * Deprecated - Use m_OnStreamStopped + */ + OnStreamReset m_OnStreamReset; friend class SecureTunnel; }; @@ -738,6 +804,7 @@ namespace Aws OnDataReceive onDataReceive, OnStreamStarted onStreamStarted, OnStreamStart onStreamStart, + OnStreamStopped onStreamStopped, OnStreamReset onStreamReset, OnSessionReset onSessionReset, OnStopped onStopped); @@ -751,7 +818,7 @@ namespace Aws static void s_OnConnectionFailure(int error_code, void *user_data); static void s_OnConnectionShutdown(int error_code, void *user_data); static void s_OnSendDataComplete(int error_code, void *user_data); - static void s_OnStreamReset( + static void s_OnStreamStopped( const struct aws_secure_tunnel_message_view *message, int error_code, void *user_data); @@ -798,9 +865,9 @@ namespace Aws OnStreamStarted m_OnStreamStarted; /** - * Callback handler trigged when secure tunnel receives a stream reset. + * Callback handler trigged when secure tunnel closes a stream */ - OnStreamReset m_OnStreamReset; + OnStreamStopped m_OnStreamStopped; /** * Callback handler trigged when secure tunnel receives a session reset from the secure tunnel service. @@ -827,6 +894,10 @@ namespace Aws * Deprecated - Use m_OnStreamStarted */ OnStreamStart m_OnStreamStart; + /** + * Deprecated - Use m_OnStreamStopped + */ + OnStreamReset m_OnStreamReset; std::shared_ptr m_selfRef; // std::promise m_TerminationComplete; diff --git a/secure_tunneling/source/SecureTunnel.cpp b/secure_tunneling/source/SecureTunnel.cpp index fa0d644fb..b9820d33b 100644 --- a/secure_tunneling/source/SecureTunnel.cpp +++ b/secure_tunneling/source/SecureTunnel.cpp @@ -190,8 +190,8 @@ namespace Aws const std::string &endpointHost) // Make a copy and save in this object : m_allocator(allocator), m_clientBootstrap(&clientBootstrap), m_socketOptions(socketOptions), m_accessToken(accessToken), m_localProxyMode(localProxyMode), m_endpointHost(endpointHost), m_rootCa(""), - m_httpClientConnectionProxyOptions(), m_OnConnectionShutdown(), m_OnSendDataComplete(), m_OnStreamReset(), - m_OnSessionReset(), m_OnConnectionComplete(), m_OnDataReceive(), m_OnStreamStart() + m_httpClientConnectionProxyOptions(), m_OnConnectionShutdown(), m_OnSendDataComplete(), + m_OnSessionReset(), m_OnConnectionComplete(), m_OnDataReceive(), m_OnStreamStart(), m_OnStreamReset() { } @@ -204,8 +204,8 @@ namespace Aws : m_allocator(allocator), m_clientBootstrap(Crt::ApiHandle::GetOrCreateStaticDefaultClientBootstrap()), m_socketOptions(socketOptions), m_accessToken(accessToken), m_localProxyMode(localProxyMode), m_endpointHost(endpointHost), m_rootCa(""), m_httpClientConnectionProxyOptions(), - m_OnConnectionShutdown(), m_OnSendDataComplete(), m_OnStreamReset(), m_OnSessionReset(), - m_OnConnectionComplete(), m_OnDataReceive(), m_OnStreamStart() + m_OnConnectionShutdown(), m_OnSendDataComplete(), m_OnSessionReset(), m_OnConnectionComplete(), + m_OnDataReceive(), m_OnStreamStart(), m_OnStreamReset() { } @@ -217,8 +217,8 @@ namespace Aws : m_allocator(allocator), m_clientBootstrap(Crt::ApiHandle::GetOrCreateStaticDefaultClientBootstrap()), m_socketOptions(Crt::Io::SocketOptions()), m_accessToken(accessToken), m_localProxyMode(localProxyMode), m_endpointHost(endpointHost), m_rootCa(""), m_httpClientConnectionProxyOptions(), - m_OnConnectionShutdown(), m_OnSendDataComplete(), m_OnStreamReset(), m_OnSessionReset(), - m_OnConnectionComplete(), m_OnDataReceive(), m_OnStreamStart() + m_OnConnectionShutdown(), m_OnSendDataComplete(), m_OnSessionReset(), m_OnConnectionComplete(), + m_OnDataReceive(), m_OnStreamStart(), m_OnStreamReset() { } @@ -335,6 +335,7 @@ namespace Aws m_OnDataReceive, m_OnStreamStarted, m_OnStreamStart, + m_OnStreamStopped, m_OnStreamReset, m_OnSessionReset, m_OnStopped)); @@ -376,6 +377,7 @@ namespace Aws OnDataReceive onDataReceive, OnStreamStarted onStreamStarted, OnStreamStart onStreamStart, + OnStreamStopped onStreamStopped, OnStreamReset onStreamReset, OnSessionReset onSessionReset, OnStopped onStopped) @@ -421,7 +423,7 @@ namespace Aws config.on_connection_shutdown = s_OnConnectionShutdown; config.on_send_data_complete = s_OnSendDataComplete; config.on_stream_start = s_OnStreamStarted; - config.on_stream_reset = s_OnStreamReset; + config.on_stream_reset = s_OnStreamStopped; config.on_session_reset = s_OnSessionReset; config.on_stopped = s_OnStopped; @@ -479,6 +481,7 @@ namespace Aws onDataReceive, nullptr, onStreamStart, + nullptr, onStreamReset, onSessionReset, nullptr) @@ -523,6 +526,7 @@ namespace Aws onDataReceive, nullptr, onStreamStart, + nullptr, onStreamReset, onSessionReset, nullptr) @@ -777,7 +781,7 @@ namespace Aws } } - void SecureTunnel::s_OnStreamReset( + void SecureTunnel::s_OnStreamStopped( const struct aws_secure_tunnel_message_view *message, int error_code, void *user_data) @@ -785,6 +789,18 @@ namespace Aws (void)message; (void)error_code; SecureTunnel *secureTunnel = static_cast(user_data); + + if (secureTunnel->m_OnStreamStopped) + { + std::shared_ptr packet = + std::make_shared(*message, secureTunnel->m_allocator); + StreamStoppedEventData eventData; + eventData.streamStoppedData = packet; + secureTunnel->m_OnStreamStopped(secureTunnel, eventData); + return; + } + + /* Fall back on deprecated stream reset callback */ if (secureTunnel->m_OnStreamReset) { secureTunnel->m_OnStreamReset(); From cbe140a6cf0020741cb1d072e300305fa5f831fb Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Thu, 23 Feb 2023 09:20:26 -0800 Subject: [PATCH 24/32] finish implementing StreamStoppedData --- .../secure_tunneling/secure_tunnel/main.cpp | 16 ++++++++++++ .../aws/iotsecuretunneling/SecureTunnel.h | 9 +++++++ secure_tunneling/source/SecureTunnel.cpp | 26 ++++++++++++++++++- 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/samples/secure_tunneling/secure_tunnel/main.cpp b/samples/secure_tunneling/secure_tunnel/main.cpp index 782739d0e..5e5858b15 100644 --- a/samples/secure_tunneling/secure_tunnel/main.cpp +++ b/samples/secure_tunneling/secure_tunnel/main.cpp @@ -366,6 +366,22 @@ int main(int argc, char *argv[]) } }); + builder.WithOnStreamStopped([&](SecureTunnel *secureTunnel, const StreamStoppedEventData &eventData) { + std::shared_ptr streamStoppedData = eventData.streamStoppedData; + + if (streamStoppedData->getServiceId().has_value()) + { + fprintf( + stdout, + "Stream stopped on service id: '" PRInSTR "'\n", + AWS_BYTE_CURSOR_PRI(streamStoppedData->getServiceId().value())); + } + else + { + fprintf(stdout, "Stream stopped using V1 Protocol"); + } + }); + builder.WithOnStopped([&](SecureTunnel *secureTunnel) { fprintf(stdout, "Secure Tunnel has entered Stopped State\n"); clientStoppedPromise.set_value(true); diff --git a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h index d51b80c23..d4e62367d 100644 --- a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h +++ b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h @@ -473,6 +473,15 @@ namespace Aws */ SecureTunnelBuilder &WithOnStreamStarted(OnStreamStarted onStreamStarted); + /** + * Setup callback handler trigged when an Secure Tunnel stops a stream. + * + * @param callback + * + * @return this builder object + */ + SecureTunnelBuilder &WithOnStreamStopped(OnStreamStopped onStreamStopped); + /** * Setup callback handler trigged when an Secure Tunnel receives a stream reset. * diff --git a/secure_tunneling/source/SecureTunnel.cpp b/secure_tunneling/source/SecureTunnel.cpp index b9820d33b..b76d182e9 100644 --- a/secure_tunneling/source/SecureTunnel.cpp +++ b/secure_tunneling/source/SecureTunnel.cpp @@ -161,7 +161,7 @@ namespace Aws } //*********************************************************************************************************************** - /* StreamStartData */ + /* StreamStartedData */ //*********************************************************************************************************************** StreamStartedData::StreamStartedData( @@ -178,6 +178,24 @@ namespace Aws StreamStartedData::~StreamStartedData() { aws_byte_buf_clean_up(&m_serviceIdStorage); } + //*********************************************************************************************************************** + /* StreamStoppedData */ + //*********************************************************************************************************************** + + StreamStoppedData::StreamStoppedData( + const aws_secure_tunnel_message_view &message, + Crt::Allocator *allocator) noexcept + : m_allocator(allocator) + { + AWS_ZERO_STRUCT(m_serviceIdStorage); + + setPacketByteBufOptional(m_serviceId, m_serviceIdStorage, m_allocator, message.service_id); + } + + const Crt::Optional &StreamStoppedData::getServiceId() const noexcept { return m_serviceId; } + + StreamStoppedData::~StreamStoppedData() { aws_byte_buf_clean_up(&m_serviceIdStorage); } + //*********************************************************************************************************************** /* SecureTunnelBuilder */ //*********************************************************************************************************************** @@ -271,6 +289,12 @@ namespace Aws return *this; } + SecureTunnelBuilder &SecureTunnelBuilder::WithOnStreamStopped(OnStreamStopped onStreamStopped) + { + m_OnStreamStopped = std::move(onStreamStopped); + return *this; + } + SecureTunnelBuilder &SecureTunnelBuilder::WithOnStreamReset(OnStreamReset onStreamReset) { m_OnStreamReset = std::move(onStreamReset); From da1c84a29439ca327194d970e2e5e0989aebb5f8 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Thu, 23 Feb 2023 09:32:07 -0800 Subject: [PATCH 25/32] fix warnings in sample --- samples/secure_tunneling/secure_tunnel/main.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/samples/secure_tunneling/secure_tunnel/main.cpp b/samples/secure_tunneling/secure_tunnel/main.cpp index 5e5858b15..af15ef9e4 100644 --- a/samples/secure_tunneling/secure_tunnel/main.cpp +++ b/samples/secure_tunneling/secure_tunnel/main.cpp @@ -307,6 +307,7 @@ int main(int argc, char *argv[]) }); builder.WithOnConnectionFailure([&](SecureTunnel *secureTunnel, int errorCode) { + (void)secureTunnel; fprintf(stdout, "Connection attempt failed with error code %d(%s)\n", errorCode, ErrorDebugString(errorCode)); }); @@ -348,6 +349,7 @@ int main(int argc, char *argv[]) builder.WithOnStreamStarted( [&](SecureTunnel *secureTunnel, int errorCode, const StreamStartedEventData &eventData) { + (void)secureTunnel; if (!errorCode) { std::shared_ptr streamStartedData = eventData.streamStartedData; @@ -367,6 +369,7 @@ int main(int argc, char *argv[]) }); builder.WithOnStreamStopped([&](SecureTunnel *secureTunnel, const StreamStoppedEventData &eventData) { + (void)secureTunnel; std::shared_ptr streamStoppedData = eventData.streamStoppedData; if (streamStoppedData->getServiceId().has_value()) @@ -383,6 +386,7 @@ int main(int argc, char *argv[]) }); builder.WithOnStopped([&](SecureTunnel *secureTunnel) { + (void)secureTunnel; fprintf(stdout, "Secure Tunnel has entered Stopped State\n"); clientStoppedPromise.set_value(true); }); From 8ce6f0306092920046e72b6fd3f8bd064370ef32 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Thu, 23 Feb 2023 09:57:36 -0800 Subject: [PATCH 26/32] doc fixes --- .../secure_tunneling/secure_tunnel/main.cpp | 15 ++++++++---- .../aws/iotsecuretunneling/SecureTunnel.h | 24 +++++++++---------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/samples/secure_tunneling/secure_tunnel/main.cpp b/samples/secure_tunneling/secure_tunnel/main.cpp index af15ef9e4..779accfce 100644 --- a/samples/secure_tunneling/secure_tunnel/main.cpp +++ b/samples/secure_tunneling/secure_tunnel/main.cpp @@ -68,6 +68,7 @@ void setupCommandLineUtils(Utils::CommandLineUtils *cmdUtils, int argc, char *ar "proxy_user_name", "", "User name passed if proxy server requires a user name (optional)"); cmdUtils->RegisterCommand( "proxy_password", "", "Password passed if proxy server requires a password (optional)"); + cmdUtils->RegisterCommand("count", "", "Number of messages to send before completing (optional, default='5')"); cmdUtils->AddLoggingCommands(); const char **const_argv = (const char **)argv; cmdUtils->SendArguments(const_argv, const_argv + argc); @@ -83,6 +84,7 @@ void setupCommandLineValues( String *proxyUserName, String *proxyPassword, uint16_t &proxyPort, + uint16_t &messageCount, aws_secure_tunneling_local_proxy_mode &localProxyMode, String *payloadMessage) { @@ -179,6 +181,9 @@ void setupCommandLineValues( } payloadMessage->assign(cmdUtils->GetCommandOrDefault("message", "Hello World")); + + int count = atoi(cmdUtils->GetCommandOrDefault("count", "5").c_str()); + messageCount = static_cast(count); } int main(int argc, char *argv[]) @@ -214,6 +219,7 @@ int main(int argc, char *argv[]) String proxyPassword; aws_secure_tunneling_local_proxy_mode localProxyMode; String payloadMessage; + uint16_t messageCount(5); /*********************** Parse Arguments ***************************/ Utils::CommandLineUtils cmdUtils = Utils::CommandLineUtils(); @@ -229,6 +235,7 @@ int main(int argc, char *argv[]) &proxyUserName, &proxyPassword, proxyPort, + messageCount, localProxyMode, &payloadMessage); @@ -437,8 +444,8 @@ int main(int argc, char *argv[]) exit(-1); } - int messageCount = 0; bool keepRunning = true; + uint16_t messagesSent = 0; if (connectionCompletedPromise.get_future().get()) { @@ -452,10 +459,10 @@ int main(int argc, char *argv[]) { if (localProxyMode == AWS_SECURE_TUNNELING_SOURCE_MODE) { - messageCount++; - String toSend = (std::to_string(messageCount) + ": " + payloadMessage.c_str()).c_str(); + messagesSent++; + String toSend = (std::to_string(messagesSent) + ": " + payloadMessage.c_str()).c_str(); - if (messageCount < 5) + if (messagesSent <= messageCount) { std::shared_ptr message = std::make_shared(ByteCursorFromCString(toSend.c_str())); diff --git a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h index d4e62367d..4f79669e7 100644 --- a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h +++ b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h @@ -420,7 +420,7 @@ namespace Aws * Setup callback handler trigged when an Secure Tunnel establishes a connection and receives available * service ids. * - * @param callback + * @param onConnectionSuccess * * @return this builder object */ @@ -429,7 +429,7 @@ namespace Aws /** * Setup callback handler trigged when an Secure Tunnel fails a connection attempt. * - * @param callback + * @param onConnectionFailure * * @return this builder object */ @@ -438,7 +438,7 @@ namespace Aws /** * Setup callback handler trigged when an Secure Tunnel shuts down connection to the secure tunnel service. * - * @param callback + * @param onConnectionShutdown * * @return this builder object */ @@ -447,7 +447,7 @@ namespace Aws /** * Setup callback handler trigged when an Secure Tunnel completes sending data to the secure tunnel service. * - * @param callback + * @param onSendDataComplete * * @return this builder object */ @@ -457,7 +457,7 @@ namespace Aws * Setup callback handler trigged when an Secure Tunnel receives a Message through the secure tunnel * service. * - * @param callback + * @param onMessageReceived * * @return this builder object */ @@ -467,7 +467,7 @@ namespace Aws * Setup callback handler trigged when an Secure Tunnel starts a stream with a source through the secure * tunnel service. * - * @param callback + * @param onStreamStarted * * @return this builder object */ @@ -476,7 +476,7 @@ namespace Aws /** * Setup callback handler trigged when an Secure Tunnel stops a stream. * - * @param callback + * @param onStreamStopped * * @return this builder object */ @@ -485,7 +485,7 @@ namespace Aws /** * Setup callback handler trigged when an Secure Tunnel receives a stream reset. * - * @param callback + * @param onStreamReset * * @return this builder object */ @@ -495,7 +495,7 @@ namespace Aws * Setup callback handler trigged when an Secure Tunnel receives a session reset from the secure tunnel * service. * - * @param callback + * @param onSessionReset * * @return this builder object */ @@ -504,7 +504,7 @@ namespace Aws /** * Setup callback handler trigged when an Secure Tunnel completes entering a stopped state * - * @param callback + * @param onStopped * * @return this builder object */ @@ -746,7 +746,7 @@ namespace Aws * Notifies the secure tunnel that you want to start a stream with the Destination device on a specific * service id. This will result in a V2 stream. * - * @param string: The Service Id to start a stream on. + * @param serviceId: The Service Id to start a stream on. * * @return bool: true if operation succeed, otherwise false */ @@ -756,7 +756,7 @@ namespace Aws * Notifies the secure tunnel that you want to start a stream with the Destination device on a specific * service id. This will result in a V2 stream. * - * @param ByteCursor: The Service Id to start a stream on. + * @param serviceId: The Service Id to start a stream on. * * @return bool: true if operation succeed, otherwise false */ From c4ce894b61f19c37635c66f7d083a95abfe3693a Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Thu, 23 Feb 2023 10:12:49 -0800 Subject: [PATCH 27/32] update sample readme and add services to ci --- samples/README.md | 10 +++------ utils/run_secure_tunnel_ci.py | 39 +++++++++++++++++++++-------------- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/samples/README.md b/samples/README.md index 985278448..24d265bd9 100644 --- a/samples/README.md +++ b/samples/README.md @@ -977,7 +977,7 @@ using a permanent certificate set, replace the paths specified in the `--cert` a ## Secure Tunnel -This sample uses AWS IoT [Secure Tunneling](https://docs.aws.amazon.com/iot/latest/developerguide/secure-tunneling.html) Service to connect a destination and a source with each other through the AWS Secure Tunnel endpoint using access tokens. +This sample uses AWS IoT [Secure Tunneling](https://docs.aws.amazon.com/iot/latest/developerguide/secure-tunneling.html) Service to connect a destination and a source with each other through the AWS Secure Tunnel endpoint using access tokens using the [V2WebSocketProtocol](https://github.com/aws-samples/aws-iot-securetunneling-localproxy/blob/main/V2WebSocketProtocolGuide.md). Source: `samples/secure_tunneling/secure_tunnel` @@ -986,17 +986,13 @@ Create a new secure tunnel in the AWS IoT console (https://console.aws.amazon.co Provide the necessary arguments along with the destination access token and start the sample in destination mode (default). ``` sh -./secure_tunnel --endpoint --ca_file ---cert --key ---thing_name --region --access_token_file +./secure_tunnel --region --access_token_file ``` Provide the necessary arguments along with the source access token and start a second sample in source mode by using the flag --localProxyModeSource. ``` sh -./secure_tunnel --endpoint --ca_file ---cert --key ---thing_name --region --access_token_file +./secure_tunnel --region --access_token_file --localProxyModeSource ``` diff --git a/utils/run_secure_tunnel_ci.py b/utils/run_secure_tunnel_ci.py index baa33997f..a9d7105fc 100644 --- a/utils/run_secure_tunnel_ci.py +++ b/utils/run_secure_tunnel_ci.py @@ -17,29 +17,32 @@ def getSecretsAndLaunch(parsed_commands): exit_code = 0 - print ("Creating secure tunnel client using Boto3") + print("Creating secure tunnel client using Boto3") tunnel_client = None try: - tunnel_client = boto3.client("iotsecuretunneling", region_name=parsed_commands.sample_region) + tunnel_client = boto3.client( + "iotsecuretunneling", region_name=parsed_commands.sample_region) except Exception: - print ("Could not create tunnel client!") + print("Could not create tunnel client!") exit(-1) tunnel_data = None try: - tunnel_data = tunnel_client.open_tunnel() + tunnel_data = tunnel_client.open_tunnel( + destinationConfig={'services': ['ssh', 'http', ]}) except Exception: - print ("Could not open tunnel!") + print("Could not open tunnel!") exit(-1) - print ("Launching Secure Tunnel samples...") + print("Launching Secure Tunnel samples...") exit_code = launch_samples(parsed_commands, tunnel_data) - print ("Closing tunnel...") + print("Closing tunnel...") try: - tunnel_client.close_tunnel(tunnelId=tunnel_data["tunnelId"], delete=True) + tunnel_client.close_tunnel( + tunnelId=tunnel_data["tunnelId"], delete=True) except Exception: - print ("Could not close tunnel!") + print("Could not close tunnel!") exit(-1) return exit_code @@ -49,13 +52,17 @@ def launch_samples(parsed_commands, tunnel_data): exit_code = 0 # Right now secure tunneling is only in C++, so we only support launching the sample in the C++ way - launch_arguments_destination = ["--test", "--region", parsed_commands.sample_region, "--access_token", tunnel_data["destinationAccessToken"]] - launch_arguments_source = ["--local_proxy_mode_source", "--region", parsed_commands.sample_region, "--access_token", tunnel_data["sourceAccessToken"]] - - destination_run = subprocess.Popen(args=launch_arguments_destination, executable=parsed_commands.sample_file) - print ("About to sleep before running source part of sample...") - sleep(10) # Sleep to give the destination some time to run - source_run = subprocess.Popen(args=launch_arguments_source, executable=parsed_commands.sample_file) + launch_arguments_destination = [ + "--test", "--region", parsed_commands.sample_region, "--access_token", tunnel_data["destinationAccessToken"]] + launch_arguments_source = ["--local_proxy_mode_source", "--region", + parsed_commands.sample_region, "--access_token", tunnel_data["sourceAccessToken"]] + + destination_run = subprocess.Popen( + args=launch_arguments_destination, executable=parsed_commands.sample_file) + print("About to sleep before running source part of sample...") + sleep(10) # Sleep to give the destination some time to run + source_run = subprocess.Popen( + args=launch_arguments_source, executable=parsed_commands.sample_file) # Wait for the source to finish source_run.wait() From 8e878d9e6c452581f116578d3e9404478a07430b Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Thu, 23 Feb 2023 11:56:42 -0800 Subject: [PATCH 28/32] User Guide --- README.md | 1 + documents/Secure_Tunnel_Userguide.md | 165 ++++++++++++++++++ .../secure_tunneling/secure_tunnel/main.cpp | 1 + .../aws/iotsecuretunneling/SecureTunnel.h | 22 +-- 4 files changed, 178 insertions(+), 11 deletions(-) create mode 100644 documents/Secure_Tunnel_Userguide.md diff --git a/README.md b/README.md index 2dfde6e2b..e1341dbca 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ __Jump To:__ * [FAQ](./documents/FAQ.md) * [Giving Feedback and Contributions](#Giving-Feedback-and-Contributions) * [MQTT5 User Guide](./documents/MQTT5_Userguide.md) +* [Secure Tunnel User Guide](./documents/Secure_Tunnel_Userguide.md) ## Installation diff --git a/documents/Secure_Tunnel_Userguide.md b/documents/Secure_Tunnel_Userguide.md new file mode 100644 index 000000000..4087f2902 --- /dev/null +++ b/documents/Secure_Tunnel_Userguide.md @@ -0,0 +1,165 @@ +# Introduction +When devices are deployed behind restricted firewalls at remote sites, you need a way to gain access to those devices for troubleshooting, configuration updates, and other operational tasks. Use secure tunneling to establish bidirectional communication to remote devices over a secure connection that is managed by AWS IoT. Secure tunneling does not require updates to your existing inbound firewall rules, so you can keep the same security level provided by firewall rules at a remote site. + +More information on the service and how to open, close, and manage secure tunnels can be found here: https://docs.aws.amazon.com/iot/latest/developerguide/secure-tunneling.html + +A sample is also provided and can be found here: https://github.com/aws/aws-iot-device-sdk-cpp-v2/tree/main/samples#secure-tunnel + + + +# Getting started with Secure Tunnels +## How to Create a Secure Tunnel Client +Once a Secure Tunnel builder has been created, it is ready to make a Secure Tunnel client. Something important to note is that once a Secure Tunnel client is built and finalized, the configuration is immutable. Further modifications to the Secure Tunnel builder will not change the settings of already created Secure Tunnel clients. + +``` +// Create Secure Tunnel Builder +SecureTunnelBuilder builder = SecureTunnelBuilder(...); + +// Build Secure Tunnel Client +std::shared_ptr secureTunnel = builder.Build(); + +if (secureTunnel == nullptr) +{ + fprintf(stdout, "Secure Tunnel creation failed.\n"); + return -1; +} + +// Start the secure tunnel connection +if (!secureTunnel->Start()) +{ + fprintf("Failed to start Secure Tunnel\n"); + return -1; +} +``` +## Callbacks + +### OnConnectionSuccess +When the Secure Tunnel Client successfully connects with the Secure Tunnel service, this callback will return the available (if any) service ids. + +### OnConnectionFailure +When a WebSocket upgrade request fails, this callback will return an error code. + +### OnConnectionShutdown +When the WebSocket connection shuts down, this callback will be invoked. + +### OnSendDataComplete +When a message has been completely written to the socket, this callback will be invoked. + +### OnMessageReceived +When a message is received on an open Secure Tunnel stream, this callback will return the message. + +### OnStreamStarted +When a stream is started by a Source connected to the Destination, the Destination will invoke this callback and return the stream information. + +### OnStreamStopped +When an open stream is closed, this callback will be invoked and return the stopped stream's information. + +### OnSessionReset +When the Secure Tunnel Service requests the Secure Tunnel client fully reset, this callback is invoked. + +### OnStopped +When the Secure Tunnel has reached a fully stopped state this callback is invoked. + +## Setting Secure Tunnel Callbacks +The Secure Tunnel client uses callbacks to keep the user updated on its status and pass along relavant information. These can be set up using the Secure Tunnel builder's With functions. + +``` +// Create Secure Tunnel Builder +SecureTunnelBuilder builder = SecureTunnelBuilder(...); + +// Setting the onMessageReceived callback using the builder +builder.WithOnMessageReceived([&](SecureTunnel *secureTunnel, const MessageReceivedEventData &eventData) { + { + std::shared_ptr message = eventData.message; + if (message->getServiceId().has_value()){ + fprintf( + stdout, + "Message received on service id:'" PRInSTR "'\n", + AWS_BYTE_CURSOR_PRI(message->getServiceId().value())); + } + + if(message->getPayload().has_value()){ + fprintf( + stdout, + "Message has payload:'" PRInSTR "'\n", + AWS_BYTE_CURSOR_PRI(message->getPayload().value())); + } + } + }); + +// Build Secure Tunnel Client +std::shared_ptr secureTunnel = builder.Build(); + +if (secureTunnel == nullptr) +{ + fprintf(stdout, "Secure Tunnel creation failed.\n"); + return -1; +} + +// Start the secure tunnel connection +if (!secureTunnel->Start()) +{ + fprintf("Failed to start Secure Tunnel\n"); + return -1; +} + +// Messages received on a stream will now be printed to stdout. +``` + +# How to Start and Stop + +## Start +Invoking Start() on the Secure Tunnel Client will put it into an active state where it recurrently establishes a connection to the configured Secure Tunnel endpoint using the provided [Client Access Token](https://docs.aws.amazon.com/iot/latest/developerguide/secure-tunneling-concepts.html). If a [Client Token](https://docs.aws.amazon.com/iot/latest/developerguide/secure-tunneling-concepts.html) is provided, the Secure Tunnel Client will use it but in the event one is not provided, the Secure Tunnel Client will automatically generate one for use on a reconnection attempts. The Client Token for any initial connection to the Secure Tunnel service MUST be unique. Reusing a Client Token from a previous connection will result in a failed connection to the Secure Tunnel Service. +``` +// Create Secure Tunnel Builder +SecureTunnelBuilder builder = SecureTunnelBuilder(...); + +// Adding a client token to the builder +String clientToken; +builder.WithClientToken(clientToken.c_str()); + +// Build Secure Tunnel Client +std::shared_ptr secureTunnel = builder.Build(); + +if (secureTunnel == nullptr) +{ + fprintf(stdout, "Secure Tunnel creation failed.\n"); + return -1; +} + +// Start the secure tunnel connection +if (!secureTunnel->Start()) +{ + fprintf("Failed to start Secure Tunnel\n"); + return -1; +} +``` + +## Stop +Invoking Stop() on the Secure Tunnel Client breaks the current connection (if any) and moves the client into an idle state. +``` +if(!secureTunnel->Stop()){ + fprintf(stdout, "Failed to stop the Secure Tunnel connection session. Exiting..\n"); +} +``` + +# Secure Tunnel Operations + +## Send Message +The SendMessage operation takes a description of the Message you wish to send and returns a success/failure in the synchronous logic that kicks off the Send Message operation. When the message is fully written to the socket, the OnSendDataComplete callback will be invoked. + +``` +Crt::String serviceId_string = "ssh"; +Crt::String message_string = "any payload"; + +ByteCursor serviceId = ByteCursorFromString(serviceId_string); +ByteCursor payload = ByteCursorFromString(message_string); + +// Create Message +std::shared_ptr message = std::make_shared(); +message->withServiceId(serviceId); +message->withPayload(payload); + +// Send Message +secureTunnel->SendMessage(message); +``` diff --git a/samples/secure_tunneling/secure_tunnel/main.cpp b/samples/secure_tunneling/secure_tunnel/main.cpp index 779accfce..eabd2f944 100644 --- a/samples/secure_tunneling/secure_tunnel/main.cpp +++ b/samples/secure_tunneling/secure_tunnel/main.cpp @@ -38,6 +38,7 @@ void logMessage(std::shared_ptr message) "Message with service id:'" PRInSTR "' with no payload.\n", AWS_BYTE_CURSOR_PRI(message->getServiceId().value())); } + return; } if (message->getPayload().has_value()) { diff --git a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h index 4f79669e7..ac91a30cf 100644 --- a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h +++ b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h @@ -323,11 +323,6 @@ namespace Aws using OnStreamStopped = std::function; - /** - * Type signature of the callback invoked when a stream is reset. - */ - using OnStreamReset = std::function; - /** * Type signature of the callback invoked when the secure tunnel receives a Session Reset. */ @@ -351,6 +346,11 @@ namespace Aws */ using OnStreamStart = std::function; + /** + * Deprecated - Use OnStreamStopped + */ + using OnStreamReset = std::function; + /** * Represents a unique configuration for a secure tunnel */ @@ -712,7 +712,7 @@ namespace Aws * Notifies the secure tunnel that you want it to attempt to connect to the configured endpoint. * The secure tunnel will attempt to stay connected and attempt to reconnect if disconnected. * - * @return bool: true if operation succeed, otherwise false. + * @return success/failure in the synchronous logic that kicks off the start process */ int Start(); @@ -720,7 +720,7 @@ namespace Aws * Notifies the secure tunnel that you want it to transition to the stopped state, disconnecting any * existing connection and stopping subsequent reconnect attempts. * - * @return bool: true if operation succeed, otherwise false + * @return success/failure in the synchronous logic that kicks off the stop process */ int Stop(); @@ -729,7 +729,7 @@ namespace Aws * * @param messageOptions: Message to send to the secure tunnel service. * - * @return true if the message operation succeed otherwise false + * @return success/failure in the synchronous logic that kicks off the Send Message operation */ int SendMessage(std::shared_ptr messageOptions) noexcept; @@ -738,7 +738,7 @@ namespace Aws * Notifies the secure tunnel that you want to start a stream with the Destination device. This will result * in a V1 stream. * - * @return bool: true if operation succeed, otherwise false + * @return success/failure in the synchronous logic that kicks off the Stream Start operation */ int SendStreamStart(); @@ -748,7 +748,7 @@ namespace Aws * * @param serviceId: The Service Id to start a stream on. * - * @return bool: true if operation succeed, otherwise false + * @return success/failure in the synchronous logic that kicks off the Stream Start operation */ int SendStreamStart(std::string serviceId); @@ -758,7 +758,7 @@ namespace Aws * * @param serviceId: The Service Id to start a stream on. * - * @return bool: true if operation succeed, otherwise false + * @return success/failure in the synchronous logic that kicks off the Stream Start operation */ int SendStreamStart(Crt::ByteCursor serviceId); From abff1398e649261bb78d787b7aec261a4a7d3cab Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Thu, 23 Feb 2023 13:32:05 -0800 Subject: [PATCH 29/32] remove comments --- .../include/aws/iotsecuretunneling/SecureTunnel.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h index ac91a30cf..873805d93 100644 --- a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h +++ b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h @@ -909,10 +909,6 @@ namespace Aws OnStreamReset m_OnStreamReset; std::shared_ptr m_selfRef; - // std::promise m_TerminationComplete; - // std::condition_variable m_terminationCondition; - // std::mutex m_terminationMutex; - // bool m_terminationPredicate = false; friend class SecureTunnelBuilder; }; From 21c3d4d58c95f140f55686e3f07c0311e1b027e2 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Thu, 23 Feb 2023 14:21:27 -0800 Subject: [PATCH 30/32] code review changes --- documents/Secure_Tunnel_Userguide.md | 20 ++++++++++---------- samples/README.md | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/documents/Secure_Tunnel_Userguide.md b/documents/Secure_Tunnel_Userguide.md index 4087f2902..d6f071e4a 100644 --- a/documents/Secure_Tunnel_Userguide.md +++ b/documents/Secure_Tunnel_Userguide.md @@ -9,9 +9,9 @@ A sample is also provided and can be found here: https://github.com/aws/aws-iot- # Getting started with Secure Tunnels ## How to Create a Secure Tunnel Client -Once a Secure Tunnel builder has been created, it is ready to make a Secure Tunnel client. Something important to note is that once a Secure Tunnel client is built and finalized, the configuration is immutable. Further modifications to the Secure Tunnel builder will not change the settings of already created Secure Tunnel clients. +Once a Secure Tunnel builder has been created, it is ready to make a Secure Tunnel client. Something important to note is that once a Secure Tunnel client is built and finalized, the configuration is immutable and cannot be changed. Further modifications to the Secure Tunnel builder will not change the settings of already created Secure Tunnel clients. -``` +```cpp // Create Secure Tunnel Builder SecureTunnelBuilder builder = SecureTunnelBuilder(...); @@ -37,7 +37,7 @@ if (!secureTunnel->Start()) When the Secure Tunnel Client successfully connects with the Secure Tunnel service, this callback will return the available (if any) service ids. ### OnConnectionFailure -When a WebSocket upgrade request fails, this callback will return an error code. +When a WebSocket upgrade request fails to connect, this callback will return an error code. ### OnConnectionShutdown When the WebSocket connection shuts down, this callback will be invoked. @@ -55,7 +55,7 @@ When a stream is started by a Source connected to the Destination, the Destinati When an open stream is closed, this callback will be invoked and return the stopped stream's information. ### OnSessionReset -When the Secure Tunnel Service requests the Secure Tunnel client fully reset, this callback is invoked. +When the Secure Tunnel service requests the Secure Tunnel client fully reset, this callback is invoked. ### OnStopped When the Secure Tunnel has reached a fully stopped state this callback is invoked. @@ -63,7 +63,7 @@ When the Secure Tunnel has reached a fully stopped state this callback is invoke ## Setting Secure Tunnel Callbacks The Secure Tunnel client uses callbacks to keep the user updated on its status and pass along relavant information. These can be set up using the Secure Tunnel builder's With functions. -``` +```cpp // Create Secure Tunnel Builder SecureTunnelBuilder builder = SecureTunnelBuilder(...); @@ -109,8 +109,8 @@ if (!secureTunnel->Start()) # How to Start and Stop ## Start -Invoking Start() on the Secure Tunnel Client will put it into an active state where it recurrently establishes a connection to the configured Secure Tunnel endpoint using the provided [Client Access Token](https://docs.aws.amazon.com/iot/latest/developerguide/secure-tunneling-concepts.html). If a [Client Token](https://docs.aws.amazon.com/iot/latest/developerguide/secure-tunneling-concepts.html) is provided, the Secure Tunnel Client will use it but in the event one is not provided, the Secure Tunnel Client will automatically generate one for use on a reconnection attempts. The Client Token for any initial connection to the Secure Tunnel service MUST be unique. Reusing a Client Token from a previous connection will result in a failed connection to the Secure Tunnel Service. -``` +Invoking `Start()` on the Secure Tunnel Client will put it into an active state where it recurrently establishes a connection to the configured Secure Tunnel endpoint using the provided [Client Access Token](https://docs.aws.amazon.com/iot/latest/developerguide/secure-tunneling-concepts.html). If a [Client Token](https://docs.aws.amazon.com/iot/latest/developerguide/secure-tunneling-concepts.html) is provided, the Secure Tunnel Client will use it. If a Client Token is not provided, the Secure Tunnel Client will automatically generate one for use on a reconnection attempts. The Client Token for any initial connection to the Secure Tunnel service **MUST** be unique. Reusing a Client Token from a previous connection will result in a failed connection to the Secure Tunnel Service. +```cpp // Create Secure Tunnel Builder SecureTunnelBuilder builder = SecureTunnelBuilder(...); @@ -136,8 +136,8 @@ if (!secureTunnel->Start()) ``` ## Stop -Invoking Stop() on the Secure Tunnel Client breaks the current connection (if any) and moves the client into an idle state. -``` +Invoking `Stop()` on the Secure Tunnel Client breaks the current connection (if any) and moves the client into an idle state. +```cpp if(!secureTunnel->Stop()){ fprintf(stdout, "Failed to stop the Secure Tunnel connection session. Exiting..\n"); } @@ -148,7 +148,7 @@ if(!secureTunnel->Stop()){ ## Send Message The SendMessage operation takes a description of the Message you wish to send and returns a success/failure in the synchronous logic that kicks off the Send Message operation. When the message is fully written to the socket, the OnSendDataComplete callback will be invoked. -``` +```cpp Crt::String serviceId_string = "ssh"; Crt::String message_string = "any payload"; diff --git a/samples/README.md b/samples/README.md index 24d265bd9..37d52d446 100644 --- a/samples/README.md +++ b/samples/README.md @@ -977,7 +977,7 @@ using a permanent certificate set, replace the paths specified in the `--cert` a ## Secure Tunnel -This sample uses AWS IoT [Secure Tunneling](https://docs.aws.amazon.com/iot/latest/developerguide/secure-tunneling.html) Service to connect a destination and a source with each other through the AWS Secure Tunnel endpoint using access tokens using the [V2WebSocketProtocol](https://github.com/aws-samples/aws-iot-securetunneling-localproxy/blob/main/V2WebSocketProtocolGuide.md). +This sample uses AWS IoT [Secure Tunneling](https://docs.aws.amazon.com/iot/latest/developerguide/secure-tunneling.html) Service to connect a destination and a source with each other through the AWS Secure Tunnel endpoint using access tokens using the [V2WebSocketProtocol](https://github.com/aws-samples/aws-iot-securetunneling-localproxy/blob/main/V2WebSocketProtocolGuide.md). [Secure Tunnel Userguide](https://github.com/aws/aws-iot-device-sdk-cpp-v2/blob/main/documents/Secure_Tunnel_Userguide.md) Source: `samples/secure_tunneling/secure_tunnel` From 8b14d0e1787f4134c9035afee1cc8c8ba96bda7c Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Thu, 23 Feb 2023 14:26:53 -0800 Subject: [PATCH 31/32] latest submodules --- crt/aws-c-iot | 2 +- crt/aws-crt-cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crt/aws-c-iot b/crt/aws-c-iot index 0c3cd3627..09ded2b5e 160000 --- a/crt/aws-c-iot +++ b/crt/aws-c-iot @@ -1 +1 @@ -Subproject commit 0c3cd36278b4927373d87eecce8aa68106a8bf12 +Subproject commit 09ded2b5e5bd34bbcf0fd71b5482381cf7f08627 diff --git a/crt/aws-crt-cpp b/crt/aws-crt-cpp index f2adef31d..7ff9e0343 160000 --- a/crt/aws-crt-cpp +++ b/crt/aws-crt-cpp @@ -1 +1 @@ -Subproject commit f2adef31d778cfe90b8a5bb377425f825ebf92f0 +Subproject commit 7ff9e0343c978fc54f440b98147c2f72d304f6d8 From 22f74d10deed73f4aa05a376453b5091595a9e1d Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Thu, 23 Feb 2023 14:37:38 -0800 Subject: [PATCH 32/32] remove file --- .../secure_tunnel/build_sample.py | 32 ------------------- 1 file changed, 32 deletions(-) delete mode 100644 samples/secure_tunneling/secure_tunnel/build_sample.py diff --git a/samples/secure_tunneling/secure_tunnel/build_sample.py b/samples/secure_tunneling/secure_tunnel/build_sample.py deleted file mode 100644 index a123c3cc0..000000000 --- a/samples/secure_tunneling/secure_tunnel/build_sample.py +++ /dev/null @@ -1,32 +0,0 @@ -from asyncio import subprocess -import os -import os.path -import shutil -import subprocess - -# Place in the sample folder to be built -# Change DCMAKE_PREFIX_PATH below to the - -root_dir = os.path.abspath(os.path.dirname(__file__)) -build_dir = os.path.join(root_dir, 'build') - -# Delete existing Sample build directory -shutil.rmtree(build_dir, ignore_errors=True) -os.mkdir(build_dir) - -os.chdir(build_dir) - - -def check_call(args): - print('$', subprocess.list2cmdline(args)) - subprocess.check_call(args) - - -# Build -check_call(['cmake', '-DCMAKE_PREFIX_PATH="/Users/sbstevek/workplace/secure-tunnel-multiplexing/installs"', - '-DCMAKE_BUILD_TYPE="Debug"', '..']) -check_call(['cmake', '--build', '.', '--config', 'Debug']) - - -# "cmake -DCMAKE_PREFIX_PATH="/Users/sbstevek/workplace/secure-tunnel-multiplexing/installs" -DCMAKE_BUILD_TYPE="Debug" .." -# "cmake --build . --config Debug"