diff --git a/include/aws/iotdevice/iotdevice.h b/include/aws/iotdevice/iotdevice.h index ac386872..48faad90 100644 --- a/include/aws/iotdevice/iotdevice.h +++ b/include/aws/iotdevice/iotdevice.h @@ -38,9 +38,15 @@ enum aws_iotdevice_error { AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_OPERATION_FAILED_DUE_TO_OFFLINE_QUEUE_POLICY, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_UNEXPECTED_HANGUP, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_USER_REQUESTED_STOP, + /* NOTE Leave the old name for compatibility. */ AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_PROTOCOL_VERSION_MISSMATCH, + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_PROTOCOL_VERSION_MISMATCH = + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_PROTOCOL_VERSION_MISSMATCH, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_TERMINATED, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DECODE_FAILURE, + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_NO_ACTIVE_CONNECTION, + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_PROTOCOL_VERSION_MISMATCH, + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INACTIVE_SERVICE_ID, AWS_ERROR_END_IOTDEVICE_RANGE = AWS_ERROR_ENUM_END_RANGE(AWS_C_IOTDEVICE_PACKAGE_ID), }; diff --git a/include/aws/iotdevice/private/secure_tunneling_impl.h b/include/aws/iotdevice/private/secure_tunneling_impl.h index d416848b..9367d632 100644 --- a/include/aws/iotdevice/private/secure_tunneling_impl.h +++ b/include/aws/iotdevice/private/secure_tunneling_impl.h @@ -163,7 +163,7 @@ struct aws_secure_tunnel_connections { /* Table containing streams using multiplexing (service ids) */ struct aws_hash_table service_ids; - /* Message used for initializing a stream upon a reconnect due to a protocol version missmatch */ + /* Message used for initializing a stream upon a reconnect due to a protocol version mismatch */ struct aws_secure_tunnel_message_storage *restore_stream_message_view; struct aws_secure_tunnel_message_storage restore_stream_message; }; diff --git a/include/aws/iotdevice/private/secure_tunneling_operations.h b/include/aws/iotdevice/private/secure_tunneling_operations.h index 37c17d29..0cb242a0 100644 --- a/include/aws/iotdevice/private/secure_tunneling_operations.h +++ b/include/aws/iotdevice/private/secure_tunneling_operations.h @@ -62,6 +62,11 @@ struct aws_secure_tunnel_operation_vtable { int (*aws_secure_tunnel_operation_set_connection_start_id)( struct aws_secure_tunnel_operation *operation, struct aws_secure_tunnel *secure_tunnel); + + /* Perform actions on outbound message before sending it */ + void (*aws_secure_tunnel_operation_prepare_message_for_send_fn)( + struct aws_secure_tunnel_operation *operation, + struct aws_secure_tunnel *secure_tunnel); }; /** diff --git a/include/aws/iotdevice/secure_tunneling.h b/include/aws/iotdevice/secure_tunneling.h index 9729f428..10611ec8 100644 --- a/include/aws/iotdevice/secure_tunneling.h +++ b/include/aws/iotdevice/secure_tunneling.h @@ -318,11 +318,25 @@ const char *aws_secure_tunnel_message_type_to_c_string(enum aws_secure_tunnel_me //*********************************************************************************************************************** /* THIS API SHOULD ONLY BE USED FROM SOURCE MODE */ //*********************************************************************************************************************** +/** + * Queue a STREAM_START message in a secure tunnel + * @note This function should only be used from source mode. + * @param secure_tunnel secure tunnel to queue a message for + * @param message_options configuration options for the message operation + * @return success/failure in the synchronous logic that kicks off the message operation + */ AWS_IOTDEVICE_API int aws_secure_tunnel_stream_start( struct aws_secure_tunnel *secure_tunnel, const struct aws_secure_tunnel_message_view *message_options); +/** + * Queue a CONNECTION_START message in a secure tunnel + * @note This function should only be used from source mode. + * @param secure_tunnel secure tunnel to queue a message for + * @param message_options configuration options for the message operation + * @return success/failure in the synchronous logic that kicks off the message operation + */ AWS_IOTDEVICE_API int aws_secure_tunnel_connection_start( struct aws_secure_tunnel *secure_tunnel, @@ -331,6 +345,13 @@ int aws_secure_tunnel_connection_start( //*********************************************************************************************************************** /* THIS API SHOULD NOT BE USED BY THE CUSTOMER AND IS DEPRECATED */ //*********************************************************************************************************************** +/** + * Queue a STREAM_RESET message in a secure tunnel + * @deprecated This function should not be used. + * @param secure_tunnel secure tunnel to queue a message for + * @param message_options configuration options for the message operation + * @return success/failure in the synchronous logic that kicks off the message operation + */ AWS_IOTDEVICE_API int aws_secure_tunnel_stream_reset( struct aws_secure_tunnel *secure_tunnel, diff --git a/source/iotdevice.c b/source/iotdevice.c index 3a317275..558624c0 100644 --- a/source/iotdevice.c +++ b/source/iotdevice.c @@ -77,7 +77,7 @@ static struct aws_error_info s_errors[] = { "Error while processing secure tunnel operational state."), AWS_DEFINE_ERROR_INFO_IOTDEVICE( AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_OPERATION_FAILED_DUE_TO_OFFLINE_QUEUE_POLICY, - "Error while processing secure tunnel operational state."), + "Secure Tunnel operation failed due to offline queue policy."), AWS_DEFINE_ERROR_INFO_IOTDEVICE( AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_UNEXPECTED_HANGUP, "The connection was closed unexpectedly."), @@ -85,14 +85,23 @@ static struct aws_error_info s_errors[] = { AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_USER_REQUESTED_STOP, "Secure Tunnel connection interrupted by user request."), AWS_DEFINE_ERROR_INFO_IOTDEVICE( - AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_PROTOCOL_VERSION_MISSMATCH, - "Secure Tunnel connection interrupted due to a protocol version missmatch."), + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_PROTOCOL_VERSION_MISMATCH, + "Secure Tunnel connection interrupted due to a protocol version mismatch."), AWS_DEFINE_ERROR_INFO_IOTDEVICE( AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_TERMINATED, "Secure Tunnel terminated by user request."), - AWS_DEFINE_ERROR_INFO_IOTDEVICE( - AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DECODE_FAILURE, - "Error occured while decoding an incoming message." ), + AWS_DEFINE_ERROR_INFO_IOTDEVICE( + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DECODE_FAILURE, + "Error occured while decoding an incoming message." ), + AWS_DEFINE_ERROR_INFO_IOTDEVICE( + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_NO_ACTIVE_CONNECTION, + "DATA message processing failed due to no active connection found." ), + AWS_DEFINE_ERROR_INFO_IOTDEVICE( + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_PROTOCOL_VERSION_MISMATCH, + "DATA message processing failed due to a protocol version mismatch." ), + AWS_DEFINE_ERROR_INFO_IOTDEVICE( + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INACTIVE_SERVICE_ID, + "Secure Tunnel operation failed due to using inactive service id." ), }; /* clang-format on */ #undef AWS_DEFINE_ERROR_INFO_IOTDEVICE diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index da9b6405..c14bb0c2 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -205,13 +205,14 @@ static uint8_t s_aws_secure_tunnel_message_min_protocol_check(const struct aws_s } static bool s_aws_secure_tunnel_protocol_version_match_check( - struct aws_secure_tunnel *secure_tunnel, + const struct aws_secure_tunnel *secure_tunnel, const struct aws_secure_tunnel_message_view *message) { uint8_t message_protocol_version = s_aws_secure_tunnel_message_min_protocol_check(message); if (secure_tunnel->connections->protocol_version != message_protocol_version) { AWS_LOGF_WARN( AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: Secure Tunnel is currently using Protocol V%d and received a message using Protocol V%d", + "id=%p: Protocol Version mismatch: Secure Tunnel is currently using Protocol V%d and a message " + "is using Protocol V%d", (void *)secure_tunnel, (int)secure_tunnel->connections->protocol_version, message_protocol_version); @@ -221,7 +222,7 @@ static bool s_aws_secure_tunnel_protocol_version_match_check( } static bool s_aws_secure_tunnel_stream_id_match_check( - struct aws_secure_tunnel *secure_tunnel, + const struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *service_id, int32_t stream_id) { /* @@ -242,7 +243,7 @@ static bool s_aws_secure_tunnel_stream_id_match_check( } static bool s_aws_secure_tunnel_active_stream_check( - struct aws_secure_tunnel *secure_tunnel, + const struct aws_secure_tunnel *secure_tunnel, const struct aws_secure_tunnel_message_view *message_view) { /* * No service id means either V1 protocol is being used or V3 protocol is being used on a tunnel without service ids @@ -510,12 +511,12 @@ static void s_aws_secure_tunnel_on_data_received( if (!s_aws_secure_tunnel_protocol_version_match_check(secure_tunnel, message_view)) { /* - * Protocol missmatch results in a full disconnect/reconnect to the Secure Tunnel Service followed by - * initializing the stream that caused the missmatch + * Protocol mismatch results in a full disconnect/reconnect to the Secure Tunnel Service followed by + * initializing the stream that caused the mismatch */ AWS_LOGF_INFO( AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: Secure Tunnel will be reset due to Protocol Version missmatch between previously established " + "id=%p: Secure Tunnel will be reset due to Protocol Version mismatch between previously established " "Protocol Version and Protocol Version used by incoming STREAM START message.", (void *)secure_tunnel); reset_secure_tunnel_connection(secure_tunnel); @@ -572,12 +573,12 @@ static void s_aws_secure_tunnel_on_stream_start_received( secure_tunnel->connections->protocol_version); } else if (!s_aws_secure_tunnel_protocol_version_match_check(secure_tunnel, message_view)) { /* - * Protocol missmatch results in a full disconnect/reconnect to the Secure Tunnel Service followed by - * initializing the stream that caused the missmatch + * Protocol mismatch results in a full disconnect/reconnect to the Secure Tunnel Service followed by + * initializing the stream that caused the mismatch */ AWS_LOGF_INFO( AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: Secure Tunnel will be reset due to Protocol Version missmatch between previously established " + "id=%p: Secure Tunnel will be reset due to Protocol Version mismatch between previously established " "Protocol Version and Protocol Version used by incoming STREAM START message.", (void *)secure_tunnel); reset_secure_tunnel_connection(secure_tunnel); @@ -615,7 +616,7 @@ static void s_aws_secure_tunnel_on_stream_reset_received( !s_aws_secure_tunnel_protocol_version_match_check(secure_tunnel, message_view)) { AWS_LOGF_INFO( AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: Secure Tunnel will be reset due to Protocol Version missmatch between previously established " + "id=%p: Secure Tunnel will be reset due to Protocol Version mismatch between previously established " "Protocol Version and Protocol Version used by incoming STREAM RESET message.", (void *)secure_tunnel); reset_secure_tunnel_connection(secure_tunnel); @@ -728,7 +729,7 @@ static void s_aws_secure_tunnel_on_connection_start_received( if (secure_tunnel->connections->protocol_version != 3) { AWS_LOGF_INFO( AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: Secure Tunnel will be reset due to Protocol Version missmatch between previously established " + "id=%p: Secure Tunnel will be reset due to Protocol Version mismatch between previously established " "Protocol Version and Protocol Version used by incoming CONNECTION START message.", (void *)secure_tunnel); reset_secure_tunnel_connection(secure_tunnel); @@ -771,7 +772,7 @@ static void s_aws_secure_tunnel_on_connection_reset_received( if (secure_tunnel->connections->protocol_version != 3) { AWS_LOGF_INFO( AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: Secure Tunnel will be reset due to Protocol Version missmatch between previously established " + "id=%p: Secure Tunnel will be reset due to Protocol Version mismatch between previously established " "Protocol Version and Protocol Version used by incoming CONNECTION RESET message.", (void *)secure_tunnel); reset_secure_tunnel_connection(secure_tunnel); @@ -1657,7 +1658,14 @@ static void s_complete_operation( struct aws_secure_tunnel_operation *operation, int error_code, const void *view) { - (void)secure_tunnel; + + AWS_LOGF_TRACE( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Completing operation %s with error %d (%s)", + (void *)secure_tunnel, + aws_secure_tunnel_operation_type_to_c_string(operation->operation_type), + error_code, + aws_error_str(error_code)); aws_secure_tunnel_operation_complete(operation, error_code, view); aws_secure_tunnel_operation_release(operation); @@ -1745,6 +1753,162 @@ static bool s_aws_secure_tunnel_should_service_operational_state( return now == s_aws_secure_tunnel_compute_operational_state_service_time(secure_tunnel, now); } +/** + * \internal + * Validate an outbound DATA message, set required fields (e.g. stream_id) and send it to the corresponding active + * connection. + * Call on_send_message_complete callback if an error occurs. + * \endinternal + */ +static void s_process_outbound_data_message( + struct aws_secure_tunnel *secure_tunnel, + struct aws_secure_tunnel_operation *current_operation) { + + int error_code = AWS_OP_SUCCESS; + + if (secure_tunnel->connections->protocol_version == 0) { + error_code = AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_NO_ACTIVE_CONNECTION; + goto done; + } + + (*current_operation->vtable->aws_secure_tunnel_operation_prepare_message_for_send_fn)( + current_operation, secure_tunnel); + + /* If the data message's protocol version doesn't match the version of the current session, the message should + * be ignored. */ + if (!s_aws_secure_tunnel_protocol_version_match_check(secure_tunnel, current_operation->message_view)) { + error_code = AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_PROTOCOL_VERSION_MISMATCH; + goto done; + } + + /* An outbound message does not have an assigned stream ID, assign an active stream ID to it. */ + if ((*current_operation->vtable->aws_secure_tunnel_operation_assign_stream_id_fn)( + current_operation, secure_tunnel)) { + error_code = aws_last_error(); + goto done; + } + + /* If a data message attempts to be sent on an unopen stream, discard it. */ + if (!s_aws_secure_tunnel_active_stream_check(secure_tunnel, current_operation->message_view)) { + error_code = aws_last_error(); + if (current_operation->message_view->service_id && current_operation->message_view->service_id->len > 0) { + AWS_LOGF_DEBUG( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: failed to send DATA message with service id '" PRInSTR + "' stream id (%d) and connection id (%d) with error %d(%s)", + (void *)secure_tunnel, + AWS_BYTE_CURSOR_PRI(*current_operation->message_view->service_id), + current_operation->message_view->stream_id, + current_operation->message_view->connection_id, + error_code, + aws_error_debug_str(error_code)); + } else { + AWS_LOGF_DEBUG( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: failed to send DATA message with stream id (%d) and connection id (%d) with error %d(%s)", + (void *)secure_tunnel, + current_operation->message_view->stream_id, + current_operation->message_view->connection_id, + error_code, + aws_error_debug_str(error_code)); + } + goto done; + } + + /* Send the Data message through the WebSocket */ + if (s_secure_tunneling_send(secure_tunnel, current_operation->message_view)) { + error_code = aws_last_error(); + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: failed to send DATA message with error %d(%s)", + (void *)secure_tunnel, + error_code, + aws_error_debug_str(error_code)); + } + aws_secure_tunnel_message_view_log(current_operation->message_view, AWS_LL_DEBUG); + +done: + if (error_code && secure_tunnel->config->on_send_message_complete) { + secure_tunnel->config->on_send_message_complete( + AWS_SECURE_TUNNEL_MT_DATA, error_code, secure_tunnel->config->user_data); + } +} + +/** + * \internal + * Send STREAM_START message to the Secure Tunnel Service. + * Call on_send_message_complete callback if an error occurs. + * \endinternal + */ +static void s_process_outbound_stream_start_message( + struct aws_secure_tunnel *secure_tunnel, + struct aws_secure_tunnel_operation *current_operation) { + + int error_code = AWS_OP_SUCCESS; + + if (secure_tunnel->connections->protocol_version != 0 && + !s_aws_secure_tunnel_protocol_version_match_check(secure_tunnel, current_operation->message_view)) { + AWS_LOGF_WARN( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Secure Tunnel client does not support STREAM_START messages with mismatched protocol version in " + "SOURCE mode", + (void *)secure_tunnel); + + error_code = AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_PROTOCOL_VERSION_MISMATCH; + + /* TODO The following doesn't work as all enqueued operations are canceled on resetting a tunnel connection. */ + bool is_reset_supported = false; + if (is_reset_supported) { + /* + * Protocol mismatch results in a full disconnect/reconnect to the Secure Tunnel Service followed by + * sending the STREAM START request that caused the mismatch. + */ + AWS_LOGF_INFO( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Secure Tunnel will be reset due to Protocol Version mismatch between previously " + "established " + "Protocol Version and Protocol Version used by outbound STREAM START message.", + (void *)secure_tunnel); + reset_secure_tunnel_connection(secure_tunnel); + aws_secure_tunnel_stream_start(secure_tunnel, current_operation->message_view); + } + goto done; + } + + if (secure_tunnel->connections->protocol_version == 0) { + secure_tunnel->connections->protocol_version = + s_aws_secure_tunnel_message_min_protocol_check(current_operation->message_view); + AWS_LOGF_INFO( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Secure tunnel client Protocol set to V%d based on outbound STREAM START", + (void *)secure_tunnel, + (int)secure_tunnel->connections->protocol_version); + } + + if ((*current_operation->vtable->aws_secure_tunnel_operation_set_next_stream_id_fn)( + current_operation, secure_tunnel)) { + error_code = aws_last_error(); + AWS_LOGF_DEBUG( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: failed to send STREAM START message with error %d(%s)", + (void *)secure_tunnel, + error_code, + aws_error_debug_str(error_code)); + goto done; + } + + if (s_secure_tunneling_send(secure_tunnel, current_operation->message_view)) { + error_code = aws_last_error(); + } + aws_secure_tunnel_message_view_log(current_operation->message_view, AWS_LL_DEBUG); + +done: + if (error_code && secure_tunnel->config->on_send_message_complete) { + secure_tunnel->config->on_send_message_complete( + AWS_SECURE_TUNNEL_MT_STREAM_START, error_code, secure_tunnel->config->user_data); + } +} + int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure_tunnel) { const struct aws_secure_tunnel_vtable *vtable = secure_tunnel->vtable; uint64_t now = (*vtable->get_current_time_fn)(); @@ -1781,6 +1945,12 @@ int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure } int error_code = AWS_OP_SUCCESS; + AWS_LOGF_TRACE( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Processing %s message", + (void *)secure_tunnel, + aws_secure_tunnel_operation_type_to_c_string(current_operation->operation_type)); + switch (current_operation->operation_type) { case AWS_STOT_PING:; /* @@ -1797,83 +1967,14 @@ int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure break; case AWS_STOT_MESSAGE: - /* If a data message attempts to be sent on an unopen stream, discard it. */ - if ((*current_operation->vtable->aws_secure_tunnel_operation_assign_stream_id_fn)( - current_operation, secure_tunnel)) { - error_code = aws_last_error(); - } else { - if (s_aws_secure_tunnel_active_stream_check(secure_tunnel, current_operation->message_view)) { - /* Send the Data message through the WebSocket */ - if (s_secure_tunneling_send(secure_tunnel, current_operation->message_view)) { - error_code = aws_last_error(); - AWS_LOGF_ERROR( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: failed to send DATA message with error %d(%s)", - (void *)secure_tunnel, - error_code, - aws_error_debug_str(error_code)); - } - aws_secure_tunnel_message_view_log(current_operation->message_view, AWS_LL_DEBUG); - } else { - error_code = aws_last_error(); - if (current_operation->message_view->service_id && - current_operation->message_view->service_id->len > 0) { - AWS_LOGF_DEBUG( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: failed to send DATA message with service id '" PRInSTR - "' stream id (%d) and connection id (%d) with error %d(%s)", - (void *)secure_tunnel, - AWS_BYTE_CURSOR_PRI(*current_operation->message_view->service_id), - current_operation->message_view->stream_id, - current_operation->message_view->connection_id, - error_code, - aws_error_debug_str(error_code)); - } else { - AWS_LOGF_DEBUG( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: failed to send DATA message with stream id (%d) and connection id (%d) " - "with " - "error %d(%s)", - (void *)secure_tunnel, - current_operation->message_view->stream_id, - current_operation->message_view->connection_id, - error_code, - aws_error_debug_str(error_code)); - } - } - } - if (error_code && secure_tunnel->config->on_send_message_complete) { - secure_tunnel->config->on_send_message_complete( - AWS_SECURE_TUNNEL_MT_DATA, error_code, secure_tunnel->config->user_data); - } - + s_process_outbound_data_message(secure_tunnel, current_operation); break; case AWS_STOT_STREAM_START: - if ((*current_operation->vtable->aws_secure_tunnel_operation_set_next_stream_id_fn)( - current_operation, secure_tunnel)) { - error_code = aws_last_error(); - AWS_LOGF_DEBUG( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: failed to send STREAM START message with error %d(%s)", - (void *)secure_tunnel, - error_code, - aws_error_debug_str(error_code)); - } else { - if (s_secure_tunneling_send(secure_tunnel, current_operation->message_view)) { - error_code = aws_last_error(); - } - aws_secure_tunnel_message_view_log(current_operation->message_view, AWS_LL_DEBUG); - } - - if (error_code && secure_tunnel->config->on_send_message_complete) { - secure_tunnel->config->on_send_message_complete( - AWS_SECURE_TUNNEL_MT_STREAM_START, error_code, secure_tunnel->config->user_data); - } + s_process_outbound_stream_start_message(secure_tunnel, current_operation); break; case AWS_STOT_STREAM_RESET: - if ((*current_operation->vtable->aws_secure_tunnel_operation_assign_stream_id_fn)( current_operation, secure_tunnel) == AWS_OP_SUCCESS) { if (current_operation->message_view->connection_id == 0) { @@ -1899,9 +2000,18 @@ int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure break; case AWS_STOT_CONNECTION_START: + if (secure_tunnel->connections->protocol_version != 3) { + AWS_LOGF_WARN( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "Connection Start may only be used with a Protocol V3 stream."); + error_code = AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_PROTOCOL_VERSION_MISMATCH; + } else if (current_operation->message_view->connection_id == 0) { + AWS_LOGF_WARN(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Connection Start must include a connection id."); + error_code = AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_CONNECTION_ID; + } /* If a connection start attempts to be sent on an unopen stream, discard it. */ - if ((*current_operation->vtable->aws_secure_tunnel_operation_assign_stream_id_fn)( - current_operation, secure_tunnel)) { + else if ((*current_operation->vtable->aws_secure_tunnel_operation_assign_stream_id_fn)( + current_operation, secure_tunnel)) { error_code = aws_last_error(); } else if ((*current_operation->vtable->aws_secure_tunnel_operation_set_connection_start_id)( current_operation, secure_tunnel)) { @@ -2310,7 +2420,7 @@ static void s_service_state_clean_disconnect(struct aws_secure_tunnel *secure_tu if (aws_linked_list_empty(&secure_tunnel->queued_operations)) { s_reset_secure_tunnel_streams(secure_tunnel); s_secure_tunnel_shutdown_websocket( - secure_tunnel, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_PROTOCOL_VERSION_MISSMATCH); + secure_tunnel, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_PROTOCOL_VERSION_MISMATCH); return; } @@ -2538,15 +2648,6 @@ int aws_secure_tunnel_send_message( return aws_last_error(); } - /* - * If message is being sent from DESTINATION MODE, it might be expected that a V2 or V1 connection has established a - * default connection id of 1. This default connection id must be stripped before sending a V1 or V2 message out. - */ - if (secure_tunnel->config->local_proxy_mode == AWS_SECURE_TUNNELING_DESTINATION_MODE && - secure_tunnel->connections->protocol_version < 3 && message_options->connection_id == 1) { - message_op->options_storage.storage_view.connection_id = 0; - } - AWS_LOGF_DEBUG( AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: Submitting MESSAGE operation (%p)", @@ -2575,32 +2676,6 @@ int aws_secure_tunnel_stream_start( return AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INCORRECT_MODE; } - uint8_t message_protocol_version = s_aws_secure_tunnel_message_min_protocol_check(message_options); - if (secure_tunnel->connections->protocol_version != 0 && - message_protocol_version != secure_tunnel->connections->protocol_version) { - /* - * Protocol missmatch results in a full disconnect/reconnect to the Secure Tunnel Service followed by - * sending the STREAM START request that caused the missmatch. - */ - AWS_LOGF_INFO( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: Secure Tunnel will be reset due to Protocol Version missmatch between previously established " - "Protocol Version (%d) and Protocol Version used by outbound STREAM START message (%d).", - (void *)secure_tunnel, - (int)secure_tunnel->connections->protocol_version, - (int)message_protocol_version); - reset_secure_tunnel_connection(secure_tunnel); - } - - if (secure_tunnel->connections->protocol_version == 0) { - secure_tunnel->connections->protocol_version = message_protocol_version; - AWS_LOGF_INFO( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: Secure tunnel client Protocol set to V%d based on outbound STREAM START", - (void *)secure_tunnel, - (int)secure_tunnel->connections->protocol_version); - } - struct aws_secure_tunnel_operation_message *message_op = aws_secure_tunnel_operation_message_new( secure_tunnel->allocator, secure_tunnel, message_options, AWS_STOT_STREAM_START); @@ -2636,17 +2711,6 @@ int aws_secure_tunnel_connection_start( return AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INCORRECT_MODE; } - if (secure_tunnel->connections->protocol_version != 3) { - AWS_LOGF_WARN( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Connection Start may only be used with a Protocol V3 stream."); - return AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_PROTOCOL_VERSION_MISSMATCH; - } - - if (message_options->connection_id == 0) { - AWS_LOGF_WARN(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Connection Start must include a connection id."); - return AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_CONNECTION_ID; - } - struct aws_secure_tunnel_operation_message *message_op = aws_secure_tunnel_operation_message_new( secure_tunnel->allocator, secure_tunnel, message_options, AWS_STOT_CONNECTION_START); diff --git a/source/secure_tunneling_operations.c b/source/secure_tunneling_operations.c index 38f65602..a727c081 100644 --- a/source/secure_tunneling_operations.c +++ b/source/secure_tunneling_operations.c @@ -142,6 +142,7 @@ static struct aws_secure_tunnel_operation_vtable s_empty_operation_vtable = { .aws_secure_tunnel_operation_assign_stream_id_fn = NULL, .aws_secure_tunnel_operation_set_next_stream_id_fn = NULL, .aws_secure_tunnel_operation_set_connection_start_id = NULL, + .aws_secure_tunnel_operation_prepare_message_for_send_fn = NULL, }; /********************************************************************************************************************* @@ -361,6 +362,8 @@ static int s_aws_secure_tunnel_operation_message_assign_stream_id( struct aws_secure_tunnel_message_view *message_view = &message_op->options_storage.storage_view; + int error_code = AWS_OP_SUCCESS; + if (message_view->service_id == NULL || message_view->service_id->len == 0) { stream_id = secure_tunnel->connections->stream_id; } else { @@ -372,13 +375,20 @@ static int s_aws_secure_tunnel_operation_message_assign_stream_id( "id=%p: invalid service id '" PRInSTR "' attempted to be assigned a stream id on an outbound message", (void *)message_view, AWS_BYTE_CURSOR_PRI(*message_view->service_id)); + error_code = AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_SERVICE_ID; goto error; } struct aws_service_id_element *service_id_elem = elem->value; stream_id = service_id_elem->stream_id; + + if (stream_id == INVALID_STREAM_ID) { + error_code = AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INACTIVE_SERVICE_ID; + goto error; + } } if (stream_id == INVALID_STREAM_ID) { + error_code = AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_STREAM_ID; goto error; } @@ -401,7 +411,7 @@ static int s_aws_secure_tunnel_operation_message_assign_stream_id( aws_secure_tunnel_message_type_to_c_string(message_view->type)); } - return aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_STREAM_ID); + return aws_raise_error(error_code); } /* @@ -561,10 +571,30 @@ static int s_aws_secure_tunnel_operation_set_connection_start_id( return AWS_OP_SUCCESS; } +static void s_aws_secure_tunnel_operation_prepare_message_for_send_fn( + struct aws_secure_tunnel_operation *operation, + struct aws_secure_tunnel *secure_tunnel) { + + struct aws_secure_tunnel_operation_message *message_op = operation->impl; + struct aws_secure_tunnel_message_view *message_view = &message_op->options_storage.storage_view; + + /* + * If message is being sent from DESTINATION MODE, it might be expected that a V2 or V1 connection has + * established a default connection id of 1. This default connection id must be stripped before sending + * a V1 or V2 message out. + */ + if (secure_tunnel->config->local_proxy_mode == AWS_SECURE_TUNNELING_DESTINATION_MODE && + secure_tunnel->connections->protocol_version < 3 && message_view->connection_id == 1) { + message_op->options_storage.storage_view.connection_id = 0; + } +} + static struct aws_secure_tunnel_operation_vtable s_message_operation_vtable = { .aws_secure_tunnel_operation_assign_stream_id_fn = s_aws_secure_tunnel_operation_message_assign_stream_id, .aws_secure_tunnel_operation_set_next_stream_id_fn = s_aws_secure_tunnel_operation_message_set_next_stream_id, .aws_secure_tunnel_operation_set_connection_start_id = s_aws_secure_tunnel_operation_set_connection_start_id, + .aws_secure_tunnel_operation_prepare_message_for_send_fn = + s_aws_secure_tunnel_operation_prepare_message_for_send_fn, }; static void s_destroy_operation_message(void *object) { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 36db970e..cbac1041 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -41,9 +41,20 @@ add_net_test_case(secure_tunneling_v2_to_v1_stream_start_test) add_net_test_case(secure_tunneling_v3_to_v1_stream_start_test) add_net_test_case(secure_tunneling_v1_stream_start_v3_message_reset_test) add_net_test_case(secure_tunneling_v2_stream_start_connection_start_reset_test) -add_net_test_case(secure_tunneling_ignore_outbound_inactive_connection_id_message_sending_test) add_net_test_case(secure_tunneling_close_stream_on_connection_reset_test) add_net_test_case(secure_tunneling_existing_connection_start_send_reset_test) +add_net_test_case(secure_tunneling_send_v2_data_message_on_v1_connection_test) +add_net_test_case(secure_tunneling_send_v3_data_message_on_v1_connection_test) +add_net_test_case(secure_tunneling_send_v1_data_message_on_v2_connection_test) +add_net_test_case(secure_tunneling_send_v3_data_message_on_v2_connection_test) +add_net_test_case(secure_tunneling_send_v1_data_message_on_v3_connection_test) +add_net_test_case(secure_tunneling_send_v2_data_message_on_v3_connection_test) +add_net_test_case(secure_tunneling_send_v2_data_message_on_incorrect_v2_connection_test) +add_net_test_case(secure_tunneling_send_v3_data_message_on_incorrect_v3_connection_test) +add_net_test_case(secure_tunneling_send_v1_data_message_with_no_active_connection_test) +add_net_test_case(secure_tunneling_send_v3_stream_start_message_test) +add_net_test_case(secure_tunneling_send_v3_stream_start_message_with_reset_test) +add_net_test_case(secure_tunneling_send_v3_connection_start_message_test) generate_test_driver(${PROJECT_NAME}-tests) diff --git a/tests/secure_tunnel_tests.c b/tests/secure_tunnel_tests.c index 18776a7e..0bc2cecc 100644 --- a/tests/secure_tunnel_tests.c +++ b/tests/secure_tunnel_tests.c @@ -70,11 +70,13 @@ struct secure_tunnel_test_options { struct aws_secure_tunnel_mock_websocket_vtable websocket_function_table; }; -static void s_secure_tunnel_test_init_default_options(struct secure_tunnel_test_options *test_options) { +static void s_secure_tunnel_test_init_default_options( + struct secure_tunnel_test_options *test_options, + enum aws_secure_tunneling_local_proxy_mode local_proxy_mode) { struct aws_secure_tunnel_options local_secure_tunnel_options = { .endpoint_host = aws_byte_cursor_from_string(s_endpoint_host), .access_token = aws_byte_cursor_from_string(s_access_token), - .local_proxy_mode = AWS_SECURE_TUNNELING_DESTINATION_MODE, + .local_proxy_mode = local_proxy_mode, }; test_options->secure_tunnel_options = local_secure_tunnel_options; } @@ -128,6 +130,12 @@ struct aws_secure_tunnel_mock_test_fixture { int secure_tunnel_message_sent_count_target; int secure_tunnel_message_sent_connection_reset_count; int secure_tunnel_message_sent_data_count; + + bool on_send_message_complete_fired; + struct { + enum aws_secure_tunnel_message_type type; + int error_code; + } on_send_message_complete_result; }; static bool s_secure_tunnel_check_active_stream_id( @@ -239,9 +247,14 @@ static void s_on_test_secure_tunnel_send_message_complete( enum aws_secure_tunnel_message_type type, int error_code, void *user_data) { - (void)type; - (void)error_code; - (void)user_data; + struct aws_secure_tunnel_mock_test_fixture *test_fixture = user_data; + + aws_mutex_lock(&test_fixture->lock); + test_fixture->on_send_message_complete_fired = true; + test_fixture->on_send_message_complete_result.type = type; + test_fixture->on_send_message_complete_result.error_code = error_code; + aws_condition_variable_notify_all(&test_fixture->signal); + aws_mutex_unlock(&test_fixture->lock); } static void s_on_test_secure_tunnel_on_session_reset(void *user_data) { @@ -495,36 +508,86 @@ static void s_wait_for_n_messages_received(struct aws_secure_tunnel_mock_test_fi aws_mutex_unlock(&test_fixture->lock); } +static bool s_has_secure_tunnel_on_send_message_complete_fired(void *arg) { + struct aws_secure_tunnel_mock_test_fixture *test_fixture = arg; + return test_fixture->on_send_message_complete_fired; +} + +static void s_wait_for_on_send_message_complete_fired(struct aws_secure_tunnel_mock_test_fixture *test_fixture) { + aws_mutex_lock(&test_fixture->lock); + aws_condition_variable_wait_pred( + &test_fixture->signal, &test_fixture->lock, s_has_secure_tunnel_on_send_message_complete_fired, test_fixture); + /* Reset flag for the next message. */ + test_fixture->on_send_message_complete_fired = false; + aws_mutex_unlock(&test_fixture->lock); +} + /***************************************************************************************************************** * WEBSOCKET MOCK FUNCTIONS *****************************************************************************************************************/ -/* Serializes message view and sends as Websocket */ +/* Task that simulates a WebSocket payload receiving. */ +struct aws_secure_tunnel_mock_websocket_receive_frame_payload_task { + struct aws_task task; + struct aws_secure_tunnel_mock_test_fixture *test_fixture; + struct aws_byte_buf data_buf; + struct aws_byte_buf out_buf; +}; + +static void s_secure_tunneling_mock_websocket_receive_frame_payload_task_fn( + struct aws_task *task, + void *arg, + enum aws_task_status status) { + + (void)task; + + struct aws_secure_tunnel_mock_websocket_receive_frame_payload_task *receive_task = arg; + if (status != AWS_TASK_STATUS_RUN_READY) { + return; + } + + struct aws_byte_cursor data_cur = aws_byte_cursor_from_buf(&receive_task->out_buf); + receive_task->test_fixture->websocket_function_table->on_incoming_frame_payload_fn( + NULL, NULL, data_cur, receive_task->test_fixture->secure_tunnel); + + aws_byte_buf_clean_up(&receive_task->out_buf); + aws_byte_buf_clean_up(&receive_task->data_buf); + aws_mem_release(receive_task->test_fixture->allocator, receive_task); +} + +/* Serialize a message view and initialize a task for the event loop. The task then will simulate receiving the + * WebSocket data. + * NOTE In the actual environment, WebSocket operations and the secure tunnel are assigned to the same loop. We can + * reproduce this by "receiving" messages from the mocked WebSocket in the same event loop the secure tunnel uses. This + * way we don't need to worry about race conditions appearing in the tests that are not possible during the actual + * execution. + */ void aws_secure_tunnel_send_mock_message( struct aws_secure_tunnel_mock_test_fixture *test_fixture, const struct aws_secure_tunnel_message_view *message_view) { - /* The actual WebSocket is assigned the same event loop as the secure tunnel but the mock websocket for tests - * requires a short sleep to insure there aren't race conditions related to the incoming websocket data being - * processed. */ - aws_thread_current_sleep(aws_timestamp_convert(350, AWS_TIMESTAMP_MILLIS, AWS_TIMESTAMP_NANOS, NULL)); - struct aws_byte_buf data_buf; + + struct aws_secure_tunnel_mock_websocket_receive_frame_payload_task *receive_task = aws_mem_calloc( + test_fixture->secure_tunnel->allocator, + 1, + sizeof(struct aws_secure_tunnel_mock_websocket_receive_frame_payload_task)); + + aws_task_init( + &receive_task->task, + s_secure_tunneling_mock_websocket_receive_frame_payload_task_fn, + (void *)receive_task, + "MockWebsocketSendMessage"); + + receive_task->test_fixture = test_fixture; + struct aws_byte_cursor data_cur; - struct aws_byte_buf out_buf; - aws_iot_st_msg_serialize_from_view(&data_buf, test_fixture->allocator, message_view); - data_cur = aws_byte_cursor_from_buf(&data_buf); - aws_byte_buf_init(&out_buf, test_fixture->allocator, data_cur.len + PAYLOAD_BYTE_LENGTH_PREFIX); - aws_byte_buf_write_be16(&out_buf, (int16_t)data_buf.len); - aws_byte_buf_write_to_capacity(&out_buf, &data_cur); - data_cur = aws_byte_cursor_from_buf(&out_buf); - test_fixture->websocket_function_table->on_incoming_frame_payload_fn( - NULL, NULL, data_cur, test_fixture->secure_tunnel); - - aws_byte_buf_clean_up(&out_buf); - aws_byte_buf_clean_up(&data_buf); - /* The actual WebSocket is assigned the same event loop as the secure tunnel but the mock websocket for tests - * requires a short sleep to insure there aren't race conditions related to the incoming websocket data being - * processed. */ - aws_thread_current_sleep(aws_timestamp_convert(350, AWS_TIMESTAMP_MILLIS, AWS_TIMESTAMP_NANOS, NULL)); + + aws_iot_st_msg_serialize_from_view(&receive_task->data_buf, test_fixture->allocator, message_view); + data_cur = aws_byte_cursor_from_buf(&receive_task->data_buf); + aws_byte_buf_init(&receive_task->out_buf, test_fixture->allocator, data_cur.len + PAYLOAD_BYTE_LENGTH_PREFIX); + aws_byte_buf_write_be16(&receive_task->out_buf, (uint16_t)receive_task->data_buf.len); + aws_byte_buf_write_to_capacity(&receive_task->out_buf, &data_cur); + + aws_event_loop_schedule_task_now(test_fixture->secure_tunnel->loop, &receive_task->task); } int aws_websocket_client_connect_mock_fn(const struct aws_websocket_client_connection_options *options) { @@ -717,12 +780,13 @@ int aws_secure_tunnel_mock_test_fixture_init( void aws_secure_tunnel_mock_test_init( struct aws_allocator *allocator, struct secure_tunnel_test_options *test_options, - struct aws_secure_tunnel_mock_test_fixture *test_fixture) { + struct aws_secure_tunnel_mock_test_fixture *test_fixture, + enum aws_secure_tunneling_local_proxy_mode local_proxy_mode) { aws_http_library_init(allocator); aws_iotdevice_library_init(allocator); - s_secure_tunnel_test_init_default_options(test_options); + s_secure_tunnel_test_init_default_options(test_options, local_proxy_mode); test_options->secure_tunnel_options.client_token = aws_byte_cursor_from_string(s_client_token); @@ -774,7 +838,7 @@ static int s_secure_tunneling_functionality_connect_test_fn(struct aws_allocator struct secure_tunnel_test_options test_options; struct aws_secure_tunnel_mock_test_fixture test_fixture; - aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture); + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture, AWS_SECURE_TUNNELING_DESTINATION_MODE); struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; test_fixture.header_check = secure_tunneling_access_token_check; @@ -809,7 +873,7 @@ static int s_secure_tunneling_functionality_client_token_test_fn(struct aws_allo (void)ctx; struct secure_tunnel_test_options test_options; struct aws_secure_tunnel_mock_test_fixture test_fixture; - aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture); + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture, AWS_SECURE_TUNNELING_DESTINATION_MODE); struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; test_fixture.header_check = secure_tunneling_client_token_check; @@ -884,7 +948,7 @@ static int s_secure_tunneling_fail_and_retry_connection_test_fn(struct aws_alloc (void)ctx; struct secure_tunnel_test_options test_options; struct aws_secure_tunnel_mock_test_fixture test_fixture; - aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture); + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture, AWS_SECURE_TUNNELING_DESTINATION_MODE); struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; test_fixture.secure_tunnel_vtable = *aws_secure_tunnel_get_default_vtable(); @@ -911,7 +975,7 @@ static int s_secure_tunneling_store_service_ids_test_fn(struct aws_allocator *al (void)ctx; struct secure_tunnel_test_options test_options; struct aws_secure_tunnel_mock_test_fixture test_fixture; - aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture); + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture, AWS_SECURE_TUNNELING_DESTINATION_MODE); struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); @@ -945,7 +1009,7 @@ static int s_secure_tunneling_receive_stream_start_test_fn(struct aws_allocator (void)ctx; struct secure_tunnel_test_options test_options; struct aws_secure_tunnel_mock_test_fixture test_fixture; - aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture); + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture, AWS_SECURE_TUNNELING_DESTINATION_MODE); struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); @@ -981,7 +1045,7 @@ static int s_secure_tunneling_rejected_service_id_stream_start_test_fn(struct aw (void)ctx; struct secure_tunnel_test_options test_options; struct aws_secure_tunnel_mock_test_fixture test_fixture; - aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture); + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture, AWS_SECURE_TUNNELING_DESTINATION_MODE); struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); @@ -1015,7 +1079,7 @@ static int s_secure_tunneling_close_stream_on_stream_reset_test_fn(struct aws_al (void)ctx; struct secure_tunnel_test_options test_options; struct aws_secure_tunnel_mock_test_fixture test_fixture; - aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture); + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture, AWS_SECURE_TUNNELING_DESTINATION_MODE); struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); @@ -1065,7 +1129,7 @@ static int s_secure_tunneling_ignore_stream_reset_for_inactive_stream_test_fn( (void)ctx; struct secure_tunnel_test_options test_options; struct aws_secure_tunnel_mock_test_fixture test_fixture; - aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture); + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture, AWS_SECURE_TUNNELING_DESTINATION_MODE); struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); @@ -1118,7 +1182,7 @@ static int s_secure_tunneling_session_reset_test_fn(struct aws_allocator *alloca (void)ctx; struct secure_tunnel_test_options test_options; struct aws_secure_tunnel_mock_test_fixture test_fixture; - aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture); + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture, AWS_SECURE_TUNNELING_DESTINATION_MODE); struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); @@ -1175,7 +1239,7 @@ static int s_secure_tunneling_serializer_data_message_test_fn(struct aws_allocat (void)ctx; struct secure_tunnel_test_options test_options; struct aws_secure_tunnel_mock_test_fixture test_fixture; - aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture); + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture, AWS_SECURE_TUNNELING_DESTINATION_MODE); struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); @@ -1227,7 +1291,7 @@ static int s_secure_tunneling_max_payload_test_fn(struct aws_allocator *allocato (void)ctx; struct secure_tunnel_test_options test_options; struct aws_secure_tunnel_mock_test_fixture test_fixture; - aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture); + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture, AWS_SECURE_TUNNELING_DESTINATION_MODE); struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); @@ -1269,7 +1333,7 @@ static int s_secure_tunneling_max_payload_exceed_test_fn(struct aws_allocator *a (void)ctx; struct secure_tunnel_test_options test_options; struct aws_secure_tunnel_mock_test_fixture test_fixture; - aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture); + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture, AWS_SECURE_TUNNELING_DESTINATION_MODE); struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); @@ -1314,7 +1378,7 @@ static int s_secure_tunneling_receive_connection_start_test_fn(struct aws_alloca (void)ctx; struct secure_tunnel_test_options test_options; struct aws_secure_tunnel_mock_test_fixture test_fixture; - aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture); + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture, AWS_SECURE_TUNNELING_DESTINATION_MODE); struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); @@ -1360,7 +1424,7 @@ static int s_secure_tunneling_ignore_inactive_stream_message_test_fn(struct aws_ (void)ctx; struct secure_tunnel_test_options test_options; struct aws_secure_tunnel_mock_test_fixture test_fixture; - aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture); + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture, AWS_SECURE_TUNNELING_DESTINATION_MODE); struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); @@ -1413,7 +1477,7 @@ static int s_secure_tunneling_ignore_inactive_connection_id_message_test_fn( (void)ctx; struct secure_tunnel_test_options test_options; struct aws_secure_tunnel_mock_test_fixture test_fixture; - aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture); + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture, AWS_SECURE_TUNNELING_DESTINATION_MODE); struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); @@ -1466,7 +1530,7 @@ static int s_secure_tunneling_v1_to_v2_stream_start_test_fn(struct aws_allocator (void)ctx; struct secure_tunnel_test_options test_options; struct aws_secure_tunnel_mock_test_fixture test_fixture; - aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture); + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture, AWS_SECURE_TUNNELING_DESTINATION_MODE); struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); @@ -1517,7 +1581,7 @@ static int s_secure_tunneling_v1_to_v3_stream_start_test_fn(struct aws_allocator (void)ctx; struct secure_tunnel_test_options test_options; struct aws_secure_tunnel_mock_test_fixture test_fixture; - aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture); + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture, AWS_SECURE_TUNNELING_DESTINATION_MODE); struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); @@ -1569,7 +1633,7 @@ static int s_secure_tunneling_v2_to_v1_stream_start_test_fn(struct aws_allocator (void)ctx; struct secure_tunnel_test_options test_options; struct aws_secure_tunnel_mock_test_fixture test_fixture; - aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture); + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture, AWS_SECURE_TUNNELING_DESTINATION_MODE); struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); @@ -1620,7 +1684,7 @@ static int s_secure_tunneling_v3_to_v1_stream_start_test_fn(struct aws_allocator (void)ctx; struct secure_tunnel_test_options test_options; struct aws_secure_tunnel_mock_test_fixture test_fixture; - aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture); + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture, AWS_SECURE_TUNNELING_DESTINATION_MODE); struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); @@ -1673,7 +1737,7 @@ static int s_secure_tunneling_v1_stream_start_v3_message_reset_test_fn(struct aw (void)ctx; struct secure_tunnel_test_options test_options; struct aws_secure_tunnel_mock_test_fixture test_fixture; - aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture); + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture, AWS_SECURE_TUNNELING_DESTINATION_MODE); struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); @@ -1727,7 +1791,7 @@ static int s_secure_tunneling_v2_stream_start_connection_start_reset_test_fn( (void)ctx; struct secure_tunnel_test_options test_options; struct aws_secure_tunnel_mock_test_fixture test_fixture; - aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture); + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture, AWS_SECURE_TUNNELING_DESTINATION_MODE); struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); @@ -1777,63 +1841,11 @@ AWS_TEST_CASE( secure_tunneling_v2_stream_start_connection_start_reset_test, s_secure_tunneling_v2_stream_start_connection_start_reset_test_fn) -static int s_secure_tunneling_ignore_outbound_inactive_connection_id_message_sending_test_fn( - struct aws_allocator *allocator, - void *ctx) { - (void)ctx; - struct secure_tunnel_test_options test_options; - struct aws_secure_tunnel_mock_test_fixture test_fixture; - aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture); - struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; - - ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); - s_wait_for_connected_successfully(&test_fixture); - - /* Create and send a stream start message from the server to the destination client */ - struct aws_byte_cursor service_1 = aws_byte_cursor_from_string(s_service_id_1); - struct aws_secure_tunnel_message_view stream_start_message_view = { - .type = AWS_SECURE_TUNNEL_MT_STREAM_START, - .service_id = &service_1, - .stream_id = 1, - .connection_id = 2, - }; - aws_secure_tunnel_send_mock_message(&test_fixture, &stream_start_message_view); - - /* Wait and confirm that a stream has been started */ - s_wait_for_stream_started(&test_fixture); - ASSERT_TRUE(s_secure_tunnel_check_active_connection_id(secure_tunnel, &service_1, 1, 2)); - - /* Create and send a data message from the server to the destination client to an inactive connection id */ - struct aws_byte_cursor payload_cur = aws_byte_cursor_from_string(s_payload_text); - struct aws_secure_tunnel_message_view data_message_view = { - .type = AWS_SECURE_TUNNEL_MT_DATA, - .service_id = &service_1, - .payload = &payload_cur, - .connection_id = 3, - }; - aws_secure_tunnel_send_message(secure_tunnel, &data_message_view); - - /* Confirm that no messages have gone out from the client */ - aws_thread_current_sleep(aws_timestamp_convert(1, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL)); - ASSERT_INT_EQUALS(test_fixture.secure_tunnel_message_sent_count, 0); - - ASSERT_SUCCESS(aws_secure_tunnel_stop(secure_tunnel)); - s_wait_for_connection_shutdown(&test_fixture); - - aws_secure_tunnel_mock_test_clean_up(&test_fixture); - - return AWS_OP_SUCCESS; -} - -AWS_TEST_CASE( - secure_tunneling_ignore_outbound_inactive_connection_id_message_sending_test, - s_secure_tunneling_ignore_outbound_inactive_connection_id_message_sending_test_fn) - static int s_secure_tunneling_close_stream_on_connection_reset_test_fn(struct aws_allocator *allocator, void *ctx) { (void)ctx; struct secure_tunnel_test_options test_options; struct aws_secure_tunnel_mock_test_fixture test_fixture; - aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture); + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture, AWS_SECURE_TUNNELING_DESTINATION_MODE); struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); @@ -1896,7 +1908,7 @@ static int s_secure_tunneling_existing_connection_start_send_reset_test_fn(struc (void)ctx; struct secure_tunnel_test_options test_options; struct aws_secure_tunnel_mock_test_fixture test_fixture; - aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture); + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture, AWS_SECURE_TUNNELING_DESTINATION_MODE); struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); @@ -1947,3 +1959,780 @@ static int s_secure_tunneling_existing_connection_start_send_reset_test_fn(struc AWS_TEST_CASE( secure_tunneling_existing_connection_start_send_reset_test, s_secure_tunneling_existing_connection_start_send_reset_test_fn) + +static int s_secure_tunneling_send_v2_data_message_on_v1_connection_test_fn( + struct aws_allocator *allocator, + void *ctx) { + (void)ctx; + struct secure_tunnel_test_options test_options; + struct aws_secure_tunnel_mock_test_fixture test_fixture; + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture, AWS_SECURE_TUNNELING_DESTINATION_MODE); + struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; + + ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); + s_wait_for_connected_successfully(&test_fixture); + + /* Create and send a V1 StreamStart message from the server to the destination client */ + struct aws_secure_tunnel_message_view stream_start_message_view = { + .type = AWS_SECURE_TUNNEL_MT_STREAM_START, + .stream_id = 1, + }; + aws_secure_tunnel_send_mock_message(&test_fixture, &stream_start_message_view); + + /* Wait and confirm that a stream has been started */ + s_wait_for_stream_started(&test_fixture); + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, NULL, 1)); + + /* Create and send a V2 DATA message, this should fail with PROTOCOL VERSION MISMATCH error */ + struct aws_byte_cursor service_1 = aws_byte_cursor_from_string(s_service_id_1); + struct aws_secure_tunnel_message_view data_message_view = { + .type = AWS_SECURE_TUNNEL_MT_DATA, + .stream_id = 0, + .service_id = &service_1, + .payload = &s_payload_cursor_max_size, + }; + int result = aws_secure_tunnel_send_message(secure_tunnel, &data_message_view); + ASSERT_INT_EQUALS(result, AWS_OP_SUCCESS); + + s_wait_for_on_send_message_complete_fired(&test_fixture); + + /* Confirm that no messages have gone out from the client */ + ASSERT_INT_EQUALS(test_fixture.secure_tunnel_message_sent_count, 0); + + /* Ensure that the established stream was not affected by the message */ + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, NULL, 1)); + + ASSERT_SUCCESS(aws_secure_tunnel_stop(secure_tunnel)); + s_wait_for_connection_shutdown(&test_fixture); + + aws_secure_tunnel_mock_test_clean_up(&test_fixture); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + secure_tunneling_send_v2_data_message_on_v1_connection_test, + s_secure_tunneling_send_v2_data_message_on_v1_connection_test_fn) + +static int s_secure_tunneling_send_v3_data_message_on_v1_connection_test_fn( + struct aws_allocator *allocator, + void *ctx) { + (void)ctx; + struct secure_tunnel_test_options test_options; + struct aws_secure_tunnel_mock_test_fixture test_fixture; + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture, AWS_SECURE_TUNNELING_DESTINATION_MODE); + struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; + + ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); + s_wait_for_connected_successfully(&test_fixture); + + /* Create and send a V1 StreamStart message from the server to the destination client */ + struct aws_secure_tunnel_message_view stream_start_message_view = { + .type = AWS_SECURE_TUNNEL_MT_STREAM_START, + .stream_id = 1, + }; + aws_secure_tunnel_send_mock_message(&test_fixture, &stream_start_message_view); + + /* Wait and confirm that a stream has been started */ + s_wait_for_stream_started(&test_fixture); + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, NULL, 1)); + + /* Create and send a V3 DATA message, this should fail with PROTOCOL VERSION MISMATCH error */ + struct aws_byte_cursor service_1 = aws_byte_cursor_from_string(s_service_id_1); + struct aws_secure_tunnel_message_view data_message_view = { + .type = AWS_SECURE_TUNNEL_MT_DATA, + .stream_id = 0, + .service_id = &service_1, + .connection_id = 3, + .payload = &s_payload_cursor_max_size, + }; + int result = aws_secure_tunnel_send_message(secure_tunnel, &data_message_view); + ASSERT_INT_EQUALS(result, AWS_OP_SUCCESS); + + s_wait_for_on_send_message_complete_fired(&test_fixture); + + /* Confirm that no messages have gone out from the client */ + ASSERT_INT_EQUALS(test_fixture.secure_tunnel_message_sent_count, 0); + + /* Confirm that on_send_message_complete callback was fired */ + ASSERT_INT_EQUALS(test_fixture.on_send_message_complete_result.type, AWS_SECURE_TUNNEL_MT_DATA); + ASSERT_INT_EQUALS( + test_fixture.on_send_message_complete_result.error_code, + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_PROTOCOL_VERSION_MISMATCH); + + /* Ensure that the established stream was not affected by the message */ + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, NULL, 1)); + + ASSERT_SUCCESS(aws_secure_tunnel_stop(secure_tunnel)); + s_wait_for_connection_shutdown(&test_fixture); + + aws_secure_tunnel_mock_test_clean_up(&test_fixture); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + secure_tunneling_send_v3_data_message_on_v1_connection_test, + s_secure_tunneling_send_v3_data_message_on_v1_connection_test_fn) + +static int s_secure_tunneling_send_v1_data_message_on_v2_connection_test_fn( + struct aws_allocator *allocator, + void *ctx) { + (void)ctx; + struct secure_tunnel_test_options test_options; + struct aws_secure_tunnel_mock_test_fixture test_fixture; + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture, AWS_SECURE_TUNNELING_DESTINATION_MODE); + struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; + + ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); + s_wait_for_connected_successfully(&test_fixture); + + /* Create and send a V2 StreamStart message from the server to the destination client */ + struct aws_byte_cursor service_1 = aws_byte_cursor_from_string(s_service_id_1); + struct aws_secure_tunnel_message_view stream_start_message_view = { + .type = AWS_SECURE_TUNNEL_MT_STREAM_START, + .service_id = &service_1, + .stream_id = 1, + }; + aws_secure_tunnel_send_mock_message(&test_fixture, &stream_start_message_view); + + /* Wait and confirm that a stream has been started */ + s_wait_for_stream_started(&test_fixture); + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, &service_1, 1)); + + /* Create and send a V1 DATA message, this should fail with PROTOCOL VERSION MISMATCH error */ + struct aws_secure_tunnel_message_view data_message_view = { + .type = AWS_SECURE_TUNNEL_MT_DATA, + .stream_id = 0, + .payload = &s_payload_cursor_max_size, + }; + int result = aws_secure_tunnel_send_message(secure_tunnel, &data_message_view); + ASSERT_INT_EQUALS(result, AWS_OP_SUCCESS); + + s_wait_for_on_send_message_complete_fired(&test_fixture); + + /* Confirm that no messages have gone out from the client */ + ASSERT_INT_EQUALS(test_fixture.secure_tunnel_message_sent_count, 0); + + /* Confirm that on_send_message_complete callback was fired */ + ASSERT_INT_EQUALS(test_fixture.on_send_message_complete_result.type, AWS_SECURE_TUNNEL_MT_DATA); + ASSERT_INT_EQUALS( + test_fixture.on_send_message_complete_result.error_code, + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_PROTOCOL_VERSION_MISMATCH); + + /* Ensure that the established stream was not affected by the message */ + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, &service_1, 1)); + + ASSERT_SUCCESS(aws_secure_tunnel_stop(secure_tunnel)); + s_wait_for_connection_shutdown(&test_fixture); + + aws_secure_tunnel_mock_test_clean_up(&test_fixture); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + secure_tunneling_send_v1_data_message_on_v2_connection_test, + s_secure_tunneling_send_v1_data_message_on_v2_connection_test_fn) + +static int s_secure_tunneling_send_v3_data_message_on_v2_connection_test_fn( + struct aws_allocator *allocator, + void *ctx) { + (void)ctx; + struct secure_tunnel_test_options test_options; + struct aws_secure_tunnel_mock_test_fixture test_fixture; + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture, AWS_SECURE_TUNNELING_DESTINATION_MODE); + struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; + + ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); + s_wait_for_connected_successfully(&test_fixture); + + /* Create and send a V2 StreamStart message from the server to the destination client */ + struct aws_byte_cursor service_1 = aws_byte_cursor_from_string(s_service_id_1); + struct aws_secure_tunnel_message_view stream_start_message_view = { + .type = AWS_SECURE_TUNNEL_MT_STREAM_START, + .service_id = &service_1, + .stream_id = 1, + }; + aws_secure_tunnel_send_mock_message(&test_fixture, &stream_start_message_view); + + /* Wait and confirm that a stream has been started */ + s_wait_for_stream_started(&test_fixture); + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, &service_1, 1)); + + /* Create and send a V3 DATA message, this should fail with PROTOCOL VERSION MISMATCH error */ + struct aws_secure_tunnel_message_view data_message_view = { + .type = AWS_SECURE_TUNNEL_MT_DATA, + .stream_id = 0, + .service_id = &service_1, + .connection_id = 3, + .payload = &s_payload_cursor_max_size, + }; + int result = aws_secure_tunnel_send_message(secure_tunnel, &data_message_view); + ASSERT_INT_EQUALS(result, AWS_OP_SUCCESS); + + s_wait_for_on_send_message_complete_fired(&test_fixture); + + /* Confirm that no messages have gone out from the client */ + ASSERT_INT_EQUALS(test_fixture.secure_tunnel_message_sent_count, 0); + + /* Confirm that on_send_message_complete callback was fired */ + ASSERT_INT_EQUALS(test_fixture.on_send_message_complete_result.type, AWS_SECURE_TUNNEL_MT_DATA); + ASSERT_INT_EQUALS( + test_fixture.on_send_message_complete_result.error_code, + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_PROTOCOL_VERSION_MISMATCH); + + /* Ensure that the established stream was not affected by the message */ + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, &service_1, 1)); + + ASSERT_SUCCESS(aws_secure_tunnel_stop(secure_tunnel)); + s_wait_for_connection_shutdown(&test_fixture); + + aws_secure_tunnel_mock_test_clean_up(&test_fixture); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + secure_tunneling_send_v3_data_message_on_v2_connection_test, + s_secure_tunneling_send_v3_data_message_on_v2_connection_test_fn) + +static int s_secure_tunneling_send_v1_data_message_on_v3_connection_test_fn( + struct aws_allocator *allocator, + void *ctx) { + (void)ctx; + struct secure_tunnel_test_options test_options; + struct aws_secure_tunnel_mock_test_fixture test_fixture; + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture, AWS_SECURE_TUNNELING_DESTINATION_MODE); + struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; + + ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); + s_wait_for_connected_successfully(&test_fixture); + + /* Create and send a V3 StreamStart message from the server to the destination client */ + struct aws_byte_cursor service_1 = aws_byte_cursor_from_string(s_service_id_1); + struct aws_secure_tunnel_message_view stream_start_message_view = { + .type = AWS_SECURE_TUNNEL_MT_STREAM_START, + .service_id = &service_1, + .stream_id = 1, + .connection_id = 3, + }; + aws_secure_tunnel_send_mock_message(&test_fixture, &stream_start_message_view); + + /* Wait and confirm that a stream has been started */ + s_wait_for_stream_started(&test_fixture); + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, &service_1, 1)); + + /* Create and send a V1 DATA message, this should fail with PROTOCOL VERSION MISMATCH error */ + struct aws_secure_tunnel_message_view data_message_view = { + .type = AWS_SECURE_TUNNEL_MT_DATA, + .stream_id = 0, + .payload = &s_payload_cursor_max_size, + }; + int result = aws_secure_tunnel_send_message(secure_tunnel, &data_message_view); + ASSERT_INT_EQUALS(result, AWS_OP_SUCCESS); + + s_wait_for_on_send_message_complete_fired(&test_fixture); + + /* Confirm that no messages have gone out from the client */ + ASSERT_INT_EQUALS(test_fixture.secure_tunnel_message_sent_count, 0); + + /* Confirm that on_send_message_complete callback was fired */ + ASSERT_INT_EQUALS(test_fixture.on_send_message_complete_result.type, AWS_SECURE_TUNNEL_MT_DATA); + ASSERT_INT_EQUALS( + test_fixture.on_send_message_complete_result.error_code, + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_PROTOCOL_VERSION_MISMATCH); + + /* Ensure that the established stream was not affected by the message */ + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, &service_1, 1)); + + ASSERT_SUCCESS(aws_secure_tunnel_stop(secure_tunnel)); + s_wait_for_connection_shutdown(&test_fixture); + + aws_secure_tunnel_mock_test_clean_up(&test_fixture); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + secure_tunneling_send_v1_data_message_on_v3_connection_test, + s_secure_tunneling_send_v1_data_message_on_v3_connection_test_fn) + +static int s_secure_tunneling_send_v2_data_message_on_v3_connection_test_fn( + struct aws_allocator *allocator, + void *ctx) { + (void)ctx; + struct secure_tunnel_test_options test_options; + struct aws_secure_tunnel_mock_test_fixture test_fixture; + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture, AWS_SECURE_TUNNELING_DESTINATION_MODE); + struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; + + ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); + s_wait_for_connected_successfully(&test_fixture); + + /* Create and send a V3 StreamStart message from the server to the destination client */ + struct aws_byte_cursor service_1 = aws_byte_cursor_from_string(s_service_id_1); + struct aws_secure_tunnel_message_view stream_start_message_view = { + .type = AWS_SECURE_TUNNEL_MT_STREAM_START, + .service_id = &service_1, + .stream_id = 1, + .connection_id = 3, + }; + aws_secure_tunnel_send_mock_message(&test_fixture, &stream_start_message_view); + + /* Wait and confirm that a stream has been started */ + s_wait_for_stream_started(&test_fixture); + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, &service_1, 1)); + + /* Create and send a V2 DATA message, this should fail with PROTOCOL VERSION MISMATCH error */ + struct aws_secure_tunnel_message_view data_message_view = { + .type = AWS_SECURE_TUNNEL_MT_DATA, + .stream_id = 0, + .service_id = &service_1, + .payload = &s_payload_cursor_max_size, + }; + int result = aws_secure_tunnel_send_message(secure_tunnel, &data_message_view); + ASSERT_INT_EQUALS(result, AWS_OP_SUCCESS); + + s_wait_for_on_send_message_complete_fired(&test_fixture); + + /* Confirm that no messages have gone out from the client */ + ASSERT_INT_EQUALS(test_fixture.secure_tunnel_message_sent_count, 0); + + /* Confirm that on_send_message_complete callback was fired */ + ASSERT_INT_EQUALS(test_fixture.on_send_message_complete_result.type, AWS_SECURE_TUNNEL_MT_DATA); + ASSERT_INT_EQUALS( + test_fixture.on_send_message_complete_result.error_code, + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_PROTOCOL_VERSION_MISMATCH); + + /* Ensure that the established stream was not affected by the message */ + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, &service_1, 1)); + + ASSERT_SUCCESS(aws_secure_tunnel_stop(secure_tunnel)); + s_wait_for_connection_shutdown(&test_fixture); + + aws_secure_tunnel_mock_test_clean_up(&test_fixture); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + secure_tunneling_send_v2_data_message_on_v3_connection_test, + s_secure_tunneling_send_v2_data_message_on_v3_connection_test_fn) + +static int s_secure_tunneling_send_v2_data_message_on_incorrect_v2_connection_test_fn( + struct aws_allocator *allocator, + void *ctx) { + + (void)ctx; + + struct secure_tunnel_test_options test_options; + struct aws_secure_tunnel_mock_test_fixture test_fixture; + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture, AWS_SECURE_TUNNELING_DESTINATION_MODE); + struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; + + ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); + s_wait_for_connected_successfully(&test_fixture); + + /* Create and send a V2 StreamStart message from the server to the destination client */ + struct aws_byte_cursor service_1 = aws_byte_cursor_from_string(s_service_id_1); + struct aws_secure_tunnel_message_view stream_start_message_view = { + .type = AWS_SECURE_TUNNEL_MT_STREAM_START, + .service_id = &service_1, + .stream_id = 1, + }; + aws_secure_tunnel_send_mock_message(&test_fixture, &stream_start_message_view); + + /* Wait and confirm that a stream has been started */ + s_wait_for_stream_started(&test_fixture); + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, &service_1, 1)); + + /* Create and send a V2 DATA message with incorrect service ID, + * this should fail with INVALID SERVICE ID error */ + struct aws_byte_cursor service_2 = aws_byte_cursor_from_string(s_service_id_2); + struct aws_secure_tunnel_message_view data_message_view = { + .type = AWS_SECURE_TUNNEL_MT_DATA, + .stream_id = 0, + .service_id = &service_2, + .payload = &s_payload_cursor_max_size, + }; + + int result = aws_secure_tunnel_send_message(secure_tunnel, &data_message_view); + ASSERT_INT_EQUALS(result, AWS_OP_SUCCESS); + + s_wait_for_on_send_message_complete_fired(&test_fixture); + + /* Confirm that no messages have gone out from the client */ + ASSERT_INT_EQUALS(test_fixture.secure_tunnel_message_sent_count, 0); + + /* Confirm that on_send_message_complete callback was fired */ + ASSERT_INT_EQUALS(test_fixture.on_send_message_complete_result.type, AWS_SECURE_TUNNEL_MT_DATA); + ASSERT_INT_EQUALS( + test_fixture.on_send_message_complete_result.error_code, + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INACTIVE_SERVICE_ID); + + /* Ensure that the established stream was not affected by the message */ + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, &service_1, 1)); + + ASSERT_SUCCESS(aws_secure_tunnel_stop(secure_tunnel)); + s_wait_for_connection_shutdown(&test_fixture); + + aws_secure_tunnel_mock_test_clean_up(&test_fixture); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + secure_tunneling_send_v2_data_message_on_incorrect_v2_connection_test, + s_secure_tunneling_send_v2_data_message_on_incorrect_v2_connection_test_fn) + +static int s_secure_tunneling_send_v3_data_message_on_incorrect_v3_connection_test_fn( + struct aws_allocator *allocator, + void *ctx) { + + (void)ctx; + + struct secure_tunnel_test_options test_options; + struct aws_secure_tunnel_mock_test_fixture test_fixture; + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture, AWS_SECURE_TUNNELING_DESTINATION_MODE); + struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; + + ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); + s_wait_for_connected_successfully(&test_fixture); + + /* Create and send a V3 StreamStart message from the server to the destination client */ + struct aws_byte_cursor service_1 = aws_byte_cursor_from_string(s_service_id_1); + struct aws_secure_tunnel_message_view stream_start_message_view = { + .type = AWS_SECURE_TUNNEL_MT_STREAM_START, + .service_id = &service_1, + .stream_id = 1, + .connection_id = 2, + }; + aws_secure_tunnel_send_mock_message(&test_fixture, &stream_start_message_view); + + /* Wait and confirm that a stream has been started */ + s_wait_for_stream_started(&test_fixture); + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, &service_1, 1)); + + /* Create and send a V3 DATA message with incorrect service ID, + * this should fail with INVALID CONNECTION ID error */ + struct aws_secure_tunnel_message_view data_message_view = { + .type = AWS_SECURE_TUNNEL_MT_DATA, + .stream_id = 0, + .service_id = &service_1, + .connection_id = 3, + .payload = &s_payload_cursor_max_size, + }; + + int result = aws_secure_tunnel_send_message(secure_tunnel, &data_message_view); + ASSERT_INT_EQUALS(result, AWS_OP_SUCCESS); + + s_wait_for_on_send_message_complete_fired(&test_fixture); + + /* Confirm that no messages have gone out from the client */ + ASSERT_INT_EQUALS(test_fixture.secure_tunnel_message_sent_count, 0); + + /* Confirm that on_send_message_complete callback was fired */ + ASSERT_INT_EQUALS(test_fixture.on_send_message_complete_result.type, AWS_SECURE_TUNNEL_MT_DATA); + ASSERT_INT_EQUALS( + test_fixture.on_send_message_complete_result.error_code, + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_CONNECTION_ID); + + /* Ensure that the established stream was not affected by the message */ + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, &service_1, 1)); + + ASSERT_SUCCESS(aws_secure_tunnel_stop(secure_tunnel)); + s_wait_for_connection_shutdown(&test_fixture); + + aws_secure_tunnel_mock_test_clean_up(&test_fixture); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + secure_tunneling_send_v3_data_message_on_incorrect_v3_connection_test, + s_secure_tunneling_send_v3_data_message_on_incorrect_v3_connection_test_fn) + +static int s_secure_tunneling_send_v1_data_message_with_no_active_connection_test_fn( + struct aws_allocator *allocator, + void *ctx) { + + (void)ctx; + + struct secure_tunnel_test_options test_options; + struct aws_secure_tunnel_mock_test_fixture test_fixture; + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture, AWS_SECURE_TUNNELING_DESTINATION_MODE); + struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; + + ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); + s_wait_for_connected_successfully(&test_fixture); + + /* Create and send a V1 DATA message, + * this should fail with INVALID CONNECTION ID error */ + struct aws_secure_tunnel_message_view data_message_view = { + .type = AWS_SECURE_TUNNEL_MT_DATA, + .stream_id = 0, + .payload = &s_payload_cursor_max_size, + }; + + int result = aws_secure_tunnel_send_message(secure_tunnel, &data_message_view); + ASSERT_INT_EQUALS(result, AWS_OP_SUCCESS); + + s_wait_for_on_send_message_complete_fired(&test_fixture); + + /* Confirm that no messages have gone out from the client */ + ASSERT_INT_EQUALS(test_fixture.secure_tunnel_message_sent_count, 0); + + /* Confirm that on_send_message_complete callback was fired */ + ASSERT_INT_EQUALS(test_fixture.on_send_message_complete_result.type, AWS_SECURE_TUNNEL_MT_DATA); + ASSERT_INT_EQUALS( + test_fixture.on_send_message_complete_result.error_code, + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_NO_ACTIVE_CONNECTION); + + ASSERT_SUCCESS(aws_secure_tunnel_stop(secure_tunnel)); + s_wait_for_connection_shutdown(&test_fixture); + + aws_secure_tunnel_mock_test_clean_up(&test_fixture); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + secure_tunneling_send_v1_data_message_with_no_active_connection_test, + s_secure_tunneling_send_v1_data_message_with_no_active_connection_test_fn) + +static int s_secure_tunneling_send_v3_stream_start_message_test_fn(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + struct secure_tunnel_test_options test_options; + struct aws_secure_tunnel_mock_test_fixture test_fixture; + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture, AWS_SECURE_TUNNELING_SOURCE_MODE); + struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; + + ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); + s_wait_for_connected_successfully(&test_fixture); + + /* Create and send a V3 STREAM_START message to the server */ + struct aws_byte_cursor service_1 = aws_byte_cursor_from_string(s_service_id_1); + struct aws_secure_tunnel_message_view stream_start_message_view = { + .type = AWS_SECURE_TUNNEL_MT_STREAM_START, + .service_id = &service_1, + .stream_id = 0, + .connection_id = 2, + }; + aws_secure_tunnel_stream_start(test_fixture.secure_tunnel, &stream_start_message_view); + + /* Wait and confirm that a stream has been started */ + s_wait_for_on_send_message_complete_fired(&test_fixture); + + /* Confirm that the message has been sent */ + ASSERT_INT_EQUALS(test_fixture.secure_tunnel_message_sent_count, 1); + + /* Confirm that on_send_message_complete callback was fired */ + ASSERT_INT_EQUALS(test_fixture.on_send_message_complete_result.type, AWS_SECURE_TUNNEL_MT_STREAM_START); + ASSERT_INT_EQUALS(test_fixture.on_send_message_complete_result.error_code, AWS_ERROR_SUCCESS); + + /* Ensure that the established connection is active */ + ASSERT_TRUE(s_secure_tunnel_check_active_connection_id(secure_tunnel, &service_1, 1, 2)); + + /* Create and send a V3 DATA message */ + struct aws_secure_tunnel_message_view data_message_view = { + .type = AWS_SECURE_TUNNEL_MT_DATA, + .stream_id = 0, + .service_id = &service_1, + .connection_id = 2, + .payload = &s_payload_cursor_max_size, + }; + + int result = aws_secure_tunnel_send_message(secure_tunnel, &data_message_view); + ASSERT_INT_EQUALS(result, AWS_OP_SUCCESS); + + /* Since there is no feedback on successful sending, simply sleep. */ + aws_thread_current_sleep(aws_timestamp_convert(1, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL)); + + /* Confirm that the message has been sent */ + ASSERT_INT_EQUALS(test_fixture.secure_tunnel_message_sent_count, 2); + + /* Ensure that the established connection is still active */ + ASSERT_TRUE(s_secure_tunnel_check_active_connection_id(secure_tunnel, &service_1, 1, 2)); + + ASSERT_SUCCESS(aws_secure_tunnel_stop(secure_tunnel)); + s_wait_for_connection_shutdown(&test_fixture); + + aws_secure_tunnel_mock_test_clean_up(&test_fixture); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + secure_tunneling_send_v3_stream_start_message_test, + s_secure_tunneling_send_v3_stream_start_message_test_fn) + +static int s_secure_tunneling_send_v3_stream_start_message_with_reset_test_fn( + struct aws_allocator *allocator, + void *ctx) { + (void)ctx; + + struct secure_tunnel_test_options test_options; + struct aws_secure_tunnel_mock_test_fixture test_fixture; + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture, AWS_SECURE_TUNNELING_SOURCE_MODE); + struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; + + ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); + s_wait_for_connected_successfully(&test_fixture); + + /* Create and send a V2 STREAM_START message to the server */ + struct aws_byte_cursor service_1 = aws_byte_cursor_from_string(s_service_id_1); + struct aws_secure_tunnel_message_view stream_start_v2_message_view = { + .type = AWS_SECURE_TUNNEL_MT_STREAM_START, + .service_id = &service_1, + .stream_id = 0, + }; + ASSERT_INT_EQUALS( + aws_secure_tunnel_stream_start(test_fixture.secure_tunnel, &stream_start_v2_message_view), AWS_OP_SUCCESS); + + /* Wait and confirm that a stream has been started */ + s_wait_for_on_send_message_complete_fired(&test_fixture); + + /* Confirm that the message has been sent */ + ASSERT_INT_EQUALS(test_fixture.secure_tunnel_message_sent_count, 1); + + /* Confirm that on_send_message_complete callback was fired */ + ASSERT_INT_EQUALS(test_fixture.on_send_message_complete_result.type, AWS_SECURE_TUNNEL_MT_STREAM_START); + ASSERT_INT_EQUALS(test_fixture.on_send_message_complete_result.error_code, AWS_ERROR_SUCCESS); + + /* Ensure that the established stream is active */ + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, &service_1, 1)); + + /* Create and send a V3 STREAM_START message to the server */ + struct aws_byte_cursor service_2 = aws_byte_cursor_from_string(s_service_id_2); + struct aws_secure_tunnel_message_view stream_start_v3_message_view = { + .type = AWS_SECURE_TUNNEL_MT_STREAM_START, + .service_id = &service_2, + .stream_id = 0, + .connection_id = 2, + }; + ASSERT_INT_EQUALS( + aws_secure_tunnel_stream_start(test_fixture.secure_tunnel, &stream_start_v3_message_view), AWS_OP_SUCCESS); + + /* Wait and confirm that a stream has been started */ + s_wait_for_on_send_message_complete_fired(&test_fixture); + + /* Confirm that the message has been sent */ + ASSERT_INT_EQUALS(test_fixture.secure_tunnel_message_sent_count, 1); + + /* Confirm that on_send_message_complete callback was fired */ + ASSERT_INT_EQUALS(test_fixture.on_send_message_complete_result.type, AWS_SECURE_TUNNEL_MT_STREAM_START); + ASSERT_INT_EQUALS( + test_fixture.on_send_message_complete_result.error_code, + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_PROTOCOL_VERSION_MISMATCH); + + /* Ensure that the old stream is still active */ + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, &service_1, 1)); + + ASSERT_SUCCESS(aws_secure_tunnel_stop(secure_tunnel)); + s_wait_for_connection_shutdown(&test_fixture); + + aws_secure_tunnel_mock_test_clean_up(&test_fixture); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + secure_tunneling_send_v3_stream_start_message_with_reset_test, + s_secure_tunneling_send_v3_stream_start_message_with_reset_test_fn) + +static int s_secure_tunneling_send_v3_connection_start_message_test_fn(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + struct secure_tunnel_test_options test_options; + struct aws_secure_tunnel_mock_test_fixture test_fixture; + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture, AWS_SECURE_TUNNELING_SOURCE_MODE); + struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; + + ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); + s_wait_for_connected_successfully(&test_fixture); + + /* Create and send a V3 StreamStart message to the server */ + struct aws_byte_cursor service_1 = aws_byte_cursor_from_string(s_service_id_1); + struct aws_secure_tunnel_message_view stream_start_message_view = { + .type = AWS_SECURE_TUNNEL_MT_STREAM_START, + .service_id = &service_1, + .stream_id = 0, + .connection_id = 2, + }; + aws_secure_tunnel_stream_start(test_fixture.secure_tunnel, &stream_start_message_view); + + /* Wait and confirm that a stream has been started */ + s_wait_for_on_send_message_complete_fired(&test_fixture); + + /* Confirm that the message has been sent */ + ASSERT_INT_EQUALS(test_fixture.secure_tunnel_message_sent_count, 1); + + /* Confirm that on_send_message_complete callback was fired */ + ASSERT_INT_EQUALS(test_fixture.on_send_message_complete_result.type, AWS_SECURE_TUNNEL_MT_STREAM_START); + ASSERT_INT_EQUALS(test_fixture.on_send_message_complete_result.error_code, AWS_ERROR_SUCCESS); + + ASSERT_TRUE(s_secure_tunnel_check_active_connection_id(secure_tunnel, &service_1, 1, 2)); + + /* Create and send a V3 CONNECTION_START message to the server */ + struct aws_secure_tunnel_message_view connection_start_message_view = { + .type = AWS_SECURE_TUNNEL_MT_CONNECTION_START, + .service_id = &service_1, + .stream_id = 0, + .connection_id = 3, + }; + aws_secure_tunnel_connection_start(test_fixture.secure_tunnel, &connection_start_message_view); + + /* Wait and confirm that a stream has been started */ + s_wait_for_on_send_message_complete_fired(&test_fixture); + + /* Confirm that the message has been sent */ + ASSERT_INT_EQUALS(test_fixture.secure_tunnel_message_sent_count, 2); + + /* Confirm that on_send_message_complete callback was fired */ + ASSERT_INT_EQUALS(test_fixture.on_send_message_complete_result.type, AWS_SECURE_TUNNEL_MT_CONNECTION_START); + ASSERT_INT_EQUALS(test_fixture.on_send_message_complete_result.error_code, AWS_ERROR_SUCCESS); + + /* Confirm that the both connections are active */ + ASSERT_TRUE(s_secure_tunnel_check_active_connection_id(secure_tunnel, &service_1, 1, 2)); + ASSERT_TRUE(s_secure_tunnel_check_active_connection_id(secure_tunnel, &service_1, 1, 3)); + + /* Create and send a V3 DATA message to the first connection */ + struct aws_secure_tunnel_message_view data_message_view = { + .type = AWS_SECURE_TUNNEL_MT_DATA, + .stream_id = 0, + .service_id = &service_1, + .connection_id = 2, + .payload = &s_payload_cursor_max_size, + }; + + int result = aws_secure_tunnel_send_message(secure_tunnel, &data_message_view); + ASSERT_INT_EQUALS(result, AWS_OP_SUCCESS); + + /* Send a V3 DATA message to the second connection */ + data_message_view.connection_id = 3; + result = aws_secure_tunnel_send_message(secure_tunnel, &data_message_view); + ASSERT_INT_EQUALS(result, AWS_OP_SUCCESS); + + /* Since there is no feedback on successful sending, simply sleep. */ + aws_thread_current_sleep(aws_timestamp_convert(1, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL)); + + /* Confirm that the messages have been sent */ + ASSERT_INT_EQUALS(test_fixture.secure_tunnel_message_sent_count, 4); + + /* Ensure that the established connections are still active */ + ASSERT_TRUE(s_secure_tunnel_check_active_connection_id(secure_tunnel, &service_1, 1, 2)); + ASSERT_TRUE(s_secure_tunnel_check_active_connection_id(secure_tunnel, &service_1, 1, 3)); + + ASSERT_SUCCESS(aws_secure_tunnel_stop(secure_tunnel)); + s_wait_for_connection_shutdown(&test_fixture); + + aws_secure_tunnel_mock_test_clean_up(&test_fixture); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + secure_tunneling_send_v3_connection_start_message_test, + s_secure_tunneling_send_v3_connection_start_message_test_fn)