From 86b77717074953f632082248eba1fb29cfb8d495 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 27 Feb 2023 15:22:27 -0800 Subject: [PATCH 01/50] add connection_id and related API --- .../iotdevice/private/secure_tunneling_impl.h | 3 +- .../private/secure_tunneling_operations.h | 5 +- include/aws/iotdevice/secure_tunneling.h | 9 ++ source/secure_tunneling.c | 82 ++++++++++++++++++- source/secure_tunneling_operations.c | 46 +++++++++-- source/serializer.c | 28 +++++++ 6 files changed, 162 insertions(+), 11 deletions(-) diff --git a/include/aws/iotdevice/private/secure_tunneling_impl.h b/include/aws/iotdevice/private/secure_tunneling_impl.h index 4cbfd8fb..a1308bb4 100644 --- a/include/aws/iotdevice/private/secure_tunneling_impl.h +++ b/include/aws/iotdevice/private/secure_tunneling_impl.h @@ -122,13 +122,14 @@ struct aws_secure_tunnel_options_storage { /* Stream related info */ int32_t stream_id; - struct aws_hash_table service_ids; + // Steve TODO Add an aws_hash_table for connection id with no service id /* Callbacks */ aws_secure_tunnel_message_received_fn *on_message_received; aws_secure_tunneling_on_connection_complete_fn *on_connection_complete; aws_secure_tunneling_on_connection_shutdown_fn *on_connection_shutdown; + aws_secure_tunneling_on_connection_reset_fn *on_connection_reset; aws_secure_tunneling_on_stream_start_fn *on_stream_start; aws_secure_tunneling_on_stream_reset_fn *on_stream_reset; aws_secure_tunneling_on_session_reset_fn *on_session_reset; diff --git a/include/aws/iotdevice/private/secure_tunneling_operations.h b/include/aws/iotdevice/private/secure_tunneling_operations.h index 6196351d..23470471 100644 --- a/include/aws/iotdevice/private/secure_tunneling_operations.h +++ b/include/aws/iotdevice/private/secure_tunneling_operations.h @@ -23,7 +23,9 @@ enum aws_secure_tunnel_operation_type { AWS_STOT_PING, AWS_STOT_MESSAGE, AWS_STOT_STREAM_RESET, - AWS_STOT_STREAM_START + AWS_STOT_STREAM_START, + AWS_STOT_CONNECTION_START, + AWS_STOT_CONNECTION_RESET, }; struct aws_service_id_element { @@ -39,6 +41,7 @@ struct aws_secure_tunnel_message_storage { bool ignorable; int32_t stream_id; + uint32_t connection_id; struct aws_byte_cursor service_id; struct aws_byte_cursor payload; diff --git a/include/aws/iotdevice/secure_tunneling.h b/include/aws/iotdevice/secure_tunneling.h index d6e8f932..78c3939d 100644 --- a/include/aws/iotdevice/secure_tunneling.h +++ b/include/aws/iotdevice/secure_tunneling.h @@ -86,6 +86,7 @@ struct aws_secure_tunnel_message_view { bool ignorable; int32_t stream_id; + uint32_t connection_id; /** * Secure tunnel multiplexing identifier @@ -119,6 +120,8 @@ typedef void(aws_secure_tunneling_on_connection_complete_fn)( int error_code, void *user_data); typedef void(aws_secure_tunneling_on_connection_shutdown_fn)(int error_code, void *user_data); +typedef void( + aws_secure_tunneling_on_connection_reset_fn)(const struct aws_secure_tunnel_message_view *message, void *user_data); typedef void(aws_secure_tunneling_on_send_data_complete_fn)(int error_code, void *user_data); typedef void(aws_secure_tunneling_on_stream_start_fn)( const struct aws_secure_tunnel_message_view *message, @@ -180,6 +183,7 @@ struct aws_secure_tunnel_options { aws_secure_tunneling_on_connection_complete_fn *on_connection_complete; aws_secure_tunneling_on_connection_shutdown_fn *on_connection_shutdown; aws_secure_tunneling_on_send_data_complete_fn *on_send_data_complete; + aws_secure_tunneling_on_connection_reset_fn *on_connection_reset; aws_secure_tunneling_on_stream_start_fn *on_stream_start; aws_secure_tunneling_on_stream_reset_fn *on_stream_reset; aws_secure_tunneling_on_session_reset_fn *on_session_reset; @@ -277,6 +281,11 @@ int aws_secure_tunnel_stream_start( struct aws_secure_tunnel *secure_tunnel, const struct aws_secure_tunnel_message_view *message_options); +AWS_IOTDEVICE_API +int aws_secure_tunnel_connection_start( + struct aws_secure_tunnel *secure_tunnel, + const struct aws_secure_tunnel_message_view *message_options); + //*********************************************************************************************************************** /* THIS API SHOULD NOT BE USED BY THE CUSTOMER AND SHOULD BE DEPRECATED */ //*********************************************************************************************************************** diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index e0a2eb11..af89f29c 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -32,7 +32,7 @@ #define WEBSOCKET_HEADER_NAME_ACCESS_TOKEN "access-token" #define WEBSOCKET_HEADER_NAME_CLIENT_TOKEN "client-token" #define WEBSOCKET_HEADER_NAME_PROTOCOL "Sec-WebSocket-Protocol" -#define WEBSOCKET_HEADER_PROTOCOL_VALUE "aws.iot.securetunneling-2.0" +#define WEBSOCKET_HEADER_PROTOCOL_VALUE "aws.iot.securetunneling-3.0" static void s_change_current_state(struct aws_secure_tunnel *secure_tunnel, enum aws_secure_tunnel_state next_state); void aws_secure_tunnel_operational_state_clean_up(struct aws_secure_tunnel *secure_tunnel); @@ -1307,6 +1307,11 @@ int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure break; + case AWS_STOT_CONNECTION_START: + case AWS_STOT_CONNECTION_RESET: + // Steve TODO implement these + break; + case AWS_STOT_NONE: break; } @@ -1861,7 +1866,7 @@ int aws_secure_tunnel_stream_start( AWS_PRECONDITION(message_options != NULL); if (secure_tunnel->config->local_proxy_mode == AWS_SECURE_TUNNELING_DESTINATION_MODE) { - AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Stream Start can only be sent from source mode"); + AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Stream Start can only be sent from Source Mode"); return AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INCORRECT_MODE; } @@ -1889,6 +1894,49 @@ int aws_secure_tunnel_stream_start( return AWS_OP_ERR; } +int aws_secure_tunnel_connection_start( + struct aws_secure_tunnel *secure_tunnel, + const struct aws_secure_tunnel_message_view *message_options) { + AWS_PRECONDITION(secure_tunnel != NULL); + AWS_PRECONDITION(message_options != NULL); + + if (secure_tunnel->config->local_proxy_mode == AWS_SECURE_TUNNELING_DESTINATION_MODE) { + AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Connection Start can only be sent from Source Mode"); + return AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INCORRECT_MODE; + } + + 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); + + if (message_op == NULL) { + return AWS_OP_ERR; + } + + AWS_LOGF_DEBUG( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Submitting CONNECTION START operation (%p)", + (void *)secure_tunnel, + (void *)message_op); + + if (s_submit_operation(secure_tunnel, &message_op->base)) { + goto error; + } + + return AWS_OP_SUCCESS; + +error: + aws_secure_tunnel_operation_release(&message_op->base); + return AWS_OP_ERR; +} + +/********************************************************************************************************************* + * Internal Operation Calls + ********************************************************************************************************************/ + +/* + * This is currently exposed by the initial implementation of Secure Tunnel and has been marked as deprecated. + * Should this be called, it will be honored but it should be made private when possible. + */ int aws_secure_tunnel_stream_reset( struct aws_secure_tunnel *secure_tunnel, const struct aws_secure_tunnel_message_view *message_options) { @@ -1918,3 +1966,33 @@ int aws_secure_tunnel_stream_reset( aws_secure_tunnel_operation_release(&message_op->base); return AWS_OP_ERR; } + +int aws_secure_tunnel_connection_reset( + struct aws_secure_tunnel *secure_tunnel, + const struct aws_secure_tunnel_message_view *message_options) { + AWS_PRECONDITION(secure_tunnel != NULL); + AWS_PRECONDITION(message_options != NULL); + + struct aws_secure_tunnel_operation_message *message_op = aws_secure_tunnel_operation_message_new( + secure_tunnel->allocator, secure_tunnel, message_options, AWS_STOT_CONNECTION_RESET); + + if (message_op == NULL) { + return AWS_OP_ERR; + } + + AWS_LOGF_DEBUG( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Submitting STREAM RESET operation (%p)", + (void *)secure_tunnel, + (void *)message_op); + + if (s_submit_operation(secure_tunnel, &message_op->base)) { + goto error; + } + + return AWS_OP_SUCCESS; + +error: + aws_secure_tunnel_operation_release(&message_op->base); + return AWS_OP_ERR; +} diff --git a/source/secure_tunneling_operations.c b/source/secure_tunneling_operations.c index 9931805d..385c6a72 100644 --- a/source/secure_tunneling_operations.c +++ b/source/secure_tunneling_operations.c @@ -14,6 +14,7 @@ #include #define INVALID_STREAM_ID 0 +#define DEFAULT_CONNECTION_ID 1 /* for the hash table, to destroy elements */ static void s_destroy_service_id(void *data) { @@ -163,6 +164,14 @@ void aws_secure_tunnel_message_view_log( (void *)message_view, (int)message_view->stream_id); + AWS_LOGUF( + log_handle, + level, + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: aws_secure_tunnel_message_view connection_id set to %d", + (void *)message_view, + (int)message_view->connection_id); + if (message_view->payload != NULL) { AWS_LOGUF( log_handle, @@ -199,6 +208,7 @@ int aws_secure_tunnel_message_storage_init( storage_view->type = message_options->type; storage_view->ignorable = message_options->ignorable; storage_view->stream_id = message_options->stream_id; + storage_view->connection_id = message_options->connection_id; switch (type) { case AWS_STOT_MESSAGE: @@ -210,6 +220,12 @@ int aws_secure_tunnel_message_storage_init( case AWS_STOT_STREAM_RESET: storage_view->type = AWS_SECURE_TUNNEL_MT_STREAM_RESET; break; + case AWS_STOT_CONNECTION_START: + storage_view->type = AWS_SECURE_TUNNEL_MT_CONNECTION_START; + break; + case AWS_STOT_CONNECTION_RESET: + storage_view->type = AWS_SECURE_TUNNEL_MT_CONNECTION_RESET; + break; default: storage_view->type = AWS_SECURE_TUNNEL_MT_UNKNOWN; break; @@ -238,6 +254,8 @@ void aws_secure_tunnel_message_storage_clean_up(struct aws_secure_tunnel_message aws_byte_buf_clean_up(&message_storage->storage); } +// Steve TODO assign connection id here. We will use the one that's been provided by the SendMessage() function +// but if it's missing we will default to 1. /* Sets the stream id on outbound message based on the service id (or lack of for V1) to the current one being used. */ static int s_aws_secure_tunnel_operation_message_assign_stream_id( struct aws_secure_tunnel_operation *operation, @@ -259,10 +277,16 @@ static int s_aws_secure_tunnel_operation_message_assign_stream_id( AWS_BYTE_CURSOR_PRI(*message_view->service_id)); stream_id = INVALID_STREAM_ID; } else { + // Steve TODO check against hash table of currently active connection ids + // to see if the user set connection id is active. If it isn't, log a warning and fail + // the message being attempted. struct aws_service_id_element *service_id_elem = elem->value; stream_id = service_id_elem->stream_id; } } else { + // Steve TODO check against hash table of currently active connection ids + // to see if the user set connection id is active. If it isn't, log a warning and fail + // the message being attempted. stream_id = secure_tunnel->config->stream_id; } @@ -306,10 +330,24 @@ static int s_aws_secure_tunnel_operation_message_set_next_stream_id( aws_service_id_element_new(secure_tunnel->allocator, message_view->service_id, stream_id); aws_hash_table_put( &secure_tunnel->config->service_ids, &replacement_elem->service_id_cur, replacement_elem, NULL); + + AWS_LOGF_INFO( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Secure tunnel service_id '" PRInSTR "' stream_id set to %d", + (void *)secure_tunnel, + AWS_BYTE_CURSOR_PRI(*message_view->service_id), + stream_id); } } else { stream_id = secure_tunnel->config->stream_id + 1; secure_tunnel->config->stream_id = stream_id; + + AWS_LOGF_INFO( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Secure tunnel stream_id set to %d", + (void *)secure_tunnel, + AWS_BYTE_CURSOR_PRI(*message_view->service_id), + stream_id); } if (stream_id == INVALID_STREAM_ID) { @@ -318,13 +356,6 @@ static int s_aws_secure_tunnel_operation_message_set_next_stream_id( message_op->options_storage.storage_view.stream_id = stream_id; - AWS_LOGF_INFO( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: Secure tunnel service_id '" PRInSTR "' stream_id set to %d", - (void *)secure_tunnel, - AWS_BYTE_CURSOR_PRI(*message_view->service_id), - stream_id); - return AWS_OP_SUCCESS; } @@ -643,6 +674,7 @@ struct aws_secure_tunnel_options_storage *aws_secure_tunnel_options_storage_new( storage->local_proxy_mode = options->local_proxy_mode; storage->on_connection_complete = options->on_connection_complete; storage->on_connection_shutdown = options->on_connection_shutdown; + storage->on_connection_reset = options->on_connection_reset; storage->on_send_data_complete = options->on_send_data_complete; storage->on_stream_start = options->on_stream_start; storage->on_stream_reset = options->on_stream_reset; diff --git a/source/serializer.c b/source/serializer.c index 565178a3..72ebbaab 100644 --- a/source/serializer.c +++ b/source/serializer.c @@ -92,6 +92,10 @@ static int s_iot_st_encode_stream_id(int32_t data, struct aws_byte_buf *buffer) return s_iot_st_encode_varint(AWS_SECURE_TUNNEL_FN_STREAM_ID, AWS_SECURE_TUNNEL_PBWT_VARINT, data, buffer); } +static int s_iot_st_encode_connection_id(uint32_t data, struct aws_byte_buf *buffer) { + return s_iot_st_encode_varint(AWS_SECURE_TUNNEL_FN_CONNECTION_ID, AWS_SECURE_TUNNEL_PBWT_VARINT, data, buffer); +} + static int s_iot_st_encode_ignorable(int32_t data, struct aws_byte_buf *buffer) { return s_iot_st_encode_varint(AWS_SECURE_TUNNEL_FN_IGNORABLE, AWS_SECURE_TUNNEL_PBWT_VARINT, data, buffer); } @@ -158,6 +162,21 @@ static int s_iot_st_compute_message_length( local_length += (1 + stream_id_length); } + if (message->connection_id != 0) { + /* + * 1 byte connection_id key + * 1-4 byte connection_id varint + */ + + size_t connection_id_length = 0; + + if (s_iot_st_get_varint_size(message->connection_id, &connection_id_length)) { + return AWS_OP_ERR; + } + + local_length += (1 + connection_id_length); + } + if (message->ignorable != 0) { /* * 1 byte ignorable key @@ -256,6 +275,12 @@ int aws_iot_st_msg_serialize_from_view( } } + if (message_view->connection_id != 0) { + if (s_iot_st_encode_connection_id(message_view->connection_id, buffer)) { + goto cleanup; + } + } + if (message_view->ignorable != 0) { if (s_iot_st_encode_ignorable(message_view->ignorable, buffer)) { goto cleanup; @@ -349,6 +374,9 @@ int aws_secure_tunnel_deserialize_varint_from_cursor_to_message( case AWS_SECURE_TUNNEL_FN_IGNORABLE: message->ignorable = result; break; + case AWS_SECURE_TUNNEL_FN_CONNECTION_ID: + message->connection_id = result; + break; default: AWS_LOGF_WARN( AWS_LS_IOTDEVICE_SECURE_TUNNELING, From 8677550e3144380c2185e1f329e45bd9c9de09e5 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 8 Mar 2023 14:07:05 -0800 Subject: [PATCH 02/50] connection ids wip --- .../iotdevice/private/secure_tunneling_impl.h | 3 +- include/aws/iotdevice/private/serializer.h | 2 +- include/aws/iotdevice/secure_tunneling.h | 14 +++++-- source/secure_tunneling.c | 38 ++++++++++++++++++- source/secure_tunneling_operations.c | 12 +++--- source/serializer.c | 6 --- 6 files changed, 55 insertions(+), 20 deletions(-) diff --git a/include/aws/iotdevice/private/secure_tunneling_impl.h b/include/aws/iotdevice/private/secure_tunneling_impl.h index a1308bb4..08c03687 100644 --- a/include/aws/iotdevice/private/secure_tunneling_impl.h +++ b/include/aws/iotdevice/private/secure_tunneling_impl.h @@ -129,9 +129,10 @@ struct aws_secure_tunnel_options_storage { aws_secure_tunnel_message_received_fn *on_message_received; aws_secure_tunneling_on_connection_complete_fn *on_connection_complete; aws_secure_tunneling_on_connection_shutdown_fn *on_connection_shutdown; - aws_secure_tunneling_on_connection_reset_fn *on_connection_reset; aws_secure_tunneling_on_stream_start_fn *on_stream_start; aws_secure_tunneling_on_stream_reset_fn *on_stream_reset; + aws_secure_tunneling_on_connection_start_fn *on_connection_start; + aws_secure_tunneling_on_connection_reset_fn *on_connection_reset; aws_secure_tunneling_on_session_reset_fn *on_session_reset; aws_secure_tunneling_on_stopped_fn *on_stopped; diff --git a/include/aws/iotdevice/private/serializer.h b/include/aws/iotdevice/private/serializer.h index 306f800c..5ca98da8 100644 --- a/include/aws/iotdevice/private/serializer.h +++ b/include/aws/iotdevice/private/serializer.h @@ -17,7 +17,7 @@ #define AWS_IOT_ST_MAXIMUM_1_BYTE_VARINT_VALUE 128 #define AWS_IOT_ST_MAXIMUM_2_BYTE_VARINT_VALUE 16384 #define AWS_IOT_ST_MAXIMUM_3_BYTE_VARINT_VALUE 2097152 -#define AWS_IOT_ST_MAX_MESSAGE_SIZE (64 * 1024) +#define AWS_IOT_ST_MAX_PAYLOAD_SIZE (63 * 1024) enum aws_secure_tunnel_field_number { AWS_SECURE_TUNNEL_FN_TYPE = 1, diff --git a/include/aws/iotdevice/secure_tunneling.h b/include/aws/iotdevice/secure_tunneling.h index 78c3939d..50384e27 100644 --- a/include/aws/iotdevice/secure_tunneling.h +++ b/include/aws/iotdevice/secure_tunneling.h @@ -114,14 +114,11 @@ struct aws_secure_tunnel_connection_view { */ typedef void( aws_secure_tunnel_message_received_fn)(const struct aws_secure_tunnel_message_view *message, void *user_data); - typedef void(aws_secure_tunneling_on_connection_complete_fn)( const struct aws_secure_tunnel_connection_view *connection_view, int error_code, void *user_data); typedef void(aws_secure_tunneling_on_connection_shutdown_fn)(int error_code, void *user_data); -typedef void( - aws_secure_tunneling_on_connection_reset_fn)(const struct aws_secure_tunnel_message_view *message, void *user_data); typedef void(aws_secure_tunneling_on_send_data_complete_fn)(int error_code, void *user_data); typedef void(aws_secure_tunneling_on_stream_start_fn)( const struct aws_secure_tunnel_message_view *message, @@ -131,6 +128,14 @@ typedef void(aws_secure_tunneling_on_stream_reset_fn)( const struct aws_secure_tunnel_message_view *message, int error_code, void *user_data); +typedef void(aws_secure_tunneling_on_connection_start_fn)( + const struct aws_secure_tunnel_message_view *message, + int error_code, + void *user_data); +typedef void(aws_secure_tunneling_on_connection_reset_fn)( + const struct aws_secure_tunnel_message_view *message, + int error_code, + void *user_data); typedef void(aws_secure_tunneling_on_session_reset_fn)(void *user_data); typedef void(aws_secure_tunneling_on_stopped_fn)(void *user_data); typedef void(aws_secure_tunneling_on_termination_complete_fn)(void *user_data); @@ -183,9 +188,10 @@ struct aws_secure_tunnel_options { aws_secure_tunneling_on_connection_complete_fn *on_connection_complete; aws_secure_tunneling_on_connection_shutdown_fn *on_connection_shutdown; aws_secure_tunneling_on_send_data_complete_fn *on_send_data_complete; - aws_secure_tunneling_on_connection_reset_fn *on_connection_reset; aws_secure_tunneling_on_stream_start_fn *on_stream_start; aws_secure_tunneling_on_stream_reset_fn *on_stream_reset; + aws_secure_tunneling_on_connection_start_fn *on_connection_start; + aws_secure_tunneling_on_connection_reset_fn *on_connection_reset; aws_secure_tunneling_on_session_reset_fn *on_session_reset; aws_secure_tunneling_on_stopped_fn *on_stopped; diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index af89f29c..5d46e483 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -291,6 +291,38 @@ static void s_aws_secure_tunnel_on_service_ids_received( } } +static int s_aws_secure_tunnel_set_connection_id( + struct aws_secure_tunnel *secure_tunnel, + const struct aws_byte_cursor *service_id, + int32_t stream_id, + uint32_t connection_id) { + (void)secure_tunnel; + (void)service_id; + (void)stream_id; + (void)connection_id; + // Steve TODO + return AWS_OP_SUCCESS; +} + +static void s_aws_secure_tunnel_on_connection_start_received( + struct aws_secure_tunnel *secure_tunnel, + struct aws_secure_tunnel_message_view *message_view) { + int result = s_aws_secure_tunnel_set_connection_id( + secure_tunnel, message_view->service_id, message_view->stream_id, message_view->connection_id); + + if (secure_tunnel->config->on_connection_start) { + secure_tunnel->config->on_connection_start(message_view, result, secure_tunnel->config->user_data); + } +} + +static void s_aws_secure_tunnel_on_connection_reset_received( + struct aws_secure_tunnel *secure_tunnel, + struct aws_secure_tunnel_message_view *message_view) { + (void)secure_tunnel; + (void)message_view; + // Steve TODO +} + static void s_aws_secure_tunnel_connected_on_message_received( struct aws_secure_tunnel *secure_tunnel, struct aws_secure_tunnel_message_view *message_view) { @@ -314,7 +346,11 @@ static void s_aws_secure_tunnel_connected_on_message_received( s_aws_secure_tunnel_on_service_ids_received(secure_tunnel, message_view); break; case AWS_SECURE_TUNNEL_MT_CONNECTION_START: + s_aws_secure_tunnel_on_connection_start_received(secure_tunnel, message_view); + break; case AWS_SECURE_TUNNEL_MT_CONNECTION_RESET: + s_aws_secure_tunnel_on_connection_reset_received(secure_tunnel, message_view); + break; case AWS_SECURE_TUNNEL_MT_UNKNOWN: default: if (!message_view->ignorable) { @@ -401,7 +437,7 @@ static bool secure_tunneling_websocket_stream_outgoing_payload( } if (pair->length_prefix_written == true) { - pair->cur = aws_byte_buf_write_to_capacity(out_buf, &pair->cur); + aws_byte_buf_write_to_capacity(out_buf, &pair->cur); } return true; diff --git a/source/secure_tunneling_operations.c b/source/secure_tunneling_operations.c index 385c6a72..582dc08b 100644 --- a/source/secure_tunneling_operations.c +++ b/source/secure_tunneling_operations.c @@ -107,15 +107,15 @@ int aws_secure_tunnel_message_view_validate(const struct aws_secure_tunnel_messa if (message_view->type == AWS_SECURE_TUNNEL_MT_DATA && message_view->stream_id != 0) { AWS_LOGF_ERROR( AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: aws_secure_tunnel_message_view stream id for DATA MESSAGES must be 0", + "id=%p: aws_secure_tunnel_message_view - stream id for DATA MESSAGES must be 0", (void *)message_view); return aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_OPTIONS_VALIDATION); } - if (message_view->payload != NULL && message_view->payload->len > AWS_IOT_ST_MAX_MESSAGE_SIZE) { + if (message_view->payload != NULL && message_view->payload->len > AWS_IOT_ST_MAX_PAYLOAD_SIZE) { AWS_LOGF_ERROR( AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: aws_secure_tunnel_message_view - payload too long", + "id=%p: aws_secure_tunnel_message_view - payload too large", (void *)message_view); return aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_OPTIONS_VALIDATION); } @@ -678,6 +678,8 @@ struct aws_secure_tunnel_options_storage *aws_secure_tunnel_options_storage_new( storage->on_send_data_complete = options->on_send_data_complete; storage->on_stream_start = options->on_stream_start; storage->on_stream_reset = options->on_stream_reset; + storage->on_connection_start = options->on_connection_start; + storage->on_connection_reset = options->on_connection_reset; storage->on_session_reset = options->on_session_reset; storage->on_stopped = options->on_stopped; storage->on_termination_complete = options->on_termination_complete; @@ -721,10 +723,6 @@ struct data_tunnel_pair *aws_secure_tunnel_data_tunnel_pair_new( AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Failure serializing message"); goto error; } - if (pair->buf.len > AWS_IOT_ST_MAX_MESSAGE_SIZE) { - AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Message size greater than AWS_IOT_ST_MAX_MESSAGE_SIZE"); - goto error; - } pair->cur = aws_byte_cursor_from_buf(&pair->buf); diff --git a/source/serializer.c b/source/serializer.c index 72ebbaab..3e16dd26 100644 --- a/source/serializer.c +++ b/source/serializer.c @@ -315,11 +315,6 @@ int aws_iot_st_msg_serialize_from_view( } } - if (buffer->capacity > AWS_IOT_ST_MAX_MESSAGE_SIZE) { - aws_raise_error(AWS_ERROR_INVALID_BUFFER_SIZE); - goto cleanup; - } - return AWS_OP_SUCCESS; cleanup: @@ -400,7 +395,6 @@ int aws_secure_tunnel_deserialize_message_from_cursor( (void *)secure_tunnel, cursor->len); - AWS_RETURN_ERROR_IF2(cursor->len < AWS_IOT_ST_MAX_MESSAGE_SIZE, AWS_ERROR_INVALID_BUFFER_SIZE); uint8_t wire_type; uint8_t field_number; struct aws_secure_tunnel_message_view message_view; From 720d26192671716c5c1cd54380b41ab6b76011f1 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 8 Mar 2023 14:11:19 -0800 Subject: [PATCH 03/50] removed duplicate assignment --- source/secure_tunneling_operations.c | 1 - 1 file changed, 1 deletion(-) diff --git a/source/secure_tunneling_operations.c b/source/secure_tunneling_operations.c index 582dc08b..61b212d1 100644 --- a/source/secure_tunneling_operations.c +++ b/source/secure_tunneling_operations.c @@ -674,7 +674,6 @@ struct aws_secure_tunnel_options_storage *aws_secure_tunnel_options_storage_new( storage->local_proxy_mode = options->local_proxy_mode; storage->on_connection_complete = options->on_connection_complete; storage->on_connection_shutdown = options->on_connection_shutdown; - storage->on_connection_reset = options->on_connection_reset; storage->on_send_data_complete = options->on_send_data_complete; storage->on_stream_start = options->on_stream_start; storage->on_stream_reset = options->on_stream_reset; From b7200c19c4c9b7dd433a32595a98168d2ab1c753 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 15 Mar 2023 09:57:22 -0700 Subject: [PATCH 04/50] Connection id related to existing API --- include/aws/iotdevice/iotdevice.h | 4 +- .../iotdevice/private/secure_tunneling_impl.h | 4 +- .../private/secure_tunneling_operations.h | 11 + source/iotdevice.c | 8 +- source/secure_tunneling.c | 339 ++++++++++++++---- source/secure_tunneling_operations.c | 260 ++++++++++---- 6 files changed, 499 insertions(+), 127 deletions(-) diff --git a/include/aws/iotdevice/iotdevice.h b/include/aws/iotdevice/iotdevice.h index 1f18507c..93cf545f 100644 --- a/include/aws/iotdevice/iotdevice.h +++ b/include/aws/iotdevice/iotdevice.h @@ -21,7 +21,9 @@ enum aws_iotdevice_error { AWS_ERROR_IOTDEVICE_DEFENDER_PUBLISH_FAILURE, AWS_ERROR_IOTDEVICE_DEFENDER_UNKNOWN_TASK_STATUS, - AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_STREAM, + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_STREAM_ID, + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_CONNECTION_ID, + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_SERVICE_ID, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INCORRECT_MODE, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_BAD_SERVICE_ID, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_OPTIONS_VALIDATION, diff --git a/include/aws/iotdevice/private/secure_tunneling_impl.h b/include/aws/iotdevice/private/secure_tunneling_impl.h index 08c03687..8d6474f7 100644 --- a/include/aws/iotdevice/private/secure_tunneling_impl.h +++ b/include/aws/iotdevice/private/secure_tunneling_impl.h @@ -120,10 +120,12 @@ struct aws_secure_tunnel_options_storage { struct aws_string *endpoint_host; + uint8_t protocol_version; + /* Stream related info */ int32_t stream_id; struct aws_hash_table service_ids; - // Steve TODO Add an aws_hash_table for connection id with no service id + struct aws_hash_table connection_ids; /* Callbacks */ aws_secure_tunnel_message_received_fn *on_message_received; diff --git a/include/aws/iotdevice/private/secure_tunneling_operations.h b/include/aws/iotdevice/private/secure_tunneling_operations.h index 23470471..022baec3 100644 --- a/include/aws/iotdevice/private/secure_tunneling_operations.h +++ b/include/aws/iotdevice/private/secure_tunneling_operations.h @@ -33,6 +33,12 @@ struct aws_service_id_element { struct aws_byte_cursor service_id_cur; struct aws_string *service_id_string; int32_t stream_id; + struct aws_hash_table connection_ids; +}; + +struct aws_connection_id_element { + struct aws_allocator *allocator; + uint32_t connection_id; }; struct aws_secure_tunnel_message_storage { @@ -200,6 +206,11 @@ struct aws_service_id_element *aws_service_id_element_new( const struct aws_byte_cursor *service_id, int32_t stream_id); +AWS_IOTDEVICE_API +struct aws_connection_id_element *aws_connection_id_element_new( + struct aws_allocator *allocator, + uint32_t connection_id); + AWS_EXTERN_C_END #endif /* AWS_IOTDEVICE_SECURE_TUNNELING_OPERATION_H */ diff --git a/source/iotdevice.c b/source/iotdevice.c index 28e5475d..6f1f7587 100644 --- a/source/iotdevice.c +++ b/source/iotdevice.c @@ -40,8 +40,14 @@ static struct aws_error_info s_errors[] = { "Device defender task was invoked with an unknown task status."), AWS_DEFINE_ERROR_INFO_IOTDEVICE( - AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_STREAM, + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_STREAM_ID, "Secure Tunnel invalid stream id."), + AWS_DEFINE_ERROR_INFO_IOTDEVICE( + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_CONNECTION_ID, + "Secure Tunnel invalid connection id."), + AWS_DEFINE_ERROR_INFO_IOTDEVICE( + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_SERVICE_ID, + "Secure Tunnel invalid service id."), AWS_DEFINE_ERROR_INFO_IOTDEVICE( AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INCORRECT_MODE, "Secure Tunnel stream cannot be started while in Destination Mode."), diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 5d46e483..d436f4fc 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -43,11 +43,6 @@ static void s_complete_operation_list( struct aws_secure_tunnel *secure_tunnel, struct aws_linked_list *operation_list, int error_code); - -static int s_secure_tunneling_send( - struct aws_secure_tunnel *secure_tunnel, - const struct aws_secure_tunnel_message_view *message_view); - static void s_reevaluate_service_task(struct aws_secure_tunnel *secure_tunnel); const char *aws_secure_tunnel_state_to_c_string(enum aws_secure_tunnel_state state) { @@ -89,6 +84,7 @@ static int s_reset_service_id(void *context, struct aws_hash_element *p_element) (void)context; struct aws_service_id_element *service_id_elem = p_element->value; service_id_elem->stream_id = INVALID_STREAM_ID; + aws_hash_table_clear(&service_id_elem->connection_ids); return AWS_COMMON_HASH_TABLE_ITER_CONTINUE; } @@ -141,28 +137,57 @@ static void s_on_secure_tunnel_zero_ref_count(void *user_data) { static void s_reset_secure_tunnel(struct aws_secure_tunnel *secure_tunnel) { AWS_LOGF_INFO(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: Secure tunnel session reset.", (void *)secure_tunnel); + secure_tunnel->config->protocol_version = 0; secure_tunnel->config->stream_id = INVALID_STREAM_ID; + aws_hash_table_clear(&secure_tunnel->config->connection_ids); aws_hash_table_foreach(&secure_tunnel->config->service_ids, s_reset_service_id, NULL); secure_tunnel->received_data.len = 0; /* Drop any incomplete secure tunnel frame */ } +static uint8_t s_aws_secure_tunnel_message_min_protocol_check(const struct aws_secure_tunnel_message_view *message) { + uint8_t version = 1; + + if (message->service_id != NULL && message->service_id->len > 0) { + version = 2; + } + + if (message->connection_id > 0) { + version = 3; + } + + return version; +} + +static bool s_aws_secure_tunnel_protocol_version_check( + struct aws_secure_tunnel *secure_tunnel, + struct aws_secure_tunnel_message_view *message) { + uint8_t message_protocol_version = s_aws_secure_tunnel_message_min_protocol_check(message); + if (secure_tunnel->config->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", + (void *)secure_tunnel, + secure_tunnel->config->protocol_version, + message_protocol_version); + return false; + } + return true; +} + static bool s_aws_secure_tunnel_stream_id_check_match( struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *service_id, int32_t stream_id) { - /* No service id means V1 protocol is being used */ - if (service_id->len == 0) { + /* + * No service id means either V1 protocol is being used or V3 protocol is being used on a tunnel without service ids + */ + if (service_id == NULL || service_id->len == 0) { return (secure_tunnel->config->stream_id == stream_id); } struct aws_hash_element *elem = NULL; aws_hash_table_find(&secure_tunnel->config->service_ids, service_id, &elem); if (elem == NULL) { - AWS_LOGF_WARN( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: Secure tunnel stream id check request for unsupported service_id: " PRInSTR, - (void *)secure_tunnel, - AWS_BYTE_CURSOR_PRI(*service_id)); return false; } @@ -170,18 +195,105 @@ static bool s_aws_secure_tunnel_stream_id_check_match( return (stream_id == service_id_elem->stream_id); } +static bool s_aws_secure_tunnel_active_stream_check( + struct aws_secure_tunnel *secure_tunnel, + const struct aws_secure_tunnel_message_view *message) { + /* + * No service id means either V1 protocol is being used or V3 protocol is being used on a tunnel without service ids + */ + if (message->service_id == NULL || message->service_id->len == 0) { + if (secure_tunnel->config->stream_id != message->stream_id) { + return false; + } + + /* + * V1 and V2 connection id will always be 1. V3 can be any number > 0. Either way, connection id will be checked + * against stored connection_ids to confirm the stream is active. + */ + struct aws_hash_element *connection_id_elem = NULL; + aws_hash_table_find(&secure_tunnel->config->connection_ids, &message->connection_id, &connection_id_elem); + if (connection_id_elem == NULL) { + aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_CONNECTION_ID); + return false; + } + return true; + } + + /* Check if service id is being used by the secure tunnel */ + struct aws_hash_element *elem = NULL; + aws_hash_table_find(&secure_tunnel->config->service_ids, message->service_id, &elem); + if (elem == NULL) { + aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_SERVICE_ID); + return false; + } + + /* Check if the stream id is the currently active one */ + struct aws_service_id_element *service_id_elem = elem->value; + if (message->stream_id != service_id_elem->stream_id) { + aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_STREAM_ID); + return false; + } + + /* + * V2 connection id will always be 1. V3 can be any number > 0. Either way, connection id will be checked against + * stored connection_ids for the service id to confirm the stream is active + */ + struct aws_hash_element *connection_id_elem = NULL; + aws_hash_table_find(&service_id_elem->connection_ids, &message->connection_id, &connection_id_elem); + if (connection_id_elem == NULL) { + aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_CONNECTION_ID); + return false; + } + return true; +} + +static void s_aws_secure_tunnel_on_data_received( + struct aws_secure_tunnel *secure_tunnel, + struct aws_secure_tunnel_message_view *message_view) { + if (s_aws_secure_tunnel_active_stream_check(secure_tunnel, message_view)) { + if (secure_tunnel->config->on_message_received) { + secure_tunnel->config->on_message_received(message_view, secure_tunnel->config->user_data); + } + } else { + if (message_view->service_id->len > 0) { + AWS_LOGF_INFO( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Incomming DATA message on inactive stream with service id '" PRInSTR + "' stream id (%d) connection id (%d) ignored", + (void *)secure_tunnel, + AWS_BYTE_CURSOR_PRI(*message_view->service_id), + message_view->stream_id, + message_view->connection_id); + } else { + AWS_LOGF_INFO( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Incomming DATA message on inactive stream with stream id (%d) connection id (%d) ignored", + (void *)secure_tunnel, + message_view->stream_id, + message_view->connection_id); + } + } +} + static int s_aws_secure_tunnel_set_stream_id( struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *service_id, - int32_t stream_id) { + int32_t stream_id, + uint32_t connection_id) { /* No service id means V1 protocol is being used */ if (service_id == NULL || service_id->len == 0) { secure_tunnel->config->stream_id = stream_id; + aws_hash_table_clear(&secure_tunnel->config->connection_ids); + struct aws_connection_id_element *connection_id_elem = + aws_connection_id_element_new(secure_tunnel->allocator, connection_id); + aws_hash_table_put( + &secure_tunnel->config->connection_ids, &connection_id_elem->connection_id, connection_id_elem, NULL); AWS_LOGF_INFO( AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: Secure tunnel stream_id set to %d", + "id=%p: Secure tunnel set to stream id (%d) with active connection id(%d)", (void *)secure_tunnel, - stream_id); + stream_id, + connection_id); return AWS_OP_SUCCESS; } @@ -190,7 +302,7 @@ static int s_aws_secure_tunnel_set_stream_id( if (elem == NULL) { AWS_LOGF_WARN( AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: Secure tunnel request for unsupported service_id: " PRInSTR, + "id=%p: Incomming STREAM START request for unsupported service_id: " PRInSTR, (void *)secure_tunnel, AWS_BYTE_CURSOR_PRI(*service_id)); return AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_BAD_SERVICE_ID; @@ -199,13 +311,19 @@ static int s_aws_secure_tunnel_set_stream_id( struct aws_service_id_element *replacement_elem = aws_service_id_element_new(secure_tunnel->allocator, service_id, stream_id); + struct aws_connection_id_element *connection_id_elem = + aws_connection_id_element_new(secure_tunnel->allocator, connection_id); + + aws_hash_table_put(&replacement_elem->connection_ids, &connection_id_elem->connection_id, connection_id_elem, NULL); aws_hash_table_put(&secure_tunnel->config->service_ids, &replacement_elem->service_id_cur, replacement_elem, NULL); + AWS_LOGF_INFO( AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: Secure tunnel service_id '" PRInSTR "' stream_id set to %d", + "id=%p: Secure Tunnel service id '" PRInSTR "' set to stream id (%d) with active connection_id (%d)", (void *)secure_tunnel, AWS_BYTE_CURSOR_PRI(*service_id), - stream_id); + stream_id, + connection_id); return AWS_OP_SUCCESS; } @@ -213,7 +331,40 @@ static int s_aws_secure_tunnel_set_stream_id( static void s_aws_secure_tunnel_on_stream_start_received( struct aws_secure_tunnel *secure_tunnel, struct aws_secure_tunnel_message_view *message_view) { - int result = s_aws_secure_tunnel_set_stream_id(secure_tunnel, message_view->service_id, message_view->stream_id); + /* + * If a protocol version hasn't been established yet, the first STREAM START determines the protocol version being + * used this session + */ + if (secure_tunnel->config->protocol_version == 0) { + uint8_t message_protocol_version = s_aws_secure_tunnel_message_min_protocol_check(message_view); + secure_tunnel->config->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 received STREAM START", + (void *)secure_tunnel, + secure_tunnel->config->protocol_version); + } + + if (s_aws_secure_tunnel_protocol_version_check(secure_tunnel, message_view)) { + AWS_LOGF_WARN( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Secure Tunnel will be reset due to Protocol version switching by Source device", + (void *)secure_tunnel); + // Steve Todo Reset the connection + } + + /* + * An absent connection ID will result in connection id being set to 1. The connection is considered a V1 connection + * at this point and the future existance of a connection ID will result in a full reset of the client as mixed + * protocol versions is not supported. + */ + if (message_view->connection_id == 0) { + message_view->connection_id = 1; + } + + int result = s_aws_secure_tunnel_set_stream_id( + secure_tunnel, message_view->service_id, message_view->stream_id, message_view->connection_id); + if (secure_tunnel->config->on_stream_start) { secure_tunnel->config->on_stream_start(message_view, result, secure_tunnel->config->user_data); } @@ -223,11 +374,34 @@ static void s_aws_secure_tunnel_on_stream_reset_received( struct aws_secure_tunnel *secure_tunnel, struct aws_secure_tunnel_message_view *message_view) { int result = AWS_OP_SUCCESS; - if (s_aws_secure_tunnel_stream_id_check_match(secure_tunnel, message_view->service_id, message_view->stream_id)) { - result = s_aws_secure_tunnel_set_stream_id(secure_tunnel, message_view->service_id, INVALID_STREAM_ID); + if (s_aws_secure_tunnel_protocol_version_check(secure_tunnel, message_view)) { + AWS_LOGF_WARN( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Secure Tunnel will be reset due to Protocol version switching by Source device", + (void *)secure_tunnel); + // Steve Todo Reset the connection } - if (secure_tunnel->config->on_stream_reset) { - secure_tunnel->config->on_stream_reset(message_view, result, secure_tunnel->config->user_data); + + if (s_aws_secure_tunnel_stream_id_check_match(secure_tunnel, message_view->service_id, message_view->stream_id)) { + result = s_aws_secure_tunnel_set_stream_id(secure_tunnel, message_view->service_id, INVALID_STREAM_ID, 0); + if (secure_tunnel->config->on_stream_reset) { + secure_tunnel->config->on_stream_reset(message_view, result, secure_tunnel->config->user_data); + } + } else { + if (message_view->service_id->len > 0) { + AWS_LOGF_INFO( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Incomming STREAM RESET on inactive stream with service id '" PRInSTR "' stream id (%d) ignored", + (void *)secure_tunnel, + AWS_BYTE_CURSOR_PRI(*message_view->service_id), + message_view->stream_id); + } else { + AWS_LOGF_INFO( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Incomming STREAM RESET on inactive stream with stream id (%d) ignored", + (void *)secure_tunnel, + message_view->stream_id); + } } } @@ -300,13 +474,17 @@ static int s_aws_secure_tunnel_set_connection_id( (void)service_id; (void)stream_id; (void)connection_id; - // Steve TODO + // Steve TODO Logic for Connection Start return AWS_OP_SUCCESS; } static void s_aws_secure_tunnel_on_connection_start_received( struct aws_secure_tunnel *secure_tunnel, struct aws_secure_tunnel_message_view *message_view) { + // Steve TODO Logic for Connection Start + if (message_view->connection_id == 0) { + message_view->connection_id = 1; + } int result = s_aws_secure_tunnel_set_connection_id( secure_tunnel, message_view->service_id, message_view->stream_id, message_view->connection_id); @@ -319,8 +497,10 @@ static void s_aws_secure_tunnel_on_connection_reset_received( struct aws_secure_tunnel *secure_tunnel, struct aws_secure_tunnel_message_view *message_view) { (void)secure_tunnel; - (void)message_view; - // Steve TODO + if (message_view->connection_id == 0) { + message_view->connection_id = 1; + } + // Steve TODO Logic for Connection Reset } static void s_aws_secure_tunnel_connected_on_message_received( @@ -329,9 +509,7 @@ static void s_aws_secure_tunnel_connected_on_message_received( aws_secure_tunnel_message_view_log(message_view, AWS_LL_DEBUG); switch (message_view->type) { case AWS_SECURE_TUNNEL_MT_DATA: - if (secure_tunnel->config->on_message_received) { - secure_tunnel->config->on_message_received(message_view, secure_tunnel->config->user_data); - } + s_aws_secure_tunnel_on_data_received(secure_tunnel, message_view); break; case AWS_SECURE_TUNNEL_MT_STREAM_START: s_aws_secure_tunnel_on_stream_start_received(secure_tunnel, message_view); @@ -1267,35 +1445,52 @@ int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure error_code = aws_last_error(); - if (current_operation->message_view->service_id) { - AWS_LOGF_DEBUG( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: failed to assign service id '" PRInSTR - "' DATA message a stream id with error %d(%s)", - (void *)secure_tunnel, - AWS_BYTE_CURSOR_PRI(*current_operation->message_view->service_id), - error_code, - aws_error_debug_str(error_code)); - } else { - AWS_LOGF_DEBUG( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: failed to assign V1 DATA message a stream id with error %d(%s)", - (void *)secure_tunnel, - error_code, - aws_error_debug_str(error_code)); + if (secure_tunnel->config->on_send_data_complete) { + secure_tunnel->config->on_send_data_complete(error_code, secure_tunnel->config->user_data); } } else { - /* Send the Data message through the WebSocket */ - if (s_secure_tunneling_send(secure_tunnel, current_operation->message_view)) { + 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(); - 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)); + if (secure_tunnel->config->on_send_data_complete) { + secure_tunnel->config->on_send_data_complete(error_code, secure_tunnel->config->user_data); + } + 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)); + } } - aws_secure_tunnel_message_view_log(current_operation->message_view, AWS_LL_DEBUG); } break; @@ -1323,20 +1518,38 @@ int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure if ((*current_operation->vtable->aws_secure_tunnel_operation_assign_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 RESET message with error %d(%s)", - (void *)secure_tunnel, - error_code, - aws_error_debug_str(error_code)); + + if (current_operation->message_view->service_id == NULL || + current_operation->message_view->service_id->len == 0) { + AWS_LOGF_DEBUG( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: failed to assign outbound V1 STREAM RESET message a stream id with error " + "%d(%s)", + (void *)secure_tunnel, + error_code, + aws_error_debug_str(error_code)); + } else { + AWS_LOGF_DEBUG( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: failed to assign outbound STREAM RESET message with service id '" PRInSTR + "' a stream id with error %d(%s)", + (void *)secure_tunnel, + AWS_BYTE_CURSOR_PRI(*current_operation->message_view->service_id), + error_code, + aws_error_debug_str(error_code)); + } } else { /* Send the Stream Reset message through the WebSocket */ if (s_secure_tunneling_send(secure_tunnel, current_operation->message_view)) { error_code = aws_last_error(); } else { s_aws_secure_tunnel_set_stream_id( - secure_tunnel, current_operation->message_view->service_id, INVALID_STREAM_ID); + secure_tunnel, + current_operation->message_view->service_id, + INVALID_STREAM_ID, + current_operation->message_view->connection_id); } aws_secure_tunnel_message_view_log(current_operation->message_view, AWS_LL_DEBUG); } @@ -1344,8 +1557,10 @@ int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure break; case AWS_STOT_CONNECTION_START: + // Steve Todo Logic for Connection Start + break; case AWS_STOT_CONNECTION_RESET: - // Steve TODO implement these + // Steve TODO Logic for Connection Reset break; case AWS_STOT_NONE: diff --git a/source/secure_tunneling_operations.c b/source/secure_tunneling_operations.c index 61b212d1..f375b90d 100644 --- a/source/secure_tunneling_operations.c +++ b/source/secure_tunneling_operations.c @@ -16,9 +16,44 @@ #define INVALID_STREAM_ID 0 #define DEFAULT_CONNECTION_ID 1 +static const uint32_t s_bit_scrambling_magic = 0x45d9f3bU; +static const uint32_t s_bit_shift_magic = 16U; + +/* this is a repurposed hash function based on the technique in splitmix64. The magic number was a result of numerical + * analysis on maximum bit entropy. */ +uint64_t aws_secure_tunnel_hash_connection_id(const void *to_hash) { + uint32_t int_to_hash = *(const uint32_t *)to_hash; + uint32_t hash = ((int_to_hash >> s_bit_shift_magic) ^ int_to_hash) * s_bit_scrambling_magic; + hash = ((hash >> s_bit_shift_magic) ^ hash) * s_bit_scrambling_magic; + hash = (hash >> s_bit_shift_magic) ^ hash; + return (uint64_t)hash; +} + +bool aws_secure_tunnel_connection_id_eq(const void *a, const void *b) { + return *(const uint32_t *)a == *(const uint32_t *)b; +} + +static void s_destroy_connection_id(void *data) { + struct aws_connection_id_element *elem = data; + aws_mem_release(elem->allocator, elem); +} + +struct aws_connection_id_element *aws_connection_id_element_new( + struct aws_allocator *allocator, + uint32_t connection_id) { + AWS_PRECONDITION(allocator != NULL); + AWS_PRECONDITION(connection_id > 0); + struct aws_connection_id_element *elem = aws_mem_calloc(allocator, 1, sizeof(struct aws_service_id_element)); + elem->allocator = allocator; + elem->connection_id = connection_id; + + return elem; +} + /* for the hash table, to destroy elements */ static void s_destroy_service_id(void *data) { struct aws_service_id_element *elem = data; + aws_hash_table_clean_up(&elem->connection_ids); aws_string_destroy(elem->service_id_string); aws_mem_release(elem->allocator, elem); } @@ -39,6 +74,17 @@ struct aws_service_id_element *aws_service_id_element_new( elem->service_id_cur = aws_byte_cursor_from_string(elem->service_id_string); elem->stream_id = stream_id; + if (aws_hash_table_init( + &elem->connection_ids, + allocator, + 1, + aws_secure_tunnel_hash_connection_id, + aws_secure_tunnel_connection_id_eq, + NULL, + s_destroy_connection_id)) { + goto error; + } + return elem; error: @@ -139,38 +185,83 @@ void aws_secure_tunnel_message_view_log( (void *)message_view, aws_secure_tunnel_message_type_to_c_string(message_view->type)); - if (message_view->service_id != NULL) { - AWS_LOGUF( - log_handle, - level, - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: aws_secure_tunnel_message_view service_id set to '" PRInSTR "'", - (void *)message_view, - AWS_BYTE_CURSOR_PRI(*message_view->service_id)); - } else { - AWS_LOGUF( - log_handle, - level, - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: aws_secure_tunnel_message_view service_id not set", - (void *)message_view); - } + switch (message_view->type) { + case AWS_SECURE_TUNNEL_MT_DATA: + case AWS_SECURE_TUNNEL_MT_STREAM_START: + case AWS_SECURE_TUNNEL_MT_STREAM_RESET: + case AWS_SECURE_TUNNEL_MT_CONNECTION_START: + case AWS_SECURE_TUNNEL_MT_CONNECTION_RESET: + if (message_view->service_id != NULL) { + AWS_LOGUF( + log_handle, + level, + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: aws_secure_tunnel_message_view service_id set to '" PRInSTR "'", + (void *)message_view, + AWS_BYTE_CURSOR_PRI(*message_view->service_id)); + } else { + AWS_LOGUF( + log_handle, + level, + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: aws_secure_tunnel_message_view service_id not set", + (void *)message_view); + } - AWS_LOGUF( - log_handle, - level, - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: aws_secure_tunnel_message_view stream_id set to %d", - (void *)message_view, - (int)message_view->stream_id); + AWS_LOGUF( + log_handle, + level, + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: aws_secure_tunnel_message_view stream_id set to %d", + (void *)message_view, + (int)message_view->stream_id); + + if (message_view->connection_id != 0) { + AWS_LOGUF( + log_handle, + level, + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: aws_secure_tunnel_message_view connection_id set to %d", + (void *)message_view, + (int)message_view->connection_id); + } - AWS_LOGUF( - log_handle, - level, - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: aws_secure_tunnel_message_view connection_id set to %d", - (void *)message_view, - (int)message_view->connection_id); + break; + + case AWS_SECURE_TUNNEL_MT_SERVICE_IDS: + if (message_view->service_id != NULL) { + AWS_LOGUF( + log_handle, + level, + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: aws_secure_tunnel_message_view service_id 1 set to '" PRInSTR "'", + (void *)message_view, + AWS_BYTE_CURSOR_PRI(*message_view->service_id)); + } + if (message_view->service_id_2 != NULL) { + AWS_LOGUF( + log_handle, + level, + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: aws_secure_tunnel_message_view service_id 2 set to '" PRInSTR "'", + (void *)message_view, + AWS_BYTE_CURSOR_PRI(*message_view->service_id_2)); + } + if (message_view->service_id_3 != NULL) { + AWS_LOGUF( + log_handle, + level, + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: aws_secure_tunnel_message_view service_id 3 set to '" PRInSTR "'", + (void *)message_view, + AWS_BYTE_CURSOR_PRI(*message_view->service_id_3)); + } + break; + case AWS_SECURE_TUNNEL_MT_SESSION_RESET: + case AWS_SECURE_TUNNEL_MT_UNKNOWN: + default: + break; + } if (message_view->payload != NULL) { AWS_LOGUF( @@ -254,9 +345,9 @@ void aws_secure_tunnel_message_storage_clean_up(struct aws_secure_tunnel_message aws_byte_buf_clean_up(&message_storage->storage); } -// Steve TODO assign connection id here. We will use the one that's been provided by the SendMessage() function -// but if it's missing we will default to 1. -/* Sets the stream id on outbound message based on the service id (or lack of for V1) to the current one being used. */ +/* + * Retreives and assigns the stream id on an outbound message based on the service id (or lack of one for V1). + */ static int s_aws_secure_tunnel_operation_message_assign_stream_id( struct aws_secure_tunnel_operation *operation, struct aws_secure_tunnel *secure_tunnel) { @@ -266,36 +357,47 @@ 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; - if (message_view->service_id != NULL) { + if (message_view->service_id == NULL || message_view->service_id->len == 0) { + stream_id = secure_tunnel->config->stream_id; + } else { struct aws_hash_element *elem = NULL; aws_hash_table_find(&secure_tunnel->config->service_ids, message_view->service_id, &elem); if (elem == NULL) { AWS_LOGF_WARN( AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: invalid service_id:'" PRInSTR "' attempted to be used with an outbound message", + "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)); - stream_id = INVALID_STREAM_ID; - } else { - // Steve TODO check against hash table of currently active connection ids - // to see if the user set connection id is active. If it isn't, log a warning and fail - // the message being attempted. - struct aws_service_id_element *service_id_elem = elem->value; - stream_id = service_id_elem->stream_id; + goto error; } - } else { - // Steve TODO check against hash table of currently active connection ids - // to see if the user set connection id is active. If it isn't, log a warning and fail - // the message being attempted. - stream_id = secure_tunnel->config->stream_id; + struct aws_service_id_element *service_id_elem = elem->value; + stream_id = service_id_elem->stream_id; } if (stream_id == INVALID_STREAM_ID) { - return aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_STREAM); + goto error; } message_op->options_storage.storage_view.stream_id = stream_id; return AWS_OP_SUCCESS; + +error: + if (message_view->service_id == NULL || message_view->service_id->len == 0) { + AWS_LOGF_DEBUG( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: No active stream to assign outbound %s message a stream id", + (void *)secure_tunnel, + aws_secure_tunnel_message_type_to_c_string(message_view->type)); + } else { + AWS_LOGF_DEBUG( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: No active stream with service id '" PRInSTR "' to assign outbound %s message a stream id", + (void *)secure_tunnel, + AWS_BYTE_CURSOR_PRI(*message_view->service_id), + aws_secure_tunnel_message_type_to_c_string(message_view->type)); + } + + return aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_STREAM_ID); } /* @@ -311,7 +413,28 @@ static int s_aws_secure_tunnel_operation_message_set_next_stream_id( struct aws_secure_tunnel_message_view *message_view = &message_op->options_storage.storage_view; - if (message_view->service_id != NULL && message_view->service_id->len > 0) { + if (message_view->service_id == NULL || message_view->service_id->len == 0) { + stream_id = secure_tunnel->config->stream_id + 1; + secure_tunnel->config->stream_id = stream_id; + + aws_hash_table_clear(&secure_tunnel->config->connection_ids); + struct aws_connection_id_element *connection_id_elem = NULL; + if (message_view->connection_id > 0) { + connection_id_elem = aws_connection_id_element_new(secure_tunnel->allocator, message_view->connection_id); + } else { + connection_id_elem = aws_connection_id_element_new(secure_tunnel->allocator, 1); + } + + aws_hash_table_put( + &secure_tunnel->config->connection_ids, &connection_id_elem->connection_id, connection_id_elem, NULL); + + AWS_LOGF_INFO( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Secure tunnel set to stream id (%d) with connection id (%d)", + (void *)secure_tunnel, + stream_id, + connection_id_elem->connection_id); + } else { struct aws_hash_element *elem = NULL; aws_hash_table_find(&secure_tunnel->config->service_ids, message_view->service_id, &elem); if (elem == NULL) { @@ -328,30 +451,32 @@ static int s_aws_secure_tunnel_operation_message_set_next_stream_id( struct aws_service_id_element *replacement_elem = aws_service_id_element_new(secure_tunnel->allocator, message_view->service_id, stream_id); + + struct aws_connection_id_element *connection_id_elem = NULL; + if (message_view->connection_id > 0) { + connection_id_elem = + aws_connection_id_element_new(secure_tunnel->allocator, message_view->connection_id); + } else { + connection_id_elem = aws_connection_id_element_new(secure_tunnel->allocator, 1); + } + + aws_hash_table_put( + &replacement_elem->connection_ids, &connection_id_elem->connection_id, connection_id_elem, NULL); aws_hash_table_put( &secure_tunnel->config->service_ids, &replacement_elem->service_id_cur, replacement_elem, NULL); AWS_LOGF_INFO( AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: Secure tunnel service_id '" PRInSTR "' stream_id set to %d", + "id=%p: Secure tunnel service id '" PRInSTR "' set to stream id (%d) with connection id (%d)", (void *)secure_tunnel, AWS_BYTE_CURSOR_PRI(*message_view->service_id), - stream_id); + stream_id, + connection_id_elem->connection_id); } - } else { - stream_id = secure_tunnel->config->stream_id + 1; - secure_tunnel->config->stream_id = stream_id; - - AWS_LOGF_INFO( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: Secure tunnel stream_id set to %d", - (void *)secure_tunnel, - AWS_BYTE_CURSOR_PRI(*message_view->service_id), - stream_id); } if (stream_id == INVALID_STREAM_ID) { - return aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_STREAM); + return aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_STREAM_ID); } message_op->options_storage.storage_view.stream_id = stream_id; @@ -579,6 +704,7 @@ void aws_secure_tunnel_options_storage_destroy(struct aws_secure_tunnel_options_ aws_string_destroy(storage->access_token); aws_string_destroy(storage->client_token); aws_hash_table_clean_up(&storage->service_ids); + aws_hash_table_clean_up(&storage->connection_ids); aws_mem_release(storage->allocator, storage); } @@ -667,6 +793,16 @@ struct aws_secure_tunnel_options_storage *aws_secure_tunnel_options_storage_new( s_destroy_service_id)) { goto error; } + if (aws_hash_table_init( + &storage->connection_ids, + allocator, + 1, + aws_secure_tunnel_hash_connection_id, + aws_secure_tunnel_connection_id_eq, + NULL, + s_destroy_connection_id)) { + goto error; + } storage->on_message_received = options->on_message_received; storage->user_data = options->user_data; From fdfce92165a1c61beb1aeade98bad1c97d79d743 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 15 Mar 2023 10:05:38 -0700 Subject: [PATCH 05/50] change function name to better reflect its action --- source/secure_tunneling.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index d436f4fc..d0924f75 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -275,7 +275,7 @@ static void s_aws_secure_tunnel_on_data_received( } } -static int s_aws_secure_tunnel_set_stream_id( +static int s_aws_secure_tunnel_set_stream( struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *service_id, int32_t stream_id, @@ -362,7 +362,7 @@ static void s_aws_secure_tunnel_on_stream_start_received( message_view->connection_id = 1; } - int result = s_aws_secure_tunnel_set_stream_id( + int result = s_aws_secure_tunnel_set_stream( secure_tunnel, message_view->service_id, message_view->stream_id, message_view->connection_id); if (secure_tunnel->config->on_stream_start) { @@ -383,7 +383,7 @@ static void s_aws_secure_tunnel_on_stream_reset_received( } if (s_aws_secure_tunnel_stream_id_check_match(secure_tunnel, message_view->service_id, message_view->stream_id)) { - result = s_aws_secure_tunnel_set_stream_id(secure_tunnel, message_view->service_id, INVALID_STREAM_ID, 0); + result = s_aws_secure_tunnel_set_stream(secure_tunnel, message_view->service_id, INVALID_STREAM_ID, 0); if (secure_tunnel->config->on_stream_reset) { secure_tunnel->config->on_stream_reset(message_view, result, secure_tunnel->config->user_data); } @@ -1545,7 +1545,7 @@ int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure if (s_secure_tunneling_send(secure_tunnel, current_operation->message_view)) { error_code = aws_last_error(); } else { - s_aws_secure_tunnel_set_stream_id( + s_aws_secure_tunnel_set_stream( secure_tunnel, current_operation->message_view->service_id, INVALID_STREAM_ID, From 0db4a3632e566c7ad2740e181655ebffe40d38fb Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 15 Mar 2023 10:19:00 -0700 Subject: [PATCH 06/50] function name change --- source/secure_tunneling.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index d0924f75..e0dbc21e 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -158,7 +158,7 @@ static uint8_t s_aws_secure_tunnel_message_min_protocol_check(const struct aws_s return version; } -static bool s_aws_secure_tunnel_protocol_version_check( +static bool s_aws_secure_tunnel_protocol_version_match_check( struct aws_secure_tunnel *secure_tunnel, struct aws_secure_tunnel_message_view *message) { uint8_t message_protocol_version = s_aws_secure_tunnel_message_min_protocol_check(message); @@ -302,7 +302,7 @@ static int s_aws_secure_tunnel_set_stream( if (elem == NULL) { AWS_LOGF_WARN( AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: Incomming STREAM START request for unsupported service_id: " PRInSTR, + "id=%p: Incomming stream set request for unsupported service_id: " PRInSTR, (void *)secure_tunnel, AWS_BYTE_CURSOR_PRI(*service_id)); return AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_BAD_SERVICE_ID; @@ -345,7 +345,7 @@ static void s_aws_secure_tunnel_on_stream_start_received( secure_tunnel->config->protocol_version); } - if (s_aws_secure_tunnel_protocol_version_check(secure_tunnel, message_view)) { + if (!s_aws_secure_tunnel_protocol_version_match_check(secure_tunnel, message_view)) { AWS_LOGF_WARN( AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: Secure Tunnel will be reset due to Protocol version switching by Source device", @@ -374,7 +374,7 @@ static void s_aws_secure_tunnel_on_stream_reset_received( struct aws_secure_tunnel *secure_tunnel, struct aws_secure_tunnel_message_view *message_view) { int result = AWS_OP_SUCCESS; - if (s_aws_secure_tunnel_protocol_version_check(secure_tunnel, message_view)) { + if (!s_aws_secure_tunnel_protocol_version_match_check(secure_tunnel, message_view)) { AWS_LOGF_WARN( AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: Secure Tunnel will be reset due to Protocol version switching by Source device", From 8a8ba69a52c5f2a9d875c6a3a191b9f2d5295225 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 15 Mar 2023 10:25:14 -0700 Subject: [PATCH 07/50] names again --- source/secure_tunneling.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index e0dbc21e..5cec9730 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -174,7 +174,7 @@ static bool s_aws_secure_tunnel_protocol_version_match_check( return true; } -static bool s_aws_secure_tunnel_stream_id_check_match( +static bool s_aws_secure_tunnel_stream_id_match_check( struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *service_id, int32_t stream_id) { @@ -382,7 +382,7 @@ static void s_aws_secure_tunnel_on_stream_reset_received( // Steve Todo Reset the connection } - if (s_aws_secure_tunnel_stream_id_check_match(secure_tunnel, message_view->service_id, message_view->stream_id)) { + if (s_aws_secure_tunnel_stream_id_match_check(secure_tunnel, message_view->service_id, message_view->stream_id)) { result = s_aws_secure_tunnel_set_stream(secure_tunnel, message_view->service_id, INVALID_STREAM_ID, 0); if (secure_tunnel->config->on_stream_reset) { secure_tunnel->config->on_stream_reset(message_view, result, secure_tunnel->config->user_data); From 1cdf0e4cd3054685d53c03d13af96dabd07c4b87 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 15 Mar 2023 10:41:38 -0700 Subject: [PATCH 08/50] remove unecessary logging --- source/secure_tunneling.c | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 5cec9730..448ceacd 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -1517,30 +1517,7 @@ int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure case AWS_STOT_STREAM_RESET: if ((*current_operation->vtable->aws_secure_tunnel_operation_assign_stream_id_fn)( - current_operation, secure_tunnel)) { - - error_code = aws_last_error(); - - if (current_operation->message_view->service_id == NULL || - current_operation->message_view->service_id->len == 0) { - AWS_LOGF_DEBUG( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: failed to assign outbound V1 STREAM RESET message a stream id with error " - "%d(%s)", - (void *)secure_tunnel, - error_code, - aws_error_debug_str(error_code)); - } else { - AWS_LOGF_DEBUG( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: failed to assign outbound STREAM RESET message with service id '" PRInSTR - "' a stream id with error %d(%s)", - (void *)secure_tunnel, - AWS_BYTE_CURSOR_PRI(*current_operation->message_view->service_id), - error_code, - aws_error_debug_str(error_code)); - } - } else { + current_operation, secure_tunnel) == AWS_OP_SUCCESS) { /* Send the Stream Reset message through the WebSocket */ if (s_secure_tunneling_send(secure_tunnel, current_operation->message_view)) { error_code = aws_last_error(); From e5e49cf775ccb99cac22067b70b4b00b5d894f46 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 15 Mar 2023 10:44:54 -0700 Subject: [PATCH 09/50] removed unused define --- source/secure_tunneling_operations.c | 1 - 1 file changed, 1 deletion(-) diff --git a/source/secure_tunneling_operations.c b/source/secure_tunneling_operations.c index f375b90d..db04b51c 100644 --- a/source/secure_tunneling_operations.c +++ b/source/secure_tunneling_operations.c @@ -14,7 +14,6 @@ #include #define INVALID_STREAM_ID 0 -#define DEFAULT_CONNECTION_ID 1 static const uint32_t s_bit_scrambling_magic = 0x45d9f3bU; static const uint32_t s_bit_shift_magic = 16U; From b18723bc6a1a4af6ecb9b058281b4cedca6e36b1 Mon Sep 17 00:00:00 2001 From: Steve Kim <86316075+sbSteveK@users.noreply.github.com> Date: Mon, 27 Mar 2023 09:19:32 -0700 Subject: [PATCH 10/50] back compat and related tests (#86) * Back compat for V3 with V1 and V2 and tests --------- Co-authored-by: Michael Graeb --- include/aws/iotdevice/iotdevice.h | 1 + .../iotdevice/private/secure_tunneling_impl.h | 20 +- .../private/secure_tunneling_operations.h | 23 +- include/aws/iotdevice/secure_tunneling.h | 62 +- source/iotdevice.c | 3 + source/secure_tunneling.c | 631 +++++++-- source/secure_tunneling_operations.c | 100 +- tests/CMakeLists.txt | 33 +- tests/secure_tunnel_tests.c | 1212 ++++++++++++++--- 9 files changed, 1712 insertions(+), 373 deletions(-) diff --git a/include/aws/iotdevice/iotdevice.h b/include/aws/iotdevice/iotdevice.h index 93cf545f..230947d2 100644 --- a/include/aws/iotdevice/iotdevice.h +++ b/include/aws/iotdevice/iotdevice.h @@ -36,6 +36,7 @@ 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, + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_PROTOCOL_VERSION_MISSMATCH, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_TERMINATED, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DECODE_FAILURE, diff --git a/include/aws/iotdevice/private/secure_tunneling_impl.h b/include/aws/iotdevice/private/secure_tunneling_impl.h index 8d6474f7..df79a854 100644 --- a/include/aws/iotdevice/private/secure_tunneling_impl.h +++ b/include/aws/iotdevice/private/secure_tunneling_impl.h @@ -99,10 +99,21 @@ struct data_tunnel_pair { struct aws_allocator *allocator; struct aws_byte_buf buf; struct aws_byte_cursor cur; + enum aws_secure_tunnel_message_type type; const struct aws_secure_tunnel *secure_tunnel; bool length_prefix_written; }; +struct aws_secure_tunnel_message_storage { + struct aws_allocator *allocator; + struct aws_secure_tunnel_message_view storage_view; + + struct aws_byte_cursor service_id; + struct aws_byte_cursor payload; + + struct aws_byte_buf storage; +}; + /* * Secure tunnel configuration */ @@ -123,9 +134,14 @@ struct aws_secure_tunnel_options_storage { uint8_t protocol_version; /* Stream related info */ + /* Used for streams not using multiplexing (service ids) */ int32_t stream_id; - struct aws_hash_table service_ids; struct aws_hash_table connection_ids; + /* 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 */ + struct aws_secure_tunnel_message_storage *restore_stream_message_view; + struct aws_secure_tunnel_message_storage restore_stream_message; /* Callbacks */ aws_secure_tunnel_message_received_fn *on_message_received; @@ -137,8 +153,8 @@ struct aws_secure_tunnel_options_storage { aws_secure_tunneling_on_connection_reset_fn *on_connection_reset; aws_secure_tunneling_on_session_reset_fn *on_session_reset; aws_secure_tunneling_on_stopped_fn *on_stopped; + aws_secure_tunneling_on_send_message_complete_fn *on_send_message_complete; - aws_secure_tunneling_on_send_data_complete_fn *on_send_data_complete; aws_secure_tunneling_on_termination_complete_fn *on_termination_complete; void *secure_tunnel_on_termination_user_data; diff --git a/include/aws/iotdevice/private/secure_tunneling_operations.h b/include/aws/iotdevice/private/secure_tunneling_operations.h index 022baec3..6c0ed534 100644 --- a/include/aws/iotdevice/private/secure_tunneling_operations.h +++ b/include/aws/iotdevice/private/secure_tunneling_operations.h @@ -41,19 +41,6 @@ struct aws_connection_id_element { uint32_t connection_id; }; -struct aws_secure_tunnel_message_storage { - struct aws_allocator *allocator; - struct aws_secure_tunnel_message_view storage_view; - - bool ignorable; - int32_t stream_id; - uint32_t connection_id; - struct aws_byte_cursor service_id; - struct aws_byte_cursor payload; - - struct aws_byte_buf storage; -}; - /* Basic vtable for all secure tunnel operations. Implementations are per-message type */ struct aws_secure_tunnel_operation_vtable { void (*aws_secure_tunnel_operation_completion_fn)( @@ -66,10 +53,15 @@ struct aws_secure_tunnel_operation_vtable { struct aws_secure_tunnel_operation *operation, struct aws_secure_tunnel *secure_tunnel); - /* Set the stream id of outgoing st_msg to +1 of the currently set stream id */ + /* Set the stream id of outgoing STREAM START message to +1 of the currently set stream id */ int (*aws_secure_tunnel_operation_set_next_stream_id_fn)( struct aws_secure_tunnel_operation *operation, struct aws_secure_tunnel *secure_tunnel); + + /* Set the connection id of outbound CONNECTION START as active for the Source device */ + int (*aws_secure_tunnel_operation_set_connection_start_id)( + struct aws_secure_tunnel_operation *operation, + struct aws_secure_tunnel *secure_tunnel); }; /** @@ -206,6 +198,9 @@ struct aws_service_id_element *aws_service_id_element_new( const struct aws_byte_cursor *service_id, int32_t stream_id); +AWS_IOTDEVICE_API +void aws_destroy_connection_id(void *data); + AWS_IOTDEVICE_API struct aws_connection_id_element *aws_connection_id_element_new( struct aws_allocator *allocator, diff --git a/include/aws/iotdevice/secure_tunneling.h b/include/aws/iotdevice/secure_tunneling.h index 50384e27..f48507b4 100644 --- a/include/aws/iotdevice/secure_tunneling.h +++ b/include/aws/iotdevice/secure_tunneling.h @@ -67,7 +67,7 @@ enum aws_secure_tunnel_message_type { /** * ConnectionReset messages convey that the connection has ended, either in error, or closed intentionally for the - * tunnel peer. + * tunnel peer. These should not be manually sent from either Destination or Source clients. */ AWS_SECURE_TUNNEL_MT_CONNECTION_RESET = 7 }; @@ -114,30 +114,63 @@ struct aws_secure_tunnel_connection_view { */ typedef void( aws_secure_tunnel_message_received_fn)(const struct aws_secure_tunnel_message_view *message, void *user_data); +/** + * Signature of callback to invoke on fully established connection to Secure Tunnel Service + */ typedef void(aws_secure_tunneling_on_connection_complete_fn)( const struct aws_secure_tunnel_connection_view *connection_view, int error_code, void *user_data); +/** + * Signature of callback to invoke on shutdown of connection to Secure Tunnel Service + */ typedef void(aws_secure_tunneling_on_connection_shutdown_fn)(int error_code, void *user_data); -typedef void(aws_secure_tunneling_on_send_data_complete_fn)(int error_code, void *user_data); +/** + * Signature of callback to invoke on completion of an outbound message + */ +typedef void(aws_secure_tunneling_on_send_message_complete_fn( + enum aws_secure_tunnel_message_type type, + int error_code, + void *user_data)); +/** + * Signature of callback to invoke on the start of a stream + */ typedef void(aws_secure_tunneling_on_stream_start_fn)( const struct aws_secure_tunnel_message_view *message, int error_code, void *user_data); +/** + * Signature of callback to invoke on a stream being reset + */ typedef void(aws_secure_tunneling_on_stream_reset_fn)( const struct aws_secure_tunnel_message_view *message, int error_code, void *user_data); +/** + * Signature of callback to invoke on start of a connection id stream + */ typedef void(aws_secure_tunneling_on_connection_start_fn)( const struct aws_secure_tunnel_message_view *message, int error_code, void *user_data); +/** + * Signature of callback to invoke on a connection id stream being reset + */ typedef void(aws_secure_tunneling_on_connection_reset_fn)( const struct aws_secure_tunnel_message_view *message, int error_code, void *user_data); +/** + * Signature of callback to invoke on session reset recieved from the Secure Tunnel Service + */ typedef void(aws_secure_tunneling_on_session_reset_fn)(void *user_data); +/** + * Signature of callback to invoke on Secure Tunnel reaching a STOPPED state + */ typedef void(aws_secure_tunneling_on_stopped_fn)(void *user_data); +/** + * Signature of callback to invoke on termination completion of the Native Secure Tunnel Client + */ typedef void(aws_secure_tunneling_on_termination_complete_fn)(void *user_data); /** @@ -187,7 +220,7 @@ struct aws_secure_tunnel_options { aws_secure_tunneling_on_connection_complete_fn *on_connection_complete; aws_secure_tunneling_on_connection_shutdown_fn *on_connection_shutdown; - aws_secure_tunneling_on_send_data_complete_fn *on_send_data_complete; + aws_secure_tunneling_on_send_message_complete_fn *on_send_message_complete; aws_secure_tunneling_on_stream_start_fn *on_stream_start; aws_secure_tunneling_on_stream_reset_fn *on_stream_reset; aws_secure_tunneling_on_connection_start_fn *on_connection_start; @@ -202,19 +235,6 @@ struct aws_secure_tunnel_options { void *secure_tunnel_on_termination_user_data; }; -/** - * Signature of callback to invoke when secure tunnel enters a fully disconnected state - */ -typedef void(aws_secure_tunnel_disconnect_completion_fn)(int error_code, void *complete_ctx); - -/** - * Public completion callback options for the DISCONNECT operation - */ -struct aws_secure_tunnel_disconnect_completion_options { - aws_secure_tunnel_disconnect_completion_fn *completion_callback; - void *completion_user_data; -}; - AWS_EXTERN_C_BEGIN /** @@ -293,13 +313,21 @@ int aws_secure_tunnel_connection_start( const struct aws_secure_tunnel_message_view *message_options); //*********************************************************************************************************************** -/* THIS API SHOULD NOT BE USED BY THE CUSTOMER AND SHOULD BE DEPRECATED */ +/* THIS API SHOULD NOT BE USED BY THE CUSTOMER AND IS DEPRECATED */ //*********************************************************************************************************************** AWS_IOTDEVICE_API int aws_secure_tunnel_stream_reset( struct aws_secure_tunnel *secure_tunnel, const struct aws_secure_tunnel_message_view *message_options); +//*********************************************************************************************************************** +/* THIS API SHOULD NOT BE USED BY THE CUSTOMER AND IS FOR TESTING PURPOSES ONLY */ +//*********************************************************************************************************************** +AWS_IOTDEVICE_API +int aws_secure_tunnel_connection_reset( + struct aws_secure_tunnel *secure_tunnel, + const struct aws_secure_tunnel_message_view *message_options); + AWS_EXTERN_C_END #endif /* AWS_IOTDEVICE_SECURE_TUNNELING_H */ diff --git a/source/iotdevice.c b/source/iotdevice.c index 6f1f7587..3a317275 100644 --- a/source/iotdevice.c +++ b/source/iotdevice.c @@ -84,6 +84,9 @@ static struct aws_error_info s_errors[] = { AWS_DEFINE_ERROR_INFO_IOTDEVICE( 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_DEFINE_ERROR_INFO_IOTDEVICE( AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_TERMINATED, "Secure Tunnel terminated by user request."), diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 448ceacd..b61f0cab 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -44,6 +44,13 @@ static void s_complete_operation_list( struct aws_linked_list *operation_list, int error_code); static void s_reevaluate_service_task(struct aws_secure_tunnel *secure_tunnel); +static void s_aws_secure_tunnel_connected_on_message_received( + struct aws_secure_tunnel *secure_tunnel, + struct aws_secure_tunnel_message_view *message_view); +static int s_aws_secure_tunnel_remove_connection_id( + struct aws_secure_tunnel *secure_tunnel, + const struct aws_secure_tunnel_message_view *message_view); +int reset_secure_tunnel_connection(struct aws_secure_tunnel *secure_tunnel); const char *aws_secure_tunnel_state_to_c_string(enum aws_secure_tunnel_state state) { switch (state) { @@ -80,14 +87,6 @@ static const char *s_get_proxy_mode_string(enum aws_secure_tunneling_local_proxy return "destination"; } -static int s_reset_service_id(void *context, struct aws_hash_element *p_element) { - (void)context; - struct aws_service_id_element *service_id_elem = p_element->value; - service_id_elem->stream_id = INVALID_STREAM_ID; - aws_hash_table_clear(&service_id_elem->connection_ids); - return AWS_COMMON_HASH_TABLE_ITER_CONTINUE; -} - /********************************************************************************************************************* * Secure Tunnel Clean Up ********************************************************************************************************************/ @@ -128,13 +127,21 @@ static void s_on_secure_tunnel_zero_ref_count(void *user_data) { } /***************************************************************************************************************** - * RECEIVE MESSAGE HANDLING + * STREAM HANDLING *****************************************************************************************************************/ +static int s_reset_service_id(void *context, struct aws_hash_element *p_element) { + (void)context; + struct aws_service_id_element *service_id_elem = p_element->value; + service_id_elem->stream_id = INVALID_STREAM_ID; + aws_hash_table_clear(&service_id_elem->connection_ids); + return AWS_COMMON_HASH_TABLE_ITER_CONTINUE; +} + /* * Close and reset all stream ids */ -static void s_reset_secure_tunnel(struct aws_secure_tunnel *secure_tunnel) { +static void s_reset_secure_tunnel_streams(struct aws_secure_tunnel *secure_tunnel) { AWS_LOGF_INFO(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: Secure tunnel session reset.", (void *)secure_tunnel); secure_tunnel->config->protocol_version = 0; @@ -160,7 +167,7 @@ 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, - struct aws_secure_tunnel_message_view *message) { + 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->config->protocol_version != message_protocol_version) { AWS_LOGF_WARN( @@ -197,12 +204,12 @@ 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_message_view *message) { + 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 */ - if (message->service_id == NULL || message->service_id->len == 0) { - if (secure_tunnel->config->stream_id != message->stream_id) { + if (message_view->service_id == NULL || message_view->service_id->len == 0) { + if (secure_tunnel->config->stream_id != message_view->stream_id) { return false; } @@ -211,7 +218,7 @@ static bool s_aws_secure_tunnel_active_stream_check( * against stored connection_ids to confirm the stream is active. */ struct aws_hash_element *connection_id_elem = NULL; - aws_hash_table_find(&secure_tunnel->config->connection_ids, &message->connection_id, &connection_id_elem); + aws_hash_table_find(&secure_tunnel->config->connection_ids, &message_view->connection_id, &connection_id_elem); if (connection_id_elem == NULL) { aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_CONNECTION_ID); return false; @@ -221,7 +228,7 @@ static bool s_aws_secure_tunnel_active_stream_check( /* Check if service id is being used by the secure tunnel */ struct aws_hash_element *elem = NULL; - aws_hash_table_find(&secure_tunnel->config->service_ids, message->service_id, &elem); + aws_hash_table_find(&secure_tunnel->config->service_ids, message_view->service_id, &elem); if (elem == NULL) { aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_SERVICE_ID); return false; @@ -229,7 +236,7 @@ static bool s_aws_secure_tunnel_active_stream_check( /* Check if the stream id is the currently active one */ struct aws_service_id_element *service_id_elem = elem->value; - if (message->stream_id != service_id_elem->stream_id) { + if (message_view->stream_id != service_id_elem->stream_id) { aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_STREAM_ID); return false; } @@ -239,7 +246,7 @@ static bool s_aws_secure_tunnel_active_stream_check( * stored connection_ids for the service id to confirm the stream is active */ struct aws_hash_element *connection_id_elem = NULL; - aws_hash_table_find(&service_id_elem->connection_ids, &message->connection_id, &connection_id_elem); + aws_hash_table_find(&service_id_elem->connection_ids, &message_view->connection_id, &connection_id_elem); if (connection_id_elem == NULL) { aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_CONNECTION_ID); return false; @@ -247,34 +254,6 @@ static bool s_aws_secure_tunnel_active_stream_check( return true; } -static void s_aws_secure_tunnel_on_data_received( - struct aws_secure_tunnel *secure_tunnel, - struct aws_secure_tunnel_message_view *message_view) { - if (s_aws_secure_tunnel_active_stream_check(secure_tunnel, message_view)) { - if (secure_tunnel->config->on_message_received) { - secure_tunnel->config->on_message_received(message_view, secure_tunnel->config->user_data); - } - } else { - if (message_view->service_id->len > 0) { - AWS_LOGF_INFO( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: Incomming DATA message on inactive stream with service id '" PRInSTR - "' stream id (%d) connection id (%d) ignored", - (void *)secure_tunnel, - AWS_BYTE_CURSOR_PRI(*message_view->service_id), - message_view->stream_id, - message_view->connection_id); - } else { - AWS_LOGF_INFO( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: Incomming DATA message on inactive stream with stream id (%d) connection id (%d) ignored", - (void *)secure_tunnel, - message_view->stream_id, - message_view->connection_id); - } - } -} - static int s_aws_secure_tunnel_set_stream( struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *service_id, @@ -284,10 +263,12 @@ static int s_aws_secure_tunnel_set_stream( if (service_id == NULL || service_id->len == 0) { secure_tunnel->config->stream_id = stream_id; aws_hash_table_clear(&secure_tunnel->config->connection_ids); - struct aws_connection_id_element *connection_id_elem = - aws_connection_id_element_new(secure_tunnel->allocator, connection_id); - aws_hash_table_put( - &secure_tunnel->config->connection_ids, &connection_id_elem->connection_id, connection_id_elem, NULL); + if (connection_id > 0) { + struct aws_connection_id_element *connection_id_elem = + aws_connection_id_element_new(secure_tunnel->allocator, connection_id); + aws_hash_table_put( + &secure_tunnel->config->connection_ids, &connection_id_elem->connection_id, connection_id_elem, NULL); + } AWS_LOGF_INFO( AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: Secure tunnel set to stream id (%d) with active connection id(%d)", @@ -311,10 +292,12 @@ static int s_aws_secure_tunnel_set_stream( struct aws_service_id_element *replacement_elem = aws_service_id_element_new(secure_tunnel->allocator, service_id, stream_id); - struct aws_connection_id_element *connection_id_elem = - aws_connection_id_element_new(secure_tunnel->allocator, connection_id); - - aws_hash_table_put(&replacement_elem->connection_ids, &connection_id_elem->connection_id, connection_id_elem, NULL); + if (connection_id > 0) { + struct aws_connection_id_element *connection_id_elem = + aws_connection_id_element_new(secure_tunnel->allocator, connection_id); + aws_hash_table_put( + &replacement_elem->connection_ids, &connection_id_elem->connection_id, connection_id_elem, NULL); + } aws_hash_table_put(&secure_tunnel->config->service_ids, &replacement_elem->service_id_cur, replacement_elem, NULL); AWS_LOGF_INFO( @@ -328,14 +311,209 @@ static int s_aws_secure_tunnel_set_stream( return AWS_OP_SUCCESS; } +static int s_aws_secure_tunnel_set_connection_id( + struct aws_secure_tunnel *secure_tunnel, + struct aws_byte_cursor *service_id, + uint32_t connection_id) { + struct aws_hash_table *table_to_put_in = NULL; + if (service_id == NULL || service_id->len == 0) { + table_to_put_in = &secure_tunnel->config->connection_ids; + } else { + struct aws_hash_element *elem = NULL; + aws_hash_table_find(&secure_tunnel->config->service_ids, service_id, &elem); + if (elem == NULL) { + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: invalid service_id:'" PRInSTR + "' attempted to be used to start a stream using a connection id (%d)", + (void *)secure_tunnel, + AWS_BYTE_CURSOR_PRI(*service_id), + connection_id); + return aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_SERVICE_ID); + } else { + struct aws_service_id_element *service_id_elem = elem->value; + table_to_put_in = &service_id_elem->connection_ids; + } + } + + if (connection_id != 0) { + struct aws_connection_id_element *connection_id_elem = NULL; + connection_id_elem = aws_connection_id_element_new(secure_tunnel->allocator, connection_id); + struct aws_hash_element *preexisting_connection_id_elem = NULL; + + aws_hash_table_find(table_to_put_in, &connection_id_elem->connection_id, &preexisting_connection_id_elem); + + if (preexisting_connection_id_elem == NULL) { + aws_hash_table_put(table_to_put_in, &connection_id_elem->connection_id, connection_id_elem, NULL); + + if (service_id == NULL || service_id->len == 0) { + AWS_LOGF_INFO( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Stream started using connection id (%d)", + (void *)secure_tunnel, + connection_id); + } else { + AWS_LOGF_INFO( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Stream started on service_id:'" PRInSTR "' using connection id (%d)", + (void *)secure_tunnel, + AWS_BYTE_CURSOR_PRI(*service_id), + connection_id); + } + + } else { + /* + * If the connection id is already stored something is wrong and this connection id must be removed and a + * connection reset must be sent for this connection id + */ + aws_destroy_connection_id(connection_id_elem); + + if (service_id == NULL || service_id->len == 0) { + AWS_LOGF_INFO( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Connection Start on existing connection id (%d) received. Closing active stream and " + "sending CONNECTION RESET.", + (void *)secure_tunnel, + connection_id); + } else { + AWS_LOGF_INFO( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Connection Start on service_id:'" PRInSTR + "' on existing connection id (%d) received. Closing active stream and sending CONNECTION RESET.", + (void *)secure_tunnel, + AWS_BYTE_CURSOR_PRI(*service_id), + connection_id); + } + + struct aws_secure_tunnel_message_view reset_message = { + .type = AWS_SECURE_TUNNEL_MT_CONNECTION_RESET, + .service_id = service_id, + .connection_id = connection_id, + }; + + s_aws_secure_tunnel_remove_connection_id(secure_tunnel, &reset_message); + if (secure_tunnel->config->on_connection_reset) { + secure_tunnel->config->on_connection_reset( + &reset_message, + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_CONNECTION_ID, + secure_tunnel->config->user_data); + } + + aws_secure_tunnel_connection_reset(secure_tunnel, &reset_message); + + return aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_CONNECTION_ID); + } + } else { + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Connection Id can not be set to 0 on a CONNECTION START", + (void *)secure_tunnel); + return aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_CONNECTION_ID); + } + return AWS_OP_SUCCESS; +} + +static int s_aws_secure_tunnel_remove_connection_id( + struct aws_secure_tunnel *secure_tunnel, + const struct aws_secure_tunnel_message_view *message_view) { + + if (s_aws_secure_tunnel_active_stream_check(secure_tunnel, message_view)) { + struct aws_hash_table *table_to_remove_from = NULL; + + if (message_view->service_id == NULL || message_view->service_id->len == 0) { + table_to_remove_from = &secure_tunnel->config->connection_ids; + } else { + struct aws_hash_element *elem = NULL; + aws_hash_table_find(&secure_tunnel->config->service_ids, message_view->service_id, &elem); + struct aws_service_id_element *service_id_elem = elem->value; + table_to_remove_from = &service_id_elem->connection_ids; + } + + aws_hash_table_remove(table_to_remove_from, &message_view->connection_id, NULL, NULL); + + if (message_view->service_id == NULL || message_view->service_id->len == 0) { + AWS_LOGF_INFO( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Stream using connection id (%d) closed", + (void *)secure_tunnel, + message_view->connection_id); + } else { + AWS_LOGF_INFO( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Stream on service_id:'" PRInSTR "' using connection id (%d) closed", + (void *)secure_tunnel, + AWS_BYTE_CURSOR_PRI(*message_view->service_id), + message_view->connection_id); + } + } else { + return aws_last_error(); + } + + return AWS_OP_SUCCESS; +} + +/***************************************************************************************************************** + * RECEIVE MESSAGE HANDLING + *****************************************************************************************************************/ + +static void s_aws_secure_tunnel_on_data_received( + struct aws_secure_tunnel *secure_tunnel, + struct aws_secure_tunnel_message_view *message_view) { + + 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 + */ + 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 and Protocol Version used by incoming STREAM START message.", + (void *)secure_tunnel); + reset_secure_tunnel_connection(secure_tunnel); + } + + /* + * An absent connection ID will result in connection id being set to 1. + */ + if (message_view->connection_id == 0) { + message_view->connection_id = 1; + } + + if (s_aws_secure_tunnel_active_stream_check(secure_tunnel, message_view)) { + if (secure_tunnel->config->on_message_received) { + secure_tunnel->config->on_message_received(message_view, secure_tunnel->config->user_data); + } + } else { + if (message_view->service_id->len > 0) { + AWS_LOGF_INFO( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Incomming DATA message on inactive stream with service id '" PRInSTR + "' stream id (%d) connection id (%d) ignored", + (void *)secure_tunnel, + AWS_BYTE_CURSOR_PRI(*message_view->service_id), + message_view->stream_id, + message_view->connection_id); + } else { + AWS_LOGF_INFO( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Incomming DATA message on inactive stream with stream id (%d) connection id (%d) ignored", + (void *)secure_tunnel, + message_view->stream_id, + message_view->connection_id); + } + } +} + static void s_aws_secure_tunnel_on_stream_start_received( struct aws_secure_tunnel *secure_tunnel, struct aws_secure_tunnel_message_view *message_view) { /* - * If a protocol version hasn't been established yet, the first STREAM START determines the protocol version being - * used this session + * If a protocol version hasn't been established yet, the first STREAM START determines the protocol version + * being used this session */ if (secure_tunnel->config->protocol_version == 0) { + uint8_t message_protocol_version = s_aws_secure_tunnel_message_min_protocol_check(message_view); secure_tunnel->config->protocol_version = message_protocol_version; AWS_LOGF_INFO( @@ -343,20 +521,30 @@ static void s_aws_secure_tunnel_on_stream_start_received( "id=%p: Secure tunnel client Protocol set to V%d based on received STREAM START", (void *)secure_tunnel, secure_tunnel->config->protocol_version); - } - - if (!s_aws_secure_tunnel_protocol_version_match_check(secure_tunnel, message_view)) { - AWS_LOGF_WARN( + } 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 + */ + AWS_LOGF_INFO( AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: Secure Tunnel will be reset due to Protocol version switching by Source device", + "id=%p: Secure Tunnel will be reset due to Protocol Version missmatch between previously established " + "Protocol Version and Protocol Version used by incoming STREAM START message.", (void *)secure_tunnel); - // Steve Todo Reset the connection + reset_secure_tunnel_connection(secure_tunnel); + aws_secure_tunnel_message_storage_init( + &secure_tunnel->config->restore_stream_message, + secure_tunnel->allocator, + message_view, + AWS_STOT_STREAM_START); + secure_tunnel->config->restore_stream_message_view = &secure_tunnel->config->restore_stream_message; + return; } /* - * An absent connection ID will result in connection id being set to 1. The connection is considered a V1 connection - * at this point and the future existance of a connection ID will result in a full reset of the client as mixed - * protocol versions is not supported. + * An absent connection ID will result in connection id being set to 1. The connection is considered a V1 + * connection at this point and the future existance of an unexpected connection ID will result in a full reset + * of the client as mixed protocol versions is not supported. */ if (message_view->connection_id == 0) { message_view->connection_id = 1; @@ -373,15 +561,18 @@ static void s_aws_secure_tunnel_on_stream_start_received( static void s_aws_secure_tunnel_on_stream_reset_received( struct aws_secure_tunnel *secure_tunnel, struct aws_secure_tunnel_message_view *message_view) { - int result = AWS_OP_SUCCESS; - if (!s_aws_secure_tunnel_protocol_version_match_check(secure_tunnel, message_view)) { - AWS_LOGF_WARN( + if (secure_tunnel->config->protocol_version != 0 && + !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 switching by Source device", + "id=%p: Secure Tunnel will be reset due to Protocol Version missmatch between previously established " + "Protocol Version and Protocol Version used by incoming STREAM RESET message.", (void *)secure_tunnel); - // Steve Todo Reset the connection + reset_secure_tunnel_connection(secure_tunnel); + return; } + int result = AWS_OP_SUCCESS; if (s_aws_secure_tunnel_stream_id_match_check(secure_tunnel, message_view->service_id, message_view->stream_id)) { result = s_aws_secure_tunnel_set_stream(secure_tunnel, message_view->service_id, INVALID_STREAM_ID, 0); if (secure_tunnel->config->on_stream_reset) { @@ -406,7 +597,7 @@ static void s_aws_secure_tunnel_on_stream_reset_received( } static void s_aws_secure_tunnel_on_session_reset_received(struct aws_secure_tunnel *secure_tunnel) { - s_reset_secure_tunnel(secure_tunnel); + s_reset_secure_tunnel_streams(secure_tunnel); if (secure_tunnel->config->on_session_reset) { secure_tunnel->config->on_session_reset(secure_tunnel->config->user_data); } @@ -463,72 +654,125 @@ static void s_aws_secure_tunnel_on_service_ids_received( secure_tunnel->config->on_connection_complete( &connection_view, AWS_ERROR_SUCCESS, secure_tunnel->config->user_data); } -} -static int s_aws_secure_tunnel_set_connection_id( - struct aws_secure_tunnel *secure_tunnel, - const struct aws_byte_cursor *service_id, - int32_t stream_id, - uint32_t connection_id) { - (void)secure_tunnel; - (void)service_id; - (void)stream_id; - (void)connection_id; - // Steve TODO Logic for Connection Start - return AWS_OP_SUCCESS; + /* Initialize stream if one is set to be started upon a reconnect */ + if (secure_tunnel->config->restore_stream_message_view != NULL) { + AWS_LOGF_INFO( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Secure Tunnel will process the STREAM START that caused reconnection due to changing protocol by " + "Source Device.", + (void *)secure_tunnel); + s_aws_secure_tunnel_connected_on_message_received( + secure_tunnel, &secure_tunnel->config->restore_stream_message_view->storage_view); + aws_secure_tunnel_message_storage_clean_up(&secure_tunnel->config->restore_stream_message); + secure_tunnel->config->restore_stream_message_view = NULL; + } } static void s_aws_secure_tunnel_on_connection_start_received( struct aws_secure_tunnel *secure_tunnel, struct aws_secure_tunnel_message_view *message_view) { - // Steve TODO Logic for Connection Start + if (secure_tunnel->config->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 " + "Protocol Version and Protocol Version used by incoming CONNECTION START message.", + (void *)secure_tunnel); + reset_secure_tunnel_connection(secure_tunnel); + } + + /* + * An absent connection ID will result in connection id being set to 1. + */ if (message_view->connection_id == 0) { message_view->connection_id = 1; } - int result = s_aws_secure_tunnel_set_connection_id( - secure_tunnel, message_view->service_id, message_view->stream_id, message_view->connection_id); - if (secure_tunnel->config->on_connection_start) { - secure_tunnel->config->on_connection_start(message_view, result, secure_tunnel->config->user_data); + if (s_aws_secure_tunnel_stream_id_match_check(secure_tunnel, message_view->service_id, message_view->stream_id)) { + int result = + s_aws_secure_tunnel_set_connection_id(secure_tunnel, message_view->service_id, message_view->connection_id); + if (secure_tunnel->config->on_connection_start) { + secure_tunnel->config->on_connection_start(message_view, result, secure_tunnel->config->user_data); + } + } else { + if (message_view->service_id->len > 0) { + AWS_LOGF_INFO( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Incomming CONNECTION START on inactive stream with service id '" PRInSTR + "' stream id (%d) ignored", + (void *)secure_tunnel, + AWS_BYTE_CURSOR_PRI(*message_view->service_id), + message_view->stream_id); + } else { + AWS_LOGF_INFO( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Incomming CONNECTION START on inactive stream with stream id (%d) ignored", + (void *)secure_tunnel, + message_view->stream_id); + } } } static void s_aws_secure_tunnel_on_connection_reset_received( struct aws_secure_tunnel *secure_tunnel, struct aws_secure_tunnel_message_view *message_view) { - (void)secure_tunnel; + if (secure_tunnel->config->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 " + "Protocol Version and Protocol Version used by incoming CONNECTION RESET message.", + (void *)secure_tunnel); + reset_secure_tunnel_connection(secure_tunnel); + } + + /* + * An absent connection ID will result in connection id being set to 1. + */ if (message_view->connection_id == 0) { message_view->connection_id = 1; } - // Steve TODO Logic for Connection Reset + + int result = s_aws_secure_tunnel_remove_connection_id(secure_tunnel, message_view); + + if (secure_tunnel->config->on_connection_reset) { + secure_tunnel->config->on_connection_reset(message_view, result, secure_tunnel->config->user_data); + } } static void s_aws_secure_tunnel_connected_on_message_received( struct aws_secure_tunnel *secure_tunnel, struct aws_secure_tunnel_message_view *message_view) { + aws_secure_tunnel_message_view_log(message_view, AWS_LL_DEBUG); switch (message_view->type) { case AWS_SECURE_TUNNEL_MT_DATA: s_aws_secure_tunnel_on_data_received(secure_tunnel, message_view); break; + case AWS_SECURE_TUNNEL_MT_STREAM_START: s_aws_secure_tunnel_on_stream_start_received(secure_tunnel, message_view); break; + case AWS_SECURE_TUNNEL_MT_STREAM_RESET: s_aws_secure_tunnel_on_stream_reset_received(secure_tunnel, message_view); break; + case AWS_SECURE_TUNNEL_MT_SESSION_RESET: s_aws_secure_tunnel_on_session_reset_received(secure_tunnel); break; + case AWS_SECURE_TUNNEL_MT_SERVICE_IDS: s_aws_secure_tunnel_on_service_ids_received(secure_tunnel, message_view); break; + case AWS_SECURE_TUNNEL_MT_CONNECTION_START: s_aws_secure_tunnel_on_connection_start_received(secure_tunnel, message_view); break; + case AWS_SECURE_TUNNEL_MT_CONNECTION_RESET: s_aws_secure_tunnel_on_connection_reset_received(secure_tunnel, message_view); break; + case AWS_SECURE_TUNNEL_MT_UNKNOWN: default: if (!message_view->ignorable) { @@ -546,8 +790,8 @@ static int s_process_received_data(struct aws_secure_tunnel *secure_tunnel) { struct aws_byte_cursor cursor = aws_byte_cursor_from_buf(received_data); uint16_t data_length = 0; /* - * If there are at least two bytes for the data_length, but not enough data for a complete secure tunnel frame, we - * don't want to move `cursor`. + * If there are at least two bytes for the data_length, but not enough data for a complete secure tunnel frame, + * we don't want to move `cursor`. */ struct aws_byte_cursor tmp_cursor = cursor; while (aws_byte_cursor_read_be16(&tmp_cursor, &data_length) && tmp_cursor.len >= data_length) { @@ -590,8 +834,8 @@ static void s_secure_tunneling_websocket_on_send_data_complete_callback( (void)websocket; struct data_tunnel_pair *pair = user_data; struct aws_secure_tunnel *secure_tunnel = (struct aws_secure_tunnel *)pair->secure_tunnel; - if (secure_tunnel->config->on_send_data_complete) { - secure_tunnel->config->on_send_data_complete(error_code, pair->secure_tunnel->config->user_data); + if (secure_tunnel->config->on_send_message_complete) { + secure_tunnel->config->on_send_message_complete(pair->type, error_code, secure_tunnel->config->user_data); } aws_secure_tunnel_data_tunnel_pair_destroy(pair); secure_tunnel->pending_write_completion = false; @@ -753,6 +997,13 @@ static void s_secure_tunnel_shutdown_websocket(struct aws_secure_tunnel *secure_ return; } + AWS_LOGF_DEBUG( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: secure tunnel websocket shutdown invoked with error code %d(%s)", + (void *)secure_tunnel, + error_code, + aws_error_debug_str(error_code)); + s_change_current_state(secure_tunnel, AWS_STS_WEBSOCKET_SHUTDOWN); } @@ -1013,7 +1264,7 @@ static void s_change_current_state_to_stopped(struct aws_secure_tunnel *secure_t secure_tunnel, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_USER_REQUESTED_STOP); /* Stop works as a complete session wipe, and so the next time we connect, we want it to be clean */ - s_reset_secure_tunnel(secure_tunnel); + s_reset_secure_tunnel_streams(secure_tunnel); if (secure_tunnel->config->on_stopped) { secure_tunnel->config->on_stopped(secure_tunnel->config->user_data); @@ -1176,6 +1427,11 @@ static void s_change_state_task_fn(struct aws_task *task, void *arg, enum aws_ta goto done; } + if (desired_state == AWS_STS_CLEAN_DISCONNECT) { + s_change_current_state(secure_tunnel, AWS_STS_CLEAN_DISCONNECT); + goto done; + } + if (secure_tunnel->desired_state != desired_state) { AWS_LOGF_INFO( AWS_LS_IOTDEVICE_SECURE_TUNNELING, @@ -1251,6 +1507,27 @@ static int s_aws_secure_tunnel_change_desired_state( return AWS_OP_SUCCESS; } +/* + * Disconnect the Secure Tunnel from the Secure Tunnel service and reset all stream ids + */ +int reset_secure_tunnel_connection(struct aws_secure_tunnel *secure_tunnel) { + + struct aws_secure_tunnel_change_desired_state_task *task = s_aws_secure_tunnel_change_desired_state_task_new( + secure_tunnel->allocator, secure_tunnel, AWS_STS_CLEAN_DISCONNECT); + + if (task == NULL) { + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: failed to create change desired state task", + (void *)secure_tunnel); + return AWS_OP_ERR; + } + + aws_event_loop_schedule_task_now(secure_tunnel->loop, &task->task); + + return AWS_OP_SUCCESS; +} + /********************************************************************************************************************* * vtable functions ********************************************************************************************************************/ @@ -1324,16 +1601,8 @@ static bool s_aws_secure_tunnel_has_pending_operational_work(const struct aws_se return false; } - struct aws_linked_list_node *next_operation_node = aws_linked_list_front(&secure_tunnel->queued_operations); - struct aws_secure_tunnel_operation *next_operation = - AWS_CONTAINER_OF(next_operation_node, struct aws_secure_tunnel_operation, node); - switch (secure_tunnel->current_state) { case AWS_STS_CLEAN_DISCONNECT: - /* Except for finishing the current operation, only allowed to send STREAM RESET messages in this state - */ - return next_operation->operation_type == AWS_STOT_STREAM_RESET; - case AWS_STS_CONNECTED: return true; @@ -1426,8 +1695,8 @@ int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure switch (current_operation->operation_type) { case AWS_STOT_PING:; /* - * TODO Currently, pings are sent to keep the websocket alive but we do not receive responses from the - * secure tunnel service until a src is also connected. This is a known bug that is in their + * TODO Currently, pings are sent to keep the websocket alive but we do not receive responses from + * the secure tunnel service until a src is also connected. This is a known bug that is in their * backlog. Once it is fixed, we should implement ping timeout checks to determine whether we are * still connected to the secure tunnel through WebSocket. */ @@ -1436,18 +1705,13 @@ int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure frame_options.opcode = AWS_WEBSOCKET_OPCODE_PING; frame_options.fin = true; secure_tunnel->vtable->aws_websocket_send_frame_fn(secure_tunnel->websocket, &frame_options); - 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(); - - if (secure_tunnel->config->on_send_data_complete) { - secure_tunnel->config->on_send_data_complete(error_code, secure_tunnel->config->user_data); - } } else { if (s_aws_secure_tunnel_active_stream_check(secure_tunnel, current_operation->message_view)) { /* Send the Data message through the WebSocket */ @@ -1463,9 +1727,6 @@ int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure aws_secure_tunnel_message_view_log(current_operation->message_view, AWS_LL_DEBUG); } else { error_code = aws_last_error(); - if (secure_tunnel->config->on_send_data_complete) { - secure_tunnel->config->on_send_data_complete(error_code, secure_tunnel->config->user_data); - } if (current_operation->message_view->service_id && current_operation->message_view->service_id->len > 0) { AWS_LOGF_DEBUG( @@ -1492,6 +1753,10 @@ int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure } } } + 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); + } break; @@ -1506,38 +1771,106 @@ int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure error_code, aws_error_debug_str(error_code)); } else { - /* Send the Stream Start message through the WebSocket */ + /* + * Protocol version will always be V3 as a Source Device. + * Send the Stream Start message through the WebSocket. + */ 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); + } 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) { - /* Send the Stream Reset message through the WebSocket */ - if (s_secure_tunneling_send(secure_tunnel, current_operation->message_view)) { - error_code = aws_last_error(); + if (current_operation->message_view->connection_id == 0) { + /* Send the Stream Reset message through the WebSocket */ + if (s_secure_tunneling_send(secure_tunnel, current_operation->message_view)) { + error_code = aws_last_error(); + } else { + s_aws_secure_tunnel_set_stream( + secure_tunnel, + current_operation->message_view->service_id, + INVALID_STREAM_ID, + current_operation->message_view->connection_id); + } + aws_secure_tunnel_message_view_log(current_operation->message_view, AWS_LL_DEBUG); } else { - s_aws_secure_tunnel_set_stream( - secure_tunnel, - current_operation->message_view->service_id, - INVALID_STREAM_ID, - current_operation->message_view->connection_id); + AWS_LOGF_WARN( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: failed to send STREAM RESET message must not have a connection id", + (void *)secure_tunnel); } - aws_secure_tunnel_message_view_log(current_operation->message_view, AWS_LL_DEBUG); } break; case AWS_STOT_CONNECTION_START: - // Steve Todo Logic for Connection Start + /* 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)) { + error_code = aws_last_error(); + } else if ((*current_operation->vtable->aws_secure_tunnel_operation_set_connection_start_id)( + current_operation, secure_tunnel)) { + error_code = aws_last_error(); + } else { + 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 CONNECTION START 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); + } + + if (error_code && secure_tunnel->config->on_send_message_complete) { + secure_tunnel->config->on_send_message_complete( + AWS_SECURE_TUNNEL_MT_CONNECTION_START, error_code, secure_tunnel->config->user_data); + } break; + case AWS_STOT_CONNECTION_RESET: - // Steve TODO Logic for Connection Reset + if ((*current_operation->vtable->aws_secure_tunnel_operation_assign_stream_id_fn)( + current_operation, secure_tunnel)) { + error_code = aws_last_error(); + } else { + error_code = + s_aws_secure_tunnel_remove_connection_id(secure_tunnel, current_operation->message_view); + + /* + * If we have a stream id, we should send the CONNECTION RESET message even if we do not have a + * currently active stream + */ + if (s_secure_tunneling_send(secure_tunnel, current_operation->message_view)) { + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: failed to send DATA message with error %d(%s)", + (void *)secure_tunnel, + aws_last_error(), + aws_error_debug_str(aws_last_error())); + } + } + + if (error_code) { + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: failed to send CONNECTION RESET message with error %d(%s)", + (void *)secure_tunnel, + error_code, + aws_error_debug_str(error_code)); + } + break; case AWS_STOT_NONE: @@ -1719,7 +2052,8 @@ static uint64_t s_compute_next_service_time_secure_tunnel_connected( static uint64_t s_compute_next_service_time_secure_tunnel_clean_disconnect( struct aws_secure_tunnel *secure_tunnel, uint64_t now) { - return s_aws_secure_tunnel_compute_operational_state_service_time(secure_tunnel, now); + (void)secure_tunnel; + return now; } static uint64_t s_compute_next_service_time_secure_tunnel_websocket_shutdown( @@ -1877,6 +2211,24 @@ static void s_service_state_connected(struct aws_secure_tunnel *secure_tunnel, u static void s_service_state_clean_disconnect(struct aws_secure_tunnel *secure_tunnel, uint64_t now) { (void)now; + + enum aws_secure_tunnel_state desired_state = secure_tunnel->desired_state; + if (desired_state != AWS_STS_CONNECTED) { + AWS_LOGF_INFO( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: channel shutdown due to user Stop request", + (void *)secure_tunnel); + s_secure_tunnel_shutdown_websocket(secure_tunnel, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_USER_REQUESTED_STOP); + return; + } + + 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); + return; + } + if (aws_secure_tunnel_service_operational_state(secure_tunnel)) { int error_code = aws_last_error(); AWS_LOGF_ERROR( @@ -2067,7 +2419,15 @@ int aws_secure_tunnel_send_message( secure_tunnel->allocator, secure_tunnel, message_options, AWS_STOT_MESSAGE); if (message_op == NULL) { - return AWS_OP_ERR; + return aws_last_error(); + } + + /* + * We apply V3 protocol to all outbound messages to prevent disconnection by the secure tunnel service due to + * protocol switching + */ + if (message_op->options_storage.storage_view.connection_id == 0) { + message_op->options_storage.storage_view.connection_id = 1; } AWS_LOGF_DEBUG( @@ -2105,6 +2465,14 @@ int aws_secure_tunnel_stream_start( return AWS_OP_ERR; } + /* + * We apply V3 protocol to all outbound messages to prevent disconnection by the secure tunnel service due to + * protocol switching + */ + if (message_op->options_storage.storage_view.connection_id == 0) { + message_op->options_storage.storage_view.connection_id = 1; + } + AWS_LOGF_DEBUG( AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: Submitting STREAM START operation (%p)", @@ -2133,6 +2501,11 @@ int aws_secure_tunnel_connection_start( return AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INCORRECT_MODE; } + 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); @@ -2210,7 +2583,7 @@ int aws_secure_tunnel_connection_reset( AWS_LOGF_DEBUG( AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: Submitting STREAM RESET operation (%p)", + "id=%p: Submitting CONNECTION RESET operation (%p)", (void *)secure_tunnel, (void *)message_op); diff --git a/source/secure_tunneling_operations.c b/source/secure_tunneling_operations.c index db04b51c..4418cd61 100644 --- a/source/secure_tunneling_operations.c +++ b/source/secure_tunneling_operations.c @@ -15,6 +15,10 @@ #define INVALID_STREAM_ID 0 +/********************************************************************************************************************* + * SERVICE AND CONNECTION ID HASH TABLE + ********************************************************************************************************************/ + static const uint32_t s_bit_scrambling_magic = 0x45d9f3bU; static const uint32_t s_bit_shift_magic = 16U; @@ -32,7 +36,7 @@ bool aws_secure_tunnel_connection_id_eq(const void *a, const void *b) { return *(const uint32_t *)a == *(const uint32_t *)b; } -static void s_destroy_connection_id(void *data) { +void aws_destroy_connection_id(void *data) { struct aws_connection_id_element *elem = data; aws_mem_release(elem->allocator, elem); } @@ -80,7 +84,7 @@ struct aws_service_id_element *aws_service_id_element_new( aws_secure_tunnel_hash_connection_id, aws_secure_tunnel_connection_id_eq, NULL, - s_destroy_connection_id)) { + aws_destroy_connection_id)) { goto error; } @@ -92,7 +96,7 @@ struct aws_service_id_element *aws_service_id_element_new( } /********************************************************************************************************************* - * Operation base + * OPERATION BASE ********************************************************************************************************************/ struct aws_secure_tunnel_operation *aws_secure_tunnel_operation_acquire(struct aws_secure_tunnel_operation *operation) { @@ -137,10 +141,11 @@ static struct aws_secure_tunnel_operation_vtable s_empty_operation_vtable = { .aws_secure_tunnel_operation_completion_fn = NULL, .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, }; /********************************************************************************************************************* - * Message + * MESSAGE ********************************************************************************************************************/ int aws_secure_tunnel_message_view_validate(const struct aws_secure_tunnel_message_view *message_view) { @@ -483,9 +488,83 @@ static int s_aws_secure_tunnel_operation_message_set_next_stream_id( return AWS_OP_SUCCESS; } +static int s_aws_secure_tunnel_operation_set_connection_start_id( + 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; + + /* + * Get the appropriate connection id hash table to add the new connection id to + */ + struct aws_hash_table *table_to_put_in = NULL; + if (message_view->service_id == NULL || message_view->service_id->len == 0) { + table_to_put_in = &secure_tunnel->config->connection_ids; + } else { + struct aws_hash_element *elem = NULL; + aws_hash_table_find(&secure_tunnel->config->service_ids, message_view->service_id, &elem); + if (elem == NULL) { + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: invalid service_id:'" PRInSTR + "' attempted to be used to start a stream using a connection id (%d)", + (void *)message_view, + AWS_BYTE_CURSOR_PRI(*message_view->service_id), + message_view->connection_id); + aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_SERVICE_ID); + } else { + struct aws_service_id_element *service_id_elem = elem->value; + table_to_put_in = &service_id_elem->connection_ids; + } + } + + if (message_view->connection_id != 0) { + struct aws_connection_id_element *connection_id_elem = NULL; + connection_id_elem = aws_connection_id_element_new(secure_tunnel->allocator, message_view->connection_id); + struct aws_hash_element *connection_elem = NULL; + + aws_hash_table_find(table_to_put_in, &connection_id_elem->connection_id, &connection_elem); + /* + * If the connection id is already stored, it does not need to be put into the hash table. The CONNECTION START + * will still be sent but if there is already an active stream on this connection id on the Destination, they + * will send a CONNECTION RESET to close it. + */ + if (connection_elem == NULL) { + aws_hash_table_put(table_to_put_in, &connection_id_elem->connection_id, connection_id_elem, NULL); + } else { + aws_destroy_connection_id(connection_id_elem); + } + + if (message_view->service_id == NULL || message_view->service_id->len == 0) { + AWS_LOGF_INFO( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Stream started using connection id (%d)", + (void *)message_view, + message_view->connection_id); + } else { + AWS_LOGF_INFO( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Stream started on service_id:'" PRInSTR "' using connection id (%d)", + (void *)message_view, + AWS_BYTE_CURSOR_PRI(*message_view->service_id), + message_view->connection_id); + } + } else { + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Connection Id can not be set to 0 on a CONNECTION START", + (void *)message_view); + aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_CONNECTION_ID); + } + + return AWS_OP_SUCCESS; +} + 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, }; static void s_destroy_operation_message(void *object) { @@ -697,6 +776,10 @@ void aws_secure_tunnel_options_storage_destroy(struct aws_secure_tunnel_options_ return; } + if (storage->restore_stream_message_view != NULL) { + aws_secure_tunnel_message_storage_clean_up(&storage->restore_stream_message); + storage->restore_stream_message_view = NULL; + } aws_client_bootstrap_release(storage->bootstrap); aws_http_proxy_config_destroy(storage->http_proxy_config); aws_string_destroy(storage->endpoint_host); @@ -799,7 +882,7 @@ struct aws_secure_tunnel_options_storage *aws_secure_tunnel_options_storage_new( aws_secure_tunnel_hash_connection_id, aws_secure_tunnel_connection_id_eq, NULL, - s_destroy_connection_id)) { + aws_destroy_connection_id)) { goto error; } @@ -809,7 +892,7 @@ struct aws_secure_tunnel_options_storage *aws_secure_tunnel_options_storage_new( storage->local_proxy_mode = options->local_proxy_mode; storage->on_connection_complete = options->on_connection_complete; storage->on_connection_shutdown = options->on_connection_shutdown; - storage->on_send_data_complete = options->on_send_data_complete; + storage->on_send_message_complete = options->on_send_message_complete; storage->on_stream_start = options->on_stream_start; storage->on_stream_reset = options->on_stream_reset; storage->on_connection_start = options->on_connection_start; @@ -852,6 +935,7 @@ struct data_tunnel_pair *aws_secure_tunnel_data_tunnel_pair_new( struct data_tunnel_pair *pair = aws_mem_calloc(allocator, 1, sizeof(struct data_tunnel_pair)); pair->allocator = allocator; pair->secure_tunnel = secure_tunnel; + pair->type = message_view->type; pair->length_prefix_written = false; if (aws_iot_st_msg_serialize_from_view(&pair->buf, allocator, message_view)) { AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Failure serializing message"); @@ -880,6 +964,10 @@ const char *aws_secure_tunnel_operation_type_to_c_string(enum aws_secure_tunnel_ return "STREAM RESET"; case AWS_STOT_STREAM_START: return "STREAM START"; + case AWS_STOT_CONNECTION_START: + return "CONNECTION START"; + case AWS_STOT_CONNECTION_RESET: + return "CONNECTION RESET"; default: return "UNKNOWN"; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 19bf90aa..b6194aca 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -19,15 +19,30 @@ if (UNIX AND NOT APPLE) add_test_case(devicedefender_publish_failure_callback_invoked) endif() -add_test_case(secure_tunneling_functionality_connect_test) -add_test_case(secure_tunneling_functionality_client_token_test) -add_test_case(secure_tunneling_fail_and_retry_connection_test) -add_test_case(secure_tunneling_store_service_ids_test) -add_test_case(secure_tunneling_receive_stream_start_test) -add_test_case(secure_tunneling_rejected_service_id_stream_start_test) -add_test_case(secure_tunneling_close_stream_on_stream_reset_test) -add_test_case(secure_tunneling_session_reset_test) -add_test_case(secure_tunneling_serializer_data_message_test) +add_net_test_case(secure_tunneling_functionality_connect_test) +add_net_test_case(secure_tunneling_functionality_client_token_test) +add_net_test_case(secure_tunneling_fail_and_retry_connection_test) +add_net_test_case(secure_tunneling_store_service_ids_test) +add_net_test_case(secure_tunneling_receive_stream_start_test) +add_net_test_case(secure_tunneling_rejected_service_id_stream_start_test) +add_net_test_case(secure_tunneling_close_stream_on_stream_reset_test) +add_net_test_case(secure_tunneling_ignore_stream_reset_for_inactive_stream_test) +add_net_test_case(secure_tunneling_session_reset_test) +add_net_test_case(secure_tunneling_serializer_data_message_test) +add_net_test_case(secure_tunneling_max_payload_test) +add_net_test_case(secure_tunneling_max_payload_exceed_test) +add_net_test_case(secure_tunneling_receive_connection_start_test) +add_net_test_case(secure_tunneling_ignore_inactive_stream_message_test) +add_net_test_case(secure_tunneling_ignore_inactive_connection_id_message_test) +add_net_test_case(secure_tunneling_v1_to_v2_stream_start_test) +add_net_test_case(secure_tunneling_v1_to_v3_stream_start_test) +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) generate_test_driver(${PROJECT_NAME}-tests) diff --git a/tests/secure_tunnel_tests.c b/tests/secure_tunnel_tests.c index c5678ae3..d307df40 100644 --- a/tests/secure_tunnel_tests.c +++ b/tests/secure_tunnel_tests.c @@ -38,6 +38,18 @@ AWS_STATIC_STRING_FROM_LITERAL(s_payload_text, "IAmABunchOfPayloadText"); # define LOCAL_SOCK_TEST_PATTERN "testsock%llu.sock" #endif +static uint8_t s_too_long_for_uint16[UINT16_MAX + 1]; + +static struct aws_byte_cursor s_payload_cursor_max_size_exceeded = { + .ptr = s_too_long_for_uint16, + .len = AWS_IOT_ST_MAX_PAYLOAD_SIZE + 1, +}; + +static struct aws_byte_cursor s_payload_cursor_max_size = { + .ptr = s_too_long_for_uint16, + .len = AWS_IOT_ST_MAX_PAYLOAD_SIZE, +}; + struct aws_secure_tunnel_mock_websocket_vtable { aws_websocket_on_connection_setup_fn *on_connection_setup_fn; aws_websocket_on_connection_shutdown_fn *on_connection_shutdown_fn; @@ -98,14 +110,23 @@ struct aws_secure_tunnel_mock_test_fixture { bool secure_tunnel_stream_started; bool secure_tunnel_bad_stream_request; bool secure_tunnel_stream_reset_received; + bool secure_tunnel_connection_started; + bool secure_tunnel_bad_connection_request; + bool secure_tunnel_connection_reset_received; bool secure_tunnel_session_reset_received; struct aws_byte_buf last_message_payload_buf; int secure_tunnel_message_received_count; + int secure_tunnel_message_sent_count; int secure_tunnel_stream_started_count; int secure_tunnel_stream_started_count_target; - int secure_tunnel_message_count_target; + int secure_tunnel_connection_started_count; + int secure_tunnel_connection_started_count_target; + int secure_tunnel_message_received_count_target; + int secure_tunnel_message_sent_count_target; + int secure_tunnel_message_sent_connection_reset_count; + int secure_tunnel_message_sent_data_count; }; /***************************************************************************************************************** @@ -156,7 +177,11 @@ static void s_on_test_secure_tunnel_message_received( aws_condition_variable_notify_all(&test_fixture->signal); } -static void s_on_test_secure_tunnel_send_data_complete(int error_code, void *user_data) { +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; } @@ -217,6 +242,40 @@ static void s_on_test_secure_tunnel_on_stream_start( aws_condition_variable_notify_all(&test_fixture->signal); } +static void s_on_test_secure_tunnel_on_connection_start( + const struct aws_secure_tunnel_message_view *message, + int error_code, + void *user_data) { + (void)message; + + struct aws_secure_tunnel_mock_test_fixture *test_fixture = user_data; + + aws_mutex_lock(&test_fixture->lock); + if (error_code == AWS_OP_SUCCESS) { + test_fixture->secure_tunnel_connection_started = true; + test_fixture->secure_tunnel_connection_started_count++; + } else { + test_fixture->secure_tunnel_bad_connection_request = true; + } + aws_mutex_unlock(&test_fixture->lock); + aws_condition_variable_notify_all(&test_fixture->signal); +} + +static void s_on_test_secure_tunnel_on_connection_reset( + const struct aws_secure_tunnel_message_view *message, + int error_code, + void *user_data) { + (void)message; + (void)error_code; + + struct aws_secure_tunnel_mock_test_fixture *test_fixture = user_data; + + aws_mutex_lock(&test_fixture->lock); + test_fixture->secure_tunnel_connection_reset_received = true; + aws_mutex_unlock(&test_fixture->lock); + aws_condition_variable_notify_all(&test_fixture->signal); +} + /***************************************************************************************************************** * SECURE TUNNEL STATUS CHECKS *****************************************************************************************************************/ @@ -269,6 +328,54 @@ static void s_wait_for_stream_started(struct aws_secure_tunnel_mock_test_fixture aws_mutex_unlock(&test_fixture->lock); } +static bool s_has_secure_tunnel_connection_started(void *arg) { + struct aws_secure_tunnel_mock_test_fixture *test_fixture = arg; + return test_fixture->secure_tunnel_connection_started; +} + +static void s_wait_for_connection_started(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_connection_started, test_fixture); + aws_mutex_unlock(&test_fixture->lock); +} + +static bool s_has_secure_tunnel_bad_connection_started(void *arg) { + struct aws_secure_tunnel_mock_test_fixture *test_fixture = arg; + return test_fixture->secure_tunnel_bad_connection_request; +} + +static void s_wait_for_bad_connection_started(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_bad_connection_started, test_fixture); + aws_mutex_unlock(&test_fixture->lock); +} + +static bool s_has_secure_tunnel_connection_reset_message_sent(void *arg) { + struct aws_secure_tunnel_mock_test_fixture *test_fixture = arg; + return test_fixture->secure_tunnel_message_sent_connection_reset_count > 0; +} + +static void s_wait_for_connection_reset_message_sent(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_connection_reset_message_sent, test_fixture); + aws_mutex_unlock(&test_fixture->lock); +} + +static bool s_has_secure_tunnel_connection_reset_received(void *arg) { + struct aws_secure_tunnel_mock_test_fixture *test_fixture = arg; + return test_fixture->secure_tunnel_connection_reset_received; +} + +static void s_wait_for_connection_reset_received(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_connection_reset_received, test_fixture); + aws_mutex_unlock(&test_fixture->lock); +} + static bool s_has_secure_tunnel_bad_stream_request(void *arg) { struct aws_secure_tunnel_mock_test_fixture *test_fixture = arg; return test_fixture->secure_tunnel_bad_stream_request; @@ -319,7 +426,8 @@ static void s_wait_for_session_reset_received(struct aws_secure_tunnel_mock_test static bool s_has_secure_tunnel_n_messages_received(void *arg) { struct aws_secure_tunnel_mock_test_fixture *test_fixture = arg; - return test_fixture->secure_tunnel_stream_started_count == test_fixture->secure_tunnel_message_count_target; + return test_fixture->secure_tunnel_message_received_count == + test_fixture->secure_tunnel_message_received_count_target; } static void s_wait_for_n_messages_received(struct aws_secure_tunnel_mock_test_fixture *test_fixture) { @@ -381,6 +489,7 @@ int aws_websocket_client_connect_mock_fn(const struct aws_websocket_client_conne .websocket = pointer}; (test_fixture->websocket_function_table->on_connection_setup_fn)(&websocket_setup, secure_tunnel); + secure_tunnel->websocket = pointer; struct aws_byte_cursor service_1 = aws_byte_cursor_from_string(s_service_id_1); struct aws_byte_cursor service_2 = aws_byte_cursor_from_string(s_service_id_2); @@ -398,11 +507,40 @@ int aws_websocket_client_connect_mock_fn(const struct aws_websocket_client_conne return AWS_OP_SUCCESS; } +void aws_secure_tunnel_test__on_message_received( + struct aws_secure_tunnel *secure_tunnel, + struct aws_secure_tunnel_message_view *message_view) { + (void)message_view; + struct aws_secure_tunnel_mock_test_fixture *test_fixture = secure_tunnel->config->user_data; + + aws_mutex_lock(&test_fixture->lock); + test_fixture->secure_tunnel_message_sent_count++; + switch (message_view->type) { + case AWS_SECURE_TUNNEL_MT_DATA: + test_fixture->secure_tunnel_message_sent_data_count++; + break; + case AWS_SECURE_TUNNEL_MT_CONNECTION_RESET: + test_fixture->secure_tunnel_message_sent_connection_reset_count++; + break; + default: + break; + } + aws_mutex_unlock(&test_fixture->lock); + aws_condition_variable_notify_all(&test_fixture->signal); +} + int aws_websocket_send_frame_mock_fn( struct aws_websocket *websocket, const struct aws_websocket_send_frame_options *options) { - (void)websocket; - (void)options; + void *pointer = websocket; + struct aws_secure_tunnel_mock_test_fixture *test_fixture = pointer; + + struct data_tunnel_pair *pair = options->user_data; + aws_secure_tunnel_deserialize_message_from_cursor( + test_fixture->secure_tunnel, &pair->cur, &aws_secure_tunnel_test__on_message_received); + + options->on_complete(websocket, AWS_OP_SUCCESS, options->user_data); + return AWS_OP_SUCCESS; } @@ -443,11 +581,14 @@ int aws_secure_tunnel_mock_test_fixture_init( test_fixture->socket_options = socket_options; test_fixture->secure_tunnel_elg = aws_event_loop_group_new_default(allocator, 4, NULL); + ASSERT_NOT_NULL(test_fixture->secure_tunnel_elg); + struct aws_host_resolver_default_options resolver_options = { .el_group = test_fixture->secure_tunnel_elg, .max_entries = 1, }; test_fixture->host_resolver = aws_host_resolver_new_default(allocator, &resolver_options); + ASSERT_NOT_NULL(test_fixture->host_resolver); struct aws_client_bootstrap_options bootstrap_options = { .event_loop_group = test_fixture->secure_tunnel_elg, @@ -456,6 +597,7 @@ int aws_secure_tunnel_mock_test_fixture_init( }; test_fixture->secure_tunnel_bootstrap = aws_client_bootstrap_new(allocator, &bootstrap_options); + ASSERT_NOT_NULL(test_fixture->secure_tunnel_bootstrap); uint64_t timestamp = 0; ASSERT_SUCCESS(aws_sys_clock_get_ticks(×tamp)); @@ -476,15 +618,18 @@ int aws_secure_tunnel_mock_test_fixture_init( options->secure_tunnel_options->on_connection_complete = s_on_test_secure_tunnel_connection_complete; options->secure_tunnel_options->on_connection_shutdown = s_on_test_secure_tunnel_connection_shutdown; options->secure_tunnel_options->on_message_received = s_on_test_secure_tunnel_message_received; - options->secure_tunnel_options->on_send_data_complete = s_on_test_secure_tunnel_send_data_complete; + options->secure_tunnel_options->on_send_message_complete = s_on_test_secure_tunnel_send_message_complete; options->secure_tunnel_options->on_session_reset = s_on_test_secure_tunnel_on_session_reset; options->secure_tunnel_options->on_stopped = s_on_test_secure_tunnel_on_stopped; options->secure_tunnel_options->on_stream_reset = s_on_test_secure_tunnel_on_stream_reset; options->secure_tunnel_options->on_stream_start = s_on_test_secure_tunnel_on_stream_start; + options->secure_tunnel_options->on_connection_start = s_on_test_secure_tunnel_on_connection_start; + options->secure_tunnel_options->on_connection_reset = s_on_test_secure_tunnel_on_connection_reset; options->secure_tunnel_options->on_termination_complete = s_on_test_secure_tunnel_termination; options->secure_tunnel_options->secure_tunnel_on_termination_user_data = test_fixture; test_fixture->secure_tunnel = aws_secure_tunnel_new(allocator, options->secure_tunnel_options); + ASSERT_NOT_NULL(test_fixture->secure_tunnel); /* Replace Secure Tunnel's vtable functions */ test_fixture->secure_tunnel_vtable = *aws_secure_tunnel_get_default_vtable(); @@ -499,8 +644,30 @@ int aws_secure_tunnel_mock_test_fixture_init( return AWS_OP_SUCCESS; } -void aws_secure_tunnel_mock_test_fixture_clean_up(struct aws_secure_tunnel_mock_test_fixture *test_fixture) { +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) { + + aws_http_library_init(allocator); + aws_iotdevice_library_init(allocator); + + s_secure_tunnel_test_init_default_options(test_options); + + test_options->secure_tunnel_options.client_token = aws_byte_cursor_from_string(s_client_token); + + struct aws_secure_tunnel_mock_test_fixture_options test_fixture_options = { + .secure_tunnel_options = &test_options->secure_tunnel_options, + .websocket_function_table = &test_options->websocket_function_table, + }; + + aws_secure_tunnel_mock_test_fixture_init(test_fixture, allocator, &test_fixture_options); +} + +void aws_secure_tunnel_mock_test_clean_up(struct aws_secure_tunnel_mock_test_fixture *test_fixture) { + aws_secure_tunnel_release(test_fixture->secure_tunnel); s_wait_for_secure_tunnel_terminated(test_fixture); + aws_client_bootstrap_release(test_fixture->secure_tunnel_bootstrap); aws_host_resolver_release(test_fixture->host_resolver); @@ -509,13 +676,16 @@ void aws_secure_tunnel_mock_test_fixture_clean_up(struct aws_secure_tunnel_mock_ aws_byte_buf_clean_up(&test_fixture->last_message_payload_buf); aws_mutex_clean_up(&test_fixture->lock); aws_condition_variable_clean_up(&test_fixture->signal); + + aws_iotdevice_library_clean_up(); + aws_http_library_clean_up(); + aws_iotdevice_library_clean_up(); } /********************************************************************************************************************* * TESTS ********************************************************************************************************************/ -/* [Func-UC1] */ int secure_tunneling_access_token_check(const struct aws_http_headers *request_headers, void *user_data) { (void)user_data; struct aws_byte_cursor access_token_cur; @@ -531,44 +701,27 @@ int secure_tunneling_access_token_check(const struct aws_http_headers *request_h static int s_secure_tunneling_functionality_connect_test_fn(struct aws_allocator *allocator, void *ctx) { (void)ctx; - aws_http_library_init(allocator); - aws_iotdevice_library_init(allocator); struct secure_tunnel_test_options test_options; - s_secure_tunnel_test_init_default_options(&test_options); - - struct aws_secure_tunnel_mock_test_fixture_options test_fixture_options = { - .secure_tunnel_options = &test_options.secure_tunnel_options, - .websocket_function_table = &test_options.websocket_function_table, - }; - struct aws_secure_tunnel_mock_test_fixture test_fixture; - ASSERT_SUCCESS(aws_secure_tunnel_mock_test_fixture_init(&test_fixture, allocator, &test_fixture_options)); + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture); + struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; test_fixture.header_check = secure_tunneling_access_token_check; - 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); ASSERT_SUCCESS(aws_secure_tunnel_stop(secure_tunnel)); s_wait_for_connection_shutdown(&test_fixture); - aws_secure_tunnel_release(secure_tunnel); - s_wait_for_secure_tunnel_terminated(&test_fixture); - - aws_secure_tunnel_mock_test_fixture_clean_up(&test_fixture); - aws_iotdevice_library_clean_up(); - aws_http_library_clean_up(); - aws_iotdevice_library_clean_up(); + aws_secure_tunnel_mock_test_clean_up(&test_fixture); return AWS_OP_SUCCESS; } AWS_TEST_CASE(secure_tunneling_functionality_connect_test, s_secure_tunneling_functionality_connect_test_fn) -/* [Func-UC2] */ int secure_tunneling_client_token_check(const struct aws_http_headers *request_headers, void *user_data) { (void)user_data; struct aws_byte_cursor client_token_cur; @@ -584,46 +737,26 @@ int secure_tunneling_client_token_check(const struct aws_http_headers *request_h static int s_secure_tunneling_functionality_client_token_test_fn(struct aws_allocator *allocator, void *ctx) { (void)ctx; - aws_http_library_init(allocator); - aws_iotdevice_library_init(allocator); - struct secure_tunnel_test_options test_options; - s_secure_tunnel_test_init_default_options(&test_options); - test_options.secure_tunnel_options.client_token = aws_byte_cursor_from_string(s_client_token); - - struct aws_secure_tunnel_mock_test_fixture_options test_fixture_options = { - .secure_tunnel_options = &test_options.secure_tunnel_options, - .websocket_function_table = &test_options.websocket_function_table, - }; - struct aws_secure_tunnel_mock_test_fixture test_fixture; - ASSERT_SUCCESS(aws_secure_tunnel_mock_test_fixture_init(&test_fixture, allocator, &test_fixture_options)); + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture); + struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; test_fixture.header_check = secure_tunneling_client_token_check; - 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); ASSERT_SUCCESS(aws_secure_tunnel_stop(secure_tunnel)); s_wait_for_connection_shutdown(&test_fixture); - aws_secure_tunnel_release(secure_tunnel); - s_wait_for_secure_tunnel_terminated(&test_fixture); - - aws_secure_tunnel_mock_test_fixture_clean_up(&test_fixture); - aws_iotdevice_library_clean_up(); - aws_http_library_clean_up(); - aws_iotdevice_library_clean_up(); + aws_secure_tunnel_mock_test_clean_up(&test_fixture); return AWS_OP_SUCCESS; } AWS_TEST_CASE(secure_tunneling_functionality_client_token_test, s_secure_tunneling_functionality_client_token_test_fn) -/* [Func-UC3] */ - int aws_websocket_client_connect_fail_once_fn(const struct aws_websocket_client_connection_options *options) { struct aws_secure_tunnel *secure_tunnel = options->user_data; struct aws_secure_tunnel_mock_test_fixture *test_fixture = secure_tunnel->config->user_data; @@ -679,19 +812,10 @@ int aws_websocket_client_connect_fail_once_fn(const struct aws_websocket_client_ static int s_secure_tunneling_fail_and_retry_connection_test_fn(struct aws_allocator *allocator, void *ctx) { (void)ctx; - aws_http_library_init(allocator); - aws_iotdevice_library_init(allocator); - struct secure_tunnel_test_options test_options; - s_secure_tunnel_test_init_default_options(&test_options); - - struct aws_secure_tunnel_mock_test_fixture_options test_fixture_options = { - .secure_tunnel_options = &test_options.secure_tunnel_options, - .websocket_function_table = &test_options.websocket_function_table, - }; - struct aws_secure_tunnel_mock_test_fixture test_fixture; - ASSERT_SUCCESS(aws_secure_tunnel_mock_test_fixture_init(&test_fixture, allocator, &test_fixture_options)); + aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture); + struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; test_fixture.secure_tunnel_vtable = *aws_secure_tunnel_get_default_vtable(); test_fixture.secure_tunnel_vtable.aws_websocket_client_connect_fn = aws_websocket_client_connect_fail_once_fn; @@ -700,45 +824,24 @@ static int s_secure_tunneling_fail_and_retry_connection_test_fn(struct aws_alloc test_fixture.secure_tunnel_vtable.aws_websocket_close_fn = aws_websocket_close_mock_fn; test_fixture.secure_tunnel_vtable.vtable_user_data = &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); ASSERT_SUCCESS(aws_secure_tunnel_stop(secure_tunnel)); s_wait_for_connection_shutdown(&test_fixture); - aws_secure_tunnel_release(secure_tunnel); - s_wait_for_secure_tunnel_terminated(&test_fixture); - - aws_secure_tunnel_mock_test_fixture_clean_up(&test_fixture); - aws_iotdevice_library_clean_up(); - aws_http_library_clean_up(); - aws_iotdevice_library_clean_up(); + aws_secure_tunnel_mock_test_clean_up(&test_fixture); return AWS_OP_SUCCESS; } AWS_TEST_CASE(secure_tunneling_fail_and_retry_connection_test, s_secure_tunneling_fail_and_retry_connection_test_fn) -/* [Func-UC4] */ - static int s_secure_tunneling_store_service_ids_test_fn(struct aws_allocator *allocator, void *ctx) { (void)ctx; - aws_http_library_init(allocator); - aws_iotdevice_library_init(allocator); - struct secure_tunnel_test_options test_options; - s_secure_tunnel_test_init_default_options(&test_options); - - struct aws_secure_tunnel_mock_test_fixture_options test_fixture_options = { - .secure_tunnel_options = &test_options.secure_tunnel_options, - .websocket_function_table = &test_options.websocket_function_table, - }; - struct aws_secure_tunnel_mock_test_fixture test_fixture; - ASSERT_SUCCESS(aws_secure_tunnel_mock_test_fixture_init(&test_fixture, allocator, &test_fixture_options)); - + 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)); @@ -761,37 +864,18 @@ static int s_secure_tunneling_store_service_ids_test_fn(struct aws_allocator *al ASSERT_SUCCESS(aws_secure_tunnel_stop(secure_tunnel)); s_wait_for_connection_shutdown(&test_fixture); - aws_secure_tunnel_release(secure_tunnel); - s_wait_for_secure_tunnel_terminated(&test_fixture); - - aws_secure_tunnel_mock_test_fixture_clean_up(&test_fixture); - aws_iotdevice_library_clean_up(); - aws_http_library_clean_up(); - aws_iotdevice_library_clean_up(); + aws_secure_tunnel_mock_test_clean_up(&test_fixture); return AWS_OP_SUCCESS; } AWS_TEST_CASE(secure_tunneling_store_service_ids_test, s_secure_tunneling_store_service_ids_test_fn) -/* [Func-UC5] */ - static int s_secure_tunneling_receive_stream_start_test_fn(struct aws_allocator *allocator, void *ctx) { (void)ctx; - aws_http_library_init(allocator); - aws_iotdevice_library_init(allocator); - struct secure_tunnel_test_options test_options; - s_secure_tunnel_test_init_default_options(&test_options); - - struct aws_secure_tunnel_mock_test_fixture_options test_fixture_options = { - .secure_tunnel_options = &test_options.secure_tunnel_options, - .websocket_function_table = &test_options.websocket_function_table, - }; - struct aws_secure_tunnel_mock_test_fixture test_fixture; - ASSERT_SUCCESS(aws_secure_tunnel_mock_test_fixture_init(&test_fixture, allocator, &test_fixture_options)); - + 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)); @@ -819,37 +903,18 @@ static int s_secure_tunneling_receive_stream_start_test_fn(struct aws_allocator ASSERT_SUCCESS(aws_secure_tunnel_stop(secure_tunnel)); s_wait_for_connection_shutdown(&test_fixture); - aws_secure_tunnel_release(secure_tunnel); - s_wait_for_secure_tunnel_terminated(&test_fixture); - - aws_secure_tunnel_mock_test_fixture_clean_up(&test_fixture); - aws_iotdevice_library_clean_up(); - aws_http_library_clean_up(); - aws_iotdevice_library_clean_up(); + aws_secure_tunnel_mock_test_clean_up(&test_fixture); return AWS_OP_SUCCESS; } AWS_TEST_CASE(secure_tunneling_receive_stream_start_test, s_secure_tunneling_receive_stream_start_test_fn) -/* [Func-UC6] */ - static int s_secure_tunneling_rejected_service_id_stream_start_test_fn(struct aws_allocator *allocator, void *ctx) { (void)ctx; - aws_http_library_init(allocator); - aws_iotdevice_library_init(allocator); - struct secure_tunnel_test_options test_options; - s_secure_tunnel_test_init_default_options(&test_options); - - struct aws_secure_tunnel_mock_test_fixture_options test_fixture_options = { - .secure_tunnel_options = &test_options.secure_tunnel_options, - .websocket_function_table = &test_options.websocket_function_table, - }; - struct aws_secure_tunnel_mock_test_fixture test_fixture; - ASSERT_SUCCESS(aws_secure_tunnel_mock_test_fixture_init(&test_fixture, allocator, &test_fixture_options)); - + 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)); @@ -870,13 +935,7 @@ static int s_secure_tunneling_rejected_service_id_stream_start_test_fn(struct aw ASSERT_SUCCESS(aws_secure_tunnel_stop(secure_tunnel)); s_wait_for_connection_shutdown(&test_fixture); - aws_secure_tunnel_release(secure_tunnel); - s_wait_for_secure_tunnel_terminated(&test_fixture); - - aws_secure_tunnel_mock_test_fixture_clean_up(&test_fixture); - aws_iotdevice_library_clean_up(); - aws_http_library_clean_up(); - aws_iotdevice_library_clean_up(); + aws_secure_tunnel_mock_test_clean_up(&test_fixture); return AWS_OP_SUCCESS; } @@ -885,24 +944,11 @@ AWS_TEST_CASE( secure_tunneling_rejected_service_id_stream_start_test, s_secure_tunneling_rejected_service_id_stream_start_test_fn) -/* [Func-UC7] */ - static int s_secure_tunneling_close_stream_on_stream_reset_test_fn(struct aws_allocator *allocator, void *ctx) { (void)ctx; - aws_http_library_init(allocator); - aws_iotdevice_library_init(allocator); - struct secure_tunnel_test_options test_options; - s_secure_tunnel_test_init_default_options(&test_options); - - struct aws_secure_tunnel_mock_test_fixture_options test_fixture_options = { - .secure_tunnel_options = &test_options.secure_tunnel_options, - .websocket_function_table = &test_options.websocket_function_table, - }; - struct aws_secure_tunnel_mock_test_fixture test_fixture; - ASSERT_SUCCESS(aws_secure_tunnel_mock_test_fixture_init(&test_fixture, allocator, &test_fixture_options)); - + 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)); @@ -938,13 +984,7 @@ static int s_secure_tunneling_close_stream_on_stream_reset_test_fn(struct aws_al ASSERT_SUCCESS(aws_secure_tunnel_stop(secure_tunnel)); s_wait_for_connection_shutdown(&test_fixture); - aws_secure_tunnel_release(secure_tunnel); - s_wait_for_secure_tunnel_terminated(&test_fixture); - - aws_secure_tunnel_mock_test_fixture_clean_up(&test_fixture); - aws_iotdevice_library_clean_up(); - aws_http_library_clean_up(); - aws_iotdevice_library_clean_up(); + aws_secure_tunnel_mock_test_clean_up(&test_fixture); return AWS_OP_SUCCESS; } @@ -953,23 +993,61 @@ AWS_TEST_CASE( secure_tunneling_close_stream_on_stream_reset_test, s_secure_tunneling_close_stream_on_stream_reset_test_fn) -/* [Func-UC8] */ -static int s_secure_tunneling_session_reset_test_fn(struct aws_allocator *allocator, void *ctx) { +static int s_secure_tunneling_ignore_stream_reset_for_inactive_stream_test_fn( + struct aws_allocator *allocator, + void *ctx) { (void)ctx; - aws_http_library_init(allocator); - aws_iotdevice_library_init(allocator); - struct secure_tunnel_test_options test_options; - s_secure_tunnel_test_init_default_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; - struct aws_secure_tunnel_mock_test_fixture_options test_fixture_options = { - .secure_tunnel_options = &test_options.secure_tunnel_options, - .websocket_function_table = &test_options.websocket_function_table, + 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, }; + aws_secure_tunnel_send_mock_message(&test_fixture, &stream_start_message_view); - struct aws_secure_tunnel_mock_test_fixture test_fixture; - ASSERT_SUCCESS(aws_secure_tunnel_mock_test_fixture_init(&test_fixture, allocator, &test_fixture_options)); + /* Wait and confirm that a stream has been started */ + s_wait_for_stream_started(&test_fixture); + + /* Send a stream reset message for a different stream id from the server to the destination client */ + stream_start_message_view.type = AWS_SECURE_TUNNEL_MT_STREAM_RESET; + stream_start_message_view.stream_id = 2; + + aws_secure_tunnel_send_mock_message(&test_fixture, &stream_start_message_view); + aws_thread_current_sleep(aws_timestamp_convert(1, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL)); + + /* check that service id stream has been reset */ + struct aws_hash_element *elem = NULL; + aws_hash_table_find(&secure_tunnel->config->service_ids, stream_start_message_view.service_id, &elem); + ASSERT_NOT_NULL(elem); + struct aws_service_id_element *service_id_elem = elem->value; + ASSERT_TRUE(service_id_elem->stream_id == 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_ignore_stream_reset_for_inactive_stream_test, + s_secure_tunneling_ignore_stream_reset_for_inactive_stream_test_fn) + +static int s_secure_tunneling_session_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); struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); @@ -1041,13 +1119,7 @@ static int s_secure_tunneling_session_reset_test_fn(struct aws_allocator *alloca ASSERT_SUCCESS(aws_secure_tunnel_stop(secure_tunnel)); s_wait_for_connection_shutdown(&test_fixture); - aws_secure_tunnel_release(secure_tunnel); - s_wait_for_secure_tunnel_terminated(&test_fixture); - - aws_secure_tunnel_mock_test_fixture_clean_up(&test_fixture); - aws_iotdevice_library_clean_up(); - aws_http_library_clean_up(); - aws_iotdevice_library_clean_up(); + aws_secure_tunnel_mock_test_clean_up(&test_fixture); return AWS_OP_SUCCESS; } @@ -1056,20 +1128,9 @@ AWS_TEST_CASE(secure_tunneling_session_reset_test, s_secure_tunneling_session_re static int s_secure_tunneling_serializer_data_message_test_fn(struct aws_allocator *allocator, void *ctx) { (void)ctx; - aws_http_library_init(allocator); - aws_iotdevice_library_init(allocator); - struct secure_tunnel_test_options test_options; - s_secure_tunnel_test_init_default_options(&test_options); - - struct aws_secure_tunnel_mock_test_fixture_options test_fixture_options = { - .secure_tunnel_options = &test_options.secure_tunnel_options, - .websocket_function_table = &test_options.websocket_function_table, - }; - struct aws_secure_tunnel_mock_test_fixture test_fixture; - ASSERT_SUCCESS(aws_secure_tunnel_mock_test_fixture_init(&test_fixture, allocator, &test_fixture_options)); - + 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)); @@ -1084,6 +1145,9 @@ static int s_secure_tunneling_serializer_data_message_test_fn(struct aws_allocat }; 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); + /* Create and send a data message from the server to the destination client */ struct aws_byte_cursor payload_cur = aws_byte_cursor_from_string(s_payload_text); struct aws_secure_tunnel_message_view data_message_view = { @@ -1091,10 +1155,11 @@ static int s_secure_tunneling_serializer_data_message_test_fn(struct aws_allocat .service_id = &service_1, .stream_id = 1, .payload = &payload_cur, + .connection_id = 1, }; aws_secure_tunnel_send_mock_message(&test_fixture, &data_message_view); - test_fixture.secure_tunnel_message_count_target = 1; + test_fixture.secure_tunnel_message_received_count_target = 1; s_wait_for_n_messages_received(&test_fixture); struct aws_byte_cursor payload_comp_cur = { @@ -1103,21 +1168,776 @@ static int s_secure_tunneling_serializer_data_message_test_fn(struct aws_allocat }; ASSERT_CURSOR_VALUE_STRING_EQUALS(payload_comp_cur, s_payload_text); - /* Wait and confirm that a stream has been started */ - s_wait_for_stream_started(&test_fixture); - ASSERT_SUCCESS(aws_secure_tunnel_stop(secure_tunnel)); s_wait_for_connection_shutdown(&test_fixture); - aws_secure_tunnel_release(secure_tunnel); - s_wait_for_secure_tunnel_terminated(&test_fixture); + aws_secure_tunnel_mock_test_clean_up(&test_fixture); - aws_secure_tunnel_mock_test_fixture_clean_up(&test_fixture); - aws_iotdevice_library_clean_up(); - aws_http_library_clean_up(); - aws_iotdevice_library_clean_up(); + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(secure_tunneling_serializer_data_message_test, s_secure_tunneling_serializer_data_message_test_fn) + +static int s_secure_tunneling_max_payload_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, + }; + 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); + + struct aws_secure_tunnel_message_view data_message_view = { + .type = AWS_SECURE_TUNNEL_MT_DATA, + .stream_id = 0, + .service_id = &service_1, + .connection_id = 1, + .payload = &s_payload_cursor_max_size, + }; + + aws_secure_tunnel_send_message(secure_tunnel, &data_message_view); + + 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_max_payload_test, s_secure_tunneling_max_payload_test_fn) + +static int s_secure_tunneling_max_payload_exceed_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, + }; + 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); + + struct aws_secure_tunnel_message_view data_message_view = { + .type = AWS_SECURE_TUNNEL_MT_DATA, + .stream_id = 0, + .service_id = &service_1, + .connection_id = 1, + .payload = &s_payload_cursor_max_size_exceeded, + }; + + int result = aws_secure_tunnel_send_message(secure_tunnel, &data_message_view); + + ASSERT_INT_EQUALS(result, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_OPTIONS_VALIDATION); + + 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_max_payload_exceed_test, s_secure_tunneling_max_payload_exceed_test_fn) + +static int s_secure_tunneling_receive_connection_start_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, + }; + 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); + + /* check that service id stream has been set properly */ + struct aws_hash_element *elem = NULL; + aws_hash_table_find(&secure_tunnel->config->service_ids, stream_start_message_view.service_id, &elem); + ASSERT_NOT_NULL(elem); + struct aws_service_id_element *service_id_elem = elem->value; + ASSERT_TRUE(service_id_elem->stream_id == stream_start_message_view.stream_id); + + struct aws_secure_tunnel_message_view connection_start_message_view = { + .type = AWS_SECURE_TUNNEL_MT_CONNECTION_START, + .service_id = &service_1, + .stream_id = 1, + .connection_id = 2, + }; + aws_secure_tunnel_send_mock_message(&test_fixture, &connection_start_message_view); + + /* Wait and confirm that a connection has been started */ + s_wait_for_connection_started(&test_fixture); + + 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_receive_connection_start_test, s_secure_tunneling_receive_connection_start_test_fn) + +static int s_secure_tunneling_ignore_inactive_stream_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); + 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, + }; + 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); + + /* Create and send a data message on a different stream id from the server to the destination client */ + 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, + .stream_id = 2, + .payload = &payload_cur, + }; + + aws_secure_tunnel_send_mock_message(&test_fixture, &data_message_view); + + aws_thread_current_sleep(aws_timestamp_convert(1, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL)); + ASSERT_INT_EQUALS(test_fixture.secure_tunnel_message_received_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_inactive_stream_message_test, + s_secure_tunneling_ignore_inactive_stream_message_test_fn) + +static int s_secure_tunneling_ignore_inactive_connection_id_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); + 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); + + /* Create and send a data message on a different stream id from the server to the destination client */ + 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, + .stream_id = 2, + .connection_id = 4, + .payload = &payload_cur, + }; + + aws_secure_tunnel_send_mock_message(&test_fixture, &data_message_view); + + aws_thread_current_sleep(aws_timestamp_convert(1, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL)); + ASSERT_INT_EQUALS(test_fixture.secure_tunnel_message_received_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_inactive_connection_id_message_test, + s_secure_tunneling_ignore_inactive_connection_id_message_test_fn) + +static int s_secure_tunneling_v1_to_v2_stream_start_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, + .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); + + struct aws_secure_tunnel_message_view stream_start_message_view_2 = { + .type = AWS_SECURE_TUNNEL_MT_STREAM_START, + .service_id = &service_1, + .stream_id = 1, + }; + + test_fixture.secure_tunnel_stream_started = false; + + aws_secure_tunnel_send_mock_message(&test_fixture, &stream_start_message_view_2); + s_wait_for_stream_started(&test_fixture); + + /* Create and send a data message from the server to the destination client */ + 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, + .stream_id = 1, + .payload = &payload_cur, + }; + + aws_secure_tunnel_send_mock_message(&test_fixture, &data_message_view); + test_fixture.secure_tunnel_message_received_count_target = 1; + s_wait_for_n_messages_received(&test_fixture); + + 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_v1_to_v2_stream_start_test, s_secure_tunneling_v1_to_v2_stream_start_test_fn) + +static int s_secure_tunneling_v1_to_v3_stream_start_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, + .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); + + struct aws_secure_tunnel_message_view stream_start_message_view_2 = { + .type = AWS_SECURE_TUNNEL_MT_STREAM_START, + .service_id = &service_1, + .stream_id = 1, + .connection_id = 3, + }; + + test_fixture.secure_tunnel_stream_started = false; + + aws_secure_tunnel_send_mock_message(&test_fixture, &stream_start_message_view_2); + s_wait_for_stream_started(&test_fixture); + + /* Create and send a data message from the server to the destination client */ + 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, + .stream_id = 1, + .payload = &payload_cur, + .connection_id = 3, + }; + + aws_secure_tunnel_send_mock_message(&test_fixture, &data_message_view); + test_fixture.secure_tunnel_message_received_count_target = 1; + s_wait_for_n_messages_received(&test_fixture); + + 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_v1_to_v3_stream_start_test, s_secure_tunneling_v1_to_v3_stream_start_test_fn) + +static int s_secure_tunneling_v2_to_v1_stream_start_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); + test_fixture.secure_tunnel_connected_succesfully = false; + + /* Create and send a v2 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, + }; + 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); + + test_fixture.secure_tunnel_stream_started = false; + + /* check that service id stream has been set properly */ + struct aws_hash_element *elem = NULL; + aws_hash_table_find(&secure_tunnel->config->service_ids, stream_start_message_view.service_id, &elem); + ASSERT_NOT_NULL(elem); + struct aws_service_id_element *service_id_elem = elem->value; + ASSERT_TRUE(service_id_elem->stream_id == stream_start_message_view.stream_id); + + struct aws_secure_tunnel_message_view stream_start_message_view_2 = { + .type = AWS_SECURE_TUNNEL_MT_STREAM_START, + .stream_id = 2, + }; + aws_secure_tunnel_send_mock_message(&test_fixture, &stream_start_message_view_2); + + s_wait_for_connected_successfully(&test_fixture); + + /* Check that the established stream is cleared */ + elem = NULL; + aws_hash_table_find(&secure_tunnel->config->service_ids, stream_start_message_view.service_id, &elem); + service_id_elem = elem->value; + ASSERT_INT_EQUALS((int)aws_hash_table_get_entry_count(&service_id_elem->connection_ids), 0); + + /* Wait and confirm that a stream has been started */ + s_wait_for_stream_started(&test_fixture); + /* Check that V1 Stream is established */ + ASSERT_INT_EQUALS((int)aws_hash_table_get_entry_count(&secure_tunnel->config->connection_ids), 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_v2_to_v1_stream_start_test, s_secure_tunneling_v2_to_v1_stream_start_test_fn) + +static int s_secure_tunneling_v3_to_v1_stream_start_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); + test_fixture.secure_tunnel_connected_succesfully = false; + + /* Create and send a v2 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); + test_fixture.secure_tunnel_stream_started = false; + + /* check that service id stream has been set properly */ + struct aws_hash_element *elem = NULL; + aws_hash_table_find(&secure_tunnel->config->service_ids, stream_start_message_view.service_id, &elem); + ASSERT_NOT_NULL(elem); + struct aws_service_id_element *service_id_elem = elem->value; + ASSERT_TRUE(service_id_elem->stream_id == stream_start_message_view.stream_id); + ASSERT_INT_EQUALS((int)aws_hash_table_get_entry_count(&service_id_elem->connection_ids), 1); + + struct aws_secure_tunnel_message_view stream_start_message_view_2 = { + .type = AWS_SECURE_TUNNEL_MT_STREAM_START, + .stream_id = 2, + }; + aws_secure_tunnel_send_mock_message(&test_fixture, &stream_start_message_view_2); + + s_wait_for_connected_successfully(&test_fixture); + + /* Check that the established stream is cleared */ + elem = NULL; + aws_hash_table_find(&secure_tunnel->config->service_ids, stream_start_message_view.service_id, &elem); + service_id_elem = elem->value; + ASSERT_INT_EQUALS((int)aws_hash_table_get_entry_count(&service_id_elem->connection_ids), 0); + + /* Wait and confirm that a stream has been started */ + s_wait_for_stream_started(&test_fixture); + /* Check that V1 Stream is established */ + ASSERT_INT_EQUALS((int)aws_hash_table_get_entry_count(&secure_tunnel->config->connection_ids), 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_serializer_data_message_test, s_secure_tunneling_serializer_data_message_test_fn) +AWS_TEST_CASE(secure_tunneling_v3_to_v1_stream_start_test, s_secure_tunneling_v3_to_v1_stream_start_test_fn) + +static int s_secure_tunneling_v1_stream_start_v3_message_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); + 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); + test_fixture.secure_tunnel_connected_succesfully = false; + + /* 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, + .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); + + /* Check that a stream has been established */ + ASSERT_INT_EQUALS((int)aws_hash_table_get_entry_count(&secure_tunnel->config->connection_ids), 1); + + 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, + .stream_id = 1, + .payload = &payload_cur, + .connection_id = 3, + }; + aws_secure_tunnel_send_mock_message(&test_fixture, &data_message_view); + + s_wait_for_connected_successfully(&test_fixture); + + /* Check that the established stream is cleared */ + ASSERT_INT_EQUALS((int)aws_hash_table_get_entry_count(&secure_tunnel->config->connection_ids), 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_v1_stream_start_v3_message_reset_test, + s_secure_tunneling_v1_stream_start_v3_message_reset_test_fn) + +static int s_secure_tunneling_v2_stream_start_connection_start_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); + 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); + test_fixture.secure_tunnel_connected_succesfully = false; + + /* 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, + }; + 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); + + /* check that service id stream has been set properly */ + struct aws_hash_element *elem = NULL; + aws_hash_table_find(&secure_tunnel->config->service_ids, stream_start_message_view.service_id, &elem); + ASSERT_NOT_NULL(elem); + struct aws_service_id_element *service_id_elem = elem->value; + ASSERT_TRUE(service_id_elem->stream_id == stream_start_message_view.stream_id); + + struct aws_secure_tunnel_message_view connection_start_message_view = { + .type = AWS_SECURE_TUNNEL_MT_CONNECTION_START, + .service_id = &service_1, + .stream_id = 1, + .connection_id = 3, + }; + aws_secure_tunnel_send_mock_message(&test_fixture, &connection_start_message_view); + + s_wait_for_connected_successfully(&test_fixture); + + /* Check that the established stream is cleared */ + elem = NULL; + aws_hash_table_find(&secure_tunnel->config->service_ids, stream_start_message_view.service_id, &elem); + service_id_elem = elem->value; + ASSERT_INT_EQUALS((int)aws_hash_table_get_entry_count(&service_id_elem->connection_ids), 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_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); + + /* 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); + 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); + test_fixture.secure_tunnel_connected_succesfully = false; + + /* Create and send a v3 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); + + /* check that stream with connection id has been set properly */ + struct aws_hash_element *elem = NULL; + aws_hash_table_find(&secure_tunnel->config->service_ids, stream_start_message_view.service_id, &elem); + ASSERT_NOT_NULL(elem); + struct aws_service_id_element *service_id_elem = elem->value; + ASSERT_TRUE(service_id_elem->stream_id == stream_start_message_view.stream_id); + ASSERT_INT_EQUALS((int)aws_hash_table_get_entry_count(&service_id_elem->connection_ids), 1); + + /* Send a connection start */ + struct aws_secure_tunnel_message_view connection_start_message_view = { + .type = AWS_SECURE_TUNNEL_MT_CONNECTION_START, + .service_id = &service_1, + .stream_id = 1, + .connection_id = 3, + }; + aws_secure_tunnel_send_mock_message(&test_fixture, &connection_start_message_view); + + s_wait_for_connection_started(&test_fixture); + /* Check that two connections are active on the service id */ + ASSERT_INT_EQUALS((int)aws_hash_table_get_entry_count(&service_id_elem->connection_ids), 2); + + /* Send a connection reset */ + struct aws_secure_tunnel_message_view connection_reset_message_view = { + .type = AWS_SECURE_TUNNEL_MT_CONNECTION_RESET, + .service_id = &service_1, + .stream_id = 1, + .connection_id = 3, + }; + aws_secure_tunnel_send_mock_message(&test_fixture, &connection_reset_message_view); + + s_wait_for_connection_reset_received(&test_fixture); + + /* Check that only one connections is active on the service id */ + ASSERT_INT_EQUALS((int)aws_hash_table_get_entry_count(&service_id_elem->connection_ids), 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_close_stream_on_connection_reset_test, + s_secure_tunneling_close_stream_on_connection_reset_test_fn) + +static int s_secure_tunneling_existing_connection_start_send_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); + 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); + test_fixture.secure_tunnel_connected_succesfully = false; + + /* Create and send a v3 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); + + /* Send a CONNECTION START on existing connection id */ + struct aws_secure_tunnel_message_view connection_start_message_view = { + .type = AWS_SECURE_TUNNEL_MT_CONNECTION_START, + .service_id = &service_1, + .stream_id = 1, + .connection_id = 2, + }; + aws_secure_tunnel_send_mock_message(&test_fixture, &connection_start_message_view); + + /* Wait and confirm that a bad connection request was received */ + s_wait_for_bad_connection_started(&test_fixture); + + s_wait_for_connection_reset_message_sent(&test_fixture); + + // aws_thread_current_sleep(aws_timestamp_convert(1, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL)); + + /* check that stream with connection id has been closed properly */ + struct aws_hash_element *elem = NULL; + aws_hash_table_find(&secure_tunnel->config->service_ids, stream_start_message_view.service_id, &elem); + ASSERT_NOT_NULL(elem); + struct aws_service_id_element *service_id_elem = elem->value; + ASSERT_INT_EQUALS((int)aws_hash_table_get_entry_count(&service_id_elem->connection_ids), 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_existing_connection_start_send_reset_test, + s_secure_tunneling_existing_connection_start_send_reset_test_fn) From 21f18640434294f890a8b32ae6c12454153f848f Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 27 Mar 2023 09:27:29 -0700 Subject: [PATCH 11/50] test edit --- tests/secure_tunnel_tests.c | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/secure_tunnel_tests.c b/tests/secure_tunnel_tests.c index d307df40..bbdec87f 100644 --- a/tests/secure_tunnel_tests.c +++ b/tests/secure_tunnel_tests.c @@ -1155,7 +1155,6 @@ static int s_secure_tunneling_serializer_data_message_test_fn(struct aws_allocat .service_id = &service_1, .stream_id = 1, .payload = &payload_cur, - .connection_id = 1, }; aws_secure_tunnel_send_mock_message(&test_fixture, &data_message_view); From eefbc945ca7a529d497dd345caefb4e562da91fe Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 27 Mar 2023 09:34:09 -0700 Subject: [PATCH 12/50] test cmake --- tests/CMakeLists.txt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ea6ad1ad..36db970e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -19,6 +19,7 @@ if (UNIX AND NOT APPLE) add_test_case(devicedefender_publish_failure_callback_invoked) endif() +# Secure Tunnel Tets add_net_test_case(secure_tunneling_functionality_connect_test) add_net_test_case(secure_tunneling_functionality_client_token_test) add_net_test_case(secure_tunneling_fail_and_retry_connection_test) @@ -26,7 +27,6 @@ add_net_test_case(secure_tunneling_store_service_ids_test) add_net_test_case(secure_tunneling_receive_stream_start_test) add_net_test_case(secure_tunneling_rejected_service_id_stream_start_test) add_net_test_case(secure_tunneling_close_stream_on_stream_reset_test) -<<<<<<< HEAD add_net_test_case(secure_tunneling_ignore_stream_reset_for_inactive_stream_test) add_net_test_case(secure_tunneling_session_reset_test) add_net_test_case(secure_tunneling_serializer_data_message_test) @@ -44,10 +44,6 @@ 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_session_reset_test) -add_net_test_case(secure_tunneling_serializer_data_message_test) ->>>>>>> main generate_test_driver(${PROJECT_NAME}-tests) From 430cf40aad685ae1157b00d2816f7c1c55fa9132 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 27 Mar 2023 09:45:04 -0700 Subject: [PATCH 13/50] fixed connection start test --- tests/secure_tunnel_tests.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/secure_tunnel_tests.c b/tests/secure_tunnel_tests.c index bbdec87f..9f05e6a0 100644 --- a/tests/secure_tunnel_tests.c +++ b/tests/secure_tunnel_tests.c @@ -1279,6 +1279,7 @@ static int s_secure_tunneling_receive_connection_start_test_fn(struct aws_alloca .type = AWS_SECURE_TUNNEL_MT_STREAM_START, .service_id = &service_1, .stream_id = 1, + .connection_id = 1, }; aws_secure_tunnel_send_mock_message(&test_fixture, &stream_start_message_view); From 94c651ed3e36be5d4a5b7bdc3638051f6af241a0 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 27 Mar 2023 10:24:59 -0700 Subject: [PATCH 14/50] checking on reconnect tests improved --- tests/secure_tunnel_tests.c | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/tests/secure_tunnel_tests.c b/tests/secure_tunnel_tests.c index 9f05e6a0..f31d2ffd 100644 --- a/tests/secure_tunnel_tests.c +++ b/tests/secure_tunnel_tests.c @@ -127,6 +127,8 @@ 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; + int secure_tunnel_connected_succesfully_count; + int secure_tunnel_connected_succesfully_count_target; }; /***************************************************************************************************************** @@ -143,6 +145,7 @@ static void s_on_test_secure_tunnel_connection_complete( aws_mutex_lock(&test_fixture->lock); if (error_code == 0) { test_fixture->secure_tunnel_connected_succesfully = true; + test_fixture->secure_tunnel_connected_succesfully_count++; } else { test_fixture->secure_tunnel_connection_failed = true; } @@ -304,6 +307,19 @@ static void s_wait_for_connected_successfully(struct aws_secure_tunnel_mock_test aws_mutex_unlock(&test_fixture->lock); } +static bool s_has_secure_tunnel_connected_succesfully_n_times(void *arg) { + struct aws_secure_tunnel_mock_test_fixture *test_fixture = arg; + return test_fixture->secure_tunnel_connected_succesfully_count == + test_fixture->secure_tunnel_connected_succesfully_count_target; +} + +static void s_wait_for_connected_successfully_n_times(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_connected_succesfully_n_times, test_fixture); + aws_mutex_unlock(&test_fixture->lock); +} + static bool s_has_secure_tunnel_connection_shutdown(void *arg) { struct aws_secure_tunnel_mock_test_fixture *test_fixture = arg; return test_fixture->secure_tunnel_connection_shutdown; @@ -1535,7 +1551,6 @@ static int s_secure_tunneling_v2_to_v1_stream_start_test_fn(struct aws_allocator ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); s_wait_for_connected_successfully(&test_fixture); - test_fixture.secure_tunnel_connected_succesfully = false; /* Create and send a v2 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); @@ -1564,7 +1579,8 @@ static int s_secure_tunneling_v2_to_v1_stream_start_test_fn(struct aws_allocator }; aws_secure_tunnel_send_mock_message(&test_fixture, &stream_start_message_view_2); - s_wait_for_connected_successfully(&test_fixture); + test_fixture.secure_tunnel_connected_succesfully_count_target = 2; + s_wait_for_connected_successfully_n_times(&test_fixture); /* Check that the established stream is cleared */ elem = NULL; @@ -1596,7 +1612,6 @@ static int s_secure_tunneling_v3_to_v1_stream_start_test_fn(struct aws_allocator ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); s_wait_for_connected_successfully(&test_fixture); - test_fixture.secure_tunnel_connected_succesfully = false; /* Create and send a v2 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); @@ -1626,7 +1641,8 @@ static int s_secure_tunneling_v3_to_v1_stream_start_test_fn(struct aws_allocator }; aws_secure_tunnel_send_mock_message(&test_fixture, &stream_start_message_view_2); - s_wait_for_connected_successfully(&test_fixture); + test_fixture.secure_tunnel_connected_succesfully_count_target = 2; + s_wait_for_connected_successfully_n_times(&test_fixture); /* Check that the established stream is cleared */ elem = NULL; @@ -1658,7 +1674,6 @@ static int s_secure_tunneling_v1_stream_start_v3_message_reset_test_fn(struct aw ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); s_wait_for_connected_successfully(&test_fixture); - test_fixture.secure_tunnel_connected_succesfully = false; /* 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); @@ -1684,7 +1699,8 @@ static int s_secure_tunneling_v1_stream_start_v3_message_reset_test_fn(struct aw }; aws_secure_tunnel_send_mock_message(&test_fixture, &data_message_view); - s_wait_for_connected_successfully(&test_fixture); + test_fixture.secure_tunnel_connected_succesfully_count_target = 2; + s_wait_for_connected_successfully_n_times(&test_fixture); /* Check that the established stream is cleared */ ASSERT_INT_EQUALS((int)aws_hash_table_get_entry_count(&secure_tunnel->config->connection_ids), 0); @@ -1712,7 +1728,6 @@ static int s_secure_tunneling_v2_stream_start_connection_start_reset_test_fn( ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); s_wait_for_connected_successfully(&test_fixture); - test_fixture.secure_tunnel_connected_succesfully = false; /* 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); @@ -1741,7 +1756,8 @@ static int s_secure_tunneling_v2_stream_start_connection_start_reset_test_fn( }; aws_secure_tunnel_send_mock_message(&test_fixture, &connection_start_message_view); - s_wait_for_connected_successfully(&test_fixture); + test_fixture.secure_tunnel_connected_succesfully_count_target = 2; + s_wait_for_connected_successfully_n_times(&test_fixture); /* Check that the established stream is cleared */ elem = NULL; @@ -1821,7 +1837,6 @@ static int s_secure_tunneling_close_stream_on_connection_reset_test_fn(struct aw ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); s_wait_for_connected_successfully(&test_fixture); - test_fixture.secure_tunnel_connected_succesfully = false; /* Create and send a v3 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); @@ -1892,7 +1907,6 @@ static int s_secure_tunneling_existing_connection_start_send_reset_test_fn(struc ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); s_wait_for_connected_successfully(&test_fixture); - test_fixture.secure_tunnel_connected_succesfully = false; /* Create and send a v3 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); @@ -1921,8 +1935,6 @@ static int s_secure_tunneling_existing_connection_start_send_reset_test_fn(struc s_wait_for_connection_reset_message_sent(&test_fixture); - // aws_thread_current_sleep(aws_timestamp_convert(1, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL)); - /* check that stream with connection id has been closed properly */ struct aws_hash_element *elem = NULL; aws_hash_table_find(&secure_tunnel->config->service_ids, stream_start_message_view.service_id, &elem); From e6d6dbe3b90a6cc470c98e61c7e1ed4d9b63a53f Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 27 Mar 2023 15:37:59 -0700 Subject: [PATCH 15/50] fix windows race condition --- tests/secure_tunnel_tests.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/tests/secure_tunnel_tests.c b/tests/secure_tunnel_tests.c index f31d2ffd..01da3474 100644 --- a/tests/secure_tunnel_tests.c +++ b/tests/secure_tunnel_tests.c @@ -103,6 +103,7 @@ struct aws_secure_tunnel_mock_test_fixture { struct aws_mutex lock; struct aws_condition_variable signal; bool listener_destroyed; + bool secure_tunnel_connected; bool secure_tunnel_terminated; bool secure_tunnel_connected_succesfully; bool secure_tunnel_connection_shutdown; @@ -143,8 +144,9 @@ static void s_on_test_secure_tunnel_connection_complete( struct aws_secure_tunnel_mock_test_fixture *test_fixture = user_data; aws_mutex_lock(&test_fixture->lock); - if (error_code == 0) { + if (error_code == 0 && test_fixture->secure_tunnel_connected == false) { test_fixture->secure_tunnel_connected_succesfully = true; + test_fixture->secure_tunnel_connected = true; test_fixture->secure_tunnel_connected_succesfully_count++; } else { test_fixture->secure_tunnel_connection_failed = true; @@ -159,6 +161,7 @@ static void s_on_test_secure_tunnel_connection_shutdown(int error_code, void *us aws_mutex_lock(&test_fixture->lock); test_fixture->secure_tunnel_connection_shutdown = true; + test_fixture->secure_tunnel_connected = false; aws_mutex_unlock(&test_fixture->lock); aws_condition_variable_notify_all(&test_fixture->signal); } @@ -523,7 +526,7 @@ int aws_websocket_client_connect_mock_fn(const struct aws_websocket_client_conne return AWS_OP_SUCCESS; } -void aws_secure_tunnel_test__on_message_received( +void aws_secure_tunnel_test_on_message_received( struct aws_secure_tunnel *secure_tunnel, struct aws_secure_tunnel_message_view *message_view) { (void)message_view; @@ -553,7 +556,7 @@ int aws_websocket_send_frame_mock_fn( struct data_tunnel_pair *pair = options->user_data; aws_secure_tunnel_deserialize_message_from_cursor( - test_fixture->secure_tunnel, &pair->cur, &aws_secure_tunnel_test__on_message_received); + test_fixture->secure_tunnel, &pair->cur, &aws_secure_tunnel_test_on_message_received); options->on_complete(websocket, AWS_OP_SUCCESS, options->user_data); @@ -1579,10 +1582,12 @@ static int s_secure_tunneling_v2_to_v1_stream_start_test_fn(struct aws_allocator }; aws_secure_tunnel_send_mock_message(&test_fixture, &stream_start_message_view_2); + /* Client should disconnect, reconnect, and start a stream */ + test_fixture.secure_tunnel_connected_succesfully_count_target = 2; s_wait_for_connected_successfully_n_times(&test_fixture); - /* Check that the established stream is cleared */ + /* Check that the established stream on previous service id is cleared */ elem = NULL; aws_hash_table_find(&secure_tunnel->config->service_ids, stream_start_message_view.service_id, &elem); service_id_elem = elem->value; @@ -1641,6 +1646,8 @@ static int s_secure_tunneling_v3_to_v1_stream_start_test_fn(struct aws_allocator }; aws_secure_tunnel_send_mock_message(&test_fixture, &stream_start_message_view_2); + /* Client should disconnect, clear previous V3 connection and stream, reconnect, and start a V1 stream */ + test_fixture.secure_tunnel_connected_succesfully_count_target = 2; s_wait_for_connected_successfully_n_times(&test_fixture); @@ -1699,6 +1706,8 @@ static int s_secure_tunneling_v1_stream_start_v3_message_reset_test_fn(struct aw }; aws_secure_tunnel_send_mock_message(&test_fixture, &data_message_view); + /* Client should disconnect and clear the V1 stream */ + test_fixture.secure_tunnel_connected_succesfully_count_target = 2; s_wait_for_connected_successfully_n_times(&test_fixture); @@ -1756,6 +1765,8 @@ static int s_secure_tunneling_v2_stream_start_connection_start_reset_test_fn( }; aws_secure_tunnel_send_mock_message(&test_fixture, &connection_start_message_view); + /* Client should disconnect and reconnect with no active streams on receiving a wrong version connection start */ + test_fixture.secure_tunnel_connected_succesfully_count_target = 2; s_wait_for_connected_successfully_n_times(&test_fixture); From a07692cbe1c49c86145c9f40e4dd5977545ab120 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 27 Mar 2023 16:04:58 -0700 Subject: [PATCH 16/50] move condition variable notify to within lock --- tests/secure_tunnel_tests.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/secure_tunnel_tests.c b/tests/secure_tunnel_tests.c index 01da3474..45be0684 100644 --- a/tests/secure_tunnel_tests.c +++ b/tests/secure_tunnel_tests.c @@ -151,8 +151,8 @@ static void s_on_test_secure_tunnel_connection_complete( } else { test_fixture->secure_tunnel_connection_failed = true; } - aws_mutex_unlock(&test_fixture->lock); aws_condition_variable_notify_all(&test_fixture->signal); + aws_mutex_unlock(&test_fixture->lock); } static void s_on_test_secure_tunnel_connection_shutdown(int error_code, void *user_data) { @@ -162,8 +162,8 @@ static void s_on_test_secure_tunnel_connection_shutdown(int error_code, void *us aws_mutex_lock(&test_fixture->lock); test_fixture->secure_tunnel_connection_shutdown = true; test_fixture->secure_tunnel_connected = false; - aws_mutex_unlock(&test_fixture->lock); aws_condition_variable_notify_all(&test_fixture->signal); + aws_mutex_unlock(&test_fixture->lock); } static void s_on_test_secure_tunnel_message_received( @@ -179,8 +179,8 @@ static void s_on_test_secure_tunnel_message_received( .len = message->payload->len, }; aws_byte_buf_write_from_whole_cursor(&test_fixture->last_message_payload_buf, payload_cur); - aws_mutex_unlock(&test_fixture->lock); aws_condition_variable_notify_all(&test_fixture->signal); + aws_mutex_unlock(&test_fixture->lock); } static void s_on_test_secure_tunnel_send_message_complete( @@ -197,8 +197,8 @@ static void s_on_test_secure_tunnel_on_session_reset(void *user_data) { aws_mutex_lock(&test_fixture->lock); test_fixture->secure_tunnel_session_reset_received = true; - aws_mutex_unlock(&test_fixture->lock); aws_condition_variable_notify_all(&test_fixture->signal); + aws_mutex_unlock(&test_fixture->lock); } static void s_on_test_secure_tunnel_on_stopped(void *user_data) { @@ -210,8 +210,8 @@ static void s_on_test_secure_tunnel_termination(void *user_data) { aws_mutex_lock(&test_fixture->lock); test_fixture->secure_tunnel_terminated = true; - aws_mutex_unlock(&test_fixture->lock); aws_condition_variable_notify_all(&test_fixture->signal); + aws_mutex_unlock(&test_fixture->lock); } static void s_on_test_secure_tunnel_on_stream_reset( @@ -225,8 +225,8 @@ static void s_on_test_secure_tunnel_on_stream_reset( aws_mutex_lock(&test_fixture->lock); test_fixture->secure_tunnel_stream_reset_received = true; - aws_mutex_unlock(&test_fixture->lock); aws_condition_variable_notify_all(&test_fixture->signal); + aws_mutex_unlock(&test_fixture->lock); } static void s_on_test_secure_tunnel_on_stream_start( @@ -244,8 +244,8 @@ static void s_on_test_secure_tunnel_on_stream_start( } else { test_fixture->secure_tunnel_bad_stream_request = true; } - aws_mutex_unlock(&test_fixture->lock); aws_condition_variable_notify_all(&test_fixture->signal); + aws_mutex_unlock(&test_fixture->lock); } static void s_on_test_secure_tunnel_on_connection_start( @@ -263,8 +263,8 @@ static void s_on_test_secure_tunnel_on_connection_start( } else { test_fixture->secure_tunnel_bad_connection_request = true; } - aws_mutex_unlock(&test_fixture->lock); aws_condition_variable_notify_all(&test_fixture->signal); + aws_mutex_unlock(&test_fixture->lock); } static void s_on_test_secure_tunnel_on_connection_reset( @@ -278,8 +278,8 @@ static void s_on_test_secure_tunnel_on_connection_reset( aws_mutex_lock(&test_fixture->lock); test_fixture->secure_tunnel_connection_reset_received = true; - aws_mutex_unlock(&test_fixture->lock); aws_condition_variable_notify_all(&test_fixture->signal); + aws_mutex_unlock(&test_fixture->lock); } /***************************************************************************************************************** @@ -544,8 +544,8 @@ void aws_secure_tunnel_test_on_message_received( default: break; } - aws_mutex_unlock(&test_fixture->lock); aws_condition_variable_notify_all(&test_fixture->signal); + aws_mutex_unlock(&test_fixture->lock); } int aws_websocket_send_frame_mock_fn( From 4022217e701de7dd7f17eb88723c74f8b85af4fd Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 27 Mar 2023 16:20:22 -0700 Subject: [PATCH 17/50] fix logic path on a protocol version missmatch --- source/secure_tunneling.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index b61f0cab..45c797ab 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -679,6 +679,7 @@ static void s_aws_secure_tunnel_on_connection_start_received( "Protocol Version and Protocol Version used by incoming CONNECTION START message.", (void *)secure_tunnel); reset_secure_tunnel_connection(secure_tunnel); + return; } /* @@ -723,6 +724,7 @@ static void s_aws_secure_tunnel_on_connection_reset_received( "Protocol Version and Protocol Version used by incoming CONNECTION RESET message.", (void *)secure_tunnel); reset_secure_tunnel_connection(secure_tunnel); + return; } /* From 7565fadb97ae641a3e4e887734fa3828a7d8ea5d Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 27 Mar 2023 16:23:02 -0700 Subject: [PATCH 18/50] missed a spot --- source/secure_tunneling.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 45c797ab..05da33c1 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -471,6 +471,7 @@ static void s_aws_secure_tunnel_on_data_received( "Protocol Version and Protocol Version used by incoming STREAM START message.", (void *)secure_tunnel); reset_secure_tunnel_connection(secure_tunnel); + return; } /* From c74fa07af5d10f7575e2d3dfb3f857eecba1d602 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 27 Mar 2023 16:41:45 -0700 Subject: [PATCH 19/50] more race conditions --- tests/secure_tunnel_tests.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/tests/secure_tunnel_tests.c b/tests/secure_tunnel_tests.c index 45be0684..988d46f5 100644 --- a/tests/secure_tunnel_tests.c +++ b/tests/secure_tunnel_tests.c @@ -145,6 +145,7 @@ static void s_on_test_secure_tunnel_connection_complete( aws_mutex_lock(&test_fixture->lock); if (error_code == 0 && test_fixture->secure_tunnel_connected == false) { + test_fixture->secure_tunnel_connection_shutdown = false; test_fixture->secure_tunnel_connected_succesfully = true; test_fixture->secure_tunnel_connected = true; test_fixture->secure_tunnel_connected_succesfully_count++; @@ -162,6 +163,7 @@ static void s_on_test_secure_tunnel_connection_shutdown(int error_code, void *us aws_mutex_lock(&test_fixture->lock); test_fixture->secure_tunnel_connection_shutdown = true; test_fixture->secure_tunnel_connected = false; + test_fixture->secure_tunnel_stream_started = false; aws_condition_variable_notify_all(&test_fixture->signal); aws_mutex_unlock(&test_fixture->lock); } @@ -1460,9 +1462,10 @@ static int s_secure_tunneling_v1_to_v2_stream_start_test_fn(struct aws_allocator .stream_id = 1, }; - test_fixture.secure_tunnel_stream_started = false; - aws_secure_tunnel_send_mock_message(&test_fixture, &stream_start_message_view_2); + + s_wait_for_connection_shutdown(&test_fixture); + s_wait_for_stream_started(&test_fixture); /* Create and send a data message from the server to the destination client */ @@ -1516,9 +1519,10 @@ static int s_secure_tunneling_v1_to_v3_stream_start_test_fn(struct aws_allocator .connection_id = 3, }; - test_fixture.secure_tunnel_stream_started = false; - aws_secure_tunnel_send_mock_message(&test_fixture, &stream_start_message_view_2); + + s_wait_for_connection_shutdown(&test_fixture); + s_wait_for_stream_started(&test_fixture); /* Create and send a data message from the server to the destination client */ @@ -1567,8 +1571,6 @@ static int s_secure_tunneling_v2_to_v1_stream_start_test_fn(struct aws_allocator /* Wait and confirm that a stream has been started */ s_wait_for_stream_started(&test_fixture); - test_fixture.secure_tunnel_stream_started = false; - /* check that service id stream has been set properly */ struct aws_hash_element *elem = NULL; aws_hash_table_find(&secure_tunnel->config->service_ids, stream_start_message_view.service_id, &elem); @@ -1630,7 +1632,6 @@ static int s_secure_tunneling_v3_to_v1_stream_start_test_fn(struct aws_allocator /* Wait and confirm that a stream has been started */ s_wait_for_stream_started(&test_fixture); - test_fixture.secure_tunnel_stream_started = false; /* check that service id stream has been set properly */ struct aws_hash_element *elem = NULL; From a643ca7b095c5854b7105b29aa1b9a6c47734c08 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Tue, 28 Mar 2023 09:59:31 -0700 Subject: [PATCH 20/50] clean up tests for clarity --- tests/secure_tunnel_tests.c | 267 +++++++++++++++++------------------- 1 file changed, 127 insertions(+), 140 deletions(-) diff --git a/tests/secure_tunnel_tests.c b/tests/secure_tunnel_tests.c index 988d46f5..7e16dc2e 100644 --- a/tests/secure_tunnel_tests.c +++ b/tests/secure_tunnel_tests.c @@ -128,10 +128,60 @@ 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; - int secure_tunnel_connected_succesfully_count; - int secure_tunnel_connected_succesfully_count_target; }; +static bool s_secure_tunnel_check_active_stream_id( + struct aws_secure_tunnel *secure_tunnel, + struct aws_byte_cursor *service_id, + int32_t stream_id) { + if (service_id == NULL) { + return secure_tunnel->config->stream_id == stream_id; + } + + struct aws_hash_element *elem = NULL; + aws_hash_table_find(&secure_tunnel->config->service_ids, service_id, &elem); + if (elem == NULL) { + return false; + } + + struct aws_service_id_element *service_id_elem = elem->value; + if (service_id_elem->stream_id != stream_id) { + return false; + } + + return true; +} + +static bool s_secure_tunnel_check_active_connection_id( + struct aws_secure_tunnel *secure_tunnel, + struct aws_byte_cursor *service_id, + int32_t stream_id, + uint32_t connection_id) { + struct aws_hash_table *table_to_check = NULL; + if (service_id) { + struct aws_hash_element *elem = NULL; + aws_hash_table_find(&secure_tunnel->config->service_ids, service_id, &elem); + if (elem == NULL) { + return false; + } + struct aws_service_id_element *service_id_elem = elem->value; + table_to_check = &service_id_elem->connection_ids; + } else { + if (secure_tunnel->config->stream_id != stream_id) { + return false; + } + table_to_check = &secure_tunnel->config->connection_ids; + } + + struct aws_hash_element *connection_elem = NULL; + aws_hash_table_find(table_to_check, &connection_id, &connection_elem); + if (connection_elem == NULL) { + return false; + } + + return true; +} + /***************************************************************************************************************** * SECURE TUNNEL CALLBACKS *****************************************************************************************************************/ @@ -148,7 +198,6 @@ static void s_on_test_secure_tunnel_connection_complete( test_fixture->secure_tunnel_connection_shutdown = false; test_fixture->secure_tunnel_connected_succesfully = true; test_fixture->secure_tunnel_connected = true; - test_fixture->secure_tunnel_connected_succesfully_count++; } else { test_fixture->secure_tunnel_connection_failed = true; } @@ -312,19 +361,6 @@ static void s_wait_for_connected_successfully(struct aws_secure_tunnel_mock_test aws_mutex_unlock(&test_fixture->lock); } -static bool s_has_secure_tunnel_connected_succesfully_n_times(void *arg) { - struct aws_secure_tunnel_mock_test_fixture *test_fixture = arg; - return test_fixture->secure_tunnel_connected_succesfully_count == - test_fixture->secure_tunnel_connected_succesfully_count_target; -} - -static void s_wait_for_connected_successfully_n_times(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_connected_succesfully_n_times, test_fixture); - aws_mutex_unlock(&test_fixture->lock); -} - static bool s_has_secure_tunnel_connection_shutdown(void *arg) { struct aws_secure_tunnel_mock_test_fixture *test_fixture = arg; return test_fixture->secure_tunnel_connection_shutdown; @@ -915,11 +951,8 @@ static int s_secure_tunneling_receive_stream_start_test_fn(struct aws_allocator s_wait_for_stream_started(&test_fixture); /* check that service id stream has been set properly */ - struct aws_hash_element *elem = NULL; - aws_hash_table_find(&secure_tunnel->config->service_ids, stream_start_message_view.service_id, &elem); - ASSERT_NOT_NULL(elem); - struct aws_service_id_element *service_id_elem = elem->value; - ASSERT_TRUE(service_id_elem->stream_id == stream_start_message_view.stream_id); + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id( + secure_tunnel, stream_start_message_view.service_id, stream_start_message_view.stream_id)); ASSERT_SUCCESS(aws_secure_tunnel_stop(secure_tunnel)); s_wait_for_connection_shutdown(&test_fixture); @@ -987,6 +1020,9 @@ static int s_secure_tunneling_close_stream_on_stream_reset_test_fn(struct aws_al /* Wait and confirm that a stream has been started */ s_wait_for_stream_started(&test_fixture); + /* Check that stream is active */ + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, &service_1, 1)); + /* Send a stream reset message from the server to the destination client */ stream_start_message_view.type = AWS_SECURE_TUNNEL_MT_STREAM_RESET; @@ -995,12 +1031,8 @@ static int s_secure_tunneling_close_stream_on_stream_reset_test_fn(struct aws_al /* Wait for a stream reset to have been received */ s_wait_for_stream_reset_received(&test_fixture); - /* check that service id stream has been reset */ - struct aws_hash_element *elem = NULL; - aws_hash_table_find(&secure_tunnel->config->service_ids, stream_start_message_view.service_id, &elem); - ASSERT_NOT_NULL(elem); - struct aws_service_id_element *service_id_elem = elem->value; - ASSERT_TRUE(service_id_elem->stream_id == 0); + /* Check that stream id has been reset */ + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, &service_1, 0)); ASSERT_SUCCESS(aws_secure_tunnel_stop(secure_tunnel)); s_wait_for_connection_shutdown(&test_fixture); @@ -1038,19 +1070,24 @@ static int s_secure_tunneling_ignore_stream_reset_for_inactive_stream_test_fn( /* Wait and confirm that a stream has been started */ s_wait_for_stream_started(&test_fixture); + /* Check that stream is active */ + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, &service_1, 1)); + /* Send a stream reset message for a different stream id from the server to the destination client */ - stream_start_message_view.type = AWS_SECURE_TUNNEL_MT_STREAM_RESET; - stream_start_message_view.stream_id = 2; + struct aws_secure_tunnel_message_view stream_reset_message_view = { + .type = AWS_SECURE_TUNNEL_MT_STREAM_RESET, + .service_id = &service_1, + .stream_id = 2, + }; - aws_secure_tunnel_send_mock_message(&test_fixture, &stream_start_message_view); + aws_secure_tunnel_send_mock_message(&test_fixture, &stream_reset_message_view); + + /* Stream reset is ignored by client on an inactive stream id. Wait for client to process the message that should be + * ignored. */ aws_thread_current_sleep(aws_timestamp_convert(1, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL)); - /* check that service id stream has been reset */ - struct aws_hash_element *elem = NULL; - aws_hash_table_find(&secure_tunnel->config->service_ids, stream_start_message_view.service_id, &elem); - ASSERT_NOT_NULL(elem); - struct aws_service_id_element *service_id_elem = elem->value; - ASSERT_TRUE(service_id_elem->stream_id == 1); + /* Check that 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); @@ -1083,6 +1120,7 @@ static int s_secure_tunneling_session_reset_test_fn(struct aws_allocator *alloca .service_id = &service_1, .stream_id = 1, }; + aws_secure_tunnel_send_mock_message(&test_fixture, &stream_start_message_view); stream_start_message_view.service_id = &service_2; aws_secure_tunnel_send_mock_message(&test_fixture, &stream_start_message_view); @@ -1093,24 +1131,9 @@ static int s_secure_tunneling_session_reset_test_fn(struct aws_allocator *alloca s_wait_for_n_stream_started(&test_fixture); /* check that stream ids have been set */ - struct aws_hash_element *elem = NULL; - struct aws_byte_cursor service_id_1_cur = aws_byte_cursor_from_string(s_service_id_1); - aws_hash_table_find(&secure_tunnel->config->service_ids, &service_id_1_cur, &elem); - ASSERT_NOT_NULL(elem); - struct aws_service_id_element *service_id_elem = elem->value; - ASSERT_TRUE(service_id_elem->stream_id == stream_start_message_view.stream_id); - elem = NULL; - struct aws_byte_cursor service_id_2_cur = aws_byte_cursor_from_string(s_service_id_2); - aws_hash_table_find(&secure_tunnel->config->service_ids, &service_id_2_cur, &elem); - ASSERT_NOT_NULL(elem); - service_id_elem = elem->value; - ASSERT_TRUE(service_id_elem->stream_id == stream_start_message_view.stream_id); - elem = NULL; - struct aws_byte_cursor service_id_3_cur = aws_byte_cursor_from_string(s_service_id_3); - aws_hash_table_find(&secure_tunnel->config->service_ids, &service_id_3_cur, &elem); - ASSERT_NOT_NULL(elem); - service_id_elem = elem->value; - ASSERT_TRUE(service_id_elem->stream_id == stream_start_message_view.stream_id); + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, &service_1, 1)); + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, &service_2, 1)); + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, &service_3, 1)); /* Create and send a session reset message from the server to the destination client */ struct aws_secure_tunnel_message_view reset_message_view = { @@ -1121,21 +1144,9 @@ static int s_secure_tunneling_session_reset_test_fn(struct aws_allocator *alloca s_wait_for_session_reset_received(&test_fixture); /* Check that stream ids have been reset */ - elem = NULL; - aws_hash_table_find(&secure_tunnel->config->service_ids, &service_id_1_cur, &elem); - ASSERT_NOT_NULL(elem); - service_id_elem = elem->value; - ASSERT_TRUE(service_id_elem->stream_id == 0); - elem = NULL; - aws_hash_table_find(&secure_tunnel->config->service_ids, &service_id_2_cur, &elem); - ASSERT_NOT_NULL(elem); - service_id_elem = elem->value; - ASSERT_TRUE(service_id_elem->stream_id == 0); - elem = NULL; - aws_hash_table_find(&secure_tunnel->config->service_ids, &service_id_3_cur, &elem); - ASSERT_NOT_NULL(elem); - service_id_elem = elem->value; - ASSERT_TRUE(service_id_elem->stream_id == 0); + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, &service_1, 0)); + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, &service_2, 0)); + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, &service_3, 0)); ASSERT_SUCCESS(aws_secure_tunnel_stop(secure_tunnel)); s_wait_for_connection_shutdown(&test_fixture); @@ -1168,6 +1179,7 @@ static int s_secure_tunneling_serializer_data_message_test_fn(struct aws_allocat /* 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 data message from the server to the destination client */ struct aws_byte_cursor payload_cur = aws_byte_cursor_from_string(s_payload_text); @@ -1219,6 +1231,7 @@ static int s_secure_tunneling_max_payload_test_fn(struct aws_allocator *allocato /* 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)); struct aws_secure_tunnel_message_view data_message_view = { .type = AWS_SECURE_TUNNEL_MT_DATA, @@ -1261,6 +1274,7 @@ static int s_secure_tunneling_max_payload_exceed_test_fn(struct aws_allocator *a /* 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)); struct aws_secure_tunnel_message_view data_message_view = { .type = AWS_SECURE_TUNNEL_MT_DATA, @@ -1306,13 +1320,7 @@ static int s_secure_tunneling_receive_connection_start_test_fn(struct aws_alloca /* Wait and confirm that a stream has been started */ s_wait_for_stream_started(&test_fixture); - - /* check that service id stream has been set properly */ - struct aws_hash_element *elem = NULL; - aws_hash_table_find(&secure_tunnel->config->service_ids, stream_start_message_view.service_id, &elem); - ASSERT_NOT_NULL(elem); - struct aws_service_id_element *service_id_elem = elem->value; - ASSERT_TRUE(service_id_elem->stream_id == stream_start_message_view.stream_id); + ASSERT_TRUE(s_secure_tunnel_check_active_connection_id(secure_tunnel, &service_1, 1, 1)); struct aws_secure_tunnel_message_view connection_start_message_view = { .type = AWS_SECURE_TUNNEL_MT_CONNECTION_START, @@ -1324,6 +1332,7 @@ static int s_secure_tunneling_receive_connection_start_test_fn(struct aws_alloca /* Wait and confirm that a connection has been started */ s_wait_for_connection_started(&test_fixture); + 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); @@ -1356,6 +1365,7 @@ static int s_secure_tunneling_ignore_inactive_stream_message_test_fn(struct aws_ /* Wait and confirm that a stream has been started */ s_wait_for_stream_started(&test_fixture); + s_secure_tunnel_check_active_stream_id(secure_tunnel, &service_1, 1); /* Create and send a data message on a different stream id from the server to the destination client */ struct aws_byte_cursor payload_cur = aws_byte_cursor_from_string(s_payload_text); @@ -1368,6 +1378,8 @@ static int s_secure_tunneling_ignore_inactive_stream_message_test_fn(struct aws_ aws_secure_tunnel_send_mock_message(&test_fixture, &data_message_view); + /* Messages on inactive streams are ignored and no callback is emitted. Wait for client to process and ignore + * message */ aws_thread_current_sleep(aws_timestamp_convert(1, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL)); ASSERT_INT_EQUALS(test_fixture.secure_tunnel_message_received_count, 0); @@ -1407,6 +1419,7 @@ static int s_secure_tunneling_ignore_inactive_connection_id_message_test_fn( /* 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 on a different stream id from the server to the destination client */ struct aws_byte_cursor payload_cur = aws_byte_cursor_from_string(s_payload_text); @@ -1420,6 +1433,8 @@ static int s_secure_tunneling_ignore_inactive_connection_id_message_test_fn( aws_secure_tunnel_send_mock_message(&test_fixture, &data_message_view); + /* Messages on inactive streams are ignored and no callback is emitted. Wait for client to process and ignore + * message */ aws_thread_current_sleep(aws_timestamp_convert(1, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL)); ASSERT_INT_EQUALS(test_fixture.secure_tunnel_message_received_count, 0); @@ -1455,6 +1470,7 @@ static int s_secure_tunneling_v1_to_v2_stream_start_test_fn(struct aws_allocator /* 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)); struct aws_secure_tunnel_message_view stream_start_message_view_2 = { .type = AWS_SECURE_TUNNEL_MT_STREAM_START, @@ -1464,9 +1480,12 @@ static int s_secure_tunneling_v1_to_v2_stream_start_test_fn(struct aws_allocator aws_secure_tunnel_send_mock_message(&test_fixture, &stream_start_message_view_2); + /* Client should disconnect, clear previous V1 connection and stream, reconnect, and start a V2 stream */ + s_wait_for_connection_shutdown(&test_fixture); 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 data message from the server to the destination client */ struct aws_byte_cursor payload_cur = aws_byte_cursor_from_string(s_payload_text); @@ -1511,6 +1530,7 @@ static int s_secure_tunneling_v1_to_v3_stream_start_test_fn(struct aws_allocator /* 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)); struct aws_secure_tunnel_message_view stream_start_message_view_2 = { .type = AWS_SECURE_TUNNEL_MT_STREAM_START, @@ -1521,9 +1541,13 @@ static int s_secure_tunneling_v1_to_v3_stream_start_test_fn(struct aws_allocator aws_secure_tunnel_send_mock_message(&test_fixture, &stream_start_message_view_2); + /* Client should disconnect, clear previous V1 connection and stream, reconnect, and start a V3 stream */ + s_wait_for_connection_shutdown(&test_fixture); + s_wait_for_connected_successfully(&test_fixture); s_wait_for_stream_started(&test_fixture); + ASSERT_TRUE(s_secure_tunnel_check_active_connection_id(secure_tunnel, &service_1, 1, 3)); /* Create and send a data message from the server to the destination client */ struct aws_byte_cursor payload_cur = aws_byte_cursor_from_string(s_payload_text); @@ -1570,13 +1594,7 @@ static int s_secure_tunneling_v2_to_v1_stream_start_test_fn(struct aws_allocator /* Wait and confirm that a stream has been started */ s_wait_for_stream_started(&test_fixture); - - /* check that service id stream has been set properly */ - struct aws_hash_element *elem = NULL; - aws_hash_table_find(&secure_tunnel->config->service_ids, stream_start_message_view.service_id, &elem); - ASSERT_NOT_NULL(elem); - struct aws_service_id_element *service_id_elem = elem->value; - ASSERT_TRUE(service_id_elem->stream_id == stream_start_message_view.stream_id); + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, &service_1, 1)); struct aws_secure_tunnel_message_view stream_start_message_view_2 = { .type = AWS_SECURE_TUNNEL_MT_STREAM_START, @@ -1584,21 +1602,17 @@ static int s_secure_tunneling_v2_to_v1_stream_start_test_fn(struct aws_allocator }; aws_secure_tunnel_send_mock_message(&test_fixture, &stream_start_message_view_2); - /* Client should disconnect, reconnect, and start a stream */ + /* Client should disconnect, clear previous V2 connection and stream, reconnect, and start a V1 stream */ - test_fixture.secure_tunnel_connected_succesfully_count_target = 2; - s_wait_for_connected_successfully_n_times(&test_fixture); + s_wait_for_connection_shutdown(&test_fixture); + s_wait_for_connected_successfully(&test_fixture); - /* Check that the established stream on previous service id is cleared */ - elem = NULL; - aws_hash_table_find(&secure_tunnel->config->service_ids, stream_start_message_view.service_id, &elem); - service_id_elem = elem->value; - ASSERT_INT_EQUALS((int)aws_hash_table_get_entry_count(&service_id_elem->connection_ids), 0); + /* Confirm that previous stream has been closed */ + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, &service_1, 0)); /* Wait and confirm that a stream has been started */ s_wait_for_stream_started(&test_fixture); - /* Check that V1 Stream is established */ - ASSERT_INT_EQUALS((int)aws_hash_table_get_entry_count(&secure_tunnel->config->connection_ids), 1); + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, NULL, 2)); ASSERT_SUCCESS(aws_secure_tunnel_stop(secure_tunnel)); s_wait_for_connection_shutdown(&test_fixture); @@ -1632,14 +1646,7 @@ static int s_secure_tunneling_v3_to_v1_stream_start_test_fn(struct aws_allocator /* Wait and confirm that a stream has been started */ s_wait_for_stream_started(&test_fixture); - - /* check that service id stream has been set properly */ - struct aws_hash_element *elem = NULL; - aws_hash_table_find(&secure_tunnel->config->service_ids, stream_start_message_view.service_id, &elem); - ASSERT_NOT_NULL(elem); - struct aws_service_id_element *service_id_elem = elem->value; - ASSERT_TRUE(service_id_elem->stream_id == stream_start_message_view.stream_id); - ASSERT_INT_EQUALS((int)aws_hash_table_get_entry_count(&service_id_elem->connection_ids), 1); + ASSERT_TRUE(s_secure_tunnel_check_active_connection_id(secure_tunnel, &service_1, 1, 2)); struct aws_secure_tunnel_message_view stream_start_message_view_2 = { .type = AWS_SECURE_TUNNEL_MT_STREAM_START, @@ -1649,19 +1656,16 @@ static int s_secure_tunneling_v3_to_v1_stream_start_test_fn(struct aws_allocator /* Client should disconnect, clear previous V3 connection and stream, reconnect, and start a V1 stream */ - test_fixture.secure_tunnel_connected_succesfully_count_target = 2; - s_wait_for_connected_successfully_n_times(&test_fixture); + s_wait_for_connection_shutdown(&test_fixture); + s_wait_for_connected_successfully(&test_fixture); /* Check that the established stream is cleared */ - elem = NULL; - aws_hash_table_find(&secure_tunnel->config->service_ids, stream_start_message_view.service_id, &elem); - service_id_elem = elem->value; - ASSERT_INT_EQUALS((int)aws_hash_table_get_entry_count(&service_id_elem->connection_ids), 0); + ASSERT_FALSE(s_secure_tunnel_check_active_connection_id(secure_tunnel, &service_1, 1, 2)); /* Wait and confirm that a stream has been started */ s_wait_for_stream_started(&test_fixture); /* Check that V1 Stream is established */ - ASSERT_INT_EQUALS((int)aws_hash_table_get_entry_count(&secure_tunnel->config->connection_ids), 1); + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, NULL, 2)); ASSERT_SUCCESS(aws_secure_tunnel_stop(secure_tunnel)); s_wait_for_connection_shutdown(&test_fixture); @@ -1693,9 +1697,7 @@ static int s_secure_tunneling_v1_stream_start_v3_message_reset_test_fn(struct aw /* Wait and confirm that a stream has been started */ s_wait_for_stream_started(&test_fixture); - - /* Check that a stream has been established */ - ASSERT_INT_EQUALS((int)aws_hash_table_get_entry_count(&secure_tunnel->config->connection_ids), 1); + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, NULL, 1)); struct aws_byte_cursor payload_cur = aws_byte_cursor_from_string(s_payload_text); struct aws_secure_tunnel_message_view data_message_view = { @@ -1707,13 +1709,13 @@ static int s_secure_tunneling_v1_stream_start_v3_message_reset_test_fn(struct aw }; aws_secure_tunnel_send_mock_message(&test_fixture, &data_message_view); - /* Client should disconnect and clear the V1 stream */ + /* On receipt of an unexpected protocol version message, Client should disconnect/reconnect and clear all streams */ - test_fixture.secure_tunnel_connected_succesfully_count_target = 2; - s_wait_for_connected_successfully_n_times(&test_fixture); + s_wait_for_connection_shutdown(&test_fixture); + s_wait_for_connected_successfully(&test_fixture); /* Check that the established stream is cleared */ - ASSERT_INT_EQUALS((int)aws_hash_table_get_entry_count(&secure_tunnel->config->connection_ids), 0); + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, NULL, 0)); ASSERT_SUCCESS(aws_secure_tunnel_stop(secure_tunnel)); s_wait_for_connection_shutdown(&test_fixture); @@ -1750,13 +1752,7 @@ static int s_secure_tunneling_v2_stream_start_connection_start_reset_test_fn( /* Wait and confirm that a stream has been started */ s_wait_for_stream_started(&test_fixture); - - /* check that service id stream has been set properly */ - struct aws_hash_element *elem = NULL; - aws_hash_table_find(&secure_tunnel->config->service_ids, stream_start_message_view.service_id, &elem); - ASSERT_NOT_NULL(elem); - struct aws_service_id_element *service_id_elem = elem->value; - ASSERT_TRUE(service_id_elem->stream_id == stream_start_message_view.stream_id); + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, &service_1, 1)); struct aws_secure_tunnel_message_view connection_start_message_view = { .type = AWS_SECURE_TUNNEL_MT_CONNECTION_START, @@ -1768,14 +1764,11 @@ static int s_secure_tunneling_v2_stream_start_connection_start_reset_test_fn( /* Client should disconnect and reconnect with no active streams on receiving a wrong version connection start */ - test_fixture.secure_tunnel_connected_succesfully_count_target = 2; - s_wait_for_connected_successfully_n_times(&test_fixture); + s_wait_for_connection_shutdown(&test_fixture); + s_wait_for_connected_successfully(&test_fixture); /* Check that the established stream is cleared */ - elem = NULL; - aws_hash_table_find(&secure_tunnel->config->service_ids, stream_start_message_view.service_id, &elem); - service_id_elem = elem->value; - ASSERT_INT_EQUALS((int)aws_hash_table_get_entry_count(&service_id_elem->connection_ids), 0); + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, &service_1, 0)); ASSERT_SUCCESS(aws_secure_tunnel_stop(secure_tunnel)); s_wait_for_connection_shutdown(&test_fixture); @@ -1813,6 +1806,7 @@ static int s_secure_tunneling_ignore_outbound_inactive_connection_id_message_sen /* 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); @@ -1862,14 +1856,7 @@ static int s_secure_tunneling_close_stream_on_connection_reset_test_fn(struct aw /* Wait and confirm that a stream has been started */ s_wait_for_stream_started(&test_fixture); - - /* check that stream with connection id has been set properly */ - struct aws_hash_element *elem = NULL; - aws_hash_table_find(&secure_tunnel->config->service_ids, stream_start_message_view.service_id, &elem); - ASSERT_NOT_NULL(elem); - struct aws_service_id_element *service_id_elem = elem->value; - ASSERT_TRUE(service_id_elem->stream_id == stream_start_message_view.stream_id); - ASSERT_INT_EQUALS((int)aws_hash_table_get_entry_count(&service_id_elem->connection_ids), 1); + ASSERT_TRUE(s_secure_tunnel_check_active_connection_id(secure_tunnel, &service_1, 1, 2)); /* Send a connection start */ struct aws_secure_tunnel_message_view connection_start_message_view = { @@ -1881,8 +1868,8 @@ static int s_secure_tunneling_close_stream_on_connection_reset_test_fn(struct aw aws_secure_tunnel_send_mock_message(&test_fixture, &connection_start_message_view); s_wait_for_connection_started(&test_fixture); - /* Check that two connections are active on the service id */ - ASSERT_INT_EQUALS((int)aws_hash_table_get_entry_count(&service_id_elem->connection_ids), 2); + /* Check that connections has been started */ + ASSERT_TRUE(s_secure_tunnel_check_active_connection_id(secure_tunnel, &service_1, 1, 3)); /* Send a connection reset */ struct aws_secure_tunnel_message_view connection_reset_message_view = { @@ -1895,8 +1882,8 @@ static int s_secure_tunneling_close_stream_on_connection_reset_test_fn(struct aw s_wait_for_connection_reset_received(&test_fixture); - /* Check that only one connections is active on the service id */ - ASSERT_INT_EQUALS((int)aws_hash_table_get_entry_count(&service_id_elem->connection_ids), 1); + /* Check that connection has been closed */ + ASSERT_FALSE(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); From 6445d312de142c7c77dd37c2706569597b378049 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Tue, 28 Mar 2023 10:14:20 -0700 Subject: [PATCH 21/50] thread sanitizer fix --- tests/secure_tunnel_tests.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/secure_tunnel_tests.c b/tests/secure_tunnel_tests.c index 7e16dc2e..13e58738 100644 --- a/tests/secure_tunnel_tests.c +++ b/tests/secure_tunnel_tests.c @@ -212,6 +212,7 @@ static void s_on_test_secure_tunnel_connection_shutdown(int error_code, void *us aws_mutex_lock(&test_fixture->lock); test_fixture->secure_tunnel_connection_shutdown = true; test_fixture->secure_tunnel_connected = false; + test_fixture->secure_tunnel_connected_succesfully = false; test_fixture->secure_tunnel_stream_started = false; aws_condition_variable_notify_all(&test_fixture->signal); aws_mutex_unlock(&test_fixture->lock); From 59674450029f11bc83bb05d5c61071b7ce5a7815 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Tue, 28 Mar 2023 10:28:26 -0700 Subject: [PATCH 22/50] handle websocket pings in tests --- tests/secure_tunnel_tests.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/secure_tunnel_tests.c b/tests/secure_tunnel_tests.c index 13e58738..037e8490 100644 --- a/tests/secure_tunnel_tests.c +++ b/tests/secure_tunnel_tests.c @@ -590,6 +590,11 @@ void aws_secure_tunnel_test_on_message_received( int aws_websocket_send_frame_mock_fn( struct aws_websocket *websocket, const struct aws_websocket_send_frame_options *options) { + + if (options->opcode == AWS_WEBSOCKET_OPCODE_PING) { + return AWS_OP_SUCCESS; + } + void *pointer = websocket; struct aws_secure_tunnel_mock_test_fixture *test_fixture = pointer; From bfbc50a5e446720696a87d11b7cfdbb1ee7ca691 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Tue, 28 Mar 2023 11:35:10 -0700 Subject: [PATCH 23/50] added appropriate check in between moving on in test --- tests/secure_tunnel_tests.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/secure_tunnel_tests.c b/tests/secure_tunnel_tests.c index 037e8490..7960ff65 100644 --- a/tests/secure_tunnel_tests.c +++ b/tests/secure_tunnel_tests.c @@ -1467,7 +1467,7 @@ static int s_secure_tunneling_v1_to_v2_stream_start_test_fn(struct aws_allocator 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_byte_cursor service_2 = aws_byte_cursor_from_string(s_service_id_2); struct aws_secure_tunnel_message_view stream_start_message_view = { .type = AWS_SECURE_TUNNEL_MT_STREAM_START, .stream_id = 1, @@ -1480,7 +1480,7 @@ static int s_secure_tunneling_v1_to_v2_stream_start_test_fn(struct aws_allocator struct aws_secure_tunnel_message_view stream_start_message_view_2 = { .type = AWS_SECURE_TUNNEL_MT_STREAM_START, - .service_id = &service_1, + .service_id = &service_2, .stream_id = 1, }; @@ -1489,15 +1489,16 @@ static int s_secure_tunneling_v1_to_v2_stream_start_test_fn(struct aws_allocator /* Client should disconnect, clear previous V1 connection and stream, reconnect, and start a V2 stream */ s_wait_for_connection_shutdown(&test_fixture); + s_wait_for_connected_successfully(&test_fixture); s_wait_for_stream_started(&test_fixture); - ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, &service_1, 1)); + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, &service_2, 1)); /* Create and send a data message from the server to the destination client */ 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, + .service_id = &service_2, .stream_id = 1, .payload = &payload_cur, }; From 67c24777bb049e8a46e353778fc81bdc2b877d0d Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Tue, 28 Mar 2023 13:17:44 -0700 Subject: [PATCH 24/50] ignore a second service ids message --- source/secure_tunneling.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 05da33c1..ece3ceeb 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -608,6 +608,14 @@ static void s_aws_secure_tunnel_on_service_ids_received( struct aws_secure_tunnel *secure_tunnel, struct aws_secure_tunnel_message_view *message_view) { + if (secure_tunnel->config->protocol_version != 0) { + AWS_LOGF_INFO( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Secure Tunnel will ignore a second SERVICE IDS message.", + (void *)secure_tunnel); + return; + } + aws_hash_table_clear(&secure_tunnel->config->service_ids); if (message_view->service_id != NULL) { From a842d187ea312ca5b4c38441779ca696f40a523d Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Tue, 28 Mar 2023 13:25:27 -0700 Subject: [PATCH 25/50] handling of connection id separated from message view --- source/secure_tunneling.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index ece3ceeb..5a44f90d 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -542,17 +542,19 @@ static void s_aws_secure_tunnel_on_stream_start_received( return; } + uint32_t connection_id = message_view->connection_id; + /* * An absent connection ID will result in connection id being set to 1. The connection is considered a V1 * connection at this point and the future existance of an unexpected connection ID will result in a full reset * of the client as mixed protocol versions is not supported. */ if (message_view->connection_id == 0) { - message_view->connection_id = 1; + connection_id = 1; } - int result = s_aws_secure_tunnel_set_stream( - secure_tunnel, message_view->service_id, message_view->stream_id, message_view->connection_id); + int result = + s_aws_secure_tunnel_set_stream(secure_tunnel, message_view->service_id, message_view->stream_id, connection_id); if (secure_tunnel->config->on_stream_start) { secure_tunnel->config->on_stream_start(message_view, result, secure_tunnel->config->user_data); From c0eaf23f4d5a3258bcf76c0ba0bfe78f4cb7f3c2 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Tue, 28 Mar 2023 13:41:35 -0700 Subject: [PATCH 26/50] simplified a test --- tests/secure_tunnel_tests.c | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/tests/secure_tunnel_tests.c b/tests/secure_tunnel_tests.c index 7960ff65..27253c62 100644 --- a/tests/secure_tunnel_tests.c +++ b/tests/secure_tunnel_tests.c @@ -1494,19 +1494,6 @@ static int s_secure_tunneling_v1_to_v2_stream_start_test_fn(struct aws_allocator s_wait_for_stream_started(&test_fixture); ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, &service_2, 1)); - /* Create and send a data message from the server to the destination client */ - 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_2, - .stream_id = 1, - .payload = &payload_cur, - }; - - aws_secure_tunnel_send_mock_message(&test_fixture, &data_message_view); - test_fixture.secure_tunnel_message_received_count_target = 1; - s_wait_for_n_messages_received(&test_fixture); - ASSERT_SUCCESS(aws_secure_tunnel_stop(secure_tunnel)); s_wait_for_connection_shutdown(&test_fixture); @@ -1556,20 +1543,6 @@ static int s_secure_tunneling_v1_to_v3_stream_start_test_fn(struct aws_allocator s_wait_for_stream_started(&test_fixture); ASSERT_TRUE(s_secure_tunnel_check_active_connection_id(secure_tunnel, &service_1, 1, 3)); - /* Create and send a data message from the server to the destination client */ - 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, - .stream_id = 1, - .payload = &payload_cur, - .connection_id = 3, - }; - - aws_secure_tunnel_send_mock_message(&test_fixture, &data_message_view); - test_fixture.secure_tunnel_message_received_count_target = 1; - s_wait_for_n_messages_received(&test_fixture); - ASSERT_SUCCESS(aws_secure_tunnel_stop(secure_tunnel)); s_wait_for_connection_shutdown(&test_fixture); From a09110109916f34e555a89ed06e4155912789575 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Tue, 28 Mar 2023 13:47:44 -0700 Subject: [PATCH 27/50] forgot a wait for check in a test --- tests/secure_tunnel_tests.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/secure_tunnel_tests.c b/tests/secure_tunnel_tests.c index 27253c62..9fcb85b7 100644 --- a/tests/secure_tunnel_tests.c +++ b/tests/secure_tunnel_tests.c @@ -1747,6 +1747,8 @@ static int s_secure_tunneling_v2_stream_start_connection_start_reset_test_fn( s_wait_for_connection_shutdown(&test_fixture); s_wait_for_connected_successfully(&test_fixture); + s_wait_for_stream_started(&test_fixture); + /* Check that the established stream is cleared */ ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, &service_1, 0)); From aaf3aaf7325889ae2a2794e0fe979085f072044f Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Tue, 28 Mar 2023 14:04:59 -0700 Subject: [PATCH 28/50] added wrong kind of wait --- tests/secure_tunnel_tests.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/secure_tunnel_tests.c b/tests/secure_tunnel_tests.c index 9fcb85b7..8f4575f4 100644 --- a/tests/secure_tunnel_tests.c +++ b/tests/secure_tunnel_tests.c @@ -1747,7 +1747,8 @@ static int s_secure_tunneling_v2_stream_start_connection_start_reset_test_fn( s_wait_for_connection_shutdown(&test_fixture); s_wait_for_connected_successfully(&test_fixture); - s_wait_for_stream_started(&test_fixture); + /* pause to process a new connection */ + aws_thread_current_sleep(aws_timestamp_convert(1, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL)); /* Check that the established stream is cleared */ ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, &service_1, 0)); From 12257d5061fe500ab063967afd0b33d407ff94f9 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Tue, 28 Mar 2023 15:34:09 -0700 Subject: [PATCH 29/50] added additional stream closed checks --- tests/secure_tunnel_tests.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/secure_tunnel_tests.c b/tests/secure_tunnel_tests.c index 8f4575f4..6b4f18f9 100644 --- a/tests/secure_tunnel_tests.c +++ b/tests/secure_tunnel_tests.c @@ -1491,6 +1491,9 @@ static int s_secure_tunneling_v1_to_v2_stream_start_test_fn(struct aws_allocator s_wait_for_connection_shutdown(&test_fixture); s_wait_for_connected_successfully(&test_fixture); + /* Check that the established stream is cleared */ + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, NULL, 0)); + s_wait_for_stream_started(&test_fixture); ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, &service_2, 1)); @@ -1540,6 +1543,9 @@ static int s_secure_tunneling_v1_to_v3_stream_start_test_fn(struct aws_allocator s_wait_for_connection_shutdown(&test_fixture); s_wait_for_connected_successfully(&test_fixture); + /* Check that the established stream is cleared */ + ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, NULL, 0)); + s_wait_for_stream_started(&test_fixture); ASSERT_TRUE(s_secure_tunnel_check_active_connection_id(secure_tunnel, &service_1, 1, 3)); From a8aedef3afe633f0489c869181fb27b1b4796040 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 29 Mar 2023 10:24:18 -0700 Subject: [PATCH 30/50] prevent double read of received byte buffer --- .../iotdevice/private/secure_tunneling_impl.h | 1 + source/secure_tunneling.c | 56 +++++++++---------- 2 files changed, 27 insertions(+), 30 deletions(-) diff --git a/include/aws/iotdevice/private/secure_tunneling_impl.h b/include/aws/iotdevice/private/secure_tunneling_impl.h index df79a854..3039fc2d 100644 --- a/include/aws/iotdevice/private/secure_tunneling_impl.h +++ b/include/aws/iotdevice/private/secure_tunneling_impl.h @@ -263,6 +263,7 @@ struct aws_secure_tunnel { * send additional ones/ */ bool pending_write_completion; + bool pending_read_completion; /* * When should the next PINGREQ be sent? diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 5a44f90d..8db92be0 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -610,14 +610,6 @@ static void s_aws_secure_tunnel_on_service_ids_received( struct aws_secure_tunnel *secure_tunnel, struct aws_secure_tunnel_message_view *message_view) { - if (secure_tunnel->config->protocol_version != 0) { - AWS_LOGF_INFO( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: Secure Tunnel will ignore a second SERVICE IDS message.", - (void *)secure_tunnel); - return; - } - aws_hash_table_clear(&secure_tunnel->config->service_ids); if (message_view->service_id != NULL) { @@ -807,30 +799,34 @@ static int s_process_received_data(struct aws_secure_tunnel *secure_tunnel) { * we don't want to move `cursor`. */ struct aws_byte_cursor tmp_cursor = cursor; - while (aws_byte_cursor_read_be16(&tmp_cursor, &data_length) && tmp_cursor.len >= data_length) { - cursor = tmp_cursor; - - struct aws_byte_cursor st_frame = {.len = data_length, .ptr = cursor.ptr}; - aws_byte_cursor_advance(&cursor, data_length); - tmp_cursor = cursor; - - if (aws_secure_tunnel_deserialize_message_from_cursor( - secure_tunnel, &st_frame, &s_aws_secure_tunnel_connected_on_message_received)) { - int error_code = aws_last_error(); - AWS_LOGF_ERROR( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: failed to deserialize message with error %d(%s)", - (void *)secure_tunnel, - error_code, - aws_error_debug_str(error_code)); - return error_code; + if (!secure_tunnel->pending_read_completion) { + secure_tunnel->pending_read_completion = true; + while (aws_byte_cursor_read_be16(&tmp_cursor, &data_length) && tmp_cursor.len >= data_length) { + cursor = tmp_cursor; + + struct aws_byte_cursor st_frame = {.len = data_length, .ptr = cursor.ptr}; + aws_byte_cursor_advance(&cursor, data_length); + tmp_cursor = cursor; + + if (aws_secure_tunnel_deserialize_message_from_cursor( + secure_tunnel, &st_frame, &s_aws_secure_tunnel_connected_on_message_received)) { + int error_code = aws_last_error(); + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: failed to deserialize message with error %d(%s)", + (void *)secure_tunnel, + error_code, + aws_error_debug_str(error_code)); + return error_code; + } } - } - if (cursor.ptr != received_data->buffer) { - /* Move unprocessed data to the beginning */ - received_data->len = 0; - aws_byte_buf_append(received_data, &cursor); + if (cursor.ptr != received_data->buffer) { + /* Move unprocessed data to the beginning */ + received_data->len = 0; + aws_byte_buf_append(received_data, &cursor); + } + secure_tunnel->pending_read_completion = false; } return AWS_OP_SUCCESS; From 36d07133b6bce25d6b7620adb5ff91ff2be9907d Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 29 Mar 2023 11:39:15 -0700 Subject: [PATCH 31/50] added pause for sends --- tests/secure_tunnel_tests.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/secure_tunnel_tests.c b/tests/secure_tunnel_tests.c index 6b4f18f9..ccbb253b 100644 --- a/tests/secure_tunnel_tests.c +++ b/tests/secure_tunnel_tests.c @@ -503,7 +503,7 @@ static void s_wait_for_n_messages_received(struct aws_secure_tunnel_mock_test_fi 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) { - + aws_thread_current_sleep(aws_timestamp_convert(500, AWS_TIMESTAMP_MILLIS, AWS_TIMESTAMP_NANOS, NULL)); struct aws_byte_buf data_buf; struct aws_byte_cursor data_cur; struct aws_byte_buf out_buf; From dbce7421b1586d8855a3d530ba3bae4789917e24 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 29 Mar 2023 13:30:27 -0700 Subject: [PATCH 32/50] add mutex lock for incoming data buf --- .../iotdevice/private/secure_tunneling_impl.h | 2 +- source/secure_tunneling.c | 55 ++++++++++--------- tests/secure_tunnel_tests.c | 1 - 3 files changed, 30 insertions(+), 28 deletions(-) diff --git a/include/aws/iotdevice/private/secure_tunneling_impl.h b/include/aws/iotdevice/private/secure_tunneling_impl.h index 3039fc2d..1faad08f 100644 --- a/include/aws/iotdevice/private/secure_tunneling_impl.h +++ b/include/aws/iotdevice/private/secure_tunneling_impl.h @@ -243,6 +243,7 @@ struct aws_secure_tunnel { /* Stores what has been received but not processed */ struct aws_byte_buf received_data; + struct aws_mutex received_data_lock; /* * When should the secure tunnel next attempt to reconnect? Only used by PENDING_RECONNECT state. @@ -263,7 +264,6 @@ struct aws_secure_tunnel { * send additional ones/ */ bool pending_write_completion; - bool pending_read_completion; /* * When should the next PINGREQ be sent? diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 8db92be0..beab61ed 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -112,6 +112,7 @@ static void s_secure_tunnel_final_destroy(struct aws_secure_tunnel *secure_tunne aws_secure_tunnel_options_storage_destroy(secure_tunnel->config); aws_http_message_release(secure_tunnel->handshake_request); aws_byte_buf_clean_up(&secure_tunnel->received_data); + aws_mutex_clean_up(&secure_tunnel->received_data_lock); aws_tls_connection_options_clean_up(&secure_tunnel->tls_con_opt); aws_tls_ctx_release(secure_tunnel->tls_ctx); aws_mem_release(secure_tunnel->allocator, secure_tunnel); @@ -791,6 +792,8 @@ static void s_aws_secure_tunnel_connected_on_message_received( } static int s_process_received_data(struct aws_secure_tunnel *secure_tunnel) { + + aws_mutex_lock(&secure_tunnel->received_data_lock); struct aws_byte_buf *received_data = &secure_tunnel->received_data; struct aws_byte_cursor cursor = aws_byte_cursor_from_buf(received_data); uint16_t data_length = 0; @@ -799,36 +802,33 @@ static int s_process_received_data(struct aws_secure_tunnel *secure_tunnel) { * we don't want to move `cursor`. */ struct aws_byte_cursor tmp_cursor = cursor; - if (!secure_tunnel->pending_read_completion) { - secure_tunnel->pending_read_completion = true; - while (aws_byte_cursor_read_be16(&tmp_cursor, &data_length) && tmp_cursor.len >= data_length) { - cursor = tmp_cursor; - - struct aws_byte_cursor st_frame = {.len = data_length, .ptr = cursor.ptr}; - aws_byte_cursor_advance(&cursor, data_length); - tmp_cursor = cursor; - - if (aws_secure_tunnel_deserialize_message_from_cursor( - secure_tunnel, &st_frame, &s_aws_secure_tunnel_connected_on_message_received)) { - int error_code = aws_last_error(); - AWS_LOGF_ERROR( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: failed to deserialize message with error %d(%s)", - (void *)secure_tunnel, - error_code, - aws_error_debug_str(error_code)); - return error_code; - } - } + while (aws_byte_cursor_read_be16(&tmp_cursor, &data_length) && tmp_cursor.len >= data_length) { + cursor = tmp_cursor; - if (cursor.ptr != received_data->buffer) { - /* Move unprocessed data to the beginning */ - received_data->len = 0; - aws_byte_buf_append(received_data, &cursor); + struct aws_byte_cursor st_frame = {.len = data_length, .ptr = cursor.ptr}; + aws_byte_cursor_advance(&cursor, data_length); + tmp_cursor = cursor; + + if (aws_secure_tunnel_deserialize_message_from_cursor( + secure_tunnel, &st_frame, &s_aws_secure_tunnel_connected_on_message_received)) { + int error_code = aws_last_error(); + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: failed to deserialize message with error %d(%s)", + (void *)secure_tunnel, + error_code, + aws_error_debug_str(error_code)); + return error_code; } - secure_tunnel->pending_read_completion = false; } + if (cursor.ptr != received_data->buffer) { + /* Move unprocessed data to the beginning */ + received_data->len = 0; + aws_byte_buf_append(received_data, &cursor); + } + + aws_mutex_unlock(&secure_tunnel->received_data_lock); return AWS_OP_SUCCESS; } @@ -942,7 +942,9 @@ static bool s_on_websocket_incoming_frame_payload( if (data.len > 0) { struct aws_secure_tunnel *secure_tunnel = user_data; + aws_mutex_lock(&secure_tunnel->received_data_lock); aws_byte_buf_append(&secure_tunnel->received_data, &data); + aws_mutex_unlock(&secure_tunnel->received_data_lock); if (s_process_received_data(secure_tunnel)) { return false; } @@ -2382,6 +2384,7 @@ struct aws_secure_tunnel *aws_secure_tunnel_new( secure_tunnel->websocket = NULL; aws_byte_buf_init(&secure_tunnel->received_data, allocator, MAX_WEBSOCKET_PAYLOAD); + aws_mutex_init(&secure_tunnel->received_data_lock); aws_secure_tunnel_options_storage_log(secure_tunnel->config, AWS_LL_DEBUG); diff --git a/tests/secure_tunnel_tests.c b/tests/secure_tunnel_tests.c index ccbb253b..0f1a9db1 100644 --- a/tests/secure_tunnel_tests.c +++ b/tests/secure_tunnel_tests.c @@ -503,7 +503,6 @@ static void s_wait_for_n_messages_received(struct aws_secure_tunnel_mock_test_fi 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) { - aws_thread_current_sleep(aws_timestamp_convert(500, AWS_TIMESTAMP_MILLIS, AWS_TIMESTAMP_NANOS, NULL)); struct aws_byte_buf data_buf; struct aws_byte_cursor data_cur; struct aws_byte_buf out_buf; From 25cb7a81d46919e3a2c02f74fd5dde4c2439ab6b Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Fri, 31 Mar 2023 10:59:50 -0700 Subject: [PATCH 33/50] code review changes --- .../private/secure_tunneling_operations.h | 2 +- source/secure_tunneling.c | 30 +++++++++---------- source/secure_tunneling_operations.c | 8 ++--- 3 files changed, 19 insertions(+), 21 deletions(-) diff --git a/include/aws/iotdevice/private/secure_tunneling_operations.h b/include/aws/iotdevice/private/secure_tunneling_operations.h index 6c0ed534..dcf83c2b 100644 --- a/include/aws/iotdevice/private/secure_tunneling_operations.h +++ b/include/aws/iotdevice/private/secure_tunneling_operations.h @@ -199,7 +199,7 @@ struct aws_service_id_element *aws_service_id_element_new( int32_t stream_id); AWS_IOTDEVICE_API -void aws_destroy_connection_id(void *data); +void aws_connection_id_destroy(void *data); AWS_IOTDEVICE_API struct aws_connection_id_element *aws_connection_id_element_new( diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index beab61ed..66c61b80 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -131,6 +131,12 @@ static void s_on_secure_tunnel_zero_ref_count(void *user_data) { * STREAM HANDLING *****************************************************************************************************************/ +static void s_set_absent_connection_id_to_one(struct aws_secure_tunnel_message_view *message, uint32_t *connection_id) { + if (message->connection_id == 0) { + *connection_id = 1; + } +} + static int s_reset_service_id(void *context, struct aws_hash_element *p_element) { (void)context; struct aws_service_id_element *service_id_elem = p_element->value; @@ -243,8 +249,8 @@ static bool s_aws_secure_tunnel_active_stream_check( } /* - * V2 connection id will always be 1. V3 can be any number > 0. Either way, connection id will be checked against - * stored connection_ids for the service id to confirm the stream is active + * V1 and V2 connection id will always be 1. V3 can be any number > 0. Either way, connection id will be checked + * against stored connection_ids for the service id to confirm the stream is active */ struct aws_hash_element *connection_id_elem = NULL; aws_hash_table_find(&service_id_elem->connection_ids, &message_view->connection_id, &connection_id_elem); @@ -367,8 +373,7 @@ static int s_aws_secure_tunnel_set_connection_id( * If the connection id is already stored something is wrong and this connection id must be removed and a * connection reset must be sent for this connection id */ - aws_destroy_connection_id(connection_id_elem); - + aws_connection_id_destroy(connection_id_elem); if (service_id == NULL || service_id->len == 0) { AWS_LOGF_INFO( AWS_LS_IOTDEVICE_SECURE_TUNNELING, @@ -478,9 +483,7 @@ static void s_aws_secure_tunnel_on_data_received( /* * An absent connection ID will result in connection id being set to 1. */ - if (message_view->connection_id == 0) { - message_view->connection_id = 1; - } + s_set_absent_connection_id_to_one(message_view, &message_view->connection_id); if (s_aws_secure_tunnel_active_stream_check(secure_tunnel, message_view)) { if (secure_tunnel->config->on_message_received) { @@ -550,9 +553,7 @@ static void s_aws_secure_tunnel_on_stream_start_received( * connection at this point and the future existance of an unexpected connection ID will result in a full reset * of the client as mixed protocol versions is not supported. */ - if (message_view->connection_id == 0) { - connection_id = 1; - } + s_set_absent_connection_id_to_one(message_view, &connection_id); int result = s_aws_secure_tunnel_set_stream(secure_tunnel, message_view->service_id, message_view->stream_id, connection_id); @@ -689,9 +690,7 @@ static void s_aws_secure_tunnel_on_connection_start_received( /* * An absent connection ID will result in connection id being set to 1. */ - if (message_view->connection_id == 0) { - message_view->connection_id = 1; - } + s_set_absent_connection_id_to_one(message_view, &message_view->connection_id); if (s_aws_secure_tunnel_stream_id_match_check(secure_tunnel, message_view->service_id, message_view->stream_id)) { int result = @@ -734,9 +733,7 @@ static void s_aws_secure_tunnel_on_connection_reset_received( /* * An absent connection ID will result in connection id being set to 1. */ - if (message_view->connection_id == 0) { - message_view->connection_id = 1; - } + s_set_absent_connection_id_to_one(message_view, &message_view->connection_id); int result = s_aws_secure_tunnel_remove_connection_id(secure_tunnel, message_view); @@ -818,6 +815,7 @@ static int s_process_received_data(struct aws_secure_tunnel *secure_tunnel) { (void *)secure_tunnel, error_code, aws_error_debug_str(error_code)); + aws_mutex_unlock(&secure_tunnel->received_data_lock); return error_code; } } diff --git a/source/secure_tunneling_operations.c b/source/secure_tunneling_operations.c index 4418cd61..b2c3f9a7 100644 --- a/source/secure_tunneling_operations.c +++ b/source/secure_tunneling_operations.c @@ -36,7 +36,7 @@ bool aws_secure_tunnel_connection_id_eq(const void *a, const void *b) { return *(const uint32_t *)a == *(const uint32_t *)b; } -void aws_destroy_connection_id(void *data) { +void aws_connection_id_destroy(void *data) { struct aws_connection_id_element *elem = data; aws_mem_release(elem->allocator, elem); } @@ -84,7 +84,7 @@ struct aws_service_id_element *aws_service_id_element_new( aws_secure_tunnel_hash_connection_id, aws_secure_tunnel_connection_id_eq, NULL, - aws_destroy_connection_id)) { + aws_connection_id_destroy)) { goto error; } @@ -533,7 +533,7 @@ static int s_aws_secure_tunnel_operation_set_connection_start_id( if (connection_elem == NULL) { aws_hash_table_put(table_to_put_in, &connection_id_elem->connection_id, connection_id_elem, NULL); } else { - aws_destroy_connection_id(connection_id_elem); + aws_connection_id_destroy(connection_id_elem); } if (message_view->service_id == NULL || message_view->service_id->len == 0) { @@ -882,7 +882,7 @@ struct aws_secure_tunnel_options_storage *aws_secure_tunnel_options_storage_new( aws_secure_tunnel_hash_connection_id, aws_secure_tunnel_connection_id_eq, NULL, - aws_destroy_connection_id)) { + aws_connection_id_destroy)) { goto error; } From f4e34895bc01b21a86affd0bb01b17344405046b Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 3 Apr 2023 10:14:35 -0700 Subject: [PATCH 34/50] remove defaulting source mode to V3 --- source/secure_tunneling.c | 64 ++++++++++++++++++++++++++----------- tests/secure_tunnel_tests.c | 1 - 2 files changed, 45 insertions(+), 20 deletions(-) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 66c61b80..ee22c078 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -1780,10 +1780,8 @@ int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure error_code, aws_error_debug_str(error_code)); } else { - /* - * Protocol version will always be V3 as a Source Device. - * Send the Stream Start message through the WebSocket. - */ + // Steve TODO set the protocol version based on stream starts. + if (s_secure_tunneling_send(secure_tunnel, current_operation->message_view)) { error_code = aws_last_error(); } @@ -2432,12 +2430,16 @@ int aws_secure_tunnel_send_message( return aws_last_error(); } - /* - * We apply V3 protocol to all outbound messages to prevent disconnection by the secure tunnel service due to - * protocol switching - */ - if (message_op->options_storage.storage_view.connection_id == 0) { - message_op->options_storage.storage_view.connection_id = 1; + uint8_t message_protocol_version = s_aws_secure_tunnel_message_min_protocol_check(message_options); + if (!s_aws_secure_tunnel_protocol_version_match_check(secure_tunnel, message_options)) { + AWS_LOGF_WARN( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Message not sent due to protocol version missmatch between Secure Tunnel Client Protocol Version " + "(%d) and message Protocl Version (%d).", + (void *)secure_tunnel, + secure_tunnel->config->protocol_version, + message_protocol_version); + return AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_PROTOCOL_VERSION_MISSMATCH; } AWS_LOGF_DEBUG( @@ -2468,6 +2470,32 @@ 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->config->protocol_version != 0 && + message_protocol_version != secure_tunnel->config->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, + secure_tunnel->config->protocol_version, + message_protocol_version); + reset_secure_tunnel_connection(secure_tunnel); + } + + if (secure_tunnel->config->protocol_version == 0) { + secure_tunnel->config->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, + secure_tunnel->config->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); @@ -2475,14 +2503,6 @@ int aws_secure_tunnel_stream_start( return AWS_OP_ERR; } - /* - * We apply V3 protocol to all outbound messages to prevent disconnection by the secure tunnel service due to - * protocol switching - */ - if (message_op->options_storage.storage_view.connection_id == 0) { - message_op->options_storage.storage_view.connection_id = 1; - } - AWS_LOGF_DEBUG( AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: Submitting STREAM START operation (%p)", @@ -2511,8 +2531,14 @@ int aws_secure_tunnel_connection_start( return AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INCORRECT_MODE; } + if (secure_tunnel->config->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"); + AWS_LOGF_WARN(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Connection Start must include a connection id."); return AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_CONNECTION_ID; } diff --git a/tests/secure_tunnel_tests.c b/tests/secure_tunnel_tests.c index 0f1a9db1..2e8b4434 100644 --- a/tests/secure_tunnel_tests.c +++ b/tests/secure_tunnel_tests.c @@ -1242,7 +1242,6 @@ static int s_secure_tunneling_max_payload_test_fn(struct aws_allocator *allocato .type = AWS_SECURE_TUNNEL_MT_DATA, .stream_id = 0, .service_id = &service_1, - .connection_id = 1, .payload = &s_payload_cursor_max_size, }; From 6817cd3ac46a8dfd1c3fcc99be3a1d0b4e9330f0 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 3 Apr 2023 10:15:25 -0700 Subject: [PATCH 35/50] remove comment --- source/secure_tunneling.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index ee22c078..cd040dba 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -1780,8 +1780,6 @@ int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure error_code, aws_error_debug_str(error_code)); } else { - // Steve TODO set the protocol version based on stream starts. - if (s_secure_tunneling_send(secure_tunnel, current_operation->message_view)) { error_code = aws_last_error(); } From 9197bcd93cfc78932c8f84117139bc0ad78f271e Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 3 Apr 2023 10:28:01 -0700 Subject: [PATCH 36/50] modified the active stream check to account for V1 streams --- source/secure_tunneling.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index cd040dba..1d02d009 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -248,16 +248,17 @@ static bool s_aws_secure_tunnel_active_stream_check( return false; } - /* - * V1 and V2 connection id will always be 1. V3 can be any number > 0. Either way, connection id will be checked - * against stored connection_ids for the service id to confirm the stream is active - */ - struct aws_hash_element *connection_id_elem = NULL; - aws_hash_table_find(&service_id_elem->connection_ids, &message_view->connection_id, &connection_id_elem); - if (connection_id_elem == NULL) { - aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_CONNECTION_ID); - return false; + /* V1 and V2 will be considered active at this point with a matching stream id but V3 streams will need to have + * their connection id checked */ + if (secure_tunnel->config->protocol_version == 3) { + struct aws_hash_element *connection_id_elem = NULL; + aws_hash_table_find(&service_id_elem->connection_ids, &message_view->connection_id, &connection_id_elem); + if (connection_id_elem == NULL) { + aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_CONNECTION_ID); + return false; + } } + return true; } From e6216229b43ff7be11ced6be2ceb2c50e810d05b Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 3 Apr 2023 16:06:25 -0700 Subject: [PATCH 37/50] sending of V1 messages from Destination --- source/secure_tunneling.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 1d02d009..77e4bd5e 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -567,6 +567,7 @@ static void s_aws_secure_tunnel_on_stream_start_received( static void s_aws_secure_tunnel_on_stream_reset_received( struct aws_secure_tunnel *secure_tunnel, struct aws_secure_tunnel_message_view *message_view) { + if (secure_tunnel->config->protocol_version != 0 && !s_aws_secure_tunnel_protocol_version_match_check(secure_tunnel, message_view)) { AWS_LOGF_INFO( @@ -2429,16 +2430,9 @@ int aws_secure_tunnel_send_message( return aws_last_error(); } - uint8_t message_protocol_version = s_aws_secure_tunnel_message_min_protocol_check(message_options); - if (!s_aws_secure_tunnel_protocol_version_match_check(secure_tunnel, message_options)) { - AWS_LOGF_WARN( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: Message not sent due to protocol version missmatch between Secure Tunnel Client Protocol Version " - "(%d) and message Protocl Version (%d).", - (void *)secure_tunnel, - secure_tunnel->config->protocol_version, - message_protocol_version); - return AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_PROTOCOL_VERSION_MISSMATCH; + if (secure_tunnel->config->local_proxy_mode == AWS_SECURE_TUNNELING_DESTINATION_MODE && + secure_tunnel->config->protocol_version == 1 && message_options->connection_id == 1) { + message_op->options_storage.storage_view.connection_id = 0; } AWS_LOGF_DEBUG( @@ -2455,7 +2449,7 @@ int aws_secure_tunnel_send_message( error: aws_secure_tunnel_operation_release(&message_op->base); - return AWS_OP_ERR; + return aws_last_error(); } int aws_secure_tunnel_stream_start( From a46b3c691032b57a67d36fdb9d13b53a4a9d80ec Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 3 Apr 2023 16:26:03 -0700 Subject: [PATCH 38/50] sending of V2 messages from destination --- source/secure_tunneling.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 77e4bd5e..07d5b10a 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -2431,7 +2431,7 @@ int aws_secure_tunnel_send_message( } if (secure_tunnel->config->local_proxy_mode == AWS_SECURE_TUNNELING_DESTINATION_MODE && - secure_tunnel->config->protocol_version == 1 && message_options->connection_id == 1) { + secure_tunnel->config->protocol_version < 3 && message_options->connection_id == 1) { message_op->options_storage.storage_view.connection_id = 0; } From 76805f360c1ef9a62e3ccdefba0ba8c4adb6ce74 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 3 Apr 2023 16:33:51 -0700 Subject: [PATCH 39/50] Description of connection id manipulation reasoning --- source/secure_tunneling.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 07d5b10a..1f0c2358 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -482,9 +482,11 @@ static void s_aws_secure_tunnel_on_data_received( } /* - * An absent connection ID will result in connection id being set to 1. + * An absent connection ID in DESTINATION MODE will result in connection id being set to 1. */ - s_set_absent_connection_id_to_one(message_view, &message_view->connection_id); + if (secure_tunnel->config->local_proxy_mode == AWS_SECURE_TUNNELING_DESTINATION_MODE) { + s_set_absent_connection_id_to_one(message_view, &message_view->connection_id); + } if (s_aws_secure_tunnel_active_stream_check(secure_tunnel, message_view)) { if (secure_tunnel->config->on_message_received) { @@ -2430,6 +2432,10 @@ 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->config->protocol_version < 3 && message_options->connection_id == 1) { message_op->options_storage.storage_view.connection_id = 0; From eacf7080011a76bf77f80c8854e50b25b1045098 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 3 Apr 2023 16:47:33 -0700 Subject: [PATCH 40/50] source device connection id compatibility --- source/secure_tunneling.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 1f0c2358..f194fe05 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -220,12 +220,17 @@ static bool s_aws_secure_tunnel_active_stream_check( return false; } + uint32_t connection_id = message_view->connection_id; + if (connection_id == 0) { + connection_id = 1; + } + /* - * V1 and V2 connection id will always be 1. V3 can be any number > 0. Either way, connection id will be checked - * against stored connection_ids to confirm the stream is active. + * V1 and V2 connection id has been stored as 1. V3 can be any number > 0. Either way, connection id will be + * checked against stored connection_ids to confirm the stream is active. */ struct aws_hash_element *connection_id_elem = NULL; - aws_hash_table_find(&secure_tunnel->config->connection_ids, &message_view->connection_id, &connection_id_elem); + aws_hash_table_find(&secure_tunnel->config->connection_ids, &connection_id, &connection_id_elem); if (connection_id_elem == NULL) { aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_CONNECTION_ID); return false; From 79ad39e2044228b319bce3876f226e0ce5c77b66 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 5 Apr 2023 09:49:21 -0700 Subject: [PATCH 41/50] move aws_secure_tunnel_message_type_to_c_string to secure_tunnel.h --- include/aws/iotdevice/private/serializer.h | 3 --- include/aws/iotdevice/secure_tunneling.h | 9 +++++++ source/secure_tunneling.c | 31 ++++++++++++++++++++++ source/serializer.c | 31 ---------------------- 4 files changed, 40 insertions(+), 34 deletions(-) diff --git a/include/aws/iotdevice/private/serializer.h b/include/aws/iotdevice/private/serializer.h index 5ca98da8..0d1a3339 100644 --- a/include/aws/iotdevice/private/serializer.h +++ b/include/aws/iotdevice/private/serializer.h @@ -56,9 +56,6 @@ int aws_secure_tunnel_deserialize_message_from_cursor( struct aws_byte_cursor *cursor, aws_secure_tunnel_on_message_received_fn *on_message_received); -AWS_IOTDEVICE_API -const char *aws_secure_tunnel_message_type_to_c_string(enum aws_secure_tunnel_message_type message_type); - AWS_EXTERN_C_END #endif diff --git a/include/aws/iotdevice/secure_tunneling.h b/include/aws/iotdevice/secure_tunneling.h index f48507b4..dd2cf8a2 100644 --- a/include/aws/iotdevice/secure_tunneling.h +++ b/include/aws/iotdevice/secure_tunneling.h @@ -299,6 +299,15 @@ int aws_secure_tunnel_send_message( struct aws_secure_tunnel *secure_tunnel, const struct aws_secure_tunnel_message_view *message_options); +/** + * Get the const char description of a message type + * + * @param message_type message type used by a secure tunnel message + * @return const char translation of the message type + */ +AWS_IOTDEVICE_API +const char *aws_secure_tunnel_message_type_to_c_string(enum aws_secure_tunnel_message_type message_type); + //*********************************************************************************************************************** /* THIS API SHOULD ONLY BE USED FROM SOURCE MODE */ //*********************************************************************************************************************** diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index f194fe05..aec4c1db 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -80,6 +80,37 @@ const char *aws_secure_tunnel_state_to_c_string(enum aws_secure_tunnel_state sta } } +const char *aws_secure_tunnel_message_type_to_c_string(enum aws_secure_tunnel_message_type message_type) { + switch (message_type) { + case AWS_SECURE_TUNNEL_MT_UNKNOWN: + return "ST_MT_UNKNOWN"; + + case AWS_SECURE_TUNNEL_MT_DATA: + return "DATA"; + + case AWS_SECURE_TUNNEL_MT_STREAM_START: + return "STREAM START"; + + case AWS_SECURE_TUNNEL_MT_STREAM_RESET: + return "STREAM RESET"; + + case AWS_SECURE_TUNNEL_MT_SESSION_RESET: + return "SESSION RESET"; + + case AWS_SECURE_TUNNEL_MT_SERVICE_IDS: + return "SERVICE IDS"; + + case AWS_SECURE_TUNNEL_MT_CONNECTION_START: + return "CONNECTION START"; + + case AWS_SECURE_TUNNEL_MT_CONNECTION_RESET: + return "CONNECTION RESET"; + + default: + return "UNKNOWN"; + } +} + static const char *s_get_proxy_mode_string(enum aws_secure_tunneling_local_proxy_mode local_proxy_mode) { if (local_proxy_mode == AWS_SECURE_TUNNELING_SOURCE_MODE) { return "source"; diff --git a/source/serializer.c b/source/serializer.c index 3e16dd26..15bcfba7 100644 --- a/source/serializer.c +++ b/source/serializer.c @@ -490,34 +490,3 @@ int aws_secure_tunnel_deserialize_message_from_cursor( error: return AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DECODE_FAILURE; } - -const char *aws_secure_tunnel_message_type_to_c_string(enum aws_secure_tunnel_message_type message_type) { - switch (message_type) { - case AWS_SECURE_TUNNEL_MT_UNKNOWN: - return "ST_MT_UNKNOWN"; - - case AWS_SECURE_TUNNEL_MT_DATA: - return "DATA"; - - case AWS_SECURE_TUNNEL_MT_STREAM_START: - return "STREAM START"; - - case AWS_SECURE_TUNNEL_MT_STREAM_RESET: - return "STREAM RESET"; - - case AWS_SECURE_TUNNEL_MT_SESSION_RESET: - return "SESSION RESET"; - - case AWS_SECURE_TUNNEL_MT_SERVICE_IDS: - return "SERVICE IDS"; - - case AWS_SECURE_TUNNEL_MT_CONNECTION_START: - return "CONNECTION START"; - - case AWS_SECURE_TUNNEL_MT_CONNECTION_RESET: - return "CONNECTION RESET"; - - default: - return "UNKNOWN"; - } -} From 5a886e4da008b1def9d3feef9a27a442353071b6 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Thu, 6 Apr 2023 09:52:16 -0700 Subject: [PATCH 42/50] remove mutex lock on received data buffer --- include/aws/iotdevice/private/secure_tunneling_impl.h | 1 - source/secure_tunneling.c | 9 +-------- tests/secure_tunnel_tests.c | 1 + 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/include/aws/iotdevice/private/secure_tunneling_impl.h b/include/aws/iotdevice/private/secure_tunneling_impl.h index 1faad08f..df79a854 100644 --- a/include/aws/iotdevice/private/secure_tunneling_impl.h +++ b/include/aws/iotdevice/private/secure_tunneling_impl.h @@ -243,7 +243,6 @@ struct aws_secure_tunnel { /* Stores what has been received but not processed */ struct aws_byte_buf received_data; - struct aws_mutex received_data_lock; /* * When should the secure tunnel next attempt to reconnect? Only used by PENDING_RECONNECT state. diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index aec4c1db..94381900 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -143,7 +143,6 @@ static void s_secure_tunnel_final_destroy(struct aws_secure_tunnel *secure_tunne aws_secure_tunnel_options_storage_destroy(secure_tunnel->config); aws_http_message_release(secure_tunnel->handshake_request); aws_byte_buf_clean_up(&secure_tunnel->received_data); - aws_mutex_clean_up(&secure_tunnel->received_data_lock); aws_tls_connection_options_clean_up(&secure_tunnel->tls_con_opt); aws_tls_ctx_release(secure_tunnel->tls_ctx); aws_mem_release(secure_tunnel->allocator, secure_tunnel); @@ -212,7 +211,7 @@ static bool s_aws_secure_tunnel_protocol_version_match_check( AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: Secure Tunnel is currently using Protocol V%d and received a message using Protocol V%d", (void *)secure_tunnel, - secure_tunnel->config->protocol_version, + (int)secure_tunnel->config->protocol_version, message_protocol_version); return false; } @@ -830,7 +829,6 @@ static void s_aws_secure_tunnel_connected_on_message_received( static int s_process_received_data(struct aws_secure_tunnel *secure_tunnel) { - aws_mutex_lock(&secure_tunnel->received_data_lock); struct aws_byte_buf *received_data = &secure_tunnel->received_data; struct aws_byte_cursor cursor = aws_byte_cursor_from_buf(received_data); uint16_t data_length = 0; @@ -855,7 +853,6 @@ static int s_process_received_data(struct aws_secure_tunnel *secure_tunnel) { (void *)secure_tunnel, error_code, aws_error_debug_str(error_code)); - aws_mutex_unlock(&secure_tunnel->received_data_lock); return error_code; } } @@ -866,7 +863,6 @@ static int s_process_received_data(struct aws_secure_tunnel *secure_tunnel) { aws_byte_buf_append(received_data, &cursor); } - aws_mutex_unlock(&secure_tunnel->received_data_lock); return AWS_OP_SUCCESS; } @@ -980,9 +976,7 @@ static bool s_on_websocket_incoming_frame_payload( if (data.len > 0) { struct aws_secure_tunnel *secure_tunnel = user_data; - aws_mutex_lock(&secure_tunnel->received_data_lock); aws_byte_buf_append(&secure_tunnel->received_data, &data); - aws_mutex_unlock(&secure_tunnel->received_data_lock); if (s_process_received_data(secure_tunnel)) { return false; } @@ -2418,7 +2412,6 @@ struct aws_secure_tunnel *aws_secure_tunnel_new( secure_tunnel->websocket = NULL; aws_byte_buf_init(&secure_tunnel->received_data, allocator, MAX_WEBSOCKET_PAYLOAD); - aws_mutex_init(&secure_tunnel->received_data_lock); aws_secure_tunnel_options_storage_log(secure_tunnel->config, AWS_LL_DEBUG); diff --git a/tests/secure_tunnel_tests.c b/tests/secure_tunnel_tests.c index 2e8b4434..253c01b9 100644 --- a/tests/secure_tunnel_tests.c +++ b/tests/secure_tunnel_tests.c @@ -517,6 +517,7 @@ void aws_secure_tunnel_send_mock_message( aws_byte_buf_clean_up(&out_buf); aws_byte_buf_clean_up(&data_buf); + aws_thread_current_sleep(aws_timestamp_convert(250, AWS_TIMESTAMP_MILLIS, AWS_TIMESTAMP_NANOS, NULL)); } int aws_websocket_client_connect_mock_fn(const struct aws_websocket_client_connection_options *options) { From 42a49f05fdfef8aa97be527994ec47e1b66beb2f Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Thu, 6 Apr 2023 10:07:32 -0700 Subject: [PATCH 43/50] moved connection_reset api to private header. Might move to public if needed for v2 testing --- include/aws/iotdevice/private/secure_tunneling_impl.h | 8 ++++++++ include/aws/iotdevice/secure_tunneling.h | 8 -------- tests/secure_tunnel_tests.c | 3 +++ 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/include/aws/iotdevice/private/secure_tunneling_impl.h b/include/aws/iotdevice/private/secure_tunneling_impl.h index df79a854..e9b27f8e 100644 --- a/include/aws/iotdevice/private/secure_tunneling_impl.h +++ b/include/aws/iotdevice/private/secure_tunneling_impl.h @@ -287,6 +287,14 @@ AWS_IOTDEVICE_API void aws_secure_tunnel_set_vtable( */ AWS_IOTDEVICE_API const struct aws_secure_tunnel_vtable *aws_secure_tunnel_get_default_vtable(void); +/* + * For testing purposes. This message type should only be sent due to internal logic. + */ +AWS_IOTDEVICE_API +int aws_secure_tunnel_connection_reset( + struct aws_secure_tunnel *secure_tunnel, + const struct aws_secure_tunnel_message_view *message_options); + AWS_EXTERN_C_END #endif /* AWS_IOTDEVICE_SECURE_TUNNELING_IMPL_H */ diff --git a/include/aws/iotdevice/secure_tunneling.h b/include/aws/iotdevice/secure_tunneling.h index dd2cf8a2..b7d8d12d 100644 --- a/include/aws/iotdevice/secure_tunneling.h +++ b/include/aws/iotdevice/secure_tunneling.h @@ -329,14 +329,6 @@ int aws_secure_tunnel_stream_reset( struct aws_secure_tunnel *secure_tunnel, const struct aws_secure_tunnel_message_view *message_options); -//*********************************************************************************************************************** -/* THIS API SHOULD NOT BE USED BY THE CUSTOMER AND IS FOR TESTING PURPOSES ONLY */ -//*********************************************************************************************************************** -AWS_IOTDEVICE_API -int aws_secure_tunnel_connection_reset( - struct aws_secure_tunnel *secure_tunnel, - const struct aws_secure_tunnel_message_view *message_options); - AWS_EXTERN_C_END #endif /* AWS_IOTDEVICE_SECURE_TUNNELING_H */ diff --git a/tests/secure_tunnel_tests.c b/tests/secure_tunnel_tests.c index 253c01b9..eb3ba1b0 100644 --- a/tests/secure_tunnel_tests.c +++ b/tests/secure_tunnel_tests.c @@ -517,6 +517,9 @@ void aws_secure_tunnel_send_mock_message( 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(250, AWS_TIMESTAMP_MILLIS, AWS_TIMESTAMP_NANOS, NULL)); } From 3a6ddce21fbb773c204d3240de6d667281903bb5 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Thu, 6 Apr 2023 10:38:21 -0700 Subject: [PATCH 44/50] add delay to mock websocket sends --- tests/secure_tunnel_tests.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/secure_tunnel_tests.c b/tests/secure_tunnel_tests.c index eb3ba1b0..fbcfa252 100644 --- a/tests/secure_tunnel_tests.c +++ b/tests/secure_tunnel_tests.c @@ -503,6 +503,10 @@ static void s_wait_for_n_messages_received(struct aws_secure_tunnel_mock_test_fi 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_byte_cursor data_cur; struct aws_byte_buf out_buf; @@ -520,7 +524,7 @@ void aws_secure_tunnel_send_mock_message( /* 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(250, AWS_TIMESTAMP_MILLIS, AWS_TIMESTAMP_NANOS, NULL)); + aws_thread_current_sleep(aws_timestamp_convert(350, AWS_TIMESTAMP_MILLIS, AWS_TIMESTAMP_NANOS, NULL)); } int aws_websocket_client_connect_mock_fn(const struct aws_websocket_client_connection_options *options) { From 9b2ce72918fde97adc9d43816031190be783e161 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Thu, 6 Apr 2023 13:32:45 -0700 Subject: [PATCH 45/50] moved connection related members out of options storage into its own struct --- .../iotdevice/private/secure_tunneling_impl.h | 34 ++++-- .../private/secure_tunneling_operations.h | 6 + source/secure_tunneling.c | 109 ++++++++++-------- source/secure_tunneling_operations.c | 97 ++++++++++------ tests/secure_tunnel_tests.c | 18 +-- 5 files changed, 158 insertions(+), 106 deletions(-) diff --git a/include/aws/iotdevice/private/secure_tunneling_impl.h b/include/aws/iotdevice/private/secure_tunneling_impl.h index e9b27f8e..e698c484 100644 --- a/include/aws/iotdevice/private/secure_tunneling_impl.h +++ b/include/aws/iotdevice/private/secure_tunneling_impl.h @@ -131,18 +131,6 @@ struct aws_secure_tunnel_options_storage { struct aws_string *endpoint_host; - uint8_t protocol_version; - - /* Stream related info */ - /* Used for streams not using multiplexing (service ids) */ - int32_t stream_id; - struct aws_hash_table connection_ids; - /* 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 */ - struct aws_secure_tunnel_message_storage *restore_stream_message_view; - struct aws_secure_tunnel_message_storage restore_stream_message; - /* Callbacks */ aws_secure_tunnel_message_received_fn *on_message_received; aws_secure_tunneling_on_connection_complete_fn *on_connection_complete; @@ -162,6 +150,23 @@ struct aws_secure_tunnel_options_storage { enum aws_secure_tunneling_local_proxy_mode local_proxy_mode; }; +struct aws_secure_tunnel_connections { + struct aws_allocator *allocator; + + uint8_t protocol_version; + + /* Used for streams not using multiplexing (service ids) */ + int32_t stream_id; + struct aws_hash_table connection_ids; + + /* 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 */ + struct aws_secure_tunnel_message_storage *restore_stream_message_view; + struct aws_secure_tunnel_message_storage restore_stream_message; +}; + struct aws_secure_tunnel_vtable { /* aws_high_res_clock_get_ticks */ uint64_t (*get_current_time_fn)(void); @@ -195,6 +200,11 @@ struct aws_secure_tunnel { */ struct aws_secure_tunnel_options_storage *config; + /* + * Stores connection related information + */ + struct aws_secure_tunnel_connections *connections; + struct aws_tls_ctx *tls_ctx; struct aws_tls_connection_options tls_con_opt; diff --git a/include/aws/iotdevice/private/secure_tunneling_operations.h b/include/aws/iotdevice/private/secure_tunneling_operations.h index dcf83c2b..37c17d29 100644 --- a/include/aws/iotdevice/private/secure_tunneling_operations.h +++ b/include/aws/iotdevice/private/secure_tunneling_operations.h @@ -173,6 +173,12 @@ struct aws_secure_tunnel_options_storage *aws_secure_tunnel_options_storage_new( struct aws_allocator *allocator, const struct aws_secure_tunnel_options *options); +AWS_IOTDEVICE_API +void aws_secure_tunnel_connections_destroy(struct aws_secure_tunnel_connections *storage); + +AWS_IOTDEVICE_API +struct aws_secure_tunnel_connections *aws_secure_tunnel_connections_new(struct aws_allocator *allocator); + AWS_IOTDEVICE_API void aws_secure_tunnel_options_storage_log( const struct aws_secure_tunnel_options_storage *options_storage, diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 94381900..8a73b844 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -140,6 +140,7 @@ static void s_secure_tunnel_final_destroy(struct aws_secure_tunnel *secure_tunne aws_secure_tunnel_operational_state_clean_up(secure_tunnel); /* Clean up all memory */ + aws_secure_tunnel_connections_destroy(secure_tunnel->connections); aws_secure_tunnel_options_storage_destroy(secure_tunnel->config); aws_http_message_release(secure_tunnel->handshake_request); aws_byte_buf_clean_up(&secure_tunnel->received_data); @@ -181,10 +182,10 @@ static int s_reset_service_id(void *context, struct aws_hash_element *p_element) static void s_reset_secure_tunnel_streams(struct aws_secure_tunnel *secure_tunnel) { AWS_LOGF_INFO(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: Secure tunnel session reset.", (void *)secure_tunnel); - secure_tunnel->config->protocol_version = 0; - secure_tunnel->config->stream_id = INVALID_STREAM_ID; - aws_hash_table_clear(&secure_tunnel->config->connection_ids); - aws_hash_table_foreach(&secure_tunnel->config->service_ids, s_reset_service_id, NULL); + secure_tunnel->connections->protocol_version = 0; + secure_tunnel->connections->stream_id = INVALID_STREAM_ID; + aws_hash_table_clear(&secure_tunnel->connections->connection_ids); + aws_hash_table_foreach(&secure_tunnel->connections->service_ids, s_reset_service_id, NULL); secure_tunnel->received_data.len = 0; /* Drop any incomplete secure tunnel frame */ } @@ -206,12 +207,12 @@ static bool s_aws_secure_tunnel_protocol_version_match_check( 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->config->protocol_version != message_protocol_version) { + 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", (void *)secure_tunnel, - (int)secure_tunnel->config->protocol_version, + (int)secure_tunnel->connections->protocol_version, message_protocol_version); return false; } @@ -226,11 +227,11 @@ static bool s_aws_secure_tunnel_stream_id_match_check( * No service id means either V1 protocol is being used or V3 protocol is being used on a tunnel without service ids */ if (service_id == NULL || service_id->len == 0) { - return (secure_tunnel->config->stream_id == stream_id); + return (secure_tunnel->connections->stream_id == stream_id); } struct aws_hash_element *elem = NULL; - aws_hash_table_find(&secure_tunnel->config->service_ids, service_id, &elem); + aws_hash_table_find(&secure_tunnel->connections->service_ids, service_id, &elem); if (elem == NULL) { return false; } @@ -246,7 +247,7 @@ static bool s_aws_secure_tunnel_active_stream_check( * No service id means either V1 protocol is being used or V3 protocol is being used on a tunnel without service ids */ if (message_view->service_id == NULL || message_view->service_id->len == 0) { - if (secure_tunnel->config->stream_id != message_view->stream_id) { + if (secure_tunnel->connections->stream_id != message_view->stream_id) { return false; } @@ -260,7 +261,7 @@ static bool s_aws_secure_tunnel_active_stream_check( * checked against stored connection_ids to confirm the stream is active. */ struct aws_hash_element *connection_id_elem = NULL; - aws_hash_table_find(&secure_tunnel->config->connection_ids, &connection_id, &connection_id_elem); + aws_hash_table_find(&secure_tunnel->connections->connection_ids, &connection_id, &connection_id_elem); if (connection_id_elem == NULL) { aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_CONNECTION_ID); return false; @@ -270,7 +271,7 @@ static bool s_aws_secure_tunnel_active_stream_check( /* Check if service id is being used by the secure tunnel */ struct aws_hash_element *elem = NULL; - aws_hash_table_find(&secure_tunnel->config->service_ids, message_view->service_id, &elem); + aws_hash_table_find(&secure_tunnel->connections->service_ids, message_view->service_id, &elem); if (elem == NULL) { aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_SERVICE_ID); return false; @@ -285,7 +286,7 @@ static bool s_aws_secure_tunnel_active_stream_check( /* V1 and V2 will be considered active at this point with a matching stream id but V3 streams will need to have * their connection id checked */ - if (secure_tunnel->config->protocol_version == 3) { + if (secure_tunnel->connections->protocol_version == 3) { struct aws_hash_element *connection_id_elem = NULL; aws_hash_table_find(&service_id_elem->connection_ids, &message_view->connection_id, &connection_id_elem); if (connection_id_elem == NULL) { @@ -304,13 +305,16 @@ static int s_aws_secure_tunnel_set_stream( uint32_t connection_id) { /* No service id means V1 protocol is being used */ if (service_id == NULL || service_id->len == 0) { - secure_tunnel->config->stream_id = stream_id; - aws_hash_table_clear(&secure_tunnel->config->connection_ids); + secure_tunnel->connections->stream_id = stream_id; + aws_hash_table_clear(&secure_tunnel->connections->connection_ids); if (connection_id > 0) { struct aws_connection_id_element *connection_id_elem = aws_connection_id_element_new(secure_tunnel->allocator, connection_id); aws_hash_table_put( - &secure_tunnel->config->connection_ids, &connection_id_elem->connection_id, connection_id_elem, NULL); + &secure_tunnel->connections->connection_ids, + &connection_id_elem->connection_id, + connection_id_elem, + NULL); } AWS_LOGF_INFO( AWS_LS_IOTDEVICE_SECURE_TUNNELING, @@ -322,7 +326,7 @@ static int s_aws_secure_tunnel_set_stream( } struct aws_hash_element *elem = NULL; - aws_hash_table_find(&secure_tunnel->config->service_ids, service_id, &elem); + aws_hash_table_find(&secure_tunnel->connections->service_ids, service_id, &elem); if (elem == NULL) { AWS_LOGF_WARN( AWS_LS_IOTDEVICE_SECURE_TUNNELING, @@ -341,7 +345,8 @@ static int s_aws_secure_tunnel_set_stream( aws_hash_table_put( &replacement_elem->connection_ids, &connection_id_elem->connection_id, connection_id_elem, NULL); } - aws_hash_table_put(&secure_tunnel->config->service_ids, &replacement_elem->service_id_cur, replacement_elem, NULL); + aws_hash_table_put( + &secure_tunnel->connections->service_ids, &replacement_elem->service_id_cur, replacement_elem, NULL); AWS_LOGF_INFO( AWS_LS_IOTDEVICE_SECURE_TUNNELING, @@ -360,10 +365,10 @@ static int s_aws_secure_tunnel_set_connection_id( uint32_t connection_id) { struct aws_hash_table *table_to_put_in = NULL; if (service_id == NULL || service_id->len == 0) { - table_to_put_in = &secure_tunnel->config->connection_ids; + table_to_put_in = &secure_tunnel->connections->connection_ids; } else { struct aws_hash_element *elem = NULL; - aws_hash_table_find(&secure_tunnel->config->service_ids, service_id, &elem); + aws_hash_table_find(&secure_tunnel->connections->service_ids, service_id, &elem); if (elem == NULL) { AWS_LOGF_ERROR( AWS_LS_IOTDEVICE_SECURE_TUNNELING, @@ -463,10 +468,10 @@ static int s_aws_secure_tunnel_remove_connection_id( struct aws_hash_table *table_to_remove_from = NULL; if (message_view->service_id == NULL || message_view->service_id->len == 0) { - table_to_remove_from = &secure_tunnel->config->connection_ids; + table_to_remove_from = &secure_tunnel->connections->connection_ids; } else { struct aws_hash_element *elem = NULL; - aws_hash_table_find(&secure_tunnel->config->service_ids, message_view->service_id, &elem); + aws_hash_table_find(&secure_tunnel->connections->service_ids, message_view->service_id, &elem); struct aws_service_id_element *service_id_elem = elem->value; table_to_remove_from = &service_id_elem->connection_ids; } @@ -555,15 +560,15 @@ static void s_aws_secure_tunnel_on_stream_start_received( * If a protocol version hasn't been established yet, the first STREAM START determines the protocol version * being used this session */ - if (secure_tunnel->config->protocol_version == 0) { + if (secure_tunnel->connections->protocol_version == 0) { uint8_t message_protocol_version = s_aws_secure_tunnel_message_min_protocol_check(message_view); - secure_tunnel->config->protocol_version = message_protocol_version; + 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 received STREAM START", (void *)secure_tunnel, - secure_tunnel->config->protocol_version); + 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 @@ -576,11 +581,11 @@ static void s_aws_secure_tunnel_on_stream_start_received( (void *)secure_tunnel); reset_secure_tunnel_connection(secure_tunnel); aws_secure_tunnel_message_storage_init( - &secure_tunnel->config->restore_stream_message, + &secure_tunnel->connections->restore_stream_message, secure_tunnel->allocator, message_view, AWS_STOT_STREAM_START); - secure_tunnel->config->restore_stream_message_view = &secure_tunnel->config->restore_stream_message; + secure_tunnel->connections->restore_stream_message_view = &secure_tunnel->connections->restore_stream_message; return; } @@ -605,7 +610,7 @@ static void s_aws_secure_tunnel_on_stream_reset_received( struct aws_secure_tunnel *secure_tunnel, struct aws_secure_tunnel_message_view *message_view) { - if (secure_tunnel->config->protocol_version != 0 && + if (secure_tunnel->connections->protocol_version != 0 && !s_aws_secure_tunnel_protocol_version_match_check(secure_tunnel, message_view)) { AWS_LOGF_INFO( AWS_LS_IOTDEVICE_SECURE_TUNNELING, @@ -651,13 +656,13 @@ static void s_aws_secure_tunnel_on_service_ids_received( struct aws_secure_tunnel *secure_tunnel, struct aws_secure_tunnel_message_view *message_view) { - aws_hash_table_clear(&secure_tunnel->config->service_ids); + aws_hash_table_clear(&secure_tunnel->connections->service_ids); if (message_view->service_id != NULL) { struct aws_service_id_element *service_id_1_elem = aws_service_id_element_new(secure_tunnel->allocator, message_view->service_id, INVALID_STREAM_ID); aws_hash_table_put( - &secure_tunnel->config->service_ids, &service_id_1_elem->service_id_cur, service_id_1_elem, NULL); + &secure_tunnel->connections->service_ids, &service_id_1_elem->service_id_cur, service_id_1_elem, NULL); AWS_LOGF_INFO( AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: secure tunnel service id 1 set to: " PRInSTR, @@ -667,7 +672,7 @@ static void s_aws_secure_tunnel_on_service_ids_received( struct aws_service_id_element *service_id_2_elem = aws_service_id_element_new(secure_tunnel->allocator, message_view->service_id_2, INVALID_STREAM_ID); aws_hash_table_put( - &secure_tunnel->config->service_ids, &service_id_2_elem->service_id_cur, service_id_2_elem, NULL); + &secure_tunnel->connections->service_ids, &service_id_2_elem->service_id_cur, service_id_2_elem, NULL); AWS_LOGF_INFO( AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: secure tunnel service id 2 set to: " PRInSTR, @@ -677,7 +682,10 @@ static void s_aws_secure_tunnel_on_service_ids_received( struct aws_service_id_element *service_id_3_elem = aws_service_id_element_new(secure_tunnel->allocator, message_view->service_id_3, INVALID_STREAM_ID); aws_hash_table_put( - &secure_tunnel->config->service_ids, &service_id_3_elem->service_id_cur, service_id_3_elem, NULL); + &secure_tunnel->connections->service_ids, + &service_id_3_elem->service_id_cur, + service_id_3_elem, + NULL); AWS_LOGF_INFO( AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: secure tunnel service id 3 set to: " PRInSTR, @@ -700,23 +708,23 @@ static void s_aws_secure_tunnel_on_service_ids_received( } /* Initialize stream if one is set to be started upon a reconnect */ - if (secure_tunnel->config->restore_stream_message_view != NULL) { + if (secure_tunnel->connections->restore_stream_message_view != NULL) { AWS_LOGF_INFO( AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: Secure Tunnel will process the STREAM START that caused reconnection due to changing protocol by " "Source Device.", (void *)secure_tunnel); s_aws_secure_tunnel_connected_on_message_received( - secure_tunnel, &secure_tunnel->config->restore_stream_message_view->storage_view); - aws_secure_tunnel_message_storage_clean_up(&secure_tunnel->config->restore_stream_message); - secure_tunnel->config->restore_stream_message_view = NULL; + secure_tunnel, &secure_tunnel->connections->restore_stream_message_view->storage_view); + aws_secure_tunnel_message_storage_clean_up(&secure_tunnel->connections->restore_stream_message); + secure_tunnel->connections->restore_stream_message_view = NULL; } } static void s_aws_secure_tunnel_on_connection_start_received( struct aws_secure_tunnel *secure_tunnel, struct aws_secure_tunnel_message_view *message_view) { - if (secure_tunnel->config->protocol_version != 3) { + 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 " @@ -759,7 +767,7 @@ static void s_aws_secure_tunnel_on_connection_start_received( static void s_aws_secure_tunnel_on_connection_reset_received( struct aws_secure_tunnel *secure_tunnel, struct aws_secure_tunnel_message_view *message_view) { - if (secure_tunnel->config->protocol_version != 3) { + 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 " @@ -2369,6 +2377,11 @@ struct aws_secure_tunnel *aws_secure_tunnel_new( goto error; } + secure_tunnel->connections = aws_secure_tunnel_connections_new(allocator); + if (secure_tunnel->connections == NULL) { + goto error; + } + /* all secure tunnel activity will take place on this event loop */ secure_tunnel->loop = aws_event_loop_group_get_next_loop(secure_tunnel->config->bootstrap->event_loop_group); if (secure_tunnel->loop == NULL) { @@ -2404,9 +2417,9 @@ struct aws_secure_tunnel *aws_secure_tunnel_new( aws_tls_ctx_options_clean_up(&tls_ctx_opt); /* Connection reset */ - secure_tunnel->config->stream_id = INVALID_STREAM_ID; + secure_tunnel->connections->stream_id = INVALID_STREAM_ID; - aws_hash_table_foreach(&secure_tunnel->config->service_ids, s_reset_service_id, NULL); + aws_hash_table_foreach(&secure_tunnel->connections->service_ids, s_reset_service_id, NULL); secure_tunnel->handshake_request = NULL; secure_tunnel->websocket = NULL; @@ -2466,7 +2479,7 @@ int aws_secure_tunnel_send_message( * 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->config->protocol_version < 3 && message_options->connection_id == 1) { + secure_tunnel->connections->protocol_version < 3 && message_options->connection_id == 1) { message_op->options_storage.storage_view.connection_id = 0; } @@ -2499,8 +2512,8 @@ int aws_secure_tunnel_stream_start( } uint8_t message_protocol_version = s_aws_secure_tunnel_message_min_protocol_check(message_options); - if (secure_tunnel->config->protocol_version != 0 && - message_protocol_version != secure_tunnel->config->protocol_version) { + 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. @@ -2510,18 +2523,18 @@ int aws_secure_tunnel_stream_start( "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, - secure_tunnel->config->protocol_version, - message_protocol_version); + (int)secure_tunnel->connections->protocol_version, + (int)message_protocol_version); reset_secure_tunnel_connection(secure_tunnel); } - if (secure_tunnel->config->protocol_version == 0) { - secure_tunnel->config->protocol_version = message_protocol_version; + 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, - secure_tunnel->config->protocol_version); + (int)secure_tunnel->connections->protocol_version); } struct aws_secure_tunnel_operation_message *message_op = aws_secure_tunnel_operation_message_new( @@ -2559,7 +2572,7 @@ int aws_secure_tunnel_connection_start( return AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INCORRECT_MODE; } - if (secure_tunnel->config->protocol_version != 3) { + 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; diff --git a/source/secure_tunneling_operations.c b/source/secure_tunneling_operations.c index b2c3f9a7..38f65602 100644 --- a/source/secure_tunneling_operations.c +++ b/source/secure_tunneling_operations.c @@ -362,10 +362,10 @@ 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; if (message_view->service_id == NULL || message_view->service_id->len == 0) { - stream_id = secure_tunnel->config->stream_id; + stream_id = secure_tunnel->connections->stream_id; } else { struct aws_hash_element *elem = NULL; - aws_hash_table_find(&secure_tunnel->config->service_ids, message_view->service_id, &elem); + aws_hash_table_find(&secure_tunnel->connections->service_ids, message_view->service_id, &elem); if (elem == NULL) { AWS_LOGF_WARN( AWS_LS_IOTDEVICE_SECURE_TUNNELING, @@ -418,10 +418,10 @@ static int s_aws_secure_tunnel_operation_message_set_next_stream_id( struct aws_secure_tunnel_message_view *message_view = &message_op->options_storage.storage_view; if (message_view->service_id == NULL || message_view->service_id->len == 0) { - stream_id = secure_tunnel->config->stream_id + 1; - secure_tunnel->config->stream_id = stream_id; + stream_id = secure_tunnel->connections->stream_id + 1; + secure_tunnel->connections->stream_id = stream_id; - aws_hash_table_clear(&secure_tunnel->config->connection_ids); + aws_hash_table_clear(&secure_tunnel->connections->connection_ids); struct aws_connection_id_element *connection_id_elem = NULL; if (message_view->connection_id > 0) { connection_id_elem = aws_connection_id_element_new(secure_tunnel->allocator, message_view->connection_id); @@ -430,7 +430,7 @@ static int s_aws_secure_tunnel_operation_message_set_next_stream_id( } aws_hash_table_put( - &secure_tunnel->config->connection_ids, &connection_id_elem->connection_id, connection_id_elem, NULL); + &secure_tunnel->connections->connection_ids, &connection_id_elem->connection_id, connection_id_elem, NULL); AWS_LOGF_INFO( AWS_LS_IOTDEVICE_SECURE_TUNNELING, @@ -440,7 +440,7 @@ static int s_aws_secure_tunnel_operation_message_set_next_stream_id( connection_id_elem->connection_id); } else { struct aws_hash_element *elem = NULL; - aws_hash_table_find(&secure_tunnel->config->service_ids, message_view->service_id, &elem); + aws_hash_table_find(&secure_tunnel->connections->service_ids, message_view->service_id, &elem); if (elem == NULL) { AWS_LOGF_WARN( AWS_LS_IOTDEVICE_SECURE_TUNNELING, @@ -467,7 +467,7 @@ static int s_aws_secure_tunnel_operation_message_set_next_stream_id( aws_hash_table_put( &replacement_elem->connection_ids, &connection_id_elem->connection_id, connection_id_elem, NULL); aws_hash_table_put( - &secure_tunnel->config->service_ids, &replacement_elem->service_id_cur, replacement_elem, NULL); + &secure_tunnel->connections->service_ids, &replacement_elem->service_id_cur, replacement_elem, NULL); AWS_LOGF_INFO( AWS_LS_IOTDEVICE_SECURE_TUNNELING, @@ -500,10 +500,10 @@ static int s_aws_secure_tunnel_operation_set_connection_start_id( */ struct aws_hash_table *table_to_put_in = NULL; if (message_view->service_id == NULL || message_view->service_id->len == 0) { - table_to_put_in = &secure_tunnel->config->connection_ids; + table_to_put_in = &secure_tunnel->connections->connection_ids; } else { struct aws_hash_element *elem = NULL; - aws_hash_table_find(&secure_tunnel->config->service_ids, message_view->service_id, &elem); + aws_hash_table_find(&secure_tunnel->connections->service_ids, message_view->service_id, &elem); if (elem == NULL) { AWS_LOGF_ERROR( AWS_LS_IOTDEVICE_SECURE_TUNNELING, @@ -776,17 +776,11 @@ void aws_secure_tunnel_options_storage_destroy(struct aws_secure_tunnel_options_ return; } - if (storage->restore_stream_message_view != NULL) { - aws_secure_tunnel_message_storage_clean_up(&storage->restore_stream_message); - storage->restore_stream_message_view = NULL; - } aws_client_bootstrap_release(storage->bootstrap); aws_http_proxy_config_destroy(storage->http_proxy_config); aws_string_destroy(storage->endpoint_host); aws_string_destroy(storage->access_token); aws_string_destroy(storage->client_token); - aws_hash_table_clean_up(&storage->service_ids); - aws_hash_table_clean_up(&storage->connection_ids); aws_mem_release(storage->allocator, storage); } @@ -865,27 +859,6 @@ struct aws_secure_tunnel_options_storage *aws_secure_tunnel_options_storage_new( aws_http_proxy_options_init_from_config(&storage->http_proxy_options, storage->http_proxy_config); } - if (aws_hash_table_init( - &storage->service_ids, - allocator, - 3, - aws_hash_byte_cursor_ptr, - (aws_hash_callback_eq_fn *)aws_byte_cursor_eq, - NULL, - s_destroy_service_id)) { - goto error; - } - if (aws_hash_table_init( - &storage->connection_ids, - allocator, - 1, - aws_secure_tunnel_hash_connection_id, - aws_secure_tunnel_connection_id_eq, - NULL, - aws_connection_id_destroy)) { - goto error; - } - storage->on_message_received = options->on_message_received; storage->user_data = options->user_data; @@ -909,6 +882,56 @@ struct aws_secure_tunnel_options_storage *aws_secure_tunnel_options_storage_new( return NULL; } +void aws_secure_tunnel_connections_destroy(struct aws_secure_tunnel_connections *connections) { + if (connections == NULL) { + return; + } + + if (connections->restore_stream_message_view != NULL) { + aws_secure_tunnel_message_storage_clean_up(&connections->restore_stream_message); + connections->restore_stream_message_view = NULL; + } + aws_hash_table_clean_up(&connections->service_ids); + aws_hash_table_clean_up(&connections->connection_ids); + + aws_mem_release(connections->allocator, connections); +} + +struct aws_secure_tunnel_connections *aws_secure_tunnel_connections_new(struct aws_allocator *allocator) { + AWS_PRECONDITION(allocator != NULL); + + struct aws_secure_tunnel_connections *connections = + aws_mem_calloc(allocator, 1, sizeof(struct aws_secure_tunnel_connections)); + + connections->allocator = allocator; + + if (aws_hash_table_init( + &connections->service_ids, + allocator, + 3, + aws_hash_byte_cursor_ptr, + (aws_hash_callback_eq_fn *)aws_byte_cursor_eq, + NULL, + s_destroy_service_id)) { + goto error; + } + if (aws_hash_table_init( + &connections->connection_ids, + allocator, + 1, + aws_secure_tunnel_hash_connection_id, + aws_secure_tunnel_connection_id_eq, + NULL, + aws_connection_id_destroy)) { + goto error; + } + return connections; + +error: + aws_secure_tunnel_connections_destroy(connections); + return NULL; +} + /********************************************************************************************************************* * Data Tunnel Pair ********************************************************************************************************************/ diff --git a/tests/secure_tunnel_tests.c b/tests/secure_tunnel_tests.c index fbcfa252..18776a7e 100644 --- a/tests/secure_tunnel_tests.c +++ b/tests/secure_tunnel_tests.c @@ -135,11 +135,11 @@ static bool s_secure_tunnel_check_active_stream_id( struct aws_byte_cursor *service_id, int32_t stream_id) { if (service_id == NULL) { - return secure_tunnel->config->stream_id == stream_id; + return secure_tunnel->connections->stream_id == stream_id; } struct aws_hash_element *elem = NULL; - aws_hash_table_find(&secure_tunnel->config->service_ids, service_id, &elem); + aws_hash_table_find(&secure_tunnel->connections->service_ids, service_id, &elem); if (elem == NULL) { return false; } @@ -160,17 +160,17 @@ static bool s_secure_tunnel_check_active_connection_id( struct aws_hash_table *table_to_check = NULL; if (service_id) { struct aws_hash_element *elem = NULL; - aws_hash_table_find(&secure_tunnel->config->service_ids, service_id, &elem); + aws_hash_table_find(&secure_tunnel->connections->service_ids, service_id, &elem); if (elem == NULL) { return false; } struct aws_service_id_element *service_id_elem = elem->value; table_to_check = &service_id_elem->connection_ids; } else { - if (secure_tunnel->config->stream_id != stream_id) { + if (secure_tunnel->connections->stream_id != stream_id) { return false; } - table_to_check = &secure_tunnel->config->connection_ids; + table_to_check = &secure_tunnel->connections->connection_ids; } struct aws_hash_element *connection_elem = NULL; @@ -920,15 +920,15 @@ static int s_secure_tunneling_store_service_ids_test_fn(struct aws_allocator *al /* check that service ids have been stored */ struct aws_hash_element *elem = NULL; struct aws_byte_cursor service_id_1_cur = aws_byte_cursor_from_string(s_service_id_1); - aws_hash_table_find(&secure_tunnel->config->service_ids, &service_id_1_cur, &elem); + aws_hash_table_find(&secure_tunnel->connections->service_ids, &service_id_1_cur, &elem); ASSERT_NOT_NULL(elem); elem = NULL; struct aws_byte_cursor service_id_2_cur = aws_byte_cursor_from_string(s_service_id_2); - aws_hash_table_find(&secure_tunnel->config->service_ids, &service_id_2_cur, &elem); + aws_hash_table_find(&secure_tunnel->connections->service_ids, &service_id_2_cur, &elem); ASSERT_NOT_NULL(elem); elem = NULL; struct aws_byte_cursor service_id_3_cur = aws_byte_cursor_from_string(s_service_id_3); - aws_hash_table_find(&secure_tunnel->config->service_ids, &service_id_3_cur, &elem); + aws_hash_table_find(&secure_tunnel->connections->service_ids, &service_id_3_cur, &elem); ASSERT_NOT_NULL(elem); ASSERT_SUCCESS(aws_secure_tunnel_stop(secure_tunnel)); @@ -1931,7 +1931,7 @@ static int s_secure_tunneling_existing_connection_start_send_reset_test_fn(struc /* check that stream with connection id has been closed properly */ struct aws_hash_element *elem = NULL; - aws_hash_table_find(&secure_tunnel->config->service_ids, stream_start_message_view.service_id, &elem); + aws_hash_table_find(&secure_tunnel->connections->service_ids, stream_start_message_view.service_id, &elem); ASSERT_NOT_NULL(elem); struct aws_service_id_element *service_id_elem = elem->value; ASSERT_INT_EQUALS((int)aws_hash_table_get_entry_count(&service_id_elem->connection_ids), 0); From b7487ec040f147061c9ee749d9a4f0c4f1a7ffe1 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Tue, 11 Apr 2023 10:22:35 -0700 Subject: [PATCH 46/50] init tls_ctx_opt before goto error calls --- source/secure_tunneling.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 8a73b844..73771f1c 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -2371,6 +2371,11 @@ struct aws_secure_tunnel *aws_secure_tunnel_new( aws_linked_list_init(&secure_tunnel->queued_operations); secure_tunnel->current_operation = NULL; + /* tls setup */ + struct aws_tls_ctx_options tls_ctx_opt; + AWS_ZERO_STRUCT(tls_ctx_opt); + aws_tls_ctx_options_init_default_client(&tls_ctx_opt, secure_tunnel->allocator); + /* store options */ secure_tunnel->config = aws_secure_tunnel_options_storage_new(allocator, options); if (secure_tunnel->config == NULL) { @@ -2391,11 +2396,6 @@ struct aws_secure_tunnel *aws_secure_tunnel_new( secure_tunnel->desired_state = AWS_STS_STOPPED; secure_tunnel->current_state = AWS_STS_STOPPED; - /* tls setup */ - struct aws_tls_ctx_options tls_ctx_opt; - AWS_ZERO_STRUCT(tls_ctx_opt); - aws_tls_ctx_options_init_default_client(&tls_ctx_opt, secure_tunnel->allocator); - if (options->root_ca != NULL) { if (aws_tls_ctx_options_override_default_trust_store_from_path(&tls_ctx_opt, NULL, options->root_ca)) { goto error; From cabba56a36270129040ad63d67fda1fed538bbe0 Mon Sep 17 00:00:00 2001 From: Steve Kim <86316075+sbSteveK@users.noreply.github.com> Date: Tue, 18 Apr 2023 09:29:46 -0700 Subject: [PATCH 47/50] removed header logging. added jitter (#87) * added jitter for reconnecting * remove some logging from websocket request headers --- source/secure_tunneling.c | 48 +++++++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 73771f1c..a4ecffa0 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -1198,10 +1199,9 @@ static int s_handshake_add_header( } AWS_LOGF_TRACE( AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: Added header " PRInSTR " " PRInSTR " to websocket request", + "id=%p: Added header " PRInSTR " to websocket request", (void *)secure_tunnel, - AWS_BYTE_CURSOR_PRI(header.name), - AWS_BYTE_CURSOR_PRI(header.value)); + AWS_BYTE_CURSOR_PRI(header.name)); return AWS_OP_SUCCESS; } @@ -1378,23 +1378,53 @@ static void s_change_current_state_to_websocket_shutdown(struct aws_secure_tunne } } -static void s_update_reconnect_delay_for_pending_reconnect(struct aws_secure_tunnel *secure_tunnel) { +static uint64_t s_aws_secure_tunnel_compute_reconnect_backoff_no_jitter(struct aws_secure_tunnel *secure_tunnel) { + uint64_t retry_count = aws_min_u64(secure_tunnel->reconnect_count, 63); + return aws_mul_u64_saturating((uint64_t)1 << retry_count, MIN_RECONNECT_DELAY_MS); +} + +uint64_t aws_secure_tunnel_random_in_range(uint64_t from, uint64_t to) { + uint64_t max = aws_max_u64(from, to); + uint64_t min = aws_min_u64(from, to); - uint64_t delay_ms = MIN_RECONNECT_DELAY_MS; - delay_ms = delay_ms << (int)secure_tunnel->reconnect_count; + /* Note: this contains several changes to the corresponding function in aws-c-io. Don't throw them away. + * + * 1. random range is now inclusive/closed: [from, to] rather than half-open [from, to) + * 2. as a corollary, diff == 0 => return min, not 0 + */ + uint64_t diff = max - min; + if (!diff) { + return min; + } + + uint64_t random_value = 0; + if (aws_device_random_u64(&random_value)) { + return min; + } + + if (diff == UINT64_MAX) { + return random_value; + } + return min + random_value % (diff + 1); /* + 1 is safe due to previous check */ +} + +static uint64_t s_aws_secure_tunnel_compute_reconnect_backoff_full_jitter(struct aws_secure_tunnel *secure_tunnel) { + uint64_t non_jittered = s_aws_secure_tunnel_compute_reconnect_backoff_no_jitter(secure_tunnel); + return aws_secure_tunnel_random_in_range(0, non_jittered); +} + +static void s_update_reconnect_delay_for_pending_reconnect(struct aws_secure_tunnel *secure_tunnel) { + uint64_t delay_ms = s_aws_secure_tunnel_compute_reconnect_backoff_full_jitter(secure_tunnel); delay_ms = aws_min_u64(delay_ms, MAX_RECONNECT_DELAY_MS); uint64_t now = (*secure_tunnel->vtable->get_current_time_fn)(); - secure_tunnel->next_reconnect_time_ns = aws_add_u64_saturating(now, aws_timestamp_convert(delay_ms, AWS_TIMESTAMP_MILLIS, AWS_TIMESTAMP_NANOS, NULL)); - AWS_LOGF_DEBUG( AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: next connection attempt in %" PRIu64 " milliseconds", (void *)secure_tunnel, delay_ms); - secure_tunnel->reconnect_count++; } From ab769274a77ce90882d7079a29268b9b4d4ef7af Mon Sep 17 00:00:00 2001 From: Steve Kim <86316075+sbSteveK@users.noreply.github.com> Date: Tue, 18 Apr 2023 16:00:34 -0700 Subject: [PATCH 48/50] Secure tunnel v3 tls (#88) * added ability to pass in tls options * dns ping reduction * builder v0.9.38 --- .github/workflows/ci.yml | 6 +- .../iotdevice/private/secure_tunneling_impl.h | 3 + include/aws/iotdevice/secure_tunneling.h | 5 ++ source/secure_tunneling.c | 66 +++++++++++++------ 4 files changed, 58 insertions(+), 22 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 50c92ffc..ab0a27cc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,7 @@ on: - 'main' env: - BUILDER_VERSION: v0.9.26 + BUILDER_VERSION: v0.9.38 BUILDER_SOURCE: releases BUILDER_HOST: https://d19elf31gohf1l.cloudfront.net PACKAGE_NAME: aws-c-iot @@ -128,8 +128,8 @@ jobs: python3 -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz?run=${{ env.RUN }}', 'builder')" chmod a+x builder ./builder build -p ${{ env.PACKAGE_NAME }} - - + + # Test downstream repos. # This should not be required because we can run into a chicken and egg problem if there is a change that needs some fix in a downstream repo. downstream: diff --git a/include/aws/iotdevice/private/secure_tunneling_impl.h b/include/aws/iotdevice/private/secure_tunneling_impl.h index e698c484..d416848b 100644 --- a/include/aws/iotdevice/private/secure_tunneling_impl.h +++ b/include/aws/iotdevice/private/secure_tunneling_impl.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -208,6 +209,8 @@ struct aws_secure_tunnel { struct aws_tls_ctx *tls_ctx; struct aws_tls_connection_options tls_con_opt; + struct aws_host_resolution_config host_resolution_config; + /* * The recurrent task that runs all secure tunnel logic outside of external event callbacks. Bound to the secure * tunnel's event loop. diff --git a/include/aws/iotdevice/secure_tunneling.h b/include/aws/iotdevice/secure_tunneling.h index b7d8d12d..a55ecbba 100644 --- a/include/aws/iotdevice/secure_tunneling.h +++ b/include/aws/iotdevice/secure_tunneling.h @@ -194,6 +194,11 @@ struct aws_secure_tunnel_options { */ const struct aws_socket_options *socket_options; + /** + * (Optional) Tls options to use whenever this Secure Tunnel Client establishes a connection + */ + const struct aws_tls_connection_options *tls_options; + /** * (Optional) Http proxy options to use whenever this Secure Tunnel establishes a connection */ diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index a4ecffa0..c46733eb 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -1153,6 +1153,8 @@ void s_websocket_transform_complete_task_fn(struct aws_task *task, void *arg, en .on_incoming_frame_begin = s_on_websocket_incoming_frame_begin, .on_incoming_frame_payload = s_on_websocket_incoming_frame_payload, .on_incoming_frame_complete = s_on_websocket_incoming_frame_complete, + + .host_resolution_config = &secure_tunnel->host_resolution_config, }; if (secure_tunnel->config->http_proxy_config != NULL) { @@ -2401,11 +2403,6 @@ struct aws_secure_tunnel *aws_secure_tunnel_new( aws_linked_list_init(&secure_tunnel->queued_operations); secure_tunnel->current_operation = NULL; - /* tls setup */ - struct aws_tls_ctx_options tls_ctx_opt; - AWS_ZERO_STRUCT(tls_ctx_opt); - aws_tls_ctx_options_init_default_client(&tls_ctx_opt, secure_tunnel->allocator); - /* store options */ secure_tunnel->config = aws_secure_tunnel_options_storage_new(allocator, options); if (secure_tunnel->config == NULL) { @@ -2423,28 +2420,60 @@ struct aws_secure_tunnel *aws_secure_tunnel_new( goto error; } + secure_tunnel->host_resolution_config = aws_host_resolver_init_default_resolution_config(); + secure_tunnel->host_resolution_config.resolve_frequency_ns = + aws_timestamp_convert(MAX_RECONNECT_DELAY_MS, AWS_TIMESTAMP_MILLIS, AWS_TIMESTAMP_NANOS, NULL); + secure_tunnel->desired_state = AWS_STS_STOPPED; secure_tunnel->current_state = AWS_STS_STOPPED; - if (options->root_ca != NULL) { - if (aws_tls_ctx_options_override_default_trust_store_from_path(&tls_ctx_opt, NULL, options->root_ca)) { + /* tls setup */ + if (options->tls_options) { + if (aws_tls_connection_options_copy(&secure_tunnel->tls_con_opt, options->tls_options)) { goto error; } - } + } else { + struct aws_tls_ctx_options tls_ctx_opt; + AWS_ZERO_STRUCT(tls_ctx_opt); - secure_tunnel->tls_ctx = aws_tls_client_ctx_new(allocator, &tls_ctx_opt); - if (secure_tunnel->tls_ctx == NULL) { - goto error; - } + aws_tls_ctx_options_init_default_client(&tls_ctx_opt, secure_tunnel->allocator); - /* tls_connection_options */ - aws_tls_connection_options_init_from_ctx(&secure_tunnel->tls_con_opt, secure_tunnel->tls_ctx); - if (aws_tls_connection_options_set_server_name( - &secure_tunnel->tls_con_opt, allocator, (struct aws_byte_cursor *)&options->endpoint_host)) { - goto error; + if (options->root_ca != NULL) { + if (aws_tls_ctx_options_override_default_trust_store_from_path(&tls_ctx_opt, NULL, options->root_ca)) { + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "Failed to load %s with error %s", + options->root_ca, + aws_error_debug_str(aws_last_error())); + aws_tls_ctx_options_clean_up(&tls_ctx_opt); + goto error; + } + } + + secure_tunnel->tls_ctx = aws_tls_client_ctx_new(allocator, &tls_ctx_opt); + if (secure_tunnel->tls_ctx == NULL) { + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "Failed to initialize TLS context with error %s.", + aws_error_debug_str(aws_last_error())); + aws_tls_ctx_options_clean_up(&tls_ctx_opt); + goto error; + } + + aws_tls_connection_options_init_from_ctx(&secure_tunnel->tls_con_opt, secure_tunnel->tls_ctx); + aws_tls_ctx_options_clean_up(&tls_ctx_opt); } - aws_tls_ctx_options_clean_up(&tls_ctx_opt); + if (!secure_tunnel->tls_con_opt.server_name) { + if (aws_tls_connection_options_set_server_name( + &secure_tunnel->tls_con_opt, secure_tunnel->allocator, &options->endpoint_host)) { + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "Failed to set endpoint host name with error %s.", + aws_error_debug_str(aws_last_error())); + goto error; + } + } /* Connection reset */ secure_tunnel->connections->stream_id = INVALID_STREAM_ID; @@ -2461,7 +2490,6 @@ struct aws_secure_tunnel *aws_secure_tunnel_new( return secure_tunnel; error: - aws_tls_ctx_options_clean_up(&tls_ctx_opt); aws_secure_tunnel_release(secure_tunnel); return NULL; } From 1ec416e6dbdcce15c25b288f78506619280b2cf6 Mon Sep 17 00:00:00 2001 From: Steve Kim <86316075+sbSteveK@users.noreply.github.com> Date: Tue, 18 Apr 2023 16:01:41 -0700 Subject: [PATCH 49/50] Secure tunnel v3 tls (#88) * added ability to pass in tls options * dns ping reduction * builder v0.9.38 From 21428bc8c2accb6be9eab1bdddbcd9c904fd88f5 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 19 Apr 2023 09:10:28 -0700 Subject: [PATCH 50/50] github ci.yml updates --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ab0a27cc..227f465e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,7 @@ on: - 'main' env: - BUILDER_VERSION: v0.9.38 + BUILDER_VERSION: v0.9.40 BUILDER_SOURCE: releases BUILDER_HOST: https://d19elf31gohf1l.cloudfront.net PACKAGE_NAME: aws-c-iot @@ -105,12 +105,12 @@ jobs: runs-on: windows-2019 # windows-2019 is last env with Visual Studio 2015 (v14.0) toolset strategy: matrix: - arch: [Win32, x64] + arch: [x86, x64] steps: - name: Build ${{ env.PACKAGE_NAME }} + consumers run: | python -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz?run=${{ env.RUN }}', 'builder.pyz')" - python builder.pyz build -p ${{ env.PACKAGE_NAME }} --cmake-extra=-Tv140 --cmake-extra=-A${{ matrix.arch }} + python builder.pyz build -p ${{ env.PACKAGE_NAME }} --target windows-${{ matrix.arch }} --compiler msvc-14 windows-shared-libs: runs-on: windows-2022 # latest