From dbda7eba675ca2cf4b70825701576615b1e390f4 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Thu, 30 Mar 2023 16:09:29 -0700 Subject: [PATCH 01/18] secure tunnel v3 wip --- .../secure_tunneling/secure_tunnel/main.cpp | 53 ++- .../aws/iotsecuretunneling/SecureTunnel.h | 335 ++++++++++++++++-- secure_tunneling/source/SecureTunnel.cpp | 222 +++++++++++- 3 files changed, 558 insertions(+), 52 deletions(-) diff --git a/samples/secure_tunneling/secure_tunnel/main.cpp b/samples/secure_tunneling/secure_tunnel/main.cpp index eabd2f944..fe6c031be 100644 --- a/samples/secure_tunneling/secure_tunnel/main.cpp +++ b/samples/secure_tunneling/secure_tunnel/main.cpp @@ -27,25 +27,38 @@ void logMessage(std::shared_ptr message) { fprintf( stdout, - "Message received on service id:'" PRInSTR "' with payload:'" PRInSTR "'\n", + "Message received on service id:'" PRInSTR "' connection id: %d with payload:'" PRInSTR "'\n", AWS_BYTE_CURSOR_PRI(message->getServiceId().value()), + message->getConnectionId(), 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())); + "Message with service id:'" PRInSTR "' connection id: %d with no payload.\n", + AWS_BYTE_CURSOR_PRI(message->getServiceId().value()), + message->getConnectionId()); } return; } if (message->getPayload().has_value()) { - fprintf( - stdout, - "Message received with payload:'" PRInSTR "'\n", - AWS_BYTE_CURSOR_PRI(message->getPayload().value())); + if (message->getConnectionId() > 0) + { + fprintf( + stdout, + "Message received on connection id: %d with payload:'" PRInSTR "'\n", + message->getConnectionId(), + AWS_BYTE_CURSOR_PRI(message->getPayload().value())); + } + else + { + fprintf( + stdout, + "Message received with payload:'" PRInSTR "'\n", + AWS_BYTE_CURSOR_PRI(message->getPayload().value())); + } } } @@ -337,12 +350,18 @@ int main(int argc, char *argv[]) echoMessage = std::make_shared(message->getPayload().value()); - /* Echo message on same service id received message came on */ + /* Echo message on same service id received message arrived on */ if (message->getServiceId().has_value()) { echoMessage->withServiceId(message->getServiceId().value()); } + /* Echo message on the same connection id received message arrived on */ + if (message->getConnectionId() > 0) + { + echoMessage->withConnectionId(message->getConnectionId()); + } + secureTunnel->SendMessage(echoMessage); fprintf(stdout, "Sending Echo Message\n"); @@ -393,6 +412,24 @@ int main(int argc, char *argv[]) } }); + builder.WithOnConnectionStarted( + [&](SecureTunnel *secureTunnel, int errorCode, const ConnectionStartedEventData &eventData) { + (void)secureTunnel; + if (!errorCode) + { + std::shared_ptr connectionStartedData = eventData.connectionStartedData; + } + }); + + builder.WithOnConnectionReset( + [&](SecureTunnel *secureTunnel, int errorCode, const ConnectionResetEventData &eventData) { + (void)secureTunnel; + if (!errorCode) + { + std::shared_ptr connectionResetData = eventData.connectionResetData; + } + }); + builder.WithOnStopped([&](SecureTunnel *secureTunnel) { (void)secureTunnel; fprintf(stdout, "Secure Tunnel has entered Stopped State\n"); diff --git a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h index 873805d93..5fae76ed8 100644 --- a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h +++ b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h @@ -27,8 +27,17 @@ namespace Aws Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; Message(Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; Message(Crt::ByteCursor payload, Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; + Message( + Crt::ByteCursor payload, + u_int32_t connectionId, + Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; + Message( + Crt::ByteCursor serviceId, + Crt::ByteCursor payload, + Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; Message( Crt::ByteCursor serviceId, + u_int32_t connectionId, Crt::ByteCursor payload, Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; @@ -40,6 +49,14 @@ namespace Aws */ Message &withServiceId(Crt::ByteCursor serviceId) noexcept; + /** + * Sets the connection id for the secure tunnel message. + * + * @param connectionId The connection id for the secure tunnel message. + * @return The Message Object after setting the payload. + */ + Message &withConnectionId(u_int32_t connectionId) noexcept; + /** * Sets the payload for the secure tunnel message. * @@ -57,6 +74,13 @@ namespace Aws */ const Crt::Optional &getServiceId() const noexcept; + /** + * The connection id of the secure tunnel message. + * + * @return The connection id of the secure tunnel message. + */ + const u_int32_t &getConnectionId() const noexcept; + /** * The payload of the secure tunnel message. * @@ -81,6 +105,13 @@ namespace Aws */ Crt::Optional m_serviceId; + /** + * The connection id used for HTTP simultaneous connections. + * + * If left empty, a V1 or V2 protocol message is assumed. + */ + u_int32_t m_connectionId; + /** * The payload of the secure tunnel message. */ @@ -194,6 +225,13 @@ namespace Aws */ const Crt::Optional &getServiceId() const noexcept; + /** + * The connection id of the secure tunnel message. + * + * @return The connection id of the secure tunnel message. + */ + const u_int32_t &getConnectionId() const noexcept; + virtual ~StreamStartedData(); /* Do not allow direct copy or move */ StreamStartedData(const StreamStartedData &) = delete; @@ -211,6 +249,13 @@ namespace Aws */ Crt::Optional m_serviceId; + /** + * The connection id used for HTTP simultaneous connections. + * + * If left empty, a V1 or V2 protocol message is assumed. + */ + u_int32_t m_connectionId; + /////////////////////////////////////////////////////////////////////////// // Underlying data storage for internal use /////////////////////////////////////////////////////////////////////////// @@ -227,7 +272,7 @@ namespace Aws }; /** - * Data model for started Secure Tunnel streams. + * Data model for stopped Secure Tunnel streams. */ class AWS_IOTSECURETUNNELING_API StreamStoppedData { @@ -243,10 +288,6 @@ namespace Aws */ 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; @@ -258,9 +299,9 @@ namespace Aws Crt::Allocator *m_allocator; /** - * Service id of started stream. + * Service id of stopped stream. * - * If left empty, a V1 protocolstream is assumed. + * If left empty, a V1 protocol stream is assumed. */ Crt::Optional m_serviceId; @@ -279,6 +320,130 @@ namespace Aws std::shared_ptr streamStoppedData; }; + /** + * Data model for opened Secure Tunnel connection. + */ + class AWS_IOTSECURETUNNELING_API ConnectionStartedData + { + public: + ConnectionStartedData( + const aws_secure_tunnel_message_view &raw_options, + Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; + + /** + * Service id the connection is using. + * + * @return Service id the connection is using. + */ + const Crt::Optional &getServiceId() const noexcept; + + /** + * The connection id of the opened connection. + * + * @return The connection id of the opened connection. + */ + const u_int32_t &getConnectionId() const noexcept; + + virtual ~ConnectionStartedData(); + /* Do not allow direct copy or move */ + ConnectionStartedData(const ConnectionStartedData &) = delete; + ConnectionStartedData(ConnectionStartedData &&) noexcept = delete; + ConnectionStartedData &operator=(const ConnectionStartedData &) = delete; + ConnectionStartedData &operator=(ConnectionStartedData &&) noexcept = delete; + + private: + Crt::Allocator *m_allocator; + + /** + * Service id the connection is using. + * + * If left empty, a V1 protocolstream is assumed. + */ + Crt::Optional m_serviceId; + + /** + * The connection id of the opened connection. + * + */ + u_int32_t m_connectionId; + + /////////////////////////////////////////////////////////////////////////// + // Underlying data storage for internal use + /////////////////////////////////////////////////////////////////////////// + Crt::ByteBuf m_serviceIdStorage; + }; + + /** + * The data returned when a connection is started on the Secure Tunnel. + */ + struct AWS_IOTSECURETUNNELING_API ConnectionStartedEventData + { + ConnectionStartedEventData() : connectionStartedData(nullptr) {} + std::shared_ptr connectionStartedData; + }; + + /** + * Data model for reset Secure Tunnel connection. + */ + class AWS_IOTSECURETUNNELING_API ConnectionResetData + { + public: + ConnectionResetData( + const aws_secure_tunnel_message_view &raw_options, + Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; + + /** + * Service id used by reset connection. + * + * @return Service id used by reset connection. + */ + const Crt::Optional &getServiceId() const noexcept; + + /** + * Connection id of the reset connection. + * + * @return Connection id of the reset connection. + */ + const u_int32_t &getConnectionId() const noexcept; + + virtual ~ConnectionResetData(); + /* Do not allow direct copy or move */ + ConnectionResetData(const ConnectionResetData &) = delete; + ConnectionResetData(ConnectionResetData &&) noexcept = delete; + ConnectionResetData &operator=(const ConnectionResetData &) = delete; + ConnectionResetData &operator=(ConnectionResetData &&) noexcept = delete; + + private: + Crt::Allocator *m_allocator; + + /** + * Service id used by the reset connection. + * + * If left empty, a V1 protocol stream is assumed. + */ + Crt::Optional m_serviceId; + + /** + * The connection id of the reset connection. + * + */ + u_int32_t m_connectionId; + + /////////////////////////////////////////////////////////////////////////// + // Underlying data storage for internal use + /////////////////////////////////////////////////////////////////////////// + Crt::ByteBuf m_serviceIdStorage; + }; + + /** + * The data returned when a stream is reset on the Secure Tunnel. + */ + struct AWS_IOTSECURETUNNELING_API ConnectionResetEventData + { + ConnectionResetEventData() : connectionResetData(nullptr) {} + std::shared_ptr connectionResetData; + }; + class SecureTunnel; // Client callback type definitions @@ -301,9 +466,10 @@ namespace Aws using OnConnectionShutdown = std::function; /** - * Type signature of the callback invoked when data has been sent through the secure tunnel connection. + * Type signature of the callback invoked when message has been sent through the secure tunnel connection. */ - using OnSendDataComplete = std::function; + using OnSendMessageComplete = + std::function; /** * Type signature of the callback invoked when a message is received through the secure tunnel connection. @@ -320,9 +486,21 @@ namespace Aws /** * Type signature of the callback invoked when a stream has been closed */ - using OnStreamStopped = std::function; + /** + * Type signature of the callback invoked when a connection has been started with a source through the secure + * tunnel connection. + */ + using OnConnectionStarted = + std::function; + + /** + * Type signature of the callback invoked when a connection has been reset + */ + using OnConnectionReset = + std::function; + /** * Type signature of the callback invoked when the secure tunnel receives a Session Reset. */ @@ -345,11 +523,14 @@ namespace Aws * Deprecated - Use OnStreamStarted */ using OnStreamStart = std::function; - /** * Deprecated - Use OnStreamStopped */ using OnStreamReset = std::function; + /** + * Deprecated - Use OnSendMessageComplete + */ + using OnSendDataComplete = std::function; /** * Represents a unique configuration for a secure tunnel @@ -445,13 +626,14 @@ namespace Aws SecureTunnelBuilder &WithOnConnectionShutdown(OnConnectionShutdown onConnectionShutdown); /** - * Setup callback handler trigged when an Secure Tunnel completes sending data to the secure tunnel service. + * Setup callback handler trigged when an Secure Tunnel completes sending a message to the secure tunnel + * service. * - * @param onSendDataComplete + * @param onSendMessageComplete * * @return this builder object */ - SecureTunnelBuilder &WithOnSendDataComplete(OnSendDataComplete onSendDataComplete); + SecureTunnelBuilder &WithOnSendMessageComplete(OnSendMessageComplete onSendMessageComplete); /** * Setup callback handler trigged when an Secure Tunnel receives a Message through the secure tunnel @@ -482,6 +664,25 @@ namespace Aws */ SecureTunnelBuilder &WithOnStreamStopped(OnStreamStopped onStreamStopped); + /** + * Setup callback handler trigged when an Secure Tunnel starts a connection with a source through the secure + * tunnel service. + * + * @param onConnectionStarted + * + * @return this builder object + */ + SecureTunnelBuilder &WithOnConnectionStarted(OnConnectionStarted onConnectionStarted); + + /** + * Setup callback handler trigged when an Secure Tunnel resets a connection. + * + * @param onConnectionReset + * + * @return this builder object + */ + SecureTunnelBuilder &WithOnConnectionReset(OnConnectionReset onConnectionReset); + /** * Setup callback handler trigged when an Secure Tunnel receives a stream reset. * @@ -522,6 +723,10 @@ namespace Aws * Deprecated - Use WithOnStreamStarted */ SecureTunnelBuilder &WithOnStreamStart(OnStreamStart onStreamStart); + /** + * Deprecated - Use WithOnSendMessageComplete + */ + SecureTunnelBuilder &WithOnSendDataComplete(OnSendDataComplete onSendDataComplete); /** * Will return a shared pointer to a new SecureTunnel that countains a @@ -602,9 +807,9 @@ namespace Aws OnConnectionShutdown m_OnConnectionShutdown; /** - * Callback handler trigged when secure tunnel completes sending data to the secure tunnel service. + * Callback handler trigged when secure tunnel completes sending message to the secure tunnel service. */ - OnSendDataComplete m_OnSendDataComplete; + OnSendMessageComplete m_OnSendMessageComplete; /** * Callback handler trigged when secure tunnel receives a message from the secure tunnel service. @@ -627,10 +832,27 @@ namespace Aws * Callback handler trigged when secure tunnel receives a stream reset. * * @param SecureTunnel: The shared secure tunnel - * @param StreamStoppedEventData: Stream Started data + * @param StreamStoppedEventData: Stream reset data */ OnStreamStopped m_OnStreamStopped; + /** + * Callback handler trigged when secure tunnel receives a connecction start from a source device. + * + * @param SecureTunnel: The shared secure tunnel + * @param int: error code + * @param ConnectionStartedEventData: Connection Started data + */ + OnConnectionStarted m_OnConnectionStarted; + + /** + * Callback handler trigged when secure tunnel receives a connection reset. + * + * @param SecureTunnel: The shared secure tunnel + * @param ConnectionResetEventData: Connection reset data + */ + OnConnectionReset m_OnConnectionReset; + /** * Callback handler trigged when secure tunnel receives a session reset from the secure tunnel service. */ @@ -657,6 +879,10 @@ namespace Aws * Deprecated - Use m_OnStreamStopped */ OnStreamReset m_OnStreamReset; + /** + * Deprecated - Use m_OnSendMessageComplete + */ + OnSendDataComplete m_OnSendDataComplete; friend class SecureTunnel; }; @@ -762,6 +988,37 @@ namespace Aws */ int SendStreamStart(Crt::ByteCursor serviceId); + /** + * Notifies the secure tunnel that you want to start a connection with the Destination device. + * + * @param connectionId: The connection id to start the connection on. + * + * @return success/failure in the synchronous logic that kicks off the Stream Start operation + */ + int SendConnectionStart(u_int32_t connectionId); + + /** + * Notifies the secure tunnel that you want to start a connection with the Destination device. + * + * @param serviceId: The Service Id to start the connection on. + * + * @param connectionId: The connection id to start the connection on. + * + * @return success/failure in the synchronous logic that kicks off the Stream Start operation + */ + int SendConnectionStart(std::string serviceId, u_int32_t connectionId); + + /** + * Notifies the secure tunnel that you want to start a connection with the Destination device. + * + * @param serviceId: The Service Id to start the connection on. + * + * @param connectionId: The connection id to start the connection on. + * + * @return success/failure in the synchronous logic that kicks off the Stream Start operation + */ + int SendConnectionStart(Crt::ByteCursor serviceId, u_int32_t connectionId); + aws_secure_tunnel *GetUnderlyingHandle(); /** @@ -806,15 +1063,16 @@ namespace Aws OnConnectionSuccess onConnectionSuccess, OnConnectionFailure onConnectionFailure, - OnConnectionComplete onConnectionComplete, + OnConnectionComplete onConnectionComplete, /* Deprecated */ OnConnectionShutdown onConnectionShutdown, - OnSendDataComplete onSendDataComplete, + OnSendMessageComplete onSendMessageComplete, + OnSendDataComplete onSendDataComplete, /* Deprecated */ OnMessageReceived onMessageReceived, - OnDataReceive onDataReceive, + OnDataReceive onDataReceive, /* Deprecated */ OnStreamStarted onStreamStarted, - OnStreamStart onStreamStart, + OnStreamStart onStreamStart, /* Deprecated */ OnStreamStopped onStreamStopped, - OnStreamReset onStreamReset, + OnStreamReset onStreamReset, /* Deprecated */ OnSessionReset onSessionReset, OnStopped onStopped); @@ -827,6 +1085,10 @@ 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_OnSendMessageComplete( + enum aws_secure_tunnel_message_type type, + int error_code, + void *user_data); static void s_OnStreamStopped( const struct aws_secure_tunnel_message_view *message, int error_code, @@ -838,6 +1100,14 @@ namespace Aws const struct aws_secure_tunnel_message_view *message, int error_code, void *user_data); + static void s_OnConnectionStarted( + const struct aws_secure_tunnel_message_view *message, + int error_code, + void *user_data); + static void s_OnConnectionReset( + const struct aws_secure_tunnel_message_view *message, + int error_code, + void *user_data); void OnTerminationComplete(); @@ -863,9 +1133,9 @@ namespace Aws OnConnectionShutdown m_OnConnectionShutdown; /** - * Callback handler trigged when secure tunnel completes sending data to the secure tunnel service. + * Callback handler trigged when secure tunnel completes sending message to the secure tunnel service. */ - OnSendDataComplete m_OnSendDataComplete; + OnSendMessageComplete m_OnSendMessageComplete; /** * Callback handler trigged when secure tunnel starts a stream with a source device through the secure @@ -878,6 +1148,17 @@ namespace Aws */ OnStreamStopped m_OnStreamStopped; + /** + * Callback handler trigged when secure tunnel starts a connection with a source device through the secure + * tunnel service. + */ + OnConnectionStarted m_OnConnectionStarted; + + /** + * Callback handler trigged when secure tunnel resets a connection + */ + OnConnectionReset m_OnConnectionReset; + /** * Callback handler trigged when secure tunnel receives a session reset from the secure tunnel service. */ @@ -892,7 +1173,7 @@ namespace Aws Crt::Allocator *m_allocator; /** - * Deprecated - m_OnMessageReceived + * Deprecated - Use m_OnMessageReceived */ OnDataReceive m_OnDataReceive; /** @@ -907,6 +1188,10 @@ namespace Aws * Deprecated - Use m_OnStreamStopped */ OnStreamReset m_OnStreamReset; + /** + * Deprecated - Use m_OnMessageSendComplete + */ + OnSendDataComplete m_OnSendDataComplete; std::shared_ptr m_selfRef; diff --git a/secure_tunneling/source/SecureTunnel.cpp b/secure_tunneling/source/SecureTunnel.cpp index b76d182e9..3dba8f542 100644 --- a/secure_tunneling/source/SecureTunnel.cpp +++ b/secure_tunneling/source/SecureTunnel.cpp @@ -53,6 +53,7 @@ namespace Aws { AWS_ZERO_STRUCT(m_payloadStorage); AWS_ZERO_STRUCT(m_serviceIdStorage); + m_connectionId = message.connection_id; setPacketByteBufOptional(m_payload, m_payloadStorage, m_allocator, message.payload); setPacketByteBufOptional(m_serviceId, m_serviceIdStorage, m_allocator, message.service_id); @@ -63,12 +64,26 @@ namespace Aws { AWS_ZERO_STRUCT(m_payloadStorage); AWS_ZERO_STRUCT(m_serviceIdStorage); + m_connectionId = 0; } Message::Message(Crt::ByteCursor payload, Crt::Allocator *allocator) noexcept : m_allocator(allocator) { AWS_ZERO_STRUCT(m_payloadStorage); AWS_ZERO_STRUCT(m_serviceIdStorage); + m_connectionId = 0; + + 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 payload, u_int32_t connectionId, Crt::Allocator *allocator) noexcept + : m_allocator(allocator) + { + AWS_ZERO_STRUCT(m_payloadStorage); + AWS_ZERO_STRUCT(m_serviceIdStorage); + m_connectionId = connectionId; aws_byte_buf_clean_up(&m_payloadStorage); aws_byte_buf_init_copy_from_cursor(&m_payloadStorage, m_allocator, payload); @@ -80,6 +95,27 @@ namespace Aws { AWS_ZERO_STRUCT(m_payloadStorage); AWS_ZERO_STRUCT(m_serviceIdStorage); + m_connectionId = 0; + + 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_serviceId = aws_byte_cursor_from_buf(&m_serviceIdStorage); + } + + Message::Message( + Crt::ByteCursor serviceId, + u_int32_t connectionId, + Crt::ByteCursor payload, + Crt::Allocator *allocator) noexcept + : m_allocator(allocator) + { + AWS_ZERO_STRUCT(m_payloadStorage); + AWS_ZERO_STRUCT(m_serviceIdStorage); + m_connectionId = connectionId; aws_byte_buf_clean_up(&m_payloadStorage); aws_byte_buf_init_copy_from_cursor(&m_payloadStorage, m_allocator, payload); @@ -106,6 +142,12 @@ namespace Aws return *this; } + Message &Message::withConnectionId(u_int32_t connectionId) noexcept + { + m_connectionId = connectionId; + return *this; + } + bool Message::initializeRawOptions(aws_secure_tunnel_message_view &raw_options) noexcept { AWS_ZERO_STRUCT(raw_options); @@ -117,6 +159,7 @@ namespace Aws { raw_options.service_id = &m_serviceId.value(); } + raw_options.connection_id = m_connectionId; return true; } @@ -125,6 +168,8 @@ namespace Aws const Crt::Optional &Message::getServiceId() const noexcept { return m_serviceId; } + const u_int32_t &Message::getConnectionId() const noexcept { return m_connectionId; } + Message::~Message() { aws_byte_buf_clean_up(&m_payloadStorage); @@ -172,10 +217,13 @@ namespace Aws AWS_ZERO_STRUCT(m_serviceIdStorage); setPacketByteBufOptional(m_serviceId, m_serviceIdStorage, m_allocator, message.service_id); + m_connectionId = message.connection_id; } const Crt::Optional &StreamStartedData::getServiceId() const noexcept { return m_serviceId; } + const u_int32_t &StreamStartedData::getConnectionId() const noexcept { return m_connectionId; } + StreamStartedData::~StreamStartedData() { aws_byte_buf_clean_up(&m_serviceIdStorage); } //*********************************************************************************************************************** @@ -196,6 +244,51 @@ namespace Aws StreamStoppedData::~StreamStoppedData() { aws_byte_buf_clean_up(&m_serviceIdStorage); } + //*********************************************************************************************************************** + /* ConnectionStartedData */ + //*********************************************************************************************************************** + + ConnectionStartedData::ConnectionStartedData( + 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); + m_connectionId = message.connection_id; + } + + const Crt::Optional &ConnectionStartedData::getServiceId() const noexcept + { + return m_serviceId; + } + + const u_int32_t &ConnectionStartedData::getConnectionId() const noexcept { return m_connectionId; } + + ConnectionStartedData::~ConnectionStartedData() { aws_byte_buf_clean_up(&m_serviceIdStorage); } + + //*********************************************************************************************************************** + /* ConnectionResetData */ + //*********************************************************************************************************************** + + ConnectionResetData::ConnectionResetData( + 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); + m_connectionId = message.connection_id; + } + + const Crt::Optional &ConnectionResetData::getServiceId() const noexcept { return m_serviceId; } + + const u_int32_t &ConnectionResetData::getConnectionId() const noexcept { return m_connectionId; } + + ConnectionResetData::~ConnectionResetData() { aws_byte_buf_clean_up(&m_serviceIdStorage); } + //*********************************************************************************************************************** /* SecureTunnelBuilder */ //*********************************************************************************************************************** @@ -207,9 +300,7 @@ namespace Aws aws_secure_tunneling_local_proxy_mode localProxyMode, 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_OnSessionReset(), m_OnConnectionComplete(), m_OnDataReceive(), m_OnStreamStart(), m_OnStreamReset() + m_accessToken(accessToken), m_localProxyMode(localProxyMode), m_endpointHost(endpointHost), m_rootCa("") { } @@ -221,9 +312,7 @@ namespace Aws const std::string &endpointHost) // Make a copy and save in this object : 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_OnSessionReset(), m_OnConnectionComplete(), - m_OnDataReceive(), m_OnStreamStart(), m_OnStreamReset() + m_endpointHost(endpointHost), m_rootCa("") { } @@ -234,9 +323,7 @@ namespace Aws 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_OnSessionReset(), m_OnConnectionComplete(), - m_OnDataReceive(), m_OnStreamStart(), m_OnStreamReset() + m_endpointHost(endpointHost), m_rootCa("") { } @@ -271,9 +358,9 @@ namespace Aws return *this; } - SecureTunnelBuilder &SecureTunnelBuilder::WithOnSendDataComplete(OnSendDataComplete onSendDataComplete) + SecureTunnelBuilder &SecureTunnelBuilder::WithOnSendMessageComplete(OnSendMessageComplete onSendMessageComplete) { - m_OnSendDataComplete = std::move(onSendDataComplete); + m_OnSendMessageComplete = std::move(onSendMessageComplete); return *this; } @@ -295,9 +382,15 @@ namespace Aws return *this; } - SecureTunnelBuilder &SecureTunnelBuilder::WithOnStreamReset(OnStreamReset onStreamReset) + SecureTunnelBuilder &SecureTunnelBuilder::WithOnConnectionStarted(OnConnectionStarted onConnectionStarted) { - m_OnStreamReset = std::move(onStreamReset); + m_OnConnectionStarted = std::move(onConnectionStarted); + return *this; + } + + SecureTunnelBuilder &SecureTunnelBuilder::WithOnConnectionReset(OnConnectionReset onConnectionReset) + { + m_OnConnectionReset = std::move(onConnectionReset); return *this; } @@ -331,12 +424,24 @@ namespace Aws m_OnStreamStart = std::move(onStreamStart); return *this; } + /* Deprecated - Use WithOnStreamStopped */ + SecureTunnelBuilder &SecureTunnelBuilder::WithOnStreamReset(OnStreamReset onStreamReset) + { + m_OnStreamReset = std::move(onStreamReset); + return *this; + } /* Deprecated - Use WithOnMessageReceived */ SecureTunnelBuilder &SecureTunnelBuilder::WithOnDataReceive(OnDataReceive onDataReceive) { m_OnDataReceive = std::move(onDataReceive); return *this; } + /* Deprecated - Use WithOnSendMessageComplete */ + SecureTunnelBuilder &SecureTunnelBuilder::WithOnSendDataComplete(OnSendDataComplete onSendDataComplete) + { + m_OnSendDataComplete = std::move(onSendDataComplete); + return *this; + } std::shared_ptr SecureTunnelBuilder::Build() noexcept { @@ -354,6 +459,7 @@ namespace Aws m_OnConnectionFailure, m_OnConnectionComplete, m_OnConnectionShutdown, + m_OnSendMessageComplete, m_OnSendDataComplete, m_OnMessageReceived, m_OnDataReceive, @@ -396,6 +502,7 @@ namespace Aws OnConnectionFailure onConnectionFailure, OnConnectionComplete onConnectionComplete, OnConnectionShutdown onConnectionShutdown, + OnSendMessageComplete onSendMessageComplete, OnSendDataComplete onSendDataComplete, OnMessageReceived onMessageReceived, OnDataReceive onDataReceive, @@ -411,6 +518,7 @@ namespace Aws m_OnConnectionFailure = std::move(onConnectionFailure); m_OnConnectionComplete = std::move(onConnectionComplete); m_OnConnectionShutdown = std::move(onConnectionShutdown); + m_OnSendMessageComplete = std::move(onSendMessageComplete); m_OnSendDataComplete = std::move(onSendDataComplete); m_OnMessageReceived = std::move(onMessageReceived); m_OnDataReceive = std::move(onDataReceive); @@ -445,9 +553,11 @@ namespace Aws 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_send_message_complete = s_OnSendMessageComplete; config.on_stream_start = s_OnStreamStarted; config.on_stream_reset = s_OnStreamStopped; + config.on_connection_start = s_OnConnectionStarted; + config.on_connection_reset = s_OnConnectionReset; config.on_session_reset = s_OnSessionReset; config.on_stopped = s_OnStopped; @@ -500,6 +610,7 @@ namespace Aws nullptr, onConnectionComplete, onConnectionShutdown, + nullptr, onSendDataComplete, nullptr, onDataReceive, @@ -545,6 +656,7 @@ namespace Aws nullptr, onConnectionComplete, onConnectionShutdown, + nullptr, onSendDataComplete, nullptr, onDataReceive, @@ -571,7 +683,7 @@ namespace Aws 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_OnSendMessageComplete = std::move(other.m_OnSendMessageComplete); m_OnMessageReceived = std::move(other.m_OnMessageReceived); m_OnStreamStarted = std::move(other.m_OnStreamStarted); m_OnStreamReset = std::move(other.m_OnStreamReset); @@ -584,6 +696,8 @@ namespace Aws m_OnDataReceive = std::move(other.m_OnDataReceive); /* Deprecated - Use m_OnStreamStarted */ m_OnStreamStart = std::move(other.m_OnStreamStart); + /* Deprecated - Use m_OnSendMessageComplete */ + m_OnSendDataComplete = std::move(other.m_OnSendDataComplete); m_secure_tunnel = other.m_secure_tunnel; @@ -599,7 +713,7 @@ namespace Aws 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_OnSendMessageComplete = std::move(other.m_OnSendMessageComplete); m_OnMessageReceived = std::move(other.m_OnMessageReceived); m_OnStreamStarted = std::move(other.m_OnStreamStarted); m_OnStreamReset = std::move(other.m_OnStreamReset); @@ -612,6 +726,8 @@ namespace Aws m_OnDataReceive = std::move(other.m_OnDataReceive); /* Deprecated - Use m_OnStreamStarted */ m_OnStreamStart = std::move(other.m_OnStreamStart); + /* Deprecated - Use m_OnSendMessageComplete */ + m_OnSendDataComplete = std::move(other.m_OnSendDataComplete); m_secure_tunnel = other.m_secure_tunnel; @@ -679,6 +795,28 @@ namespace Aws return aws_secure_tunnel_stream_start(m_secure_tunnel, &messageView); } + int SecureTunnel::SendConnectionStart(u_int32_t connectionId) { return SendConnectionStart("", connectionId); } + + int SecureTunnel::SendConnectionStart(std::string serviceId, u_int32_t connectionId) + { + struct aws_byte_cursor service_id_cur; + AWS_ZERO_STRUCT(service_id_cur); + if (serviceId.length() > 0) + { + service_id_cur = aws_byte_cursor_from_c_str(serviceId.c_str()); + } + return SendConnectionStart(service_id_cur, connectionId); + } + + int SecureTunnel::SendConnectionStart(Crt::ByteCursor serviceId, u_int32_t connectionId) + { + struct aws_secure_tunnel_message_view messageView; + AWS_ZERO_STRUCT(messageView); + messageView.service_id = &serviceId; + messageView.connection_id = connectionId; + return aws_secure_tunnel_connection_start(m_secure_tunnel, &messageView); + } + int SecureTunnel::SendStreamReset() { return aws_secure_tunnel_stream_reset(m_secure_tunnel, NULL); } aws_secure_tunnel *SecureTunnel::GetUnderlyingHandle() { return m_secure_tunnel; } @@ -728,10 +866,18 @@ namespace Aws } } - void SecureTunnel::s_OnSendDataComplete(int error_code, void *user_data) + void SecureTunnel::s_OnSendMessageComplete( + enum aws_secure_tunnel_message_type type, + int error_code, + void *user_data) { - SecureTunnel *secureTunnel = static_cast(user_data); - if (secureTunnel->m_OnSendDataComplete) + auto *secureTunnel = static_cast(user_data); + + if (secureTunnel->m_OnSendMessageComplete) + { + secureTunnel->m_OnSendMessageComplete(secureTunnel, error_code, type); + } + else if (secureTunnel->m_OnSendDataComplete) { secureTunnel->m_OnSendDataComplete(error_code); } @@ -831,6 +977,44 @@ namespace Aws } } + void SecureTunnel::s_OnConnectionStarted( + const struct aws_secure_tunnel_message_view *message, + int error_code, + void *user_data) + { + SecureTunnel *secureTunnel = static_cast(user_data); + if (!error_code) + { + if (secureTunnel->m_OnConnectionStarted) + { + std::shared_ptr packet = + std::make_shared(*message, secureTunnel->m_allocator); + ConnectionStartedEventData eventData; + eventData.connectionStartedData = packet; + secureTunnel->m_OnConnectionStarted(secureTunnel, error_code, eventData); + return; + } + } + } + + void SecureTunnel::s_OnConnectionReset( + const struct aws_secure_tunnel_message_view *message, + int error_code, + void *user_data) + { + SecureTunnel *secureTunnel = static_cast(user_data); + + if (secureTunnel->m_OnConnectionReset) + { + std::shared_ptr packet = + std::make_shared(*message, secureTunnel->m_allocator); + ConnectionResetEventData eventData; + eventData.connectionResetData = packet; + secureTunnel->m_OnConnectionReset(secureTunnel, error_code, eventData); + return; + } + } + void SecureTunnel::s_OnSessionReset(void *user_data) { SecureTunnel *secureTunnel = static_cast(user_data); From cc07ccd477231dedfe96925c9e5a971291b57da0 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Tue, 4 Apr 2023 15:56:59 -0700 Subject: [PATCH 02/18] Updated sample to use V3 and added Connection callback API --- crt/aws-c-iot | 2 +- .../secure_tunneling/secure_tunnel/main.cpp | 341 +++++++++--------- .../aws/iotsecuretunneling/SecureTunnel.h | 26 ++ secure_tunneling/source/SecureTunnel.cpp | 29 +- 4 files changed, 227 insertions(+), 171 deletions(-) diff --git a/crt/aws-c-iot b/crt/aws-c-iot index 09ded2b5e..eacf70800 160000 --- a/crt/aws-c-iot +++ b/crt/aws-c-iot @@ -1 +1 @@ -Subproject commit 09ded2b5e5bd34bbcf0fd71b5482381cf7f08627 +Subproject commit eacf7080011a76bf77f80c8854e50b25b1045098 diff --git a/samples/secure_tunneling/secure_tunnel/main.cpp b/samples/secure_tunneling/secure_tunnel/main.cpp index fe6c031be..0b245a3a0 100644 --- a/samples/secure_tunneling/secure_tunnel/main.cpp +++ b/samples/secure_tunneling/secure_tunnel/main.cpp @@ -21,45 +21,105 @@ using namespace std::chrono_literals; void logMessage(std::shared_ptr message) { + + fprintf(stdout, "Message received"); if (message->getServiceId().has_value()) { - if (message->getPayload().has_value()) - { - fprintf( - stdout, - "Message received on service id:'" PRInSTR "' connection id: %d with payload:'" PRInSTR "'\n", - AWS_BYTE_CURSOR_PRI(message->getServiceId().value()), - message->getConnectionId(), - AWS_BYTE_CURSOR_PRI(message->getPayload().value())); - } - else - { - fprintf( - stdout, - "Message with service id:'" PRInSTR "' connection id: %d with no payload.\n", - AWS_BYTE_CURSOR_PRI(message->getServiceId().value()), - message->getConnectionId()); - } - return; + fprintf(stdout, " on service id:'" PRInSTR "'", AWS_BYTE_CURSOR_PRI(message->getServiceId().value())); } + + if (message->getConnectionId() > 0) + { + fprintf(stdout, " with connection id: (%d)", message->getConnectionId()); + } + if (message->getPayload().has_value()) { - if (message->getConnectionId() > 0) - { - fprintf( - stdout, - "Message received on connection id: %d with payload:'" PRInSTR "'\n", - message->getConnectionId(), - AWS_BYTE_CURSOR_PRI(message->getPayload().value())); - } - else + fprintf(stdout, " with payload: '" PRInSTR "'", AWS_BYTE_CURSOR_PRI(message->getPayload().value())); + } + + fprintf(stdout, "\n"); +} + +void logConnectionData(const ConnectionSuccessEventData &eventData) +{ + if (eventData.connectionData->getServiceId1().has_value()) + { + fprintf( + stdout, + "Secure Tunnel connected with Service IDs '" PRInSTR "'", + AWS_BYTE_CURSOR_PRI(eventData.connectionData->getServiceId1().value())); + + if (eventData.connectionData->getServiceId2().has_value()) { - fprintf( - stdout, - "Message received with payload:'" PRInSTR "'\n", - AWS_BYTE_CURSOR_PRI(message->getPayload().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(eventData.connectionData->getServiceId3().value())); + } } + fprintf(stdout, "\n"); + } + else + { + fprintf(stdout, "Secure Tunnel connected with no Service IDs available\n"); + } +} + +void logStreamStartData(const StreamStartedEventData &eventData) +{ + fprintf(stdout, "Stream started"); + + if (eventData.streamStartedData->getServiceId().has_value()) + { + fprintf( + stdout, + " on service id: '" PRInSTR, + AWS_BYTE_CURSOR_PRI(eventData.streamStartedData->getServiceId().value())); + } + + if (eventData.streamStartedData->getConnectionId() > 0) + { + fprintf(stdout, " with Connection Id: (%d)", eventData.streamStartedData->getConnectionId()); } + + fprintf(stdout, "\n"); +} + +void logStreamStoppedData(const StreamStoppedEventData &eventData) +{ + fprintf(stdout, "Stream"); + + if (eventData.streamStoppedData->getServiceId().has_value()) + { + fprintf( + stdout, + " on service id: '" PRInSTR, + AWS_BYTE_CURSOR_PRI(eventData.streamStoppedData->getServiceId().value())); + } + + fprintf(stdout, " stopped\n"); +} + +void logConnectionStartedData(const ConnectionStartedEventData &eventData) +{ + fprintf(stdout, "Connection started"); + + if (eventData.connectionStartedData->getServiceId().has_value()) + { + fprintf( + stdout, + " on service id: '" PRInSTR, + AWS_BYTE_CURSOR_PRI(eventData.connectionStartedData->getServiceId().value())); + } + + if (eventData.connectionStartedData->getConnectionId() > 0) + { + fprintf(stdout, " with Connection Id: (%d)", eventData.connectionStartedData->getConnectionId()); + } + + fprintf(stdout, "\n"); } void setupCommandLineUtils(Utils::CommandLineUtils *cmdUtils, int argc, char *argv[]) @@ -214,8 +274,6 @@ int main(int argc, char *argv[]) * 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; /* service id storage for use in sample */ @@ -235,6 +293,9 @@ int main(int argc, char *argv[]) String payloadMessage; uint16_t messageCount(5); + /* Connection Id is used for Simultaneous HTTP Connections (Protocl V3) */ + uint32_t connectionId = 1; + /*********************** Parse Arguments ***************************/ Utils::CommandLineUtils cmdUtils = Utils::CommandLineUtils(); setupCommandLineUtils(&cmdUtils, argc, argv); @@ -275,101 +336,65 @@ int main(int argc, char *argv[]) /* Add callbacks using the builder */ builder.WithOnConnectionSuccess([&](SecureTunnel *secureTunnel, const ConnectionSuccessEventData &eventData) { - if (eventData.connectionData->getServiceId1().has_value()) + logConnectionData(eventData); + + /* Stream Start can only be called from Source Mode */ + if (localProxyMode == AWS_SECURE_TUNNELING_SOURCE_MODE) { - /* 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(eventData.connectionData->getServiceId1().value())); - if (eventData.connectionData->getServiceId2().has_value()) + /* Use a Multiplexing (Service Id) if available on this Secure Tunnel */ + if (eventData.connectionData->getServiceId1().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(eventData.connectionData->getServiceId3().value())); - } - } - fprintf(stdout, "\n"); + /* Store the service id 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); - /* 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(eventData.connectionData->getServiceId1().value())); - secureTunnel->SendStreamStart(eventData.connectionData->getServiceId1().value()); - } - } - else - { - fprintf(stdout, "Secure Tunnel is not using Service Ids.\n"); + "Sending Stream Start request on Service Id:'" PRInSTR "' with Connection Id: (%d)\n", + AWS_BYTE_CURSOR_PRI(eventData.connectionData->getServiceId1().value()), + connectionId); - /* Stream Start can only be called from Source Mode */ - if (localProxyMode == AWS_SECURE_TUNNELING_SOURCE_MODE) + secureTunnel->SendStreamStart(eventData.connectionData->getServiceId1().value(), connectionId); + } + else { fprintf(stdout, "Sending Stream Start request\n"); secureTunnel->SendStreamStart(); } } - - connectionCompletedPromise.set_value(true); }); - builder.WithOnConnectionFailure([&](SecureTunnel *secureTunnel, int errorCode) { - (void)secureTunnel; - fprintf(stdout, "Connection attempt failed with error code %d(%s)\n", errorCode, ErrorDebugString(errorCode)); - }); - - builder.WithOnConnectionShutdown([&]() { - fprintf(stdout, "Connection Shutdown\n"); - connectionClosedPromise.set_value(true); - }); + builder.WithOnConnectionShutdown([&]() { fprintf(stdout, "Connection Shutdown\n"); }); builder.WithOnMessageReceived([&](SecureTunnel *secureTunnel, const MessageReceivedEventData &eventData) { { std::shared_ptr message = eventData.message; logMessage(message); - std::shared_ptr echoMessage; - switch (localProxyMode) + /* Send an echo message back to the Source Device */ + if (localProxyMode == AWS_SECURE_TUNNELING_DESTINATION_MODE) { - case AWS_SECURE_TUNNELING_DESTINATION_MODE: - - echoMessage = std::make_shared(message->getPayload().value()); - - /* Echo message on same service id received message arrived on */ - if (message->getServiceId().has_value()) - { - echoMessage->withServiceId(message->getServiceId().value()); - } - - /* Echo message on the same connection id received message arrived on */ - if (message->getConnectionId() > 0) - { - echoMessage->withConnectionId(message->getConnectionId()); - } + std::shared_ptr echoMessage = std::make_shared(message->getPayload().value()); - secureTunnel->SendMessage(echoMessage); + /* Echo message on same service id received message arrived on */ + if (message->getServiceId().has_value()) + { + echoMessage->withServiceId(message->getServiceId().value()); + } - fprintf(stdout, "Sending Echo Message\n"); + /* Echo message on the same connection id received message arrived on */ + if (message->getConnectionId() > 0) + { + echoMessage->withConnectionId(message->getConnectionId()); + } - break; - case AWS_SECURE_TUNNELING_SOURCE_MODE: + secureTunnel->SendMessage(echoMessage); - break; + fprintf(stdout, "Sending Echo Message\n"); } } }); @@ -379,57 +404,34 @@ int main(int argc, char *argv[]) (void)secureTunnel; 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"); - } + logStreamStartData(eventData); + } + else + { + fprintf(stdout, "Stream Start failed with error code %d(%s)\n", errorCode, ErrorDebugString(errorCode)); } }); builder.WithOnStreamStopped([&](SecureTunnel *secureTunnel, const StreamStoppedEventData &eventData) { (void)secureTunnel; - std::shared_ptr streamStoppedData = eventData.streamStoppedData; - if (streamStoppedData->getServiceId().has_value()) + logStreamStoppedData(eventData); + }); + + builder.WithOnConnectionStarted([&](SecureTunnel *secureTunnel, + int errorCode, + const ConnectionStartedEventData &eventData) { + (void)secureTunnel; + if (!errorCode) { - fprintf( - stdout, - "Stream stopped on service id: '" PRInSTR "'\n", - AWS_BYTE_CURSOR_PRI(streamStoppedData->getServiceId().value())); + logConnectionStartedData(eventData); } else { - fprintf(stdout, "Stream stopped using V1 Protocol"); + fprintf(stdout, "Connection Start failed with error code %d(%s)\n", errorCode, ErrorDebugString(errorCode)); } }); - builder.WithOnConnectionStarted( - [&](SecureTunnel *secureTunnel, int errorCode, const ConnectionStartedEventData &eventData) { - (void)secureTunnel; - if (!errorCode) - { - std::shared_ptr connectionStartedData = eventData.connectionStartedData; - } - }); - - builder.WithOnConnectionReset( - [&](SecureTunnel *secureTunnel, int errorCode, const ConnectionResetEventData &eventData) { - (void)secureTunnel; - if (!errorCode) - { - std::shared_ptr connectionResetData = eventData.connectionResetData; - } - }); - builder.WithOnStopped([&](SecureTunnel *secureTunnel) { (void)secureTunnel; fprintf(stdout, "Secure Tunnel has entered Stopped State\n"); @@ -485,34 +487,46 @@ int main(int argc, char *argv[]) bool keepRunning = true; uint16_t messagesSent = 0; - if (connectionCompletedPromise.get_future().get()) + /* + * 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) { - 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) + std::this_thread::sleep_for(2000ms); + if (localProxyMode == AWS_SECURE_TUNNELING_SOURCE_MODE) { - if (localProxyMode == AWS_SECURE_TUNNELING_SOURCE_MODE) + messagesSent++; + String toSend = (std::to_string(messagesSent) + ": " + payloadMessage.c_str()).c_str(); + + if (messagesSent <= messageCount) { - messagesSent++; - String toSend = (std::to_string(messagesSent) + ": " + payloadMessage.c_str()).c_str(); - if (messagesSent <= messageCount) + 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 (m_serviceId.has_value()) { - std::shared_ptr message = std::make_shared(ByteCursorFromCString(toSend.c_str())); + message->withServiceId(m_serviceId.value()); + } - /* If the secure tunnel has service ids, we will use one for our messages. */ - if (m_serviceId.has_value()) - { - message->withServiceId(m_serviceId.value()); - } + message->withConnectionId(connectionId); - secureTunnel->SendMessage(message); + secureTunnel->SendMessage(message); - fprintf(stdout, "Sending Message:\"%s\"\n", toSend.c_str()); + fprintf(stdout, "Sending Message:\"%s\"\n", toSend.c_str()); + } + else + { + /* + * Start a new Simultaneous HTTP Connection using Connection Id 2 and send/receive messages on new + * established stream on the same service id. + */ + if (connectionId == 1 && m_serviceId.has_value()) + { + messagesSent = 0; + connectionId = 2; + secureTunnel->SendConnectionStart(m_serviceId.value(), connectionId); std::this_thread::sleep_for(2000ms); } @@ -534,11 +548,6 @@ int main(int argc, char *argv[]) exit(-1); } - if (connectionClosedPromise.get_future().get()) - { - 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()) { diff --git a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h index 5fae76ed8..919c63d0f 100644 --- a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h +++ b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h @@ -988,6 +988,30 @@ namespace Aws */ int SendStreamStart(Crt::ByteCursor 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 serviceId: The Service Id to start a stream on. + * + * @param connectionId: The Connection Id to start the stream on. + * + * @return success/failure in the synchronous logic that kicks off the Stream Start operation + */ + int SendStreamStart(std::string serviceId, u_int32_t connectionId); + + /** + * 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 serviceId: The Service Id to start a stream on. + * + * @param connectionId: The Connection Id to start the stream on. + * + * @return success/failure in the synchronous logic that kicks off the Stream Start operation + */ + int SendStreamStart(Crt::ByteCursor serviceId, u_int32_t connectionId); + /** * Notifies the secure tunnel that you want to start a connection with the Destination device. * @@ -1073,6 +1097,8 @@ namespace Aws OnStreamStart onStreamStart, /* Deprecated */ OnStreamStopped onStreamStopped, OnStreamReset onStreamReset, /* Deprecated */ + OnConnectionStarted onConnectionStarted, + OnConnectionReset onConnectionReset, OnSessionReset onSessionReset, OnStopped onStopped); diff --git a/secure_tunneling/source/SecureTunnel.cpp b/secure_tunneling/source/SecureTunnel.cpp index 3dba8f542..8e7767e0c 100644 --- a/secure_tunneling/source/SecureTunnel.cpp +++ b/secure_tunneling/source/SecureTunnel.cpp @@ -467,6 +467,8 @@ namespace Aws m_OnStreamStart, m_OnStreamStopped, m_OnStreamReset, + m_OnConnectionStarted, + m_OnConnectionReset, m_OnSessionReset, m_OnStopped)); @@ -510,6 +512,8 @@ namespace Aws OnStreamStart onStreamStart, OnStreamStopped onStreamStopped, OnStreamReset onStreamReset, + OnConnectionStarted onConnectionStarted, + OnConnectionReset onConnectionReset, OnSessionReset onSessionReset, OnStopped onStopped) { @@ -525,6 +529,8 @@ namespace Aws m_OnStreamStarted = std::move(onStreamStarted); m_OnStreamStart = std::move(onStreamStart); m_OnStreamReset = std::move(onStreamReset); + m_OnConnectionStarted = std::move(onConnectionStarted); + m_OnConnectionReset = std::move(onConnectionReset); m_OnSessionReset = std::move(onSessionReset); m_OnStopped = std::move(onStopped); @@ -618,6 +624,8 @@ namespace Aws onStreamStart, nullptr, onStreamReset, + nullptr, + nullptr, onSessionReset, nullptr) { @@ -664,6 +672,8 @@ namespace Aws onStreamStart, nullptr, onStreamReset, + nullptr, + nullptr, onSessionReset, nullptr) { @@ -687,6 +697,8 @@ namespace Aws m_OnMessageReceived = std::move(other.m_OnMessageReceived); m_OnStreamStarted = std::move(other.m_OnStreamStarted); m_OnStreamReset = std::move(other.m_OnStreamReset); + m_OnConnectionStarted = std::move(other.m_OnConnectionStarted); + m_OnConnectionReset = std::move(other.m_OnConnectionReset); m_OnSessionReset = std::move(other.m_OnSessionReset); m_OnStopped = std::move(other.m_OnStopped); @@ -717,6 +729,8 @@ namespace Aws m_OnMessageReceived = std::move(other.m_OnMessageReceived); m_OnStreamStarted = std::move(other.m_OnStreamStarted); m_OnStreamReset = std::move(other.m_OnStreamReset); + m_OnConnectionStarted = std::move(other.m_OnConnectionStarted); + m_OnConnectionReset = std::move(other.m_OnConnectionReset); m_OnSessionReset = std::move(other.m_OnSessionReset); m_OnStopped = std::move(other.m_OnStopped); @@ -777,7 +791,9 @@ namespace Aws } int SecureTunnel::SendStreamStart() { return SendStreamStart(""); } - int SecureTunnel::SendStreamStart(std::string serviceId) + int SecureTunnel::SendStreamStart(std::string serviceId) { return SendStreamStart(serviceId, 0); } + int SecureTunnel::SendStreamStart(Crt::ByteCursor serviceId) { return SendStreamStart(serviceId, 0); } + int SecureTunnel::SendStreamStart(std::string serviceId, u_int32_t connectionId) { struct aws_byte_cursor service_id_cur; AWS_ZERO_STRUCT(service_id_cur); @@ -785,13 +801,18 @@ namespace Aws { service_id_cur = aws_byte_cursor_from_c_str(serviceId.c_str()); } - return SendStreamStart(service_id_cur); + return SendStreamStart(service_id_cur, connectionId); } - int SecureTunnel::SendStreamStart(Crt::ByteCursor serviceId) + + int SecureTunnel::SendStreamStart(Crt::ByteCursor serviceId, u_int32_t connectionId) { struct aws_secure_tunnel_message_view messageView; AWS_ZERO_STRUCT(messageView); - messageView.service_id = &serviceId; + if (serviceId.len > 0) + { + messageView.service_id = &serviceId; + } + messageView.connection_id = connectionId; return aws_secure_tunnel_stream_start(m_secure_tunnel, &messageView); } From 24db41a11a436ded5accbc32fbb590503284140e Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 5 Apr 2023 09:17:58 -0700 Subject: [PATCH 03/18] move some variables --- samples/secure_tunneling/secure_tunnel/main.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/samples/secure_tunneling/secure_tunnel/main.cpp b/samples/secure_tunneling/secure_tunnel/main.cpp index 0b245a3a0..c4f80c277 100644 --- a/samples/secure_tunneling/secure_tunnel/main.cpp +++ b/samples/secure_tunneling/secure_tunnel/main.cpp @@ -295,6 +295,8 @@ int main(int argc, char *argv[]) /* Connection Id is used for Simultaneous HTTP Connections (Protocl V3) */ uint32_t connectionId = 1; + bool keepRunning = true; + uint16_t messagesSent = 0; /*********************** Parse Arguments ***************************/ Utils::CommandLineUtils cmdUtils = Utils::CommandLineUtils(); @@ -484,9 +486,6 @@ int main(int argc, char *argv[]) exit(-1); } - bool keepRunning = true; - uint16_t messagesSent = 0; - /* * 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. From b4631741d5c23689cc2e5a2c6414e8a994a40a5d Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 5 Apr 2023 09:51:55 -0700 Subject: [PATCH 04/18] added callback to sample --- .../secure_tunneling/secure_tunnel/main.cpp | 146 ++++++++++-------- 1 file changed, 78 insertions(+), 68 deletions(-) diff --git a/samples/secure_tunneling/secure_tunnel/main.cpp b/samples/secure_tunneling/secure_tunnel/main.cpp index c4f80c277..7c9826e3f 100644 --- a/samples/secure_tunneling/secure_tunnel/main.cpp +++ b/samples/secure_tunneling/secure_tunnel/main.cpp @@ -333,9 +333,79 @@ int main(int argc, char *argv[]) builder.WithRootCa(caFile.c_str()); } + /* Proxy Options */ + 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 */ + if (proxyUserName.length() > 0 || proxyPassword.length() > 0) + { + fprintf(stdout, "Creating proxy strategy\n"); + Aws::Crt::Http::HttpProxyStrategyBasicAuthConfig basicAuthConfig; + basicAuthConfig.ConnectionType = Aws::Crt::Http::AwsHttpProxyConnectionType::Tunneling; + basicAuthConfig.Username = proxyUserName.c_str(); + basicAuthConfig.Password = proxyPassword.c_str(); + proxyOptions.ProxyStrategy = + Aws::Crt::Http::HttpProxyStrategy::CreateBasicHttpProxyStrategy(basicAuthConfig, allocator); + proxyOptions.AuthType = Aws::Crt::Http::AwsHttpProxyAuthenticationType::Basic; + } + else + { + proxyOptions.AuthType = Aws::Crt::Http::AwsHttpProxyAuthenticationType::None; + } + + /* Add proxy options to the builder */ + builder.WithHttpClientConnectionProxyOptions(proxyOptions); + } + builder.WithClientToken(clientToken.c_str()); /* Add callbacks using the builder */ + builder.WithOnMessageReceived([&](SecureTunnel *secureTunnel, const MessageReceivedEventData &eventData) { + { + std::shared_ptr message = eventData.message; + + logMessage(message); + + /* Send an echo message back to the Source Device */ + if (localProxyMode == AWS_SECURE_TUNNELING_DESTINATION_MODE) + { + std::shared_ptr echoMessage = std::make_shared(message->getPayload().value()); + + /* Echo message on same service id received message arrived on */ + if (message->getServiceId().has_value()) + { + echoMessage->withServiceId(message->getServiceId().value()); + } + + /* Echo message on the same connection id received message arrived on */ + if (message->getConnectionId() > 0) + { + echoMessage->withConnectionId(message->getConnectionId()); + } + + secureTunnel->SendMessage(echoMessage); + + fprintf(stdout, "Sending Echo Message\n"); + } + } + }); + + builder.WithOnSendMessageComplete([&](SecureTunnel *secureTunnel, + int errorCode, + enum aws_secure_tunnel_message_type type) { + if (!errorCode) + { + fprintf(stdout, "Message of type %s sent successfully\n", aws_secure_tunnel_message_type_to_c_string(type)); + } + else + { + fprintf(stdout, "Send Message failed with error code %d(%s)\n", errorCode, ErrorDebugString(errorCode)); + } + }); builder.WithOnConnectionSuccess([&](SecureTunnel *secureTunnel, const ConnectionSuccessEventData &eventData) { logConnectionData(eventData); @@ -369,38 +439,6 @@ int main(int argc, char *argv[]) } }); - builder.WithOnConnectionShutdown([&]() { fprintf(stdout, "Connection Shutdown\n"); }); - - builder.WithOnMessageReceived([&](SecureTunnel *secureTunnel, const MessageReceivedEventData &eventData) { - { - std::shared_ptr message = eventData.message; - - logMessage(message); - - /* Send an echo message back to the Source Device */ - if (localProxyMode == AWS_SECURE_TUNNELING_DESTINATION_MODE) - { - std::shared_ptr echoMessage = std::make_shared(message->getPayload().value()); - - /* Echo message on same service id received message arrived on */ - if (message->getServiceId().has_value()) - { - echoMessage->withServiceId(message->getServiceId().value()); - } - - /* Echo message on the same connection id received message arrived on */ - if (message->getConnectionId() > 0) - { - echoMessage->withConnectionId(message->getConnectionId()); - } - - secureTunnel->SendMessage(echoMessage); - - fprintf(stdout, "Sending Echo Message\n"); - } - } - }); - builder.WithOnStreamStarted( [&](SecureTunnel *secureTunnel, int errorCode, const StreamStartedEventData &eventData) { (void)secureTunnel; @@ -414,12 +452,6 @@ int main(int argc, char *argv[]) } }); - builder.WithOnStreamStopped([&](SecureTunnel *secureTunnel, const StreamStoppedEventData &eventData) { - (void)secureTunnel; - - logStreamStoppedData(eventData); - }); - builder.WithOnConnectionStarted([&](SecureTunnel *secureTunnel, int errorCode, const ConnectionStartedEventData &eventData) { @@ -434,42 +466,20 @@ int main(int argc, char *argv[]) } }); + builder.WithOnStreamStopped([&](SecureTunnel *secureTunnel, const StreamStoppedEventData &eventData) { + (void)secureTunnel; + + logStreamStoppedData(eventData); + }); + + builder.WithOnConnectionShutdown([&]() { fprintf(stdout, "Connection Shutdown\n"); }); + builder.WithOnStopped([&](SecureTunnel *secureTunnel) { (void)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(); - proxyOptions.HostName = proxyHost.c_str(); - proxyOptions.Port = proxyPort; - - /* 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"); - Aws::Crt::Http::HttpProxyStrategyBasicAuthConfig basicAuthConfig; - basicAuthConfig.ConnectionType = Aws::Crt::Http::AwsHttpProxyConnectionType::Tunneling; - basicAuthConfig.Username = proxyUserName.c_str(); - basicAuthConfig.Password = proxyPassword.c_str(); - proxyOptions.ProxyStrategy = - Aws::Crt::Http::HttpProxyStrategy::CreateBasicHttpProxyStrategy(basicAuthConfig, allocator); - proxyOptions.AuthType = Aws::Crt::Http::AwsHttpProxyAuthenticationType::Basic; - } - else - { - proxyOptions.AuthType = Aws::Crt::Http::AwsHttpProxyAuthenticationType::None; - } - - /* Add proxy options to the builder */ - builder.WithHttpClientConnectionProxyOptions(proxyOptions); - } - /* Create Secure Tunnel using the options set with the builder */ std::shared_ptr secureTunnel = builder.Build(); From 25d76374b2f3c097f100941be0de465da5b2278a Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 5 Apr 2023 10:49:10 -0700 Subject: [PATCH 05/18] changed sendmessagecomplete to return an eventdata for future expandibility --- crt/aws-c-iot | 2 +- .../secure_tunneling/secure_tunnel/main.cpp | 26 +++++----- .../aws/iotsecuretunneling/SecureTunnel.h | 50 ++++++++++++++++++- secure_tunneling/source/SecureTunnel.cpp | 27 +++++++++- 4 files changed, 90 insertions(+), 15 deletions(-) diff --git a/crt/aws-c-iot b/crt/aws-c-iot index eacf70800..79ad39e20 160000 --- a/crt/aws-c-iot +++ b/crt/aws-c-iot @@ -1 +1 @@ -Subproject commit eacf7080011a76bf77f80c8854e50b25b1045098 +Subproject commit 79ad39e2044228b319bce3876f226e0ce5c77b66 diff --git a/samples/secure_tunneling/secure_tunnel/main.cpp b/samples/secure_tunneling/secure_tunnel/main.cpp index 7c9826e3f..1f384524d 100644 --- a/samples/secure_tunneling/secure_tunnel/main.cpp +++ b/samples/secure_tunneling/secure_tunnel/main.cpp @@ -394,18 +394,20 @@ int main(int argc, char *argv[]) } }); - builder.WithOnSendMessageComplete([&](SecureTunnel *secureTunnel, - int errorCode, - enum aws_secure_tunnel_message_type type) { - if (!errorCode) - { - fprintf(stdout, "Message of type %s sent successfully\n", aws_secure_tunnel_message_type_to_c_string(type)); - } - else - { - fprintf(stdout, "Send Message failed with error code %d(%s)\n", errorCode, ErrorDebugString(errorCode)); - } - }); + builder.WithOnSendMessageComplete( + [&](SecureTunnel *secureTunnel, int errorCode, const SendMessageCompleteEventData &eventData) { + if (!errorCode) + { + fprintf( + stdout, + "Message of type '" PRInSTR "' sent successfully\n", + AWS_BYTE_CURSOR_PRI(eventData.sendMessageCompleteData->getMessageType())); + } + else + { + fprintf(stdout, "Send Message failed with error code %d(%s)\n", errorCode, ErrorDebugString(errorCode)); + } + }); builder.WithOnConnectionSuccess([&](SecureTunnel *secureTunnel, const ConnectionSuccessEventData &eventData) { logConnectionData(eventData); diff --git a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h index 919c63d0f..4654ceef2 100644 --- a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h +++ b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h @@ -133,6 +133,54 @@ namespace Aws std::shared_ptr message; }; + /** + * Data model for messages sent out to the WebSocket + */ + class AWS_IOTSECURETUNNELING_API SendMessageCompleteData + { + public: + SendMessageCompleteData( + enum aws_secure_tunnel_message_type type, + Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; + + /** + * Message Type of sent message. + * + * @return Message Type of sent message. + */ + const Crt::ByteCursor &getMessageType() const noexcept; + + virtual ~SendMessageCompleteData(); + /* Do not allow direct copy or move */ + SendMessageCompleteData(const SendMessageCompleteData &) = delete; + SendMessageCompleteData(SendMessageCompleteData &&) noexcept = delete; + SendMessageCompleteData &operator=(const SendMessageCompleteData &) = delete; + SendMessageCompleteData &operator=(SendMessageCompleteData &&) noexcept = delete; + + private: + Crt::Allocator *m_allocator; + + /** + * Message Type of sent message. + * + */ + Crt::ByteCursor m_messageType; + + /////////////////////////////////////////////////////////////////////////// + // Underlying data storage for internal use + /////////////////////////////////////////////////////////////////////////// + Crt::ByteBuf m_messageTypeStorage; + }; + + /** + * The data returned when a message is sent on the secure tunnel. + */ + struct AWS_IOTSECURETUNNELING_API SendMessageCompleteEventData + { + SendMessageCompleteEventData() : sendMessageCompleteData(nullptr) {} + std::shared_ptr sendMessageCompleteData; + }; + /** * Data model for Secure Tunnel connection view. */ @@ -469,7 +517,7 @@ namespace Aws * Type signature of the callback invoked when message has been sent through the secure tunnel connection. */ using OnSendMessageComplete = - std::function; + std::function; /** * Type signature of the callback invoked when a message is received through the secure tunnel connection. diff --git a/secure_tunneling/source/SecureTunnel.cpp b/secure_tunneling/source/SecureTunnel.cpp index 8e7767e0c..4d879b243 100644 --- a/secure_tunneling/source/SecureTunnel.cpp +++ b/secure_tunneling/source/SecureTunnel.cpp @@ -176,6 +176,27 @@ namespace Aws aws_byte_buf_clean_up(&m_serviceIdStorage); } + //*********************************************************************************************************************** + /* SendMessageCompleteData */ + //*********************************************************************************************************************** + + SendMessageCompleteData::SendMessageCompleteData( + enum aws_secure_tunnel_message_type type, + Crt::Allocator *allocator) noexcept + : m_allocator(allocator) + { + aws_byte_buf_clean_up(&m_messageTypeStorage); + AWS_ZERO_STRUCT(m_messageTypeStorage); + struct aws_byte_buf typeBuf = aws_byte_buf_from_c_str(aws_secure_tunnel_message_type_to_c_string(type)); + + aws_byte_buf_init_copy(&m_messageTypeStorage, m_allocator, &typeBuf); + m_messageType = aws_byte_cursor_from_buf(&m_messageTypeStorage); + } + + const Crt::ByteCursor &SendMessageCompleteData::getMessageType() const noexcept { return m_messageType; } + + SendMessageCompleteData::~SendMessageCompleteData() { aws_byte_buf_clean_up(&m_messageTypeStorage); } + //*********************************************************************************************************************** /* ConnectionData */ //*********************************************************************************************************************** @@ -896,7 +917,11 @@ namespace Aws if (secureTunnel->m_OnSendMessageComplete) { - secureTunnel->m_OnSendMessageComplete(secureTunnel, error_code, type); + std::shared_ptr packet = + std::make_shared(type, secureTunnel->m_allocator); + SendMessageCompleteEventData eventData; + eventData.sendMessageCompleteData = packet; + secureTunnel->m_OnSendMessageComplete(secureTunnel, error_code, eventData); } else if (secureTunnel->m_OnSendDataComplete) { From 707bcb26ddc73198ff78ebfe1728b7c53c83a9a2 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 5 Apr 2023 11:05:15 -0700 Subject: [PATCH 06/18] u_int32_t -> uint32_t --- .../aws/iotsecuretunneling/SecureTunnel.h | 32 +++++++++---------- secure_tunneling/source/SecureTunnel.cpp | 24 +++++++------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h index 4654ceef2..2ccc46fd2 100644 --- a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h +++ b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h @@ -29,7 +29,7 @@ namespace Aws Message(Crt::ByteCursor payload, Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; Message( Crt::ByteCursor payload, - u_int32_t connectionId, + uint32_t connectionId, Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; Message( Crt::ByteCursor serviceId, @@ -37,7 +37,7 @@ namespace Aws Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; Message( Crt::ByteCursor serviceId, - u_int32_t connectionId, + uint32_t connectionId, Crt::ByteCursor payload, Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; @@ -55,7 +55,7 @@ namespace Aws * @param connectionId The connection id for the secure tunnel message. * @return The Message Object after setting the payload. */ - Message &withConnectionId(u_int32_t connectionId) noexcept; + Message &withConnectionId(uint32_t connectionId) noexcept; /** * Sets the payload for the secure tunnel message. @@ -79,7 +79,7 @@ namespace Aws * * @return The connection id of the secure tunnel message. */ - const u_int32_t &getConnectionId() const noexcept; + const uint32_t &getConnectionId() const noexcept; /** * The payload of the secure tunnel message. @@ -110,7 +110,7 @@ namespace Aws * * If left empty, a V1 or V2 protocol message is assumed. */ - u_int32_t m_connectionId; + uint32_t m_connectionId; /** * The payload of the secure tunnel message. @@ -278,7 +278,7 @@ namespace Aws * * @return The connection id of the secure tunnel message. */ - const u_int32_t &getConnectionId() const noexcept; + const uint32_t &getConnectionId() const noexcept; virtual ~StreamStartedData(); /* Do not allow direct copy or move */ @@ -302,7 +302,7 @@ namespace Aws * * If left empty, a V1 or V2 protocol message is assumed. */ - u_int32_t m_connectionId; + uint32_t m_connectionId; /////////////////////////////////////////////////////////////////////////// // Underlying data storage for internal use @@ -390,7 +390,7 @@ namespace Aws * * @return The connection id of the opened connection. */ - const u_int32_t &getConnectionId() const noexcept; + const uint32_t &getConnectionId() const noexcept; virtual ~ConnectionStartedData(); /* Do not allow direct copy or move */ @@ -413,7 +413,7 @@ namespace Aws * The connection id of the opened connection. * */ - u_int32_t m_connectionId; + uint32_t m_connectionId; /////////////////////////////////////////////////////////////////////////// // Underlying data storage for internal use @@ -452,7 +452,7 @@ namespace Aws * * @return Connection id of the reset connection. */ - const u_int32_t &getConnectionId() const noexcept; + const uint32_t &getConnectionId() const noexcept; virtual ~ConnectionResetData(); /* Do not allow direct copy or move */ @@ -475,7 +475,7 @@ namespace Aws * The connection id of the reset connection. * */ - u_int32_t m_connectionId; + uint32_t m_connectionId; /////////////////////////////////////////////////////////////////////////// // Underlying data storage for internal use @@ -1046,7 +1046,7 @@ namespace Aws * * @return success/failure in the synchronous logic that kicks off the Stream Start operation */ - int SendStreamStart(std::string serviceId, u_int32_t connectionId); + int SendStreamStart(std::string serviceId, uint32_t connectionId); /** * Notifies the secure tunnel that you want to start a stream with the Destination device on a specific @@ -1058,7 +1058,7 @@ namespace Aws * * @return success/failure in the synchronous logic that kicks off the Stream Start operation */ - int SendStreamStart(Crt::ByteCursor serviceId, u_int32_t connectionId); + int SendStreamStart(Crt::ByteCursor serviceId, uint32_t connectionId); /** * Notifies the secure tunnel that you want to start a connection with the Destination device. @@ -1067,7 +1067,7 @@ namespace Aws * * @return success/failure in the synchronous logic that kicks off the Stream Start operation */ - int SendConnectionStart(u_int32_t connectionId); + int SendConnectionStart(uint32_t connectionId); /** * Notifies the secure tunnel that you want to start a connection with the Destination device. @@ -1078,7 +1078,7 @@ namespace Aws * * @return success/failure in the synchronous logic that kicks off the Stream Start operation */ - int SendConnectionStart(std::string serviceId, u_int32_t connectionId); + int SendConnectionStart(std::string serviceId, uint32_t connectionId); /** * Notifies the secure tunnel that you want to start a connection with the Destination device. @@ -1089,7 +1089,7 @@ namespace Aws * * @return success/failure in the synchronous logic that kicks off the Stream Start operation */ - int SendConnectionStart(Crt::ByteCursor serviceId, u_int32_t connectionId); + int SendConnectionStart(Crt::ByteCursor serviceId, uint32_t connectionId); aws_secure_tunnel *GetUnderlyingHandle(); diff --git a/secure_tunneling/source/SecureTunnel.cpp b/secure_tunneling/source/SecureTunnel.cpp index 4d879b243..d6d31f8d7 100644 --- a/secure_tunneling/source/SecureTunnel.cpp +++ b/secure_tunneling/source/SecureTunnel.cpp @@ -78,7 +78,7 @@ namespace Aws m_payload = aws_byte_cursor_from_buf(&m_payloadStorage); } - Message::Message(Crt::ByteCursor payload, u_int32_t connectionId, Crt::Allocator *allocator) noexcept + Message::Message(Crt::ByteCursor payload, uint32_t connectionId, Crt::Allocator *allocator) noexcept : m_allocator(allocator) { AWS_ZERO_STRUCT(m_payloadStorage); @@ -108,7 +108,7 @@ namespace Aws Message::Message( Crt::ByteCursor serviceId, - u_int32_t connectionId, + uint32_t connectionId, Crt::ByteCursor payload, Crt::Allocator *allocator) noexcept : m_allocator(allocator) @@ -142,7 +142,7 @@ namespace Aws return *this; } - Message &Message::withConnectionId(u_int32_t connectionId) noexcept + Message &Message::withConnectionId(uint32_t connectionId) noexcept { m_connectionId = connectionId; return *this; @@ -168,7 +168,7 @@ namespace Aws const Crt::Optional &Message::getServiceId() const noexcept { return m_serviceId; } - const u_int32_t &Message::getConnectionId() const noexcept { return m_connectionId; } + const uint32_t &Message::getConnectionId() const noexcept { return m_connectionId; } Message::~Message() { @@ -243,7 +243,7 @@ namespace Aws const Crt::Optional &StreamStartedData::getServiceId() const noexcept { return m_serviceId; } - const u_int32_t &StreamStartedData::getConnectionId() const noexcept { return m_connectionId; } + const uint32_t &StreamStartedData::getConnectionId() const noexcept { return m_connectionId; } StreamStartedData::~StreamStartedData() { aws_byte_buf_clean_up(&m_serviceIdStorage); } @@ -285,7 +285,7 @@ namespace Aws return m_serviceId; } - const u_int32_t &ConnectionStartedData::getConnectionId() const noexcept { return m_connectionId; } + const uint32_t &ConnectionStartedData::getConnectionId() const noexcept { return m_connectionId; } ConnectionStartedData::~ConnectionStartedData() { aws_byte_buf_clean_up(&m_serviceIdStorage); } @@ -306,7 +306,7 @@ namespace Aws const Crt::Optional &ConnectionResetData::getServiceId() const noexcept { return m_serviceId; } - const u_int32_t &ConnectionResetData::getConnectionId() const noexcept { return m_connectionId; } + const uint32_t &ConnectionResetData::getConnectionId() const noexcept { return m_connectionId; } ConnectionResetData::~ConnectionResetData() { aws_byte_buf_clean_up(&m_serviceIdStorage); } @@ -814,7 +814,7 @@ namespace Aws int SecureTunnel::SendStreamStart() { return SendStreamStart(""); } int SecureTunnel::SendStreamStart(std::string serviceId) { return SendStreamStart(serviceId, 0); } int SecureTunnel::SendStreamStart(Crt::ByteCursor serviceId) { return SendStreamStart(serviceId, 0); } - int SecureTunnel::SendStreamStart(std::string serviceId, u_int32_t connectionId) + int SecureTunnel::SendStreamStart(std::string serviceId, uint32_t connectionId) { struct aws_byte_cursor service_id_cur; AWS_ZERO_STRUCT(service_id_cur); @@ -825,7 +825,7 @@ namespace Aws return SendStreamStart(service_id_cur, connectionId); } - int SecureTunnel::SendStreamStart(Crt::ByteCursor serviceId, u_int32_t connectionId) + int SecureTunnel::SendStreamStart(Crt::ByteCursor serviceId, uint32_t connectionId) { struct aws_secure_tunnel_message_view messageView; AWS_ZERO_STRUCT(messageView); @@ -837,9 +837,9 @@ namespace Aws return aws_secure_tunnel_stream_start(m_secure_tunnel, &messageView); } - int SecureTunnel::SendConnectionStart(u_int32_t connectionId) { return SendConnectionStart("", connectionId); } + int SecureTunnel::SendConnectionStart(uint32_t connectionId) { return SendConnectionStart("", connectionId); } - int SecureTunnel::SendConnectionStart(std::string serviceId, u_int32_t connectionId) + int SecureTunnel::SendConnectionStart(std::string serviceId, uint32_t connectionId) { struct aws_byte_cursor service_id_cur; AWS_ZERO_STRUCT(service_id_cur); @@ -850,7 +850,7 @@ namespace Aws return SendConnectionStart(service_id_cur, connectionId); } - int SecureTunnel::SendConnectionStart(Crt::ByteCursor serviceId, u_int32_t connectionId) + int SecureTunnel::SendConnectionStart(Crt::ByteCursor serviceId, uint32_t connectionId) { struct aws_secure_tunnel_message_view messageView; AWS_ZERO_STRUCT(messageView); From f85204eb983249628c9a1de96cced913df6d957d Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 5 Apr 2023 11:24:10 -0700 Subject: [PATCH 07/18] warning as error fix --- samples/secure_tunneling/secure_tunnel/main.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/samples/secure_tunneling/secure_tunnel/main.cpp b/samples/secure_tunneling/secure_tunnel/main.cpp index 1f384524d..388ff3214 100644 --- a/samples/secure_tunneling/secure_tunnel/main.cpp +++ b/samples/secure_tunneling/secure_tunnel/main.cpp @@ -396,6 +396,7 @@ int main(int argc, char *argv[]) builder.WithOnSendMessageComplete( [&](SecureTunnel *secureTunnel, int errorCode, const SendMessageCompleteEventData &eventData) { + (void)secureTunnel; if (!errorCode) { fprintf( From 3a26fdb7fba68118ebd2560a650e51fc62f7a5a2 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 5 Apr 2023 12:00:46 -0700 Subject: [PATCH 08/18] more warning -> error --- samples/secure_tunneling/secure_tunnel/main.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/samples/secure_tunneling/secure_tunnel/main.cpp b/samples/secure_tunneling/secure_tunnel/main.cpp index 388ff3214..b9dc634cc 100644 --- a/samples/secure_tunneling/secure_tunnel/main.cpp +++ b/samples/secure_tunneling/secure_tunnel/main.cpp @@ -397,6 +397,8 @@ int main(int argc, char *argv[]) builder.WithOnSendMessageComplete( [&](SecureTunnel *secureTunnel, int errorCode, const SendMessageCompleteEventData &eventData) { (void)secureTunnel; + (void)eventData; + if (!errorCode) { fprintf( From 75e9f9fefc2ca2bf2826efb83b315a2578cb0589 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Thu, 6 Apr 2023 11:40:51 -0700 Subject: [PATCH 09/18] clean buffer --- secure_tunneling/source/SecureTunnel.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/secure_tunneling/source/SecureTunnel.cpp b/secure_tunneling/source/SecureTunnel.cpp index d6d31f8d7..c23c005ab 100644 --- a/secure_tunneling/source/SecureTunnel.cpp +++ b/secure_tunneling/source/SecureTunnel.cpp @@ -190,6 +190,7 @@ namespace Aws struct aws_byte_buf typeBuf = aws_byte_buf_from_c_str(aws_secure_tunnel_message_type_to_c_string(type)); aws_byte_buf_init_copy(&m_messageTypeStorage, m_allocator, &typeBuf); + aws_byte_buf_clean_up(&typeBuf); m_messageType = aws_byte_cursor_from_buf(&m_messageTypeStorage); } From e800f16b2858afc567c1d5bed4b5a998114e99f0 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Thu, 6 Apr 2023 16:20:42 -0700 Subject: [PATCH 10/18] cr changes --- .../include/aws/iotsecuretunneling/SecureTunnel.h | 4 ++-- secure_tunneling/source/SecureTunnel.cpp | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h index 2ccc46fd2..057d38ba2 100644 --- a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h +++ b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h @@ -880,12 +880,12 @@ namespace Aws * Callback handler trigged when secure tunnel receives a stream reset. * * @param SecureTunnel: The shared secure tunnel - * @param StreamStoppedEventData: Stream reset data + * @param StreamStoppedEventData: Stream stopped data */ OnStreamStopped m_OnStreamStopped; /** - * Callback handler trigged when secure tunnel receives a connecction start from a source device. + * Callback handler trigged when secure tunnel receives a connection start from a source device. * * @param SecureTunnel: The shared secure tunnel * @param int: error code diff --git a/secure_tunneling/source/SecureTunnel.cpp b/secure_tunneling/source/SecureTunnel.cpp index c23c005ab..b749929d5 100644 --- a/secure_tunneling/source/SecureTunnel.cpp +++ b/secure_tunneling/source/SecureTunnel.cpp @@ -923,8 +923,11 @@ namespace Aws SendMessageCompleteEventData eventData; eventData.sendMessageCompleteData = packet; secureTunnel->m_OnSendMessageComplete(secureTunnel, error_code, eventData); + return; } - else if (secureTunnel->m_OnSendDataComplete) + + /* Fall back on deprecated complete callback */ + if (secureTunnel->m_OnSendDataComplete) { secureTunnel->m_OnSendDataComplete(error_code); } From b091ee58de93f2b9bcf3d6c55da0a373ca3092b1 Mon Sep 17 00:00:00 2001 From: Steve Kim <86316075+sbSteveK@users.noreply.github.com> Date: Thu, 13 Apr 2023 09:22:52 -0700 Subject: [PATCH 11/18] secure-tunnel-v3-tests (#561) * secure tunnel integration test --- codebuild/integration-tests.sh | 19 + codebuild/linux-integration-tests.yml | 25 ++ .../secure_tunneling/secure_tunnel/main.cpp | 2 +- secure_tunneling/source/SecureTunnel.cpp | 1 - secure_tunneling/tests/CMakeLists.txt | 29 ++ secure_tunneling/tests/main.cpp | 332 ++++++++++++++++++ 6 files changed, 406 insertions(+), 2 deletions(-) create mode 100755 codebuild/integration-tests.sh create mode 100644 codebuild/linux-integration-tests.yml create mode 100644 secure_tunneling/tests/CMakeLists.txt create mode 100644 secure_tunneling/tests/main.cpp diff --git a/codebuild/integration-tests.sh b/codebuild/integration-tests.sh new file mode 100755 index 000000000..d0852c7cc --- /dev/null +++ b/codebuild/integration-tests.sh @@ -0,0 +1,19 @@ +set -e + +env + +pushd $CODEBUILD_SRC_DIR/secure_tunneling/tests + +mkdir _build +cd _build +cmake -DCMAKE_PREFIX_PATH=/tmp/install .. +make -j + +tunnel_info=$(aws iotsecuretunneling open-tunnel --destination-config services=ssh,ssh2,ssh3 --timeout-config maxLifetimeTimeoutMinutes=10) && echo -e "$tunnel_info" > /tmp/tunnel_info.pem + +export SECTUN_SOURCE_TOKEN=$(sed '4!d' /tmp/tunnel_info.pem | cut -d'"' -f4) +export SECTUN_DESTINATION_TOKEN=$(sed '5!d' /tmp/tunnel_info.pem | cut -d'"' -f4) +export SECTUN_ENDPOINT="data.tunneling.iot.us-east-1.amazonaws.com" + +echo "Running Secure Tunnel Test" +./secure_tunnel_test diff --git a/codebuild/linux-integration-tests.yml b/codebuild/linux-integration-tests.yml new file mode 100644 index 000000000..1ae3cbe22 --- /dev/null +++ b/codebuild/linux-integration-tests.yml @@ -0,0 +1,25 @@ +version: 0.2 +env: + shell: bash +#this buildspec assumes the ubuntu 14 image +phases: + install: + commands: + - wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - + - add-apt-repository ppa:ubuntu-toolchain-r/test + - apt-add-repository "deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-8 main" + - apt-get update -y + - apt-get install cmake -y -f + + build: + commands: + - echo Build started on `date` + # Building of dependencies happens in setup-linux + - $CODEBUILD_SRC_DIR/codebuild/samples/setup-linux.sh + + # Run the integration tests + - $CODEBUILD_SRC_DIR/codebuild/integration-tests.sh + post_build: + commands: + - echo Build completed on `date` + diff --git a/samples/secure_tunneling/secure_tunnel/main.cpp b/samples/secure_tunneling/secure_tunnel/main.cpp index b9dc634cc..4cb4ba8c5 100644 --- a/samples/secure_tunneling/secure_tunnel/main.cpp +++ b/samples/secure_tunneling/secure_tunnel/main.cpp @@ -558,7 +558,7 @@ int main(int argc, char *argv[]) /* 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())); + fprintf(stderr, "Secure Tunnel Stop call failed: %s\n", ErrorDebugString(LastError())); exit(-1); } diff --git a/secure_tunneling/source/SecureTunnel.cpp b/secure_tunneling/source/SecureTunnel.cpp index b749929d5..4ae025a4d 100644 --- a/secure_tunneling/source/SecureTunnel.cpp +++ b/secure_tunneling/source/SecureTunnel.cpp @@ -185,7 +185,6 @@ namespace Aws Crt::Allocator *allocator) noexcept : m_allocator(allocator) { - aws_byte_buf_clean_up(&m_messageTypeStorage); AWS_ZERO_STRUCT(m_messageTypeStorage); struct aws_byte_buf typeBuf = aws_byte_buf_from_c_str(aws_secure_tunnel_message_type_to_c_string(type)); diff --git a/secure_tunneling/tests/CMakeLists.txt b/secure_tunneling/tests/CMakeLists.txt new file mode 100644 index 000000000..c4ddfecc6 --- /dev/null +++ b/secure_tunneling/tests/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.1) +# note: cxx-17 requires cmake 3.8, cxx-20 requires cmake 3.12 +project(secure_tunnel_test CXX) + +file(GLOB SRC_FILES + "*.cpp" + # "../../utils/CommandLineUtils.cpp" + # "../../utils/CommandLineUtils.h" +) + +add_executable(${PROJECT_NAME} ${SRC_FILES}) + +set_target_properties(${PROJECT_NAME} PROPERTIES + CXX_STANDARD 14) + +#set warnings +if (MSVC) + target_compile_options(${PROJECT_NAME} PRIVATE /W4 /WX) +else () + target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wno-long-long -pedantic -Werror) +endif () + +find_package(aws-crt-cpp REQUIRED) +find_package(IotDeviceCommon-cpp REQUIRED) +find_package(IotSecureTunneling-cpp REQUIRED) + +install(TARGETS ${PROJECT_NAME} DESTINATION bin) + +target_link_libraries(${PROJECT_NAME} AWS::aws-crt-cpp AWS::IotDeviceCommon-cpp AWS::IotSecureTunneling-cpp) diff --git a/secure_tunneling/tests/main.cpp b/secure_tunneling/tests/main.cpp new file mode 100644 index 000000000..b255bfbbc --- /dev/null +++ b/secure_tunneling/tests/main.cpp @@ -0,0 +1,332 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include +#include +#include +#include +#include +#include +#include + +using namespace Aws::Crt; +using namespace Aws::Iotsecuretunneling; +using namespace Aws::Crt::Io; +using namespace std::chrono_literals; + +AWS_STATIC_STRING_FROM_LITERAL(SECTUN_ENDPOINT, "SECTUN_ENDPOINT"); +AWS_STATIC_STRING_FROM_LITERAL(SECTUN_SOURCE_TOKEN, "SECTUN_SOURCE_TOKEN"); +AWS_STATIC_STRING_FROM_LITERAL(SECTUN_DESTINATION_TOKEN, "SECTUN_DESTINATION_TOKEN"); +AWS_STATIC_STRING_FROM_LITERAL(SECTUN_SOURCE_CLIENT_TOKEN, "SECTUN_SOURCE_CLIENT_TOKEN"); +AWS_STATIC_STRING_FROM_LITERAL(SECTUN_DESTINATION_CLIENT_TOKEN, "SECTUN_DESTINATION_CLIENT_TOKEN"); +AWS_STATIC_STRING_FROM_LITERAL(SECTUN_PAYLOAD_MESSAGE, "Payload Message"); + +void setEnvVariable(struct aws_allocator *allocator, const struct aws_string *variable_name, String &stringToSet) +{ + aws_string *awsStringToSet = NULL; + aws_get_environment_value(allocator, variable_name, &awsStringToSet); + stringToSet = awsStringToSet == nullptr ? "" : aws_string_c_str(awsStringToSet); + aws_string_destroy(awsStringToSet); +} + +int main(int argc, char *argv[]) +{ + fprintf(stdout, "Secure Tunnel Test Starting\n"); + struct aws_allocator *allocator = aws_default_allocator(); + ApiHandle apiHandle; + + /* Uncomment to produce logs in codebuild */ + // apiHandle.InitializeLogging(Aws::Crt::LogLevel::Trace, stderr); + + aws_iotdevice_library_init(allocator); + + /* service id storage for use in test */ + Aws::Crt::ByteBuf m_serviceIdStorage; + AWS_ZERO_STRUCT(m_serviceIdStorage); + Aws::Crt::Optional m_serviceId; + + std::promise promiseDestinationConnected; + std::promise promiseSourceConnected; + + std::promise promiseDestinationStreamStarted; + std::promise promiseDestinationConnectionStarted; + + std::promise promiseDestinationReceivedMessage; + std::promise promiseSourceReceivedMessage; + + std::promise promiseDestinationStopped; + std::promise promiseSourceStopped; + + String endpoint; + String destinationToken; + String sourceToken; + String destinationClientToken; + String sourceClientToken; + /* Connection Id is used for Simultaneous HTTP Connections (Protocl V3) */ + uint32_t connectionId = 1; + uint32_t connectionId2 = 2; + + setEnvVariable(allocator, SECTUN_DESTINATION_TOKEN, destinationToken); + setEnvVariable(allocator, SECTUN_SOURCE_TOKEN, sourceToken); + setEnvVariable(allocator, SECTUN_DESTINATION_CLIENT_TOKEN, destinationClientToken); + setEnvVariable(allocator, SECTUN_SOURCE_CLIENT_TOKEN, sourceClientToken); + setEnvVariable(allocator, SECTUN_ENDPOINT, endpoint); + + if (apiHandle.GetOrCreateStaticDefaultClientBootstrap()->LastError() != AWS_ERROR_SUCCESS) + { + fprintf( + stderr, + "ClientBootstrap failed with error %s\n", + ErrorDebugString(apiHandle.GetOrCreateStaticDefaultClientBootstrap()->LastError())); + exit(-1); + } + + /* Use a SecureTunnelBuilder to set up and build the secure tunnel clients */ + SecureTunnelBuilder builderDestination = SecureTunnelBuilder( + allocator, destinationToken.c_str(), AWS_SECURE_TUNNELING_DESTINATION_MODE, endpoint.c_str()); + SecureTunnelBuilder builderSource = + SecureTunnelBuilder(allocator, sourceToken.c_str(), AWS_SECURE_TUNNELING_SOURCE_MODE, endpoint.c_str()); + + if (destinationClientToken.length() > 0) + { + builderDestination.WithClientToken(destinationClientToken.c_str()); + } + if (sourceClientToken.length() > 0) + { + builderSource.WithClientToken(sourceClientToken.c_str()); + } + + builderDestination.WithOnMessageReceived( + [&](SecureTunnel *secureTunnel, const MessageReceivedEventData &eventData) { + { + (void)secureTunnel; + (void)eventData; + fprintf(stdout, "Destination Client Received Message\n"); + promiseDestinationReceivedMessage.set_value(); + } + }); + + builderSource.WithOnMessageReceived([&](SecureTunnel *secureTunnel, const MessageReceivedEventData &eventData) { + { + (void)secureTunnel; + (void)eventData; + fprintf(stdout, "Source Client Received Message\n"); + promiseSourceReceivedMessage.set_value(); + } + }); + + builderDestination.WithOnSendMessageComplete( + [&](SecureTunnel *secureTunnel, int errorCode, const SendMessageCompleteEventData &eventData) { + (void)secureTunnel; + (void)eventData; + + if (!errorCode) + { + fprintf( + stdout, + "Message of type '" PRInSTR "' sent successfully\n", + AWS_BYTE_CURSOR_PRI(eventData.sendMessageCompleteData->getMessageType())); + } + else + { + fprintf(stdout, "Send Message failed with error code %d(%s)\n", errorCode, ErrorDebugString(errorCode)); + aws_byte_buf_clean_up(&m_serviceIdStorage); + exit(-1); + } + }); + + builderSource.WithOnSendMessageComplete( + [&](SecureTunnel *secureTunnel, int errorCode, const SendMessageCompleteEventData &eventData) { + (void)secureTunnel; + (void)eventData; + + if (!errorCode) + { + fprintf( + stdout, + "Message of type '" PRInSTR "' sent successfully\n", + AWS_BYTE_CURSOR_PRI(eventData.sendMessageCompleteData->getMessageType())); + } + else + { + fprintf(stdout, "Send Message failed with error code %d(%s)\n", errorCode, ErrorDebugString(errorCode)); + aws_byte_buf_clean_up(&m_serviceIdStorage); + exit(-1); + } + }); + + builderDestination.WithOnConnectionSuccess( + [&](SecureTunnel *secureTunnel, const ConnectionSuccessEventData &eventData) { + (void)secureTunnel; + (void)eventData; + fprintf(stdout, "Destination Client Connection Success\n"); + promiseDestinationConnected.set_value(); + }); + + builderSource.WithOnConnectionSuccess([&](SecureTunnel *secureTunnel, const ConnectionSuccessEventData &eventData) { + (void)secureTunnel; + (void)eventData; + + fprintf(stdout, "Source Client Connection Success\n"); + + /* Use a Multiplexing (Service Id) if available on this Secure Tunnel */ + if (eventData.connectionData->getServiceId1().has_value()) + { + /* Store the service id 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); + secureTunnel->SendStreamStart(m_serviceId.value(), connectionId); + fprintf(stdout, "Stream Start sent from Source Client.\n"); + } + else + { + fprintf(stdout, "Secure Tunnel should have service ids set for proper testing\n"); + exit(-1); + } + + promiseSourceConnected.set_value(); + }); + + builderDestination.WithOnStreamStarted( + [&](SecureTunnel *secureTunnel, int errorCode, const StreamStartedEventData &eventData) { + (void)secureTunnel; + (void)eventData; + if (!errorCode) + { + fprintf(stdout, "Destination Client Stream Started with Source Client\n"); + promiseDestinationStreamStarted.set_value(); + } + else + { + fprintf(stdout, "Stream Start failed with error code %d(%s)\n", errorCode, ErrorDebugString(errorCode)); + aws_byte_buf_clean_up(&m_serviceIdStorage); + exit(-1); + } + }); + + builderDestination.WithOnConnectionStarted([&](SecureTunnel *secureTunnel, + int errorCode, + const ConnectionStartedEventData &eventData) { + (void)secureTunnel; + (void)eventData; + if (!errorCode) + { + fprintf(stdout, "Connection Started on Destination Client.\n"); + promiseDestinationConnectionStarted.set_value(); + } + else + { + fprintf(stdout, "Connection Start failed with error code %d(%s)\n", errorCode, ErrorDebugString(errorCode)); + aws_byte_buf_clean_up(&m_serviceIdStorage); + exit(-1); + } + }); + + builderDestination.WithOnConnectionShutdown([&]() { fprintf(stdout, "Destination Connection Shutdown\n"); }); + + builderSource.WithOnConnectionShutdown([&]() { fprintf(stdout, "Source Connection Shutdown\n"); }); + + builderDestination.WithOnStopped([&](SecureTunnel *secureTunnel) { + (void)secureTunnel; + fprintf(stdout, "Destination entered Stopped State\n"); + promiseDestinationStopped.set_value(); + }); + + builderSource.WithOnStopped([&](SecureTunnel *secureTunnel) { + (void)secureTunnel; + fprintf(stdout, "Source has entered Stopped State\n"); + promiseSourceStopped.set_value(); + }); + + /* Create Secure Tunnel using the options set with the builder */ + std::shared_ptr secureTunnelDestination = builderDestination.Build(); + + if (!secureTunnelDestination) + { + fprintf(stderr, "Secure Tunnel Destination Creation failed: %s\n", ErrorDebugString(LastError())); + exit(-1); + } + + std::shared_ptr secureTunnelSource = builderSource.Build(); + if (!secureTunnelSource) + { + fprintf(stderr, "Secure Tunnel Source Creation failed: %s\n", ErrorDebugString(LastError())); + exit(-1); + } + + fprintf(stdout, "Secure Tunnels Created\n"); + + /* Set the Secure Tunnel Client to desire a connected state */ + + if (secureTunnelDestination->Start()) + { + fprintf(stderr, "Secure Tunnel Destination Connect call failed: %s\n", ErrorDebugString(LastError())); + exit(-1); + } + + promiseDestinationConnected.get_future().wait(); + + if (secureTunnelSource->Start()) + { + fprintf(stderr, "Secure Tunnel Source Connect call failed: %s\n", ErrorDebugString(LastError())); + exit(-1); + } + + promiseSourceConnected.get_future().wait(); + + promiseDestinationStreamStarted.get_future().wait(); + + secureTunnelSource->SendConnectionStart(m_serviceId.value(), connectionId2); + + promiseDestinationConnectionStarted.get_future().wait(); + + std::shared_ptr message1 = + std::make_shared(ByteCursorFromCString(aws_string_c_str(SECTUN_PAYLOAD_MESSAGE))); + message1->withServiceId(m_serviceId.value()); + message1->withConnectionId(connectionId2); + secureTunnelSource->SendMessage(message1); + fprintf(stdout, "Source Client Sent Message\n"); + + promiseDestinationReceivedMessage.get_future().wait(); + + std::shared_ptr message2 = + std::make_shared(ByteCursorFromCString(aws_string_c_str(SECTUN_PAYLOAD_MESSAGE))); + message2->withServiceId(m_serviceId.value()); + message2->withConnectionId(connectionId); + secureTunnelDestination->SendMessage(message2); + fprintf(stdout, "Destination Client Sent Message\n"); + + promiseSourceReceivedMessage.get_future().wait(); + + if (secureTunnelDestination->Stop() == AWS_OP_ERR) + { + fprintf(stderr, "Secure Tunnel Destination Stop call failed: %s\n", ErrorDebugString(LastError())); + aws_byte_buf_clean_up(&m_serviceIdStorage); + exit(-1); + } + + promiseDestinationStopped.get_future().wait(); + secureTunnelDestination = nullptr; + + if (secureTunnelSource->Stop() == AWS_OP_ERR) + { + fprintf(stderr, "Secure Tunnel Source Stop call failed: %s\n", ErrorDebugString(LastError())); + aws_byte_buf_clean_up(&m_serviceIdStorage); + exit(-1); + } + + promiseSourceStopped.get_future().wait(); + secureTunnelSource = nullptr; + + /* Clean Up */ + aws_byte_buf_clean_up(&m_serviceIdStorage); + + fprintf(stdout, "Secure Tunnel Test Completed\n"); + + return 0; +} From c0d303ca1367aac98bb40ba272276f52d03eb901 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Thu, 13 Apr 2023 13:22:09 -0700 Subject: [PATCH 12/18] sample fix and update --- .../secure_tunneling/secure_tunnel/main.cpp | 184 +++--------------- samples/utils/CommandLineUtils.cpp | 40 +++- 2 files changed, 61 insertions(+), 163 deletions(-) diff --git a/samples/secure_tunneling/secure_tunnel/main.cpp b/samples/secure_tunneling/secure_tunnel/main.cpp index 16a1a3bb2..be08c703e 100644 --- a/samples/secure_tunneling/secure_tunnel/main.cpp +++ b/samples/secure_tunneling/secure_tunnel/main.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include @@ -122,132 +121,6 @@ void logConnectionStartedData(const ConnectionStartedEventData &eventData) fprintf(stdout, "\n"); } -void setupCommandLineUtils(Utils::CommandLineUtils *cmdUtils, int argc, char *argv[]) -{ - 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( - "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( - "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( - "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); -} - -void setupCommandLineValues( - Utils::cmdData *cmdData, - String *endpoint, - String *accessToken, - String *clientToken, - String *caFile, - String *proxyHost, - String *proxyUserName, - String *proxyPassword, - uint16_t &proxyPort, - uint16_t &messageCount, - aws_secure_tunneling_local_proxy_mode &localProxyMode, - String *payloadMessage) -{ - // Generate secure tunneling endpoint using region - String region = cmdData->input_signingRegion; - endpoint->assign("data.tunneling.iot." + region + ".amazonaws.com"); - - String tempAccessToken; - // An access token is required to connect to the secure tunnel service - if (cmdData->input_accessToken != "") - { - tempAccessToken = cmdData->input_accessToken; - } - else if (cmdData->input_accessTokenFile != "") - { - tempAccessToken = cmdData->input_accessTokenFile; - std::ifstream accessTokenFile(tempAccessToken.c_str()); - if (accessTokenFile.is_open()) - { - getline(accessTokenFile, tempAccessToken); - accessTokenFile.close(); - } - else - { - fprintf(stderr, "Failed to open access token file"); - exit(-1); - } - } - else - { - 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 (cmdData->input_clientToken != "") - { - tempClientToken = cmdData->input_clientToken; - } - else if (cmdData->input_clientTokenFile != "") - { - tempClientToken = cmdData->input_clientTokenFile; - std::ifstream clientTokenFile(tempClientToken.c_str()); - if (clientTokenFile.is_open()) - { - getline(clientTokenFile, tempClientToken); - clientTokenFile.close(); - } - else - { - fprintf(stderr, "Failed to open client token file\n"); - exit(-1); - } - } - clientToken->assign(tempClientToken); - - if (cmdData->input_ca != "") - { - caFile->assign(cmdData->input_ca); - } - - if (cmdData->input_proxyHost != "" || cmdData->input_proxyPort != 0) - { - proxyHost->assign(cmdData->input_proxyHost); - proxyPort = static_cast(cmdData->input_proxyPort); - proxyUserName->assign(cmdData->input_proxy_user_name); - proxyPassword->assign(cmdData->input_proxy_password); - } - - // localProxyMode is set to destination by default unless flag is set to source - if (cmdData->input_localProxyModeSource != "destination") - { - localProxyMode = AWS_SECURE_TUNNELING_SOURCE_MODE; - } - else - { - localProxyMode = AWS_SECURE_TUNNELING_DESTINATION_MODE; - } - - payloadMessage->assign(cmdData->input_message); - messageCount = static_cast(cmdData->input_count); -} - int main(int argc, char *argv[]) { struct aws_allocator *allocator = aws_default_allocator(); @@ -268,17 +141,7 @@ int main(int argc, char *argv[]) AWS_ZERO_STRUCT(m_serviceIdStorage); Aws::Crt::Optional m_serviceId; - 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; - uint16_t messageCount(5); /* Connection Id is used for Simultaneous HTTP Connections (Protocl V3) */ uint32_t connectionId = 1; @@ -287,19 +150,15 @@ int main(int argc, char *argv[]) /*********************** Parse Arguments ***************************/ Utils::cmdData cmdData = Utils::parseSampleInputSecureTunnel(argc, argv, &apiHandle); - setupCommandLineValues( - &cmdData, - &endpoint, - &accessToken, - &clientToken, - &caFile, - &proxyHost, - &proxyUserName, - &proxyPassword, - proxyPort, - messageCount, - localProxyMode, - &payloadMessage); + // localProxyMode is set to destination by default unless flag is set to source + if (cmdData.input_localProxyModeSource != "destination") + { + localProxyMode = AWS_SECURE_TUNNELING_SOURCE_MODE; + } + else + { + localProxyMode = AWS_SECURE_TUNNELING_DESTINATION_MODE; + } if (apiHandle.GetOrCreateStaticDefaultClientBootstrap()->LastError() != AWS_ERROR_SUCCESS) { @@ -311,28 +170,29 @@ int main(int argc, char *argv[]) } // Use a SecureTunnelBuilder to set up and build the secure tunnel client - SecureTunnelBuilder builder = SecureTunnelBuilder(allocator, accessToken.c_str(), localProxyMode, endpoint.c_str()); + SecureTunnelBuilder builder = SecureTunnelBuilder( + allocator, cmdData.input_accessToken.c_str(), localProxyMode, cmdData.input_endpoint.c_str()); - if (caFile.length() > 0) + if (cmdData.input_ca != "") { - builder.WithRootCa(caFile.c_str()); + builder.WithRootCa(cmdData.input_ca.c_str()); } /* Proxy Options */ - if (proxyHost.length() > 0) + if (cmdData.input_proxyHost != "") { auto proxyOptions = Aws::Crt::Http::HttpClientConnectionProxyOptions(); - proxyOptions.HostName = proxyHost.c_str(); - proxyOptions.Port = proxyPort; + proxyOptions.HostName = cmdData.input_proxyHost != ""; + proxyOptions.Port = static_cast(cmdData.input_proxyPort); // Set up Proxy Strategy if a user name and password is provided - if (proxyUserName.length() > 0 || proxyPassword.length() > 0) + if (cmdData.input_proxy_user_name != "" || cmdData.input_proxy_password != "") { fprintf(stdout, "Creating proxy strategy\n"); Aws::Crt::Http::HttpProxyStrategyBasicAuthConfig basicAuthConfig; basicAuthConfig.ConnectionType = Aws::Crt::Http::AwsHttpProxyConnectionType::Tunneling; - basicAuthConfig.Username = proxyUserName.c_str(); - basicAuthConfig.Password = proxyPassword.c_str(); + basicAuthConfig.Username = cmdData.input_proxy_user_name.c_str(); + basicAuthConfig.Password = cmdData.input_proxy_password.c_str(); proxyOptions.ProxyStrategy = Aws::Crt::Http::HttpProxyStrategy::CreateBasicHttpProxyStrategy(basicAuthConfig, allocator); proxyOptions.AuthType = Aws::Crt::Http::AwsHttpProxyAuthenticationType::Basic; @@ -346,7 +206,7 @@ int main(int argc, char *argv[]) builder.WithHttpClientConnectionProxyOptions(proxyOptions); } - builder.WithClientToken(clientToken.c_str()); + builder.WithClientToken(cmdData.input_clientToken.c_str()); /* Add callbacks using the builder */ builder.WithOnMessageReceived([&](SecureTunnel *secureTunnel, const MessageReceivedEventData &eventData) { @@ -495,8 +355,10 @@ int main(int argc, char *argv[]) std::this_thread::sleep_for(2000ms); if (localProxyMode == AWS_SECURE_TUNNELING_SOURCE_MODE) { + uint16_t messageCount = static_cast(cmdData.input_count); messagesSent++; - String toSend = (std::to_string(messagesSent) + ": " + payloadMessage.c_str()).c_str(); + // String toSend = (std::to_string(messagesSent) + ": " + payloadMessage.c_str()).c_str(); + String toSend = (std::to_string(messagesSent) + ": " + cmdData.input_message.c_str()).c_str(); if (messagesSent <= messageCount) { diff --git a/samples/utils/CommandLineUtils.cpp b/samples/utils/CommandLineUtils.cpp index 653d9053e..7408f3eb1 100644 --- a/samples/utils/CommandLineUtils.cpp +++ b/samples/utils/CommandLineUtils.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include namespace Utils @@ -847,13 +848,48 @@ namespace Utils s_addLoggingSendArgumentsStartLogging(argc, argv, api_handle, &cmdUtils); cmdData returnData = cmdData(); - s_parseCommonMQTTCommands(&cmdUtils, &returnData); returnData.input_signingRegion = cmdUtils.GetCommandRequired(m_cmd_signing_region); + returnData.input_endpoint = "data.tunneling.iot." + returnData.input_signingRegion + ".amazonaws.com"; returnData.input_accessTokenFile = cmdUtils.GetCommandOrDefault(m_cmd_access_token_file, ""); returnData.input_accessToken = cmdUtils.GetCommandOrDefault(m_cmd_access_token, ""); - returnData.input_localProxyModeSource = cmdUtils.GetCommandOrDefault(m_cmd_access_token, "destination"); + if (returnData.input_accessToken == "" && returnData.input_accessTokenFile != "") + { + Aws::Crt::String tempAccessToken; + + std::ifstream accessTokenFile(returnData.input_accessTokenFile.c_str()); + if (accessTokenFile.is_open()) + { + getline(accessTokenFile, returnData.input_accessToken); + accessTokenFile.close(); + } + else + { + fprintf(stderr, "Failed to open access token file"); + exit(-1); + } + } + returnData.input_clientTokenFile = cmdUtils.GetCommandOrDefault(m_cmd_client_token_file, ""); returnData.input_clientToken = cmdUtils.GetCommandOrDefault(m_cmd_client_token, ""); + if (returnData.input_clientToken == "" && returnData.input_clientTokenFile != "") + { + Aws::Crt::String tempAccessToken; + + std::ifstream clientTokenFile(returnData.input_clientTokenFile.c_str()); + if (clientTokenFile.is_open()) + { + getline(clientTokenFile, returnData.input_clientToken); + clientTokenFile.close(); + } + else + { + fprintf(stderr, "Failed to open client token file"); + exit(-1); + } + } + + returnData.input_localProxyModeSource = + cmdUtils.GetCommandOrDefault(m_cmd_local_proxy_mode_source, "destination"); returnData.input_message = cmdUtils.GetCommandOrDefault(m_cmd_message, "Hello World!"); if (cmdUtils.HasCommand(m_cmd_proxy_host)) { From 3d559c357230804db0a71c3c32f19bfd1cf02f45 Mon Sep 17 00:00:00 2001 From: Steve Kim <86316075+sbSteveK@users.noreply.github.com> Date: Fri, 14 Apr 2023 09:02:53 -0700 Subject: [PATCH 13/18] Secure tunnel v3 documentation (#567) * update documentation with secure tunnel protocol v3 --- documents/Secure_Tunnel_Userguide.md | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/documents/Secure_Tunnel_Userguide.md b/documents/Secure_Tunnel_Userguide.md index 86402fb05..6dc57ab84 100644 --- a/documents/Secure_Tunnel_Userguide.md +++ b/documents/Secure_Tunnel_Userguide.md @@ -43,7 +43,7 @@ When a WebSocket upgrade request fails to connect, this callback will return an ### OnConnectionShutdown When the WebSocket connection shuts down, this callback will be invoked. -### OnSendDataComplete +## OnSendMessageComplete When a message has been completely written to the socket, this callback will be invoked. ### OnMessageReceived @@ -55,6 +55,12 @@ When a stream is started by a Source connected to the Destination, the Destinati ### OnStreamStopped When an open stream is closed, this callback will be invoked and return the stopped stream's information. +### OnConnectionStarted +When a connection start message is received and a new active connection is established, the Destination will invoke this callback and return the connection information. + +### OnConnectionReset +When a connection has ended either in error or closed intentionally by the secure tunnel peer, the client will invoke this callback and return the connection information. + ### OnSessionReset When the Secure Tunnel service requests the Secure Tunnel client fully reset, this callback is invoked. @@ -143,13 +149,14 @@ if(!secureTunnel->Stop()){ fprintf(stdout, "Failed to stop the Secure Tunnel connection session. Exiting..\n"); } ``` -# Multiplexing -You can use multiple data streams per Secure Tunnel by using the [Multiplexing](https://docs.aws.amazon.com/iot/latest/developerguide/multiplexing.html) feature. +# Multiplexing and Simultaneous TCP Connections +You can use multiple data streams per Secure Tunnel by using the [Multiplexing and Simultaneous TCP Connections](https://docs.aws.amazon.com/iot/latest/developerguide/multiplexing.html) features. + ## Opening a Secure Tunnel with Multiplexing To use Multiplexing, a Secure Tunnel must be created with one to three "services". A Secure Tunnel can be opened through the AWS IoT console [Secure Tunnel Hub](https://console.aws.amazon.com/iot/home#/tunnelhub) or by using the [OpenTunnel API](https://docs.aws.amazon.com/iot/latest/apireference/API_Operations_AWS_IoT_Secure_Tunneling.html). Both of these methods allow you to add services with whichever names suit your needs. ## Services Within the Secure Tunnel Client -On a successfull connection to a Secure Tunnel, the Secure Tunnel Client will invoke the `OnConnectionSuccess` callback. This callback will return `ConnectionSuccessEventData` that will contain any available Service Ids that can be used for multiplexing. Below is an example of how to set the callback using the Secure Tunnel Builder and check whether a Service Id is available. +On a successfull connection to a Secure Tunnel, the Secure Tunnel Client will invoke the `OnConnectionSuccess` callback. This callback will return `ConnectionSuccessEventData` which contains any available Service Ids that can be used for multiplexing. Below is an example of how to set the callback using the Secure Tunnel Builder and check whether a Service Id is available. ```cpp // Create Secure Tunnel Builder SecureTunnelBuilder builder = SecureTunnelBuilder(...); @@ -191,6 +198,8 @@ builder.WithOnConnectionSuccess([&](SecureTunnel *secureTunnel, const Connection ``` ## Using Service Ids Service Ids can be added to outbound Messages as shown below in the Send Message example. If the Service Id is both available on the current Secure Tunnel and there is an open stream with a Source device on that Service Id, the message will be sent. If the Service Id does not exist on the current Secure Tunnel or there is no currently active stream available on that Service Id, the Message will not be sent and a Warning will be logged. The `OnStreamStarted` callback is invoked when a stream is started and it returns a `StreamStartedEventData` which can be parsed to determine if a stream was started using a Service Id for Multiplexing. Incoming messages can also be parsed to determine if a Service Id has been set as shown above in the [Setting Secure Tunnel Callbacks](#setting-secure-tunnel-callbacks) code example. +## Using Connection Ids +Connection Ids can be added to outbound Messages as shown below in the Send Message example. If there is an active stream currently open using the combination of the Service Id and Connection Id, the message will be sent. If a Connection Id is not set on an outbound message, a Connecion Id of 1 is assumed and applied to the Message. When additional streams are activated, the `OnConnectionStarted` callback is invoked and returns a `ConnectionStartedEventData` which can be parsed to determine the Connection Id of the newly activated stream. A Connection Id will also be present in the `StreamStartedEventData` that is returned when the `OnStreamStarted` callback is invoked. # Secure Tunnel Operations ## Send Message @@ -201,11 +210,16 @@ Crt::String serviceId_string = "ssh"; Crt::String message_string = "any payload"; ByteCursor serviceId = ByteCursorFromString(serviceId_string); +uint32_t connectionId = 1 ByteCursor payload = ByteCursorFromString(message_string); // Create Message std::shared_ptr message = std::make_shared(); +// Add a Service Id message->withServiceId(serviceId); +// Add a Connection Id +message->withConnectionId(connectionId); +// Add a payload message->withPayload(payload); // Send Message From 13f1cdc07ae28c6f42378607b734cd6484b9ca93 Mon Sep 17 00:00:00 2001 From: Steve Kim <86316075+sbSteveK@users.noreply.github.com> Date: Fri, 14 Apr 2023 15:07:48 -0700 Subject: [PATCH 14/18] Secure tunnel v3 update samples (#569) * Updated Secure Tunnel Notification Sample and updated READMEs --- .../secure_tunneling/secure_tunnel/README.md | 65 ++++--------------- .../secure_tunneling/secure_tunnel/main.cpp | 8 +-- .../tunnel_notification/README.md | 19 +++--- .../tunnel_notification/main.cpp | 62 +++++++++++------- .../aws/iotsecuretunneling/SecureTunnel.h | 6 +- secure_tunneling/source/SecureTunnel.cpp | 6 +- secure_tunneling/tests/main.cpp | 8 +-- 7 files changed, 75 insertions(+), 99 deletions(-) diff --git a/samples/secure_tunneling/secure_tunnel/README.md b/samples/secure_tunneling/secure_tunnel/README.md index 46b895baf..37cc66751 100644 --- a/samples/secure_tunneling/secure_tunnel/README.md +++ b/samples/secure_tunneling/secure_tunnel/README.md @@ -1,72 +1,33 @@ -## Secure Tunnel +# Secure Tunnel [**Return to main sample list**](../../README.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). For more information, see the [Secure Tunnel Userguide](../../../documents/Secure_Tunnel_Userguide.md) - -Your IoT Core Thing's [Policy](https://docs.aws.amazon.com/iot/latest/developerguide/iot-policies.html) must provide privileges for this sample to connect, subscribe, publish, and receive. Below is a sample policy that can be used on your IoT Core Thing that will allow this sample to run as intended. - -
-(see sample policy) -
-{
-  "Version": "2012-10-17",
-  "Statement": [
-    {
-      "Effect": "Allow",
-      "Action": [
-        "iot:Publish",
-        "iot:Receive"
-      ],
-      "Resource": [
-        "arn:aws:iot:region:account:topic/$aws/things//tunnels/notify"
-      ]
-    },
-    {
-      "Effect": "Allow",
-      "Action": [
-        "iot:Subscribe"
-      ],
-      "Resource": [
-        "arn:aws:iot:region:account:topicfilter/$aws/things//tunnels/notify"
-      ]
-    },
-    {
-      "Effect": "Allow",
-      "Action": [
-        "iot:Connect"
-      ],
-      "Resource": [
-        "arn:aws:iot:region:account:client/test-*"
-      ]
-    }
-  ]
-}
-
- -Replace with the following with the data from your AWS account: -* ``: The AWS IoT Core region where you created your AWS IoT Core thing you wish to use with this sample. For example `us-east-1`. -* ``: Your AWS IoT Core account ID. This is the set of numbers in the top right next to your AWS account name when using the AWS IoT Core website. -* ``: The name of your AWS IoT Core thing you want the device connection to be associated with - -Note that in a real application, you may want to avoid the use of wildcards in your ClientID or use them selectively. Please follow best practices when working with AWS on production applications using the SDK. Also, for the purposes of this sample, please make sure your policy allows a client ID of `test-*` to connect or use `--client_id ` to send the client ID your policy supports. +This sample uses AWS IoT [Secure Tunneling](https://docs.aws.amazon.com/iot/latest/developerguide/secure-tunneling.html) Service to connect a destination or a source Secure Tunnel Client to an AWS Secure Tunnel endpoint using access tokens using the [V3WebSocketProtocol](https://github.com/aws-samples/aws-iot-securetunneling-localproxy/blob/main/V3WebSocketProtocolGuide.md). For more information, see the [Secure Tunnel Userguide](../../../documents/Secure_Tunnel_Userguide.md) ## How to run Create a new secure tunnel in the AWS IoT console (https://console.aws.amazon.com/iot/) (AWS IoT/Manage/Tunnels/Create tunnel) and retrieve the destination and source access tokens. (https://docs.aws.amazon.com/iot/latest/developerguide/secure-tunneling-tutorial-open-tunnel.html). Once you have these tokens, you are ready to open a secure tunnel. +### Destination Mode + To run the sample with a destination access token in destination mode (default), you can use the following command: ``` sh -./secure_tunnel --region --access_token_file +./secure_tunnel --signing_region --access_token_file ``` -However, for this sample to work, it will also need another instance of the sample running in source mode. You can run another instance of the sample in source mode using the same command, but adding the `--localProxyModeSource` flag: +The sample will create a Secure Tunnel connection and remain connected in `DESTINATION MODE` and will echo any messages it receives through the Secure Tunnel back to the Source Device. + +### Source Mode + +While the focus of the Secure Tunnel Client for the IoT Device SDK is to connect with Secure Tunnels in `DESTINATION MODE` we also support connecting in `SOURCE MODE`. The token file should be the Source Token in this instance and you must add the `--localProxyModeSource` flag: ``` sh -./secure_tunnel --region --access_token_file --localProxyModeSource +./secure_tunnel --signing_region --access_token_file --localProxyModeSource ``` Then two samples will then connect to each other through the AWS Secure Tunnel endpoint and establish a stream through which data can be transmitted in either direction. +The sample will create a Secure Tunnel connection in `SOURCE MODE` and will open a stream using an available `Service Id`. It will then send n messages on the opened stream. It will then create a new simultaneous TCP connection on the stream and send an additional n messages on the new TCP connection. It will then exit. +### Proxy Note that a proxy server may be used via the `--proxy_host` and `--proxy_port` argument. If the proxy server requires a user name and password to connect, you can use `--proxy_user_name` and `--proxy_password` to in the sample to pass the required data to the sample. diff --git a/samples/secure_tunneling/secure_tunnel/main.cpp b/samples/secure_tunneling/secure_tunnel/main.cpp index be08c703e..c5beadaf4 100644 --- a/samples/secure_tunneling/secure_tunnel/main.cpp +++ b/samples/secure_tunneling/secure_tunnel/main.cpp @@ -223,13 +223,13 @@ int main(int argc, char *argv[]) /* Echo message on same service id received message arrived on */ if (message->getServiceId().has_value()) { - echoMessage->withServiceId(message->getServiceId().value()); + echoMessage->WithServiceId(message->getServiceId().value()); } /* Echo message on the same connection id received message arrived on */ if (message->getConnectionId() > 0) { - echoMessage->withConnectionId(message->getConnectionId()); + echoMessage->WithConnectionId(message->getConnectionId()); } secureTunnel->SendMessage(echoMessage); @@ -368,10 +368,10 @@ int main(int argc, char *argv[]) /* If the secure tunnel has service ids, we will use one for our messages. */ if (m_serviceId.has_value()) { - message->withServiceId(m_serviceId.value()); + message->WithServiceId(m_serviceId.value()); } - message->withConnectionId(connectionId); + message->WithConnectionId(connectionId); secureTunnel->SendMessage(message); diff --git a/samples/secure_tunneling/tunnel_notification/README.md b/samples/secure_tunneling/tunnel_notification/README.md index 94cbf7772..f978ff57f 100644 --- a/samples/secure_tunneling/tunnel_notification/README.md +++ b/samples/secure_tunneling/tunnel_notification/README.md @@ -2,11 +2,13 @@ [**Return to main sample list**](../../README.md) -This sample uses the AWS IoT [Secure Tunneling](https://docs.aws.amazon.com/iot/latest/developerguide/secure-tunneling.html) Service to receive a tunnel notification. +This sample uses an MQTT Client and the AWS IoT [Secure Tunneling](https://docs.aws.amazon.com/iot/latest/developerguide/secure-tunneling.html) Service to receive a tunnel notification and then connects to the Secure Tunnel using a Secure Tunnel Client. -This sample requires you to create a tunnel for your thing. See [instructions here](https://docs.aws.amazon.com/iot/latest/developerguide/secure-tunneling-tutorial.html) on how to create a tunnel. You can also read more about secure tunneling in the [Secure Tunnel Userguide](../../../documents/Secure_Tunnel_Userguide.md). +This sample requires you to create a Secure Tunnel for your thing. See [instructions here](https://docs.aws.amazon.com/iot/latest/developerguide/secure-tunneling-tutorial.html) on how to create a tunnel. You can also read more about secure tunneling in the [Secure Tunnel Userguide](../../../documents/Secure_Tunnel_Userguide.md). -On startup, the sample will wait until it receives, and then displays the tunnel notification. +This sample requires a certificate and key file using Mutual TLS (mTLS). On startup, the device connects to the server using the certificate and key files and subscribes to a specific topic generated along with the Thing Name to listen for any Secure Tunnels that are opened for that specific Thing. When a Secure Tunnel is created for the Thing, it will receive a publish with the Secure Tunnel information which it can then use to create a Secure Tunnel Client and connect to the Secure Tunnel Service in `DESTINATION MODE`. AWS IoT will publish the notification upon creation of a new tunnel and will retransmit a new notification if instructed to "Generate new access tokens". + +Your IoT Core Thing's [Policy](https://docs.aws.amazon.com/iot/latest/developerguide/iot-policies.html) must provide privileges for this sample to connect, subscribe, and receive. Below is a sample policy that can be used on your IoT Core Thing that will allow this sample to run as intended.
(see sample policy) @@ -21,7 +23,7 @@ On startup, the sample will wait until it receives, and then displays the tunnel "iot:Receive" ], "Resource": [ - "arn:aws:iot:region:account:topic/$aws/things//tunnels/notify" + "arn:aws:iot:region:account:topic/$aws/things/thing_name/tunnels/notify" ] }, { @@ -30,7 +32,7 @@ On startup, the sample will wait until it receives, and then displays the tunnel "iot:Subscribe" ], "Resource": [ - "arn:aws:iot:region:account:topicfilter/$aws/things//tunnels/notify" + "arn:aws:iot:region:account:topic/$aws/things/thing_name/tunnels/notify" ] }, { @@ -53,10 +55,11 @@ Replace with the following with the data from your AWS account: Note that in a real application, you may want to avoid the use of wildcards in your ClientID or use them selectively. Please follow best practices when working with AWS on production applications using the SDK. Also, for the purposes of this sample, please make sure your policy allows a client ID of `test-*` to connect or use `--client_id ` to send the client ID your policy supports. -## How to run - -Create a new secure tunnel in the AWS IoT console (https://console.aws.amazon.com/iot/) (AWS IoT/Manage/Tunnels/Create tunnel). Once you have the tunnel open, you can run the sample with the following command: +
+## How to run +To Run this sample, use the following command: ``` sh ./tunnel-notification --endpoint --cert --key --thing_name ``` +Once the MQTT Client is connected, create a new secure tunnel in the AWS IoT console (https://console.aws.amazon.com/iot/) (AWS IoT/Manage/Tunnels/Create tunnel) for the Thing. diff --git a/samples/secure_tunneling/tunnel_notification/main.cpp b/samples/secure_tunneling/tunnel_notification/main.cpp index 8cb88d12b..66aa02453 100644 --- a/samples/secure_tunneling/tunnel_notification/main.cpp +++ b/samples/secure_tunneling/tunnel_notification/main.cpp @@ -3,28 +3,18 @@ * SPDX-License-Identifier: Apache-2.0. */ -#include -#include -#include - #include - #include +#include #include #include -#include -#include #include -#include -#include -#include #include #include "../../utils/CommandLineUtils.h" using namespace std; -using namespace Aws::Iot; using namespace Aws::Crt; using namespace Aws::Crt::Mqtt; using namespace Aws::Iotsecuretunneling; @@ -33,8 +23,11 @@ int main(int argc, char *argv[]) { /************************ Setup ****************************/ + struct aws_allocator *allocator = aws_default_allocator(); // Do the global initialization for the API. ApiHandle apiHandle; + aws_iotdevice_library_init(allocator); + std::shared_ptr secureTunnel; /** * cmdData is the arguments/input from the command line placed into a single struct for @@ -121,32 +114,50 @@ int main(int argc, char *argv[]) { fprintf(stdout, "Received MQTT Tunnel Notification\n"); - std::string clientAccessToken = response->ClientAccessToken->c_str(); - std::string clientMode = response->ClientMode->c_str(); - std::string region = response->Region->c_str(); - fprintf( stdout, - "Recv: Token:%s, Mode:%s, Region:%s\n", - clientAccessToken.c_str(), - clientMode.c_str(), - region.c_str()); + "Recv:\n\tToken:%s\n\tMode:%s\n\tRegion:%s\n", + response->ClientAccessToken->c_str(), + response->ClientMode->c_str(), + response->Region->c_str()); + + Aws::Crt::String region = response->Region->c_str(); + Aws::Crt::String endpoint = "data.tunneling.iot." + region + ".amazonaws.com"; size_t nServices = response->Services->size(); if (nServices <= 0) { - fprintf(stdout, "No service requested\n"); + fprintf(stdout, "\tNo Service Ids requested\n"); } else { - std::string service = response->Services->at(0).c_str(); - fprintf(stdout, "Requested service=%s\n", service.c_str()); - - if (nServices > 1) + for (size_t i = 0; i < nServices; ++i) { - fprintf(stdout, "Multiple services not supported yet\n"); + std::string service = response->Services->at(i).c_str(); + fprintf(stdout, "\tService Id %zu=%s\n", (i + 1), service.c_str()); } } + + SecureTunnelBuilder builder = SecureTunnelBuilder( + allocator, + response->ClientAccessToken->c_str(), + AWS_SECURE_TUNNELING_DESTINATION_MODE, + endpoint.c_str()); + + builder.WithOnConnectionSuccess( + [&](SecureTunnel *secureTunnel, const ConnectionSuccessEventData &eventData) { + (void)secureTunnel; + (void)eventData; + fprintf(stdout, "Secure Tunnel connected to Secure Tunnel Service\n"); + }); + secureTunnel = builder.Build(); + + if (!secureTunnel) + { + fprintf(stderr, "Secure Tunnel Creation failed: %s\n", ErrorDebugString(LastError())); + exit(-1); + } + secureTunnel->Start(); } else { @@ -170,6 +181,7 @@ int main(int argc, char *argv[]) secureClient.SubscribeToTunnelsNotify( request, AWS_MQTT_QOS_AT_LEAST_ONCE, onSubscribeToTunnelsNotifyResponse, OnSubscribeComplete); } + while (1) { std::this_thread::sleep_for(500ms); diff --git a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h index 057d38ba2..fa1339f54 100644 --- a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h +++ b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h @@ -47,7 +47,7 @@ namespace Aws * @param serviceId The service id for the secure tunnel message. * @return The Message Object after setting the payload. */ - Message &withServiceId(Crt::ByteCursor serviceId) noexcept; + Message &WithServiceId(Crt::ByteCursor serviceId) noexcept; /** * Sets the connection id for the secure tunnel message. @@ -55,7 +55,7 @@ namespace Aws * @param connectionId The connection id for the secure tunnel message. * @return The Message Object after setting the payload. */ - Message &withConnectionId(uint32_t connectionId) noexcept; + Message &WithConnectionId(uint32_t connectionId) noexcept; /** * Sets the payload for the secure tunnel message. @@ -63,7 +63,7 @@ namespace Aws * @param payload The payload for the secure tunnel message. * @return The Message Object after setting the payload. */ - Message &withPayload(Crt::ByteCursor payload) noexcept; + Message &WithPayload(Crt::ByteCursor payload) noexcept; bool initializeRawOptions(aws_secure_tunnel_message_view &raw_options) noexcept; diff --git a/secure_tunneling/source/SecureTunnel.cpp b/secure_tunneling/source/SecureTunnel.cpp index 4ae025a4d..4b625c5c8 100644 --- a/secure_tunneling/source/SecureTunnel.cpp +++ b/secure_tunneling/source/SecureTunnel.cpp @@ -126,7 +126,7 @@ namespace Aws m_serviceId = aws_byte_cursor_from_buf(&m_serviceIdStorage); } - Message &Message::withPayload(Crt::ByteCursor payload) noexcept + 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); @@ -134,7 +134,7 @@ namespace Aws return *this; } - Message &Message::withServiceId(Crt::ByteCursor serviceId) noexcept + 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); @@ -142,7 +142,7 @@ namespace Aws return *this; } - Message &Message::withConnectionId(uint32_t connectionId) noexcept + Message &Message::WithConnectionId(uint32_t connectionId) noexcept { m_connectionId = connectionId; return *this; diff --git a/secure_tunneling/tests/main.cpp b/secure_tunneling/tests/main.cpp index b255bfbbc..bd6606e19 100644 --- a/secure_tunneling/tests/main.cpp +++ b/secure_tunneling/tests/main.cpp @@ -287,8 +287,8 @@ int main(int argc, char *argv[]) std::shared_ptr message1 = std::make_shared(ByteCursorFromCString(aws_string_c_str(SECTUN_PAYLOAD_MESSAGE))); - message1->withServiceId(m_serviceId.value()); - message1->withConnectionId(connectionId2); + message1->WithServiceId(m_serviceId.value()); + message1->WithConnectionId(connectionId2); secureTunnelSource->SendMessage(message1); fprintf(stdout, "Source Client Sent Message\n"); @@ -296,8 +296,8 @@ int main(int argc, char *argv[]) std::shared_ptr message2 = std::make_shared(ByteCursorFromCString(aws_string_c_str(SECTUN_PAYLOAD_MESSAGE))); - message2->withServiceId(m_serviceId.value()); - message2->withConnectionId(connectionId); + message2->WithServiceId(m_serviceId.value()); + message2->WithConnectionId(connectionId); secureTunnelDestination->SendMessage(message2); fprintf(stdout, "Destination Client Sent Message\n"); From c6f5948470dae44691285761fce3bce283436953 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 17 Apr 2023 16:15:17 -0700 Subject: [PATCH 15/18] simultaneous HTTP => TCP in descriptions --- .../include/aws/iotsecuretunneling/SecureTunnel.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h index fa1339f54..515ef55b4 100644 --- a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h +++ b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h @@ -106,7 +106,7 @@ namespace Aws Crt::Optional m_serviceId; /** - * The connection id used for HTTP simultaneous connections. + * The connection id used for simultaneous TCP connections. * * If left empty, a V1 or V2 protocol message is assumed. */ @@ -298,7 +298,7 @@ namespace Aws Crt::Optional m_serviceId; /** - * The connection id used for HTTP simultaneous connections. + * The connection id used for simultaneous TCP connections. * * If left empty, a V1 or V2 protocol message is assumed. */ From d60093194df29639b3649416dbf9ade52a36e4b6 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 17 Apr 2023 16:23:59 -0700 Subject: [PATCH 16/18] update secure tunnel documentation with retry jitter and reconnection info --- documents/Secure_Tunnel_Userguide.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/documents/Secure_Tunnel_Userguide.md b/documents/Secure_Tunnel_Userguide.md index 6dc57ab84..fff98dbc4 100644 --- a/documents/Secure_Tunnel_Userguide.md +++ b/documents/Secure_Tunnel_Userguide.md @@ -141,6 +141,8 @@ if (!secureTunnel->Start()) return -1; } ``` +### Reconnecting +A Secure Tunnel Client that has been started will attempt to reconnect upon disconnection until `Stop()` is called. The Secure Tunnel Client implements a Full Jitter Backoff Algorithm along with an exponential back off timer. More information on both can be found here: https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/ ## Stop Invoking `Stop()` on the Secure Tunnel Client breaks the current connection (if any) and moves the client into an idle state. From fab025e117f0849d1e910e8936d488c3aa9fb410 Mon Sep 17 00:00:00 2001 From: Steve Kim <86316075+sbSteveK@users.noreply.github.com> Date: Wed, 19 Apr 2023 14:43:35 -0700 Subject: [PATCH 17/18] optional tls settings on secure tunnel creation (#572) * optional tls settings on secure tunnel creation * note on rootCA being overridden if user provides tls options * document fixes --- crt/aws-c-iot | 2 +- documents/Secure_Tunnel_Userguide.md | 6 +++--- .../aws/iotsecuretunneling/SecureTunnel.h | 18 ++++++++++++++++++ secure_tunneling/source/SecureTunnel.cpp | 13 +++++++++++++ 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/crt/aws-c-iot b/crt/aws-c-iot index 79ad39e20..1ec416e6d 160000 --- a/crt/aws-c-iot +++ b/crt/aws-c-iot @@ -1 +1 @@ -Subproject commit 79ad39e2044228b319bce3876f226e0ce5c77b66 +Subproject commit 1ec416e6dbdcce15c25b288f78506619280b2cf6 diff --git a/documents/Secure_Tunnel_Userguide.md b/documents/Secure_Tunnel_Userguide.md index fff98dbc4..84ffcfd6c 100644 --- a/documents/Secure_Tunnel_Userguide.md +++ b/documents/Secure_Tunnel_Userguide.md @@ -43,7 +43,7 @@ When a WebSocket upgrade request fails to connect, this callback will return an ### OnConnectionShutdown When the WebSocket connection shuts down, this callback will be invoked. -## OnSendMessageComplete +### OnSendMessageComplete When a message has been completely written to the socket, this callback will be invoked. ### OnMessageReceived @@ -142,10 +142,10 @@ if (!secureTunnel->Start()) } ``` ### Reconnecting -A Secure Tunnel Client that has been started will attempt to reconnect upon disconnection until `Stop()` is called. The Secure Tunnel Client implements a Full Jitter Backoff Algorithm along with an exponential back off timer. More information on both can be found here: https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/ +A Secure Tunnel Client that has been started will attempt to reconnect upon a failed connection attempt or disconnection until `Stop()` is called. If the secure tunnel closes or the access tokens change, the Secure Tunnel Client will become disconnected and continue to attempt a reconnection until `Stop()` is called. The Secure Tunnel Client implements a Full Jitter Backoff Algorithm along with an exponential back off timer. More information on both can be found here: https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/ ## 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. A Secure Tunnel Client that has been stopped will no longer attempt to reconnect and is ready to be cleaned up. ```cpp if(!secureTunnel->Stop()){ fprintf(stdout, "Failed to stop the Secure Tunnel connection session. Exiting..\n"); diff --git a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h index 515ef55b4..eeb2cd85e 100644 --- a/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h +++ b/secure_tunneling/include/aws/iotsecuretunneling/SecureTunnel.h @@ -617,6 +617,17 @@ namespace Aws const std::string &endpointHost); // Make a copy and save in this object /* Optional members */ + + /** + * Sets TLS options to be used by secure tunnel connection. + * + * @param tslOptions TLS options to use for secure tunnel connection. If provided, the rootCA settings in + * these options will override any rootCA provided to the builder. + * + * @return this builder object + */ + SecureTunnelBuilder &WithTlsConnectionOptions(const Crt::Io::TlsConnectionOptions &tslOptions); + /** * Sets rootCA to be used for this secure tunnel connection overriding the default trust store. * @@ -825,6 +836,12 @@ namespace Aws */ std::string m_clientToken; + /** + * If set, TLS context for secure socket connections. + * If undefined, then default options will be used. + */ + Crt::Optional m_tlsConnectionOptions; + /** * If set, this will be used to override the default trust store. */ @@ -1130,6 +1147,7 @@ namespace Aws aws_secure_tunneling_local_proxy_mode localProxyMode, const std::string &endpointHost, + Crt::Io::TlsConnectionOptions *tslOptions, const std::string &rootCa, Crt::Http::HttpClientConnectionProxyOptions *httpClientConnectionProxyOptions, diff --git a/secure_tunneling/source/SecureTunnel.cpp b/secure_tunneling/source/SecureTunnel.cpp index 4b625c5c8..01b3680f2 100644 --- a/secure_tunneling/source/SecureTunnel.cpp +++ b/secure_tunneling/source/SecureTunnel.cpp @@ -348,6 +348,13 @@ namespace Aws { } + SecureTunnelBuilder &SecureTunnelBuilder::WithTlsConnectionOptions( + const Crt::Io::TlsConnectionOptions &tslOptions) + { + m_tlsConnectionOptions = tslOptions; + return *this; + } + SecureTunnelBuilder &SecureTunnelBuilder::WithRootCa(const std::string &rootCa) { m_rootCa = rootCa; @@ -474,6 +481,7 @@ namespace Aws m_clientToken, m_localProxyMode, m_endpointHost, + m_tlsConnectionOptions.has_value() ? &m_tlsConnectionOptions.value() : nullptr, m_rootCa, m_httpClientConnectionProxyOptions.has_value() ? &m_httpClientConnectionProxyOptions.value() : nullptr, m_OnConnectionSuccess, @@ -518,6 +526,7 @@ namespace Aws aws_secure_tunneling_local_proxy_mode localProxyMode, const std::string &endpointHost, + Crt::Io::TlsConnectionOptions *tslOptions, const std::string &rootCa, Aws::Crt::Http::HttpClientConnectionProxyOptions *httpClientConnectionProxyOptions, @@ -566,6 +575,8 @@ namespace Aws config.local_proxy_mode = localProxyMode; config.endpoint_host = aws_byte_cursor_from_c_str(endpointHost.c_str()); + config.tls_options = tslOptions ? tslOptions->GetUnderlyingHandle() : nullptr; + if (rootCa.length() > 0) { config.root_ca = rootCa.c_str(); @@ -631,6 +642,7 @@ namespace Aws nullptr, localProxyMode, endpointHost, + nullptr, rootCa, nullptr, nullptr, @@ -679,6 +691,7 @@ namespace Aws nullptr, localProxyMode, endpointHost, + nullptr, rootCa, nullptr, nullptr, From f726202b7958babf6c386934423ec2fc8e01b299 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 19 Apr 2023 14:57:24 -0700 Subject: [PATCH 18/18] 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 1ec416e6d..0f98a14e8 160000 --- a/crt/aws-c-iot +++ b/crt/aws-c-iot @@ -1 +1 @@ -Subproject commit 1ec416e6dbdcce15c25b288f78506619280b2cf6 +Subproject commit 0f98a14e87744fbacc50e854459a482d3664bcf3 diff --git a/crt/aws-crt-cpp b/crt/aws-crt-cpp index 917548f5d..f3634df6e 160000 --- a/crt/aws-crt-cpp +++ b/crt/aws-crt-cpp @@ -1 +1 @@ -Subproject commit 917548f5dcc2dfd6dbc08abfaf0134033b466d1d +Subproject commit f3634df6e3907555e38c272260518fe7a9405d36