From fc2425c1b7abd72474f4554c419c1d87b1e23495 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Tue, 20 Sep 2022 09:43:20 -0700 Subject: [PATCH 01/69] Add connection_error_code and API to access it for error checking on connection complete --- .../iotdevice/private/secure_tunneling_impl.h | 3 ++ include/aws/iotdevice/secure_tunneling.h | 3 ++ source/secure_tunneling.c | 37 ++++++++++--------- 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/include/aws/iotdevice/private/secure_tunneling_impl.h b/include/aws/iotdevice/private/secure_tunneling_impl.h index 50fbf507..5acfe18c 100644 --- a/include/aws/iotdevice/private/secure_tunneling_impl.h +++ b/include/aws/iotdevice/private/secure_tunneling_impl.h @@ -58,6 +58,9 @@ struct aws_secure_tunnel { /* Stores what has been received but not processed */ struct aws_byte_buf received_data; + /* Error code of last connection attempt */ + int connection_error_code; + /* The secure tunneling endpoint ELB drops idle connect after 1 minute. We need to send a ping periodically to keep * the connection */ diff --git a/include/aws/iotdevice/secure_tunneling.h b/include/aws/iotdevice/secure_tunneling.h index cc3dcf7a..f9c2a479 100644 --- a/include/aws/iotdevice/secure_tunneling.h +++ b/include/aws/iotdevice/secure_tunneling.h @@ -68,6 +68,9 @@ struct aws_secure_tunnel *aws_secure_tunnel_new(const struct aws_secure_tunnel_o AWS_IOTDEVICE_API struct aws_secure_tunnel *aws_secure_tunnel_acquire(struct aws_secure_tunnel *secure_tunnel); +AWS_IOTDEVICE_API +int aws_secure_tunnel_get_connection_error_code(struct aws_secure_tunnel *secure_tunnel); + AWS_IOTDEVICE_API void aws_secure_tunnel_release(struct aws_secure_tunnel *secure_tunnel); diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 236bf1c9..39e6635f 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -181,35 +181,36 @@ static void s_on_websocket_setup( size_t num_handshake_response_headers, void *user_data) { - UNUSED(error_code); UNUSED(handshake_response_status); UNUSED(handshake_response_header_array); UNUSED(num_handshake_response_headers); - /* TODO: Handle error - * https://github.com/aws-samples/aws-iot-securetunneling-localproxy/blob/master/WebsocketProtocolGuide.md#handshake-error-responses - */ + /* Setup callback contract is: if error_code is non-zero then websocket is NULL. */ + AWS_FATAL_ASSERT((error_code != 0) == (websocket == NULL)); struct aws_secure_tunnel *secure_tunnel = user_data; aws_http_message_release(secure_tunnel->handshake_request); secure_tunnel->handshake_request = NULL; - secure_tunnel->websocket = websocket; + secure_tunnel->connection_error_code = error_code; secure_tunnel->options->on_connection_complete(secure_tunnel->options->user_data); - struct ping_task_context *ping_task_context = - aws_mem_acquire(secure_tunnel->alloc, sizeof(struct ping_task_context)); - secure_tunnel->ping_task_context = ping_task_context; - AWS_ZERO_STRUCT(*ping_task_context); - ping_task_context->allocator = secure_tunnel->alloc; - ping_task_context->event_loop = - aws_event_loop_group_get_next_loop(secure_tunnel->options->bootstrap->event_loop_group); - aws_atomic_store_int(&ping_task_context->task_cancelled, 0); - ping_task_context->websocket = websocket; - ping_task_context->send_frame = secure_tunnel->websocket_vtable.send_frame; - - aws_task_init(&ping_task_context->ping_task, s_ping_task, ping_task_context, "SecureTunnelingPingTask"); - aws_event_loop_schedule_task_now(ping_task_context->event_loop, &ping_task_context->ping_task); + if (websocket) { + secure_tunnel->websocket = websocket; + struct ping_task_context *ping_task_context = + aws_mem_acquire(secure_tunnel->alloc, sizeof(struct ping_task_context)); + secure_tunnel->ping_task_context = ping_task_context; + AWS_ZERO_STRUCT(*ping_task_context); + ping_task_context->allocator = secure_tunnel->alloc; + ping_task_context->event_loop = + aws_event_loop_group_get_next_loop(secure_tunnel->options->bootstrap->event_loop_group); + aws_atomic_store_int(&ping_task_context->task_cancelled, 0); + ping_task_context->websocket = websocket; + ping_task_context->send_frame = secure_tunnel->websocket_vtable.send_frame; + + aws_task_init(&ping_task_context->ping_task, s_ping_task, ping_task_context, "SecureTunnelingPingTask"); + aws_event_loop_schedule_task_now(ping_task_context->event_loop, &ping_task_context->ping_task); + } } static void s_on_websocket_shutdown(struct aws_websocket *websocket, int error_code, void *user_data) { From 04e8055cea1b3860e07bf910e4a2b2da6b9ea924 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 3 Oct 2022 13:50:48 -0700 Subject: [PATCH 02/69] encoder/decoder refactor --- .../iotdevice/private/iotdevice_internals.h | 4 +- include/aws/iotdevice/private/serializer.h | 98 ++++-- include/aws/iotdevice/secure_tunneling.h | 6 + source/secure_tunneling.c | 66 ++-- source/serializer.c | 292 ++++++++++++++---- tests/secure_tunneling_tests.c | 24 +- tests/tests_protobuf/aws_iot_st_pb_test.cpp | 4 +- 7 files changed, 361 insertions(+), 133 deletions(-) diff --git a/include/aws/iotdevice/private/iotdevice_internals.h b/include/aws/iotdevice/private/iotdevice_internals.h index 0239108e..ab530786 100644 --- a/include/aws/iotdevice/private/iotdevice_internals.h +++ b/include/aws/iotdevice/private/iotdevice_internals.h @@ -21,7 +21,7 @@ int secure_tunneling_init_send_frame( struct aws_websocket_send_frame_options *frame_options, struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *data, - enum aws_iot_st_message_type type); + enum aws_secure_tunnel_message_type type); AWS_IOTDEVICE_API void init_websocket_client_connection_options( @@ -33,7 +33,7 @@ int secure_tunneling_init_send_frame( struct aws_websocket_send_frame_options *frame_options, struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *data, - enum aws_iot_st_message_type type); + enum aws_secure_tunnel_message_type type); AWS_IOTDEVICE_API bool secure_tunneling_send_data_call(struct aws_websocket *websocket, struct aws_byte_buf *out_buf, void *user_data); diff --git a/include/aws/iotdevice/private/serializer.h b/include/aws/iotdevice/private/serializer.h index 94fe0681..0010277d 100644 --- a/include/aws/iotdevice/private/serializer.h +++ b/include/aws/iotdevice/private/serializer.h @@ -9,36 +9,94 @@ #include -#define AWS_IOT_ST_MESSAGE_TYPEFIELD 1 -#define AWS_IOT_ST_MESSAGE_STREAM_ID 2 -#define AWS_IOT_ST_MESSAGE_IGNORABLE 3 -#define AWS_IOT_ST_MESSAGE_PAYLOAD 4 -#define AWS_IOT_ST_VARINT_WIRE 0 -#define AWS_IOT_ST_VARINT_LENGTHDELIM_WIRE 2 - #define AWS_IOT_ST_FIELD_NUMBER_SHIFT 3 -#define AWS_IOT_ST_MESSAGE_DEFAULT_STREAM_ID 0 -#define AWS_IOT_ST_MESSAGE_DEFAULT_IGNORABLE 0 -#define AWS_IOT_ST_MESSAGE_DEFAULT_TYPE 0 -#define AWS_IOT_ST_MESSAGE_DEFAULT_PAYLOAD 0 +#define AWS_IOT_ST_MAXIMUM_VARINT 268435455 +#define AWS_IOT_ST_MAX_MESSAGE_SIZE 64 * 1024 +#define AWS_IOT_ST_MAX_PAYLOAD_SIZE 64512 -#define AWS_IOT_ST_STREAM_ID_FIELD_NUMBER 2 -#define AWS_IOT_ST_IGNORABLE_FIELD_NUMBER 3 -#define AWS_IOT_ST_TYPE_FIELD_NUMBER 1 -#define AWS_IOT_ST_PAYLOAD_FIELD_NUMBER 4 -#define AWS_IOT_ST_DEFAULT_ALLO 60 -#define AWS_IOT_ST_MAX_MESSAGE_SIZE 64 * 1024 -#define AWS_IOT_ST_BLOCK_SIZE 1 +enum aws_secure_tunnel_field_number { + AWS_SECURE_TUNNEL_FN_TYPE = 1, + AWS_SECURE_TUNNEL_FN_STREAM_ID = 2, + AWS_SECURE_TUNNEL_FN_IGNORABLE = 3, + AWS_SECURE_TUNNEL_FN_PAYLOAD = 4, + AWS_SECURE_TUNNEL_FN_SERVICE_ID = 5, + AWS_SECURE_TUNNEL_FN_AVAILABLE_SERVICE_IDS = 6, + AWS_SECURE_TUNNEL_FN_CONNECTION_ID = 7, +}; + +enum aws_secure_tunnel_protocol_buffer_wire_type { + AWS_SECURE_TUNNEL_PBWT_VARINT = 0, /* int32, int64, uint32, uint64, sint32, sint64, bool, enum */ + AWS_SECURE_TUNNEL_PBWT_64_BIT = 1, /* fixed64, sfixed64, double */ + AWS_SECURE_TUNNEL_PBWT_LENGTH_DELIMINTED = 2, /* string, bytes, embedded messages, packed repeated fields */ + AWS_SECURE_TUNNEL_PBWT_START_GROUP = 3, /* groups (deprecated) */ + AWS_SECURE_TUNNEL_PBWT_END_GROUP = 4, /* groups (deprecated) */ + AWS_SECURE_TUNNEL_PBWT_32_BIT = 5, /* fixed32, sfixed32, float */ +}; + +/** + * Type of IoT Secure Tunnel message. + * Enum values match IoT Secure Tunneling Local Proxy V3 Websocket Protocol Guide values. + * + * https://github.com/aws-samples/aws-iot-securetunneling-localproxy/blob/main/V3WebSocketProtocolGuide.md + */ +enum aws_secure_tunnel_message_type { + AWS_SECURE_TUNNEL_MT_UNKNOWN = 0, -enum aws_iot_st_message_type { UNKNOWN, DATA, STREAM_START, STREAM_RESET, SESSION_RESET }; + /** + * Data messages carry a payload with a sequence of bytes to write to the the active data stream + */ + AWS_SECURE_TUNNEL_MT_DATA = 1, + /** + * StreamStart is the first message sent to start and establish a new and active data stream. This should only be + * sent from a Source to a Destination. + */ + AWS_SECURE_TUNNEL_MT_STREAM_START = 2, + + /** + * StreamReset messages convey that the data stream has ended, either in error, or closed intentionally for the + * tunnel peer. It is also sent to the source tunnel peer if an attempt to establish a new data stream fails on the + * destination side. + */ + AWS_SECURE_TUNNEL_MT_STREAM_RESET = 3, + + /** + * SessionReset messages can only originate from Secure Tunneling service if an internal data transmission error is + * detected. This will result in all active streams being closed. + */ + AWS_SECURE_TUNNEL_MT_SESSION_RESET = 4, + + /** + * ServiceIDs messages can only originate from the Secure Tunneling service and carry a list of unique service IDs + * used when opening a tunnel with services. + */ + AWS_SECURE_TUNNEL_MT_SERVICE_IDS = 5, + + /** + * ConnectionStart is the message sent to start and establish a new and active connection when the stream has been + * established and there's one active connection in the stream. + */ + AWS_SECURE_TUNNEL_MT_CONNECTION_START = 6, + + /** + * ConnectionReset messages convey that the connection has ended, either in error, or closed intentionally for the + * tunnel peer. + */ + AWS_SECURE_TUNNEL_MT_CONNECTION_RESET = 7 +}; + +/** + * A single IoT Secure Tunnel Message + */ struct aws_iot_st_msg { - enum aws_iot_st_message_type type; + enum aws_secure_tunnel_message_type type; int32_t stream_id; int ignorable; struct aws_byte_buf payload; + struct aws_byte_buf service_id; + uint32_t connection_id; }; AWS_EXTERN_C_BEGIN diff --git a/include/aws/iotdevice/secure_tunneling.h b/include/aws/iotdevice/secure_tunneling.h index f9c2a479..e810c928 100644 --- a/include/aws/iotdevice/secure_tunneling.h +++ b/include/aws/iotdevice/secure_tunneling.h @@ -24,6 +24,11 @@ typedef void(aws_secure_tunneling_on_connection_complete_fn)(void *user_data); typedef void(aws_secure_tunneling_on_connection_shutdown_fn)(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_data_receive_fn)(const struct aws_byte_buf *data, void *user_data); +typedef void(aws_secure_tunneling_on_data_receive_v3_fn)( + const struct aws_byte_cursor service_id, + int connection_id, + const struct aws_byte_buf *data, + void *user_data); typedef void(aws_secure_tunneling_on_stream_start_fn)(void *user_data); typedef void(aws_secure_tunneling_on_stream_reset_fn)(void *user_data); typedef void(aws_secure_tunneling_on_session_reset_fn)(void *user_data); @@ -44,6 +49,7 @@ struct aws_secure_tunnel_options { 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_data_receive_fn *on_data_receive; + aws_secure_tunneling_on_data_receive_v3_fn *on_data_receive_v3; 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/source/secure_tunneling.c b/source/secure_tunneling.c index 39e6635f..df9c7fdc 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -21,8 +21,6 @@ #define PAYLOAD_BYTE_LENGTH_PREFIX 2 #define PING_TASK_INTERVAL ((uint64_t)20 * 1000000000) -#define UNUSED(x) (void)(x) - struct aws_secure_tunnel_options_storage { struct aws_secure_tunnel_options options; @@ -181,9 +179,9 @@ static void s_on_websocket_setup( size_t num_handshake_response_headers, void *user_data) { - UNUSED(handshake_response_status); - UNUSED(handshake_response_header_array); - UNUSED(num_handshake_response_headers); + (void)handshake_response_status; + (void)handshake_response_header_array; + (void)num_handshake_response_headers; /* Setup callback contract is: if error_code is non-zero then websocket is NULL. */ AWS_FATAL_ASSERT((error_code != 0) == (websocket == NULL)); @@ -214,8 +212,8 @@ static void s_on_websocket_setup( } static void s_on_websocket_shutdown(struct aws_websocket *websocket, int error_code, void *user_data) { - UNUSED(websocket); - UNUSED(error_code); + (void)websocket; + (void)error_code; struct aws_secure_tunnel *secure_tunnel = user_data; aws_atomic_store_int(&secure_tunnel->ping_task_context->task_cancelled, 1); @@ -227,9 +225,9 @@ static bool s_on_websocket_incoming_frame_begin( struct aws_websocket *websocket, const struct aws_websocket_incoming_frame *frame, void *user_data) { - UNUSED(websocket); - UNUSED(frame); - UNUSED(user_data); + (void)websocket; + (void)frame; + (void)user_data; return true; } @@ -282,19 +280,23 @@ static void s_process_iot_st_msg(struct aws_secure_tunnel *secure_tunnel, struct /* TODO: Check stream_id, send reset? */ switch (st_msg->type) { - case DATA: + case AWS_SECURE_TUNNEL_MT_DATA: secure_tunnel->options->on_data_receive(&st_msg->payload, secure_tunnel->options->user_data); break; - case STREAM_START: + case AWS_SECURE_TUNNEL_MT_STREAM_START: s_handle_stream_start(secure_tunnel, st_msg); break; - case STREAM_RESET: + case AWS_SECURE_TUNNEL_MT_STREAM_RESET: s_handle_stream_reset(secure_tunnel, st_msg); break; - case SESSION_RESET: + case AWS_SECURE_TUNNEL_MT_SESSION_RESET: s_handle_session_reset(secure_tunnel); break; - case UNKNOWN: + /* Steve todo implement processing of new message types */ + case AWS_SECURE_TUNNEL_MT_SERVICE_IDS: + case AWS_SECURE_TUNNEL_MT_CONNECTION_START: + case AWS_SECURE_TUNNEL_MT_CONNECTION_RESET: + case AWS_SECURE_TUNNEL_MT_UNKNOWN: default: if (!st_msg->ignorable) { AWS_LOGF_WARN( @@ -325,7 +327,7 @@ static void s_process_received_data(struct aws_secure_tunnel *secure_tunnel) { aws_iot_st_msg_deserialize_from_cursor(&st_msg, &st_frame, secure_tunnel->alloc); s_process_iot_st_msg(secure_tunnel, &st_msg); - if (st_msg.type == DATA) { + if (st_msg.type == AWS_SECURE_TUNNEL_MT_DATA) { aws_byte_buf_clean_up(&st_msg.payload); } } @@ -339,14 +341,18 @@ static void s_process_received_data(struct aws_secure_tunnel *secure_tunnel) { } } +/***************************************************************************************************************** + * SECURE TUNNEL DECODING + *****************************************************************************************************************/ + bool on_websocket_incoming_frame_payload( struct aws_websocket *websocket, const struct aws_websocket_incoming_frame *frame, struct aws_byte_cursor data, void *user_data) { - UNUSED(websocket); - UNUSED(frame); + (void)websocket; + (void)frame; if (data.len > 0) { struct aws_secure_tunnel *secure_tunnel = user_data; @@ -362,10 +368,10 @@ static bool s_on_websocket_incoming_frame_complete( const struct aws_websocket_incoming_frame *frame, int error_code, void *user_data) { - UNUSED(websocket); - UNUSED(frame); - UNUSED(error_code); - UNUSED(user_data); + (void)websocket; + (void)frame; + (void)error_code; + (void)user_data; /* TODO: Check error_code */ @@ -465,7 +471,7 @@ static void s_secure_tunneling_on_send_data_complete_callback( struct aws_websocket *websocket, int error_code, void *user_data) { - UNUSED(websocket); + (void)websocket; struct data_tunnel_pair *pair = user_data; struct aws_secure_tunnel *secure_tunnel = (struct aws_secure_tunnel *)pair->secure_tunnel; secure_tunnel->options->on_send_data_complete(error_code, pair->secure_tunnel->options->user_data); @@ -474,7 +480,7 @@ static void s_secure_tunneling_on_send_data_complete_callback( } bool secure_tunneling_send_data_call(struct aws_websocket *websocket, struct aws_byte_buf *out_buf, void *user_data) { - UNUSED(websocket); + (void)websocket; struct data_tunnel_pair *pair = user_data; size_t space_available = out_buf->capacity - out_buf->len; if ((pair->length_prefix_written == false) && (space_available >= PAYLOAD_BYTE_LENGTH_PREFIX)) { @@ -517,7 +523,7 @@ static int s_init_data_tunnel_pair( struct data_tunnel_pair *pair, struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *data, - enum aws_iot_st_message_type type) { + enum aws_secure_tunnel_message_type type) { struct aws_iot_st_msg message; message.stream_id = secure_tunnel->stream_id; message.ignorable = 0; @@ -551,7 +557,7 @@ int secure_tunneling_init_send_frame( struct aws_websocket_send_frame_options *frame_options, struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *data, - enum aws_iot_st_message_type type) { + enum aws_secure_tunnel_message_type type) { struct data_tunnel_pair *pair = (struct data_tunnel_pair *)aws_mem_acquire(secure_tunnel->alloc, sizeof(struct data_tunnel_pair)); if (s_init_data_tunnel_pair(pair, secure_tunnel, data, type) != AWS_OP_SUCCESS) { @@ -564,7 +570,7 @@ int secure_tunneling_init_send_frame( static int s_secure_tunneling_send( struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *data, - enum aws_iot_st_message_type type) { + enum aws_secure_tunnel_message_type type) { struct aws_websocket_send_frame_options frame_options; if (secure_tunneling_init_send_frame(&frame_options, secure_tunnel, data, type) != AWS_OP_SUCCESS) { @@ -586,7 +592,7 @@ static int s_secure_tunneling_send_data(struct aws_secure_tunnel *secure_tunnel, struct aws_byte_cursor send_cursor = aws_byte_cursor_advance(&new_data, amount_to_send); AWS_FATAL_ASSERT(send_cursor.len > 0); if (send_cursor.len) { - if (s_secure_tunneling_send(secure_tunnel, &send_cursor, DATA) != AWS_OP_SUCCESS) { + if (s_secure_tunneling_send(secure_tunnel, &send_cursor, AWS_SECURE_TUNNEL_MT_DATA) != AWS_OP_SUCCESS) { AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Failure writing data to out_buf"); return AWS_OP_ERR; } @@ -604,7 +610,7 @@ static int s_secure_tunneling_send_stream_start(struct aws_secure_tunnel *secure if (secure_tunnel->stream_id == 0) { secure_tunnel->stream_id += 1; } - return s_secure_tunneling_send(secure_tunnel, NULL, STREAM_START); + return s_secure_tunneling_send(secure_tunnel, NULL, AWS_SECURE_TUNNEL_MT_STREAM_START); } static int s_secure_tunneling_send_stream_reset(struct aws_secure_tunnel *secure_tunnel) { @@ -613,7 +619,7 @@ static int s_secure_tunneling_send_stream_reset(struct aws_secure_tunnel *secure return AWS_ERROR_IOTDEVICE_SECUTRE_TUNNELING_INVALID_STREAM; } - int result = s_secure_tunneling_send(secure_tunnel, NULL, STREAM_RESET); + int result = s_secure_tunneling_send(secure_tunnel, NULL, AWS_SECURE_TUNNEL_MT_STREAM_RESET); s_reset_secure_tunnel(secure_tunnel); return result; } diff --git a/source/serializer.c b/source/serializer.c index 56518708..74c2aaa3 100644 --- a/source/serializer.c +++ b/source/serializer.c @@ -4,6 +4,10 @@ */ #include +/***************************************************************************************************************** + * ENCODING + *****************************************************************************************************************/ + static int s_iot_st_encode_varint_uint32_t(struct aws_byte_buf *buffer, uint32_t n) { // & 2's comp lement // ~0x7F == b-10000000 @@ -59,27 +63,6 @@ static int s_iot_st_encode_varint_pos(struct aws_byte_buf *buffer, int32_t n) { return AWS_OP_ERR; } -static int s_iot_st_decode_varint_uint32_t(struct aws_byte_cursor *cursor, uint32_t *result) { - int bits = 0; - // Continue while the first bit is one - // 0x80 == b10000000 - uint32_t castPtrValue; - while ((*cursor->ptr & 0x80)) { - castPtrValue = *cursor->ptr; - // Zero out the first bit - // 0x7F == b01111111 - *result += ((castPtrValue & 0x7F) << bits); - AWS_RETURN_ERROR_IF2(aws_byte_cursor_advance(cursor, 1).ptr != NULL, AWS_OP_ERR); - bits += 7; - } - castPtrValue = *cursor->ptr; - AWS_RETURN_ERROR_IF2(aws_byte_cursor_advance(cursor, 1).ptr != NULL, AWS_OP_ERR); - // Zero out the first bit - // 0x7F == b01111111 - *result += ((castPtrValue & 0x7F) << bits); - return AWS_OP_SUCCESS; -} - static int s_iot_st_encode_varint( const uint8_t field_number, const uint8_t wire_type, @@ -104,49 +87,166 @@ static int s_iot_st_encode_lengthdelim( } static int s_iot_st_encode_stream_id(int32_t data, struct aws_byte_buf *buffer) { - return s_iot_st_encode_varint(AWS_IOT_ST_MESSAGE_STREAM_ID, AWS_IOT_ST_VARINT_WIRE, data, 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_ignorable(int32_t data, struct aws_byte_buf *buffer) { - return s_iot_st_encode_varint(AWS_IOT_ST_MESSAGE_IGNORABLE, AWS_IOT_ST_VARINT_WIRE, data, buffer); + return s_iot_st_encode_varint(AWS_SECURE_TUNNEL_FN_IGNORABLE, AWS_SECURE_TUNNEL_PBWT_VARINT, data, buffer); } static int s_iot_st_encode_type(int32_t data, struct aws_byte_buf *buffer) { - return s_iot_st_encode_varint(AWS_IOT_ST_MESSAGE_TYPEFIELD, AWS_IOT_ST_VARINT_WIRE, data, buffer); + return s_iot_st_encode_varint(AWS_SECURE_TUNNEL_FN_TYPE, AWS_SECURE_TUNNEL_PBWT_VARINT, data, buffer); } static int s_iot_st_encode_payload(struct aws_byte_buf *payload, struct aws_byte_buf *buffer) { - return s_iot_st_encode_lengthdelim(AWS_IOT_ST_MESSAGE_PAYLOAD, AWS_IOT_ST_VARINT_LENGTHDELIM_WIRE, payload, buffer); + return s_iot_st_encode_lengthdelim( + AWS_SECURE_TUNNEL_FN_PAYLOAD, AWS_SECURE_TUNNEL_PBWT_LENGTH_DELIMINTED, payload, buffer); +} + +static int s_iot_st_encode_service_id(struct aws_byte_buf *service_id, struct aws_byte_buf *buffer) { + return s_iot_st_encode_lengthdelim( + AWS_SECURE_TUNNEL_FN_SERVICE_ID, AWS_SECURE_TUNNEL_PBWT_LENGTH_DELIMINTED, service_id, 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_get_varint_size(size_t value, size_t *encode_size) { + if (value > AWS_IOT_ST_MAXIMUM_VARINT) { + return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + } + + if (value < 128) { + *encode_size = 1; + } else if (value < 16384) { + *encode_size = 2; + } else if (value < 2097152) { + *encode_size = 3; + } else { + *encode_size = 4; + } + + return AWS_OP_SUCCESS; +} + +static int s_iot_st_compute_message_length(const struct aws_iot_st_msg *message, size_t *message_length) { + size_t local_length = 0; + + /* + * 1 byte type key + * 1 byte type varint + */ + local_length += 2; + + if (message->stream_id != 0) { + /* + * 1 byte steram_id key + * 1-4 byte stream_id varint + */ + size_t stream_id_length = 0; + + if (s_iot_st_get_varint_size((uint32_t)message->stream_id, &stream_id_length)) { + return AWS_OP_ERR; + } + + local_length += (1 + stream_id_length); + } + + if (message->ignorable != 0) { + /* + * 1 byte ignorable key + * 1 byte ignorable varint + */ + local_length += 2; + } + + if (message->payload.len != 0) { + /* + * 1 byte key + * n bytes payload.len + */ + + local_length += (1 + message->payload.len); + } + + if (message->service_id.len != 0) { + /* + * 1 byte key + * n bytes service_id.len + */ + + local_length += (1 + message->service_id.len); + } + + 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((uint32_t)message->connection_id, &connection_id_length)) { + return AWS_OP_ERR; + } + + local_length += (1 + connection_id_length); + } + + *message_length = local_length; + return AWS_OP_SUCCESS; } int aws_iot_st_msg_serialize_from_struct( struct aws_byte_buf *buffer, struct aws_allocator *allocator, struct aws_iot_st_msg message) { - if (aws_byte_buf_init(buffer, allocator, AWS_IOT_ST_DEFAULT_ALLO + message.payload.len) != AWS_OP_SUCCESS) { + + size_t message_total_length = 0; + if (s_iot_st_compute_message_length(&message, &message_total_length)) { return AWS_OP_ERR; } - if (message.type != AWS_IOT_ST_MESSAGE_DEFAULT_TYPE) { - if (s_iot_st_encode_type(message.type, buffer) != AWS_OP_SUCCESS) { + if (aws_byte_buf_init(buffer, allocator, message_total_length) != AWS_OP_SUCCESS) { + return AWS_OP_ERR; + } + + if (message.type != AWS_SECURE_TUNNEL_MT_UNKNOWN) { + if (s_iot_st_encode_type(message.type, buffer)) { + goto cleanup; + } + } + + if (message.stream_id != 0) { + if (s_iot_st_encode_stream_id(message.stream_id, buffer)) { + goto cleanup; + } + } + + if (message.ignorable != 0) { + if (s_iot_st_encode_ignorable(message.ignorable, buffer)) { goto cleanup; } } - if (message.stream_id != AWS_IOT_ST_MESSAGE_DEFAULT_STREAM_ID) { - if (s_iot_st_encode_stream_id(message.stream_id, buffer) != AWS_OP_SUCCESS) { + + if (message.payload.len != 0) { + if (s_iot_st_encode_payload(&message.payload, buffer)) { goto cleanup; } } - if (message.ignorable != AWS_IOT_ST_MESSAGE_DEFAULT_IGNORABLE) { - if (s_iot_st_encode_ignorable(message.ignorable, buffer) != AWS_OP_SUCCESS) { + + if (message.service_id.len != 0) { + if (s_iot_st_encode_service_id(&message.service_id, buffer)) { goto cleanup; } } - if (message.payload.len != AWS_IOT_ST_MESSAGE_DEFAULT_PAYLOAD) { - if (s_iot_st_encode_payload(&message.payload, buffer) != AWS_OP_SUCCESS) { + + if (message.connection_id != 0) { + if (s_iot_st_encode_connection_id(message.connection_id, buffer)) { goto cleanup; } } + AWS_RETURN_ERROR_IF2(buffer->capacity < AWS_IOT_ST_MAX_MESSAGE_SIZE, AWS_ERROR_INVALID_BUFFER_SIZE); return AWS_OP_SUCCESS; @@ -155,6 +255,31 @@ int aws_iot_st_msg_serialize_from_struct( return AWS_OP_ERR; } +/***************************************************************************************************************** + * DECODING + *****************************************************************************************************************/ + +static int s_iot_st_decode_varint_uint32_t(struct aws_byte_cursor *cursor, uint32_t *result) { + int bits = 0; + // Continue while the first bit is one + // 0x80 == b10000000 + uint32_t castPtrValue; + while ((*cursor->ptr & 0x80)) { + castPtrValue = *cursor->ptr; + // Zero out the first bit + // 0x7F == b01111111 + *result += ((castPtrValue & 0x7F) << bits); + AWS_RETURN_ERROR_IF2(aws_byte_cursor_advance(cursor, 1).ptr != NULL, AWS_OP_ERR); + bits += 7; + } + castPtrValue = *cursor->ptr; + AWS_RETURN_ERROR_IF2(aws_byte_cursor_advance(cursor, 1).ptr != NULL, AWS_OP_ERR); + // Zero out the first bit + // 0x7F == b01111111 + *result += ((castPtrValue & 0x7F) << bits); + return AWS_OP_SUCCESS; +} + static int s_aws_st_decode_lengthdelim(struct aws_byte_cursor *cursor, struct aws_byte_buf *buffer, int length) { struct aws_byte_cursor temp = aws_byte_cursor_from_array(cursor->ptr, length); AWS_RETURN_ERROR_IF2(aws_byte_buf_append_dynamic_secure(buffer, &temp) == 0, AWS_OP_ERR); @@ -168,8 +293,8 @@ int aws_iot_st_msg_deserialize_from_cursor( 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; - int length; int payload_check = 0; + int service_id_check = 0; while ((aws_byte_cursor_is_valid(cursor)) && (cursor->len > 0)) { // wire_type is only the first 3 bits, Zeroing out the first 5 // 0x07 == 00000111 @@ -177,46 +302,79 @@ int aws_iot_st_msg_deserialize_from_cursor( field_number = (*cursor->ptr) >> 3; aws_byte_cursor_advance(cursor, 1); - if (field_number == AWS_IOT_ST_STREAM_ID_FIELD_NUMBER && wire_type == AWS_IOT_ST_VARINT_WIRE) { - uint32_t res = 0; - if (s_iot_st_decode_varint_uint32_t(cursor, &res) != AWS_OP_SUCCESS) { - return AWS_OP_ERR; - } - message->stream_id = res; - } else if (field_number == AWS_IOT_ST_IGNORABLE_FIELD_NUMBER && wire_type == AWS_IOT_ST_VARINT_WIRE) { - uint32_t res = 0; - if (s_iot_st_decode_varint_uint32_t(cursor, &res) != AWS_OP_SUCCESS) { - return AWS_OP_ERR; - } - message->ignorable = res; - } else if (field_number == AWS_IOT_ST_TYPE_FIELD_NUMBER && wire_type == AWS_IOT_ST_VARINT_WIRE) { - uint32_t res = 0; - if (s_iot_st_decode_varint_uint32_t(cursor, &res) != AWS_OP_SUCCESS) { - return AWS_OP_ERR; - } - message->type = res; - } else if (field_number == AWS_IOT_ST_PAYLOAD_FIELD_NUMBER && wire_type == AWS_IOT_ST_VARINT_LENGTHDELIM_WIRE) { - uint32_t res = 0; - if (s_iot_st_decode_varint_uint32_t(cursor, &res) != AWS_OP_SUCCESS) { - return AWS_OP_ERR; - } - length = res; - if (aws_byte_buf_init(&message->payload, allocator, length) != AWS_OP_SUCCESS) { - return AWS_OP_ERR; - } + switch (wire_type) { + case AWS_SECURE_TUNNEL_PBWT_VARINT: { + uint32_t res = 0; + if (s_iot_st_decode_varint_uint32_t(cursor, &res)) { + return AWS_OP_ERR; + } + + switch (field_number) { + case AWS_SECURE_TUNNEL_FN_TYPE: + message->type = res; + break; + case AWS_SECURE_TUNNEL_FN_STREAM_ID: + message->stream_id = res; + break; + case AWS_SECURE_TUNNEL_FN_IGNORABLE: + message->ignorable = res; + case AWS_SECURE_TUNNEL_FN_CONNECTION_ID: + message->connection_id = res; + break; + default: + /* Unexpected field_number */ + break; + } + } break; + + case AWS_SECURE_TUNNEL_PBWT_LENGTH_DELIMINTED: { + uint32_t length = 0; + if (s_iot_st_decode_varint_uint32_t(cursor, &length)) { + return AWS_OP_ERR; + } - if (s_aws_st_decode_lengthdelim(cursor, &message->payload, length) != AWS_OP_SUCCESS) { - goto cleanup; - } - aws_byte_cursor_advance(cursor, length); - payload_check = 1; + switch (field_number) { + case AWS_SECURE_TUNNEL_FN_PAYLOAD: + if (aws_byte_buf_init(&message->payload, allocator, length)) { + return AWS_OP_ERR; + } + if (s_aws_st_decode_lengthdelim(cursor, &message->payload, length)) { + goto cleanup; + } + aws_byte_cursor_advance(cursor, length); + payload_check = 1; + break; + case AWS_SECURE_TUNNEL_FN_SERVICE_ID: + if (aws_byte_buf_init(&message->service_id, allocator, length)) { + return AWS_OP_ERR; + } + if (s_aws_st_decode_lengthdelim(cursor, &message->service_id, length)) { + goto cleanup; + } + aws_byte_cursor_advance(cursor, length); + service_id_check = 1; + break; + } + } break; + + /* These wire types are unexpected and should result in an error log */ + case AWS_SECURE_TUNNEL_PBWT_64_BIT: + case AWS_SECURE_TUNNEL_PBWT_START_GROUP: + case AWS_SECURE_TUNNEL_PBWT_END_GROUP: + case AWS_SECURE_TUNNEL_PBWT_32_BIT: + return AWS_OP_ERR; + break; } } if (payload_check == 0) { AWS_ZERO_STRUCT(message->payload); } + if (service_id_check == 0) { + AWS_ZERO_STRUCT(message->service_id); + } return AWS_OP_SUCCESS; cleanup: aws_byte_buf_clean_up(&message->payload); + aws_byte_buf_clean_up(&message->service_id); return AWS_OP_ERR; } diff --git a/tests/secure_tunneling_tests.c b/tests/secure_tunneling_tests.c index d76a69a1..627202e4 100644 --- a/tests/secure_tunneling_tests.c +++ b/tests/secure_tunneling_tests.c @@ -278,7 +278,7 @@ static int s_test_sent_data( const char *expected_payload, const int32_t expected_stream_id, const int prefix_bytes, - const enum aws_iot_st_message_type type) { + const enum aws_secure_tunnel_message_type type) { /* * The public api used to send data over a secure tunnel is aws_secure_tunnel_send_data. * @@ -373,7 +373,7 @@ static int s_secure_tunneling_handle_stream_start_test(struct aws_allocator *all struct aws_iot_st_msg st_msg; AWS_ZERO_STRUCT(st_msg); - st_msg.type = STREAM_START; + st_msg.type = AWS_SECURE_TUNNEL_MT_STREAM_START; st_msg.stream_id = STREAM_ID; s_on_stream_start_called = false; s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); @@ -403,13 +403,13 @@ static int s_secure_tunneling_handle_data_receive_test(struct aws_allocator *all /* Send StreamStart first */ struct aws_iot_st_msg st_msg; AWS_ZERO_STRUCT(st_msg); - st_msg.type = STREAM_START; + st_msg.type = AWS_SECURE_TUNNEL_MT_STREAM_START; st_msg.stream_id = STREAM_ID; s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); /* Send data */ AWS_ZERO_STRUCT(st_msg); - st_msg.type = DATA; + st_msg.type = AWS_SECURE_TUNNEL_MT_DATA; st_msg.stream_id = STREAM_ID; st_msg.payload = aws_byte_buf_from_c_str(PAYLOAD); s_on_data_receive_correct_payload = false; @@ -440,13 +440,13 @@ static int s_secure_tunneling_handle_stream_reset_test(struct aws_allocator *all /* Send StreamStart first */ struct aws_iot_st_msg st_msg; AWS_ZERO_STRUCT(st_msg); - st_msg.type = STREAM_START; + st_msg.type = AWS_SECURE_TUNNEL_MT_STREAM_START; st_msg.stream_id = STREAM_ID; s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); /* Send StreamReset */ AWS_ZERO_STRUCT(st_msg); - st_msg.type = STREAM_RESET; + st_msg.type = AWS_SECURE_TUNNEL_MT_STREAM_START; st_msg.stream_id = STREAM_ID; s_on_stream_reset_called = false; s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); @@ -476,13 +476,13 @@ static int s_secure_tunneling_handle_session_reset_test(struct aws_allocator *al /* Send StreamStart first */ struct aws_iot_st_msg st_msg; AWS_ZERO_STRUCT(st_msg); - st_msg.type = STREAM_START; + st_msg.type = AWS_SECURE_TUNNEL_MT_STREAM_START; st_msg.stream_id = STREAM_ID; s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); /* Send StreamReset */ AWS_ZERO_STRUCT(st_msg); - st_msg.type = SESSION_RESET; + st_msg.type = AWS_SECURE_TUNNEL_MT_SESSION_RESET; st_msg.stream_id = STREAM_ID; s_on_session_reset_called = false; s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); @@ -512,7 +512,7 @@ static int s_secure_tunneling_handle_session_reset_no_stream_test(struct aws_all /* Send StreamReset without existing stream */ struct aws_iot_st_msg st_msg; AWS_ZERO_STRUCT(st_msg); - st_msg.type = SESSION_RESET; + st_msg.type = AWS_SECURE_TUNNEL_MT_SESSION_RESET; s_on_session_reset_called = false; s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); @@ -595,7 +595,7 @@ static int s_secure_tunneling_handle_send_data(struct aws_allocator *allocator, const char *expected_payload = "Hi! I'm Paul / Some random payload\n"; const int32_t expected_stream_id = 1; const int prefix_bytes = 2; - const enum aws_iot_st_message_type type = DATA; + const enum aws_secure_tunnel_message_type type = AWS_SECURE_TUNNEL_MT_DATA; struct secure_tunneling_test_context *test_context = ctx; test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_SOURCE_MODE; @@ -622,7 +622,7 @@ static int s_secure_tunneling_handle_send_data_stream_start(struct aws_allocator const char *expected_payload = ""; const int32_t expected_stream_id = 1; const int prefix_bytes = 2; - const enum aws_iot_st_message_type type = STREAM_START; + const enum aws_secure_tunnel_message_type type = AWS_SECURE_TUNNEL_MT_STREAM_START; struct secure_tunneling_test_context *test_context = ctx; test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_SOURCE_MODE; @@ -649,7 +649,7 @@ static int s_secure_tunneling_handle_send_data_stream_reset(struct aws_allocator const char *expected_payload = ""; const int32_t expected_stream_id = 1; const int prefix_bytes = 2; - const enum aws_iot_st_message_type type = STREAM_RESET; + const enum aws_secure_tunnel_message_type type = AWS_SECURE_TUNNEL_MT_STREAM_RESET; struct secure_tunneling_test_context *test_context = ctx; test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_SOURCE_MODE; diff --git a/tests/tests_protobuf/aws_iot_st_pb_test.cpp b/tests/tests_protobuf/aws_iot_st_pb_test.cpp index 14c0685c..1d18bef0 100644 --- a/tests/tests_protobuf/aws_iot_st_pb_test.cpp +++ b/tests/tests_protobuf/aws_iot_st_pb_test.cpp @@ -30,7 +30,7 @@ static int execute_tests( protobufMessage.ParseFromString(pbBuffer); struct aws_iot_st_msg c_message; - c_message.type = (aws_iot_st_message_type)type; + c_message.type = (aws_secure_tunnel_message_type)type; c_message.stream_id = streamid; c_message.ignorable = ignorable; c_message.payload = aws_byte_buf_from_c_str(payload.c_str()); @@ -173,4 +173,4 @@ int main(int argc, char *argv[]) { } google::protobuf::ShutdownProtobufLibrary(); return AWS_OP_SUCCESS; -} \ No newline at end of file +} From 98545b51b3f2a264fa4fbf710e624dfc206b05e9 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 23 Nov 2022 08:48:32 -0800 Subject: [PATCH 03/69] Secure Tunnel Multiplex WIP --- include/aws/iotdevice/iotdevice.h | 12 +- .../iotdevice/private/iotdevice_internals.h | 10 +- .../iotdevice/private/secure_tunneling_impl.h | 280 ++- .../private/secure_tunneling_operations.h | 124 ++ include/aws/iotdevice/private/serializer.h | 4 +- include/aws/iotdevice/secure_tunneling.h | 41 +- .../secure_tunneling_message_storage.h | 69 + source/iotdevice.c | 36 +- source/secure_tunneling.c | 1851 ++++++++++++++--- source/secure_tunneling_operations.c | 324 +++ source/serializer.c | 62 +- tests/secure_tunneling_tests.c | 27 +- 12 files changed, 2497 insertions(+), 343 deletions(-) create mode 100644 include/aws/iotdevice/private/secure_tunneling_operations.h create mode 100644 include/aws/iotdevice/secure_tunneling_message_storage.h create mode 100644 source/secure_tunneling_operations.c diff --git a/include/aws/iotdevice/iotdevice.h b/include/aws/iotdevice/iotdevice.h index ffe1374b..0e70148c 100644 --- a/include/aws/iotdevice/iotdevice.h +++ b/include/aws/iotdevice/iotdevice.h @@ -21,8 +21,16 @@ enum aws_iotdevice_error { AWS_ERROR_IOTDEVICE_DEFENDER_PUBLISH_FAILURE, AWS_ERROR_IOTDEVICE_DEFENDER_UNKNOWN_TASK_STATUS, - AWS_ERROR_IOTDEVICE_SECUTRE_TUNNELING_INVALID_STREAM, - AWS_ERROR_IOTDEVICE_SECUTRE_TUNNELING_INCORRECT_MODE, + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_STREAM, + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INCORRECT_MODE, + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_BAD_SERVICE_ID, + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_OPTIONS_VALIDATION, + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_STREAM_OPTIONS_VALIDATION, + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_SECURE_TUNNEL_TERMINATED, + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_OPERATION_FAILED_DUE_TO_DISCONNECTION, + 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_END_IOTDEVICE_RANGE = AWS_ERROR_ENUM_END_RANGE(AWS_C_IOTDEVICE_PACKAGE_ID), }; diff --git a/include/aws/iotdevice/private/iotdevice_internals.h b/include/aws/iotdevice/private/iotdevice_internals.h index ab530786..37f0a583 100644 --- a/include/aws/iotdevice/private/iotdevice_internals.h +++ b/include/aws/iotdevice/private/iotdevice_internals.h @@ -20,7 +20,8 @@ AWS_IOTDEVICE_API int secure_tunneling_init_send_frame( struct aws_websocket_send_frame_options *frame_options, struct aws_secure_tunnel *secure_tunnel, - const struct aws_byte_cursor *data, + const struct aws_byte_cursor *payload_data, + const struct aws_byte_cursor *service_id_data, enum aws_secure_tunnel_message_type type); AWS_IOTDEVICE_API @@ -28,13 +29,6 @@ void init_websocket_client_connection_options( struct aws_secure_tunnel *secure_tunnel, struct aws_websocket_client_connection_options *websocket_options); -AWS_IOTDEVICE_API -int secure_tunneling_init_send_frame( - struct aws_websocket_send_frame_options *frame_options, - struct aws_secure_tunnel *secure_tunnel, - const struct aws_byte_cursor *data, - enum aws_secure_tunnel_message_type type); - AWS_IOTDEVICE_API bool secure_tunneling_send_data_call(struct aws_websocket *websocket, struct aws_byte_buf *out_buf, void *user_data); diff --git a/include/aws/iotdevice/private/secure_tunneling_impl.h b/include/aws/iotdevice/private/secure_tunneling_impl.h index 5acfe18c..8dd2e97d 100644 --- a/include/aws/iotdevice/private/secure_tunneling_impl.h +++ b/include/aws/iotdevice/private/secure_tunneling_impl.h @@ -9,8 +9,158 @@ #include #include +#include +#include +#include #include +/** + * The various states that the secure tunnel can be in. A secure tunnel has both a current state and a desired state. + * Desired state is only allowed to be one of {STOPPED, CONNECTED, TERMINATED}. The secure tunnel transitions states + * based on either + * (1) changes in desired state, or + * (2) external events. + * + * Most states are interruptible (in the sense of a change in desired state causing an immediate change in state) but + * CONNECTING and CHANNEL_SHUTDOWN cannot be interrupted due to waiting for an asynchronous callback (that has no + * cancel) to complete. + */ +enum aws_secure_tunnel_state { + /* + * The secure tunnel is not connected and not waiting for anything to happen. + * + * Next States: + * CONNECTING - if the user invokes Connect() on the secure tunnel + * TERMINATED - if the user releases the last ref count on the secure tunnel + */ + AWS_STS_STOPPED, + + /* + * The secure tunnel is attempting to connect to a remote endpoint, and is waiting for channel setup to complete. + * This state is not interruptible by any means other than channel setup completion. + * + * Next States: + * SECURE_TUNNEL_CONNECT - if the channel completes setup with no error and desired state is still CONNECTED + * CHANNEL_SHUTDOWN - if the channel completes setup with no error, but desired state is not CONNECTED + * PENDING_RECONNECT - if the channel fails to complete setup and desired state is still CONNECTED + * STOPPED - if the channel fails to complete setup and desired state is not CONNECTED + */ + AWS_STS_CONNECTING, + + /* + * The secure tunnel is attempting to connect through the AWS Secure Tunnel Service via a WebSocket handshake. + * + * Next States: + * CONNECTED - if WebSocket handshake is successful and desired state is still CONNECTED + * CHANNEL_SHUTDOWN - On send/encode errors, read/decode errors, unsuccessful WebSocket Handshake, + * desired state is no longer CONNECTED + * PENDING_RECONNECT - unexpected channel shutdown completion and desired state still CONNECTED + * STOPPED - unexpected channel shutdown completion and desired state no longer CONNECTED + */ + AWS_STS_SECURE_TUNNEL_CONNECT, + + /* + * The secure tunnel is ready to perform user-requested operations. + * + * Next States: + * CHANNEL_SHUTDOWN - On send/encode errors, read/decode errors, WebSocket disconnect, desired state + * no longer CONNECTED, PINGRESP timeout + * PENDING_RECONNECT - unexpected channel shutdown completion and desired state still CONNECTED + * STOPPED - unexpected channel shutdown completion and desired state no longer CONNECTED + */ + AWS_STS_CONNECTED, + + /* + * The secure tunnel is attempt to shut down a connection cleanly by finishing the current operation and then + * transmitting a STREAM RESET message to all open streams. + * + * Next States: + * CHANNEL_SHUTDOWN - on successful (or unsuccessful) send of STREAM RESET messages + * PENDING_RECONNECT - unexpected channel shutdown completion and desired state still CONNECTED + * STOPPED - unexpected channel shutdown completion and desired state no longer CONNECTED + */ + AWS_STS_CLEAN_DISCONNECT, + + /* + * The secure tunnel is waiting for the io channel to completely shut down. This state is not interruptible. + * + * Next States: + * PENDING_RECONNECT - the io channel has shut down and desired state is still CONNECTED + * STOPPED - the io channel has shut down and desired state is not CONNECTED + */ + AWS_STS_CHANNEL_SHUTDOWN, + + /* + * The secure tunnel is waiting for the reconnect timer to expire before attempting to connect again. + * + * Next States: + * CONNECTING - the reconnect timer has expired and desired state is still CONNECTED + * STOPPED - desired state is no longer CONNECTED + */ + AWS_STS_PENDING_RECONNECT, + + /* + * The secure tunnel is performing final shutdown and release of all resources. This state is only realized for + * a non-observable instant of time (transition out of STOPPED). + */ + AWS_STS_TERMINATED +}; + +/** + * Signature of the continuation function to be called after user-code transforms a websocket handshake request + */ +typedef void(aws_secure_tunnel_transform_websocket_handshake_complete_fn)( + struct aws_http_message *request, + int error_code, + void *complete_ctx); + +/** + * Signature of the websocket handshake request transformation function. After transformation, the completion + * function must be invoked to send the request. + */ +typedef void(aws_secure_tunnel_transform_websocket_handshake_fn)( + struct aws_http_message *request, + void *user_data, + aws_secure_tunnel_transform_websocket_handshake_complete_fn *complete_fn, + void *complete_ctx); + +/* + * Secure tunnel configuration + */ +struct aws_secure_tunnel_options_storage { + + // struct aws_secure_tunnel_options options; + struct aws_allocator *allocator; + struct aws_secure_tunnel *secure_tunnel; + + /* backup */ + + struct aws_client_bootstrap *bootstrap; + struct aws_socket_options socket_options; + struct aws_http_proxy_options http_proxy_options; + struct aws_http_proxy_config *http_proxy_config; + struct aws_byte_cursor access_token; + + aws_secure_tunnel_transform_websocket_handshake_fn *websocket_handshake_transform; + void *websocket_handshake_transform_user_data; + enum aws_secure_tunneling_local_proxy_mode local_proxy_mode; + + struct aws_string *endpoint_host; + struct aws_string *root_ca; + + /* Store contents of all aws_byte_cursors within single buffer */ + struct aws_byte_buf cursor_storage; + + /* Stream related info */ + int32_t stream_id; + struct aws_string *service_id_1; + int32_t service_id_1_stream_id; + struct aws_string *service_id_2; + int32_t service_id_2_stream_id; + struct aws_string *service_id_3; + int32_t service_id_3_stream_id; +}; + struct data_tunnel_pair { struct aws_byte_buf buf; struct aws_byte_cursor cur; @@ -18,10 +168,39 @@ struct data_tunnel_pair { bool length_prefix_written; }; +/* Secure Tunnel stream information */ +struct aws_secure_tunnel_stream { + struct aws_string *service_id; + int32_t stream_id; + /* for use with V3 */ + uint32_t connection_id; +}; + struct aws_secure_tunnel_vtable { + /* aws_high_res_clock_get_ticks */ + uint64_t (*get_current_time_fn)(void); + + /* aws_channel_shutdown */ + int (*channel_shutdown_fn)(struct aws_channel *channel, int error_code); + + /* aws_websocket_client_connect */ + int (*websocket_connect_fn)(const struct aws_websocket_client_connection_options *options); + + /* aws_http_proxy_new_socket_channel */ + int (*http_proxy_new_socket_channel_fn)( + struct aws_socket_channel_bootstrap_options *channel_options, + const struct aws_http_proxy_options *proxy_options); + + /* aws_client_bootstrap_new_socket_channel */ + int (*client_bootstrap_new_socket_channel_fn)(struct aws_socket_channel_bootstrap_options *options); + int (*connect)(struct aws_secure_tunnel *secure_tunnel); int (*send_data)(struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *data); + int (*send_data_v2)( + struct aws_secure_tunnel *secure_tunnel, + const struct aws_secure_tunnel_message_data_view *data_options); int (*send_stream_start)(struct aws_secure_tunnel *secure_tunnel); + int (*send_stream_start_v2)(struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *service_id_data); int (*send_stream_reset)(struct aws_secure_tunnel *secure_tunnel); int (*close)(struct aws_secure_tunnel *secure_tunnel); }; @@ -38,21 +217,55 @@ struct aws_websocket_vtable { struct aws_secure_tunnel { /* Static settings */ - struct aws_allocator *alloc; - struct aws_secure_tunnel_options_storage *options_storage; + struct aws_allocator *allocator; + struct aws_secure_tunnel_options_storage *config; struct aws_secure_tunnel_options *options; struct aws_tls_ctx *tls_ctx; struct aws_tls_connection_options tls_con_opt; + struct aws_secure_tunnel_vtable vtable; struct aws_websocket_vtable websocket_vtable; struct aws_ref_count ref_count; - /* Used only during initial websocket setup. Otherwise, should be NULL */ + /* Used to check connection state */ + bool isConnected; + + /* + * Temporary state-related data. + * + * clean_disconnect_error_code - the CLEAN_DISCONNECT state takes time to complete and we want to be able + * to pass an error code from a prior event to the channel shutdown. This holds the "override" error code + * that we'd like to shutdown the channel with while CLEAN_DISCONNECT is processed. + */ + int clean_disconnect_error_code; + /* + * handshake_request exists between the transform completion timepoint and the websocket setup callback. + */ struct aws_http_message *handshake_request; + /* + * Event loop all the secure tunnel's tasks will be pinned to, ensuring serialization and + * concurrency safety. + */ + struct aws_event_loop *loop; + + /* Channel handler information */ + struct aws_channel_handler handler; + struct aws_channel_slot *slot; + /* Dynamic data */ - int32_t stream_id; + + /* + * What state is the secure tunnel working towards? + */ + enum aws_secure_tunnel_state desired_state; + + /* + * What is the secure tunnel's current state? + */ + enum aws_secure_tunnel_state current_state; + struct aws_websocket *websocket; /* Stores what has been received but not processed */ @@ -61,10 +274,67 @@ struct aws_secure_tunnel { /* Error code of last connection attempt */ int connection_error_code; + /* + * The recurrent task that runs all secure tunnel's logic outside of external event callbacks. Bound to the secure + * tunnel's event loop. + */ + struct aws_task service_task; + + /* + * Tracks when the secure tunnel's service task is next schedule to run. Is zero if the task is not scheduled to + * run or we are in the middle of a service (so technically not scheduled too). + */ + uint64_t next_service_task_run_time; + + /* + * When should the secure tunnel next attempt to reconnect? Only used by PENDING_RECONNECT state. + */ + uint64_t next_reconnect_time_ns; + + /* + * When should the secure tunnel reset current_reconnect_delay_interval_ms to the minimum value? Only relevant to + * the CONNECTED state. + */ + uint64_t next_reconnect_delay_reset_time_ns; + + /* + * When should we shut down the channel due to failure to receive a websocket handshake? Only relevant during the + * SECURE_TUNNEL_CONNECT state. + */ + uint64_t next_secure_tunnel_websocket_connect_timeout_time; + + /* + * True if the secure tunnel's service task is running. Used to skip service task reevaluation due to state changes + * while running the service task. Reevaluation will occur at the very end of the service. + */ + bool in_service; + + struct aws_linked_list queued_operations; + struct aws_secure_tunnel_operation *current_operation; + + /* + * Is there an io message in transit (to the socket) that has not invoked its write completion callback yet? + * The secure tunnel implementation only allows one in-transit message at a time, and so if this is true, we don't + * send additional ones/ + */ + bool pending_write_completion; + + /* + * When should the next PINGREQ be sent? + */ + uint64_t next_ping_time; + + /* + * When should we shut down the channel due to failure to receive a PINGRESP? Only non-zero when an outstanding + * PINGREQ has not been answered. + */ + uint64_t next_ping_timeout_time; + + /* Steve todo remove this and use next_ping_time above with tasks */ /* The secure tunneling endpoint ELB drops idle connect after 1 minute. We need to send a ping periodically to keep * the connection */ - struct ping_task_context *ping_task_context; + // struct ping_task_context *ping_task_context; }; #endif /* AWS_IOTDEVICE_SECURE_TUNNELING_IMPL_H */ diff --git a/include/aws/iotdevice/private/secure_tunneling_operations.h b/include/aws/iotdevice/private/secure_tunneling_operations.h new file mode 100644 index 00000000..80c96ac8 --- /dev/null +++ b/include/aws/iotdevice/private/secure_tunneling_operations.h @@ -0,0 +1,124 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#ifndef AWS_IOTDEVICE_SECURE_TUNNELING_OPERATION_H +#define AWS_IOTDEVICE_SECURE_TUNNELING_OPERATION_H + +#include +#include +#include +#include +#include +#include + +/********************************************************************************************************************* + * Operations + ********************************************************************************************************************/ + +struct aws_secure_tunnel_operation; + +enum aws_secure_tunnel_operation_type { + AWS_STOT_NONE, + AWS_STOT_CONNECT, + AWS_STOT_PING, + AWS_STOT_DATA, + AWS_STOT_STREAM_RESET, + AWS_STOT_STREAM_START +}; + +/* 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)( + struct aws_secure_tunnel_operation *operation, + int error_code, + const void *completion_view); + + /* Set the stream id of outgoing st_msg */ + void (*aws_secure_tunnel_operation_set_stream_id_fn)( + struct aws_secure_tunnel_operation *operation, + struct aws_secure_tunnel *secure_tunnel); + + /* Get the stream id from an address */ + int32_t *(*aws_secure_tunnel_operation_get_stream_id_address_fn)( + const struct aws_secure_tunnel_operation *operation); +}; + +/** + * This is the base structure for all secure tunnel operations. It includes the type, a ref count, and list management. + */ +struct aws_secure_tunnel_operation { + const struct aws_secure_tunnel_operation_vtable *vtable; + struct aws_ref_count ref_count; + struct aws_linked_list_node node; + + enum aws_secure_tunnel_operation_type operation_type; + const void *message_view; + + /* Size of the secure tunnel message this operation represents */ + size_t message_size; + + void *impl; +}; + +struct aws_secure_tunnel_operation_data { + struct aws_secure_tunnel_operation base; + struct aws_allocator *allocator; + + struct aws_secure_tunnel_message_data_storage options_storage; +}; + +AWS_EXTERN_C_BEGIN + +/* Operation Base */ + +AWS_IOTDEVICE_API struct aws_secure_tunnel_operation *aws_secure_tunnel_operation_acquire( + struct aws_secure_tunnel_operation *operation); + +AWS_IOTDEVICE_API struct aws_secure_tunnel_operation *aws_secure_tunnel_operation_release( + struct aws_secure_tunnel_operation *operation); + +AWS_IOTDEVICE_API void *aws_secure_tunnel_operation_complete( + struct aws_secure_tunnel_operation *operation, + int error_code, + const void *associated_view); + +AWS_IOTDEVICE_API void aws_secure_tunnel_operation_set_stream_id( + struct aws_secure_tunnel_operation *operation, + struct aws_secure_tunnel *secure_tunnel); + +AWS_IOTDEVICE_API int32_t + aws_secure_tunnel_operation_get_stream_id(const struct aws_secure_tunnel_operation *operation); + +AWS_IOTDEVICE_API int32_t *aws_secure_tunnel_operation_get_stream_id_address( + const struct aws_secure_tunnel_operation *operation); + +/* Data */ + +AWS_IOTDEVICE_API +void aws_secure_tunnel_message_data_view_log( + const struct aws_secure_tunnel_message_data_view *data_view, + enum aws_log_level level); + +AWS_IOTDEVICE_API +struct aws_secure_tunnel_operation_data *aws_secure_tunnel_operation_data_new( + struct aws_allocator *allocator, + const struct aws_secure_tunnel *secure_tunnel, + const struct aws_secure_tunnel_message_data_view *data_options); + +AWS_IOTDEVICE_API +const char *aws_secure_tunnel_operation_type_to_c_string(enum aws_secure_tunnel_operation_type operation_type); + +/* Stream */ + +/* STEVE TODO add stream to API */ +// AWS_IOTDEVICE_API +// struct aws_secure_tunnel_operation_data *aws_secure_tunnel_operation_stream_new( +// struct aws_allocator *allocator, +// const struct aws_secure_tunnel *secure_tunnel, +// const struct aws_secure_tunnel_message_stream_view *stream_options); + +AWS_EXTERN_C_END + +#endif /* AWS_IOTDEVICE_SECURE_TUNNELING_OPERATION_H */ diff --git a/include/aws/iotdevice/private/serializer.h b/include/aws/iotdevice/private/serializer.h index 0010277d..d8c2184a 100644 --- a/include/aws/iotdevice/private/serializer.h +++ b/include/aws/iotdevice/private/serializer.h @@ -15,7 +15,6 @@ #define AWS_IOT_ST_MAX_MESSAGE_SIZE 64 * 1024 #define AWS_IOT_ST_MAX_PAYLOAD_SIZE 64512 - enum aws_secure_tunnel_field_number { AWS_SECURE_TUNNEL_FN_TYPE = 1, AWS_SECURE_TUNNEL_FN_STREAM_ID = 2, @@ -113,6 +112,9 @@ int aws_iot_st_msg_deserialize_from_cursor( struct aws_byte_cursor *cursor, struct aws_allocator *allocator); +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 e810c928..10082922 100644 --- a/include/aws/iotdevice/secure_tunneling.h +++ b/include/aws/iotdevice/secure_tunneling.h @@ -6,9 +6,8 @@ #ifndef AWS_IOTDEVICE_SECURE_TUNNELING_H #define AWS_IOTDEVICE_SECURE_TUNNELING_H -#include - #include +#include #define AWS_IOT_ST_SPLIT_MESSAGE_SIZE 15000 @@ -19,6 +18,29 @@ struct aws_websocket; struct aws_websocket_incoming_frame; struct aws_http_proxy_options; +/* + * Views + */ + +/** + * Read-only snapshot of Data Message + */ + +struct aws_secure_tunnel_message_data_view { + int32_t stream_id; + struct aws_byte_cursor service_id; + struct aws_byte_cursor payload; +}; + +/** + * Read-only snapshot of Stream Message + * Used with Stream Start and Stream Reset message types + */ +struct aws_secure_tunnel_message_stream_view { + int32_t stream_id; + const struct aws_byte_cursor *service_id; +}; + /* Callbacks */ typedef void(aws_secure_tunneling_on_connection_complete_fn)(void *user_data); typedef void(aws_secure_tunneling_on_connection_shutdown_fn)(void *user_data); @@ -41,9 +63,12 @@ struct aws_secure_tunnel_options { const struct aws_http_proxy_options *http_proxy_options; struct aws_byte_cursor access_token; - enum aws_secure_tunneling_local_proxy_mode local_proxy_mode; struct aws_byte_cursor endpoint_host; + enum aws_secure_tunneling_local_proxy_mode local_proxy_mode; const char *root_ca; + const char *service_id_1; + const char *service_id_2; + const char *service_id_3; aws_secure_tunneling_on_connection_complete_fn *on_connection_complete; aws_secure_tunneling_on_connection_shutdown_fn *on_connection_shutdown; @@ -89,9 +114,19 @@ int aws_secure_tunnel_close(struct aws_secure_tunnel *secure_tunnel); AWS_IOTDEVICE_API int aws_secure_tunnel_send_data(struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *data); +AWS_IOTDEVICE_API +int aws_secure_tunnel_send_data_v2( + struct aws_secure_tunnel *secure_tunnel, + const struct aws_secure_tunnel_message_data_view *data_options); + AWS_IOTDEVICE_API int aws_secure_tunnel_stream_start(struct aws_secure_tunnel *secure_tunnel); +AWS_IOTDEVICE_API +int aws_secure_tunnel_stream_start_v2( + struct aws_secure_tunnel *secure_tunnel, + const struct aws_byte_cursor *service_id_data); + AWS_IOTDEVICE_API int aws_secure_tunnel_stream_reset(struct aws_secure_tunnel *secure_tunnel); diff --git a/include/aws/iotdevice/secure_tunneling_message_storage.h b/include/aws/iotdevice/secure_tunneling_message_storage.h new file mode 100644 index 00000000..3c707684 --- /dev/null +++ b/include/aws/iotdevice/secure_tunneling_message_storage.h @@ -0,0 +1,69 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#ifndef AWS_IOTDEVICE_SECURE_TUNNELING_MESSAGE_STORAGE_H +#define AWS_IOTDEVICE_SECURE_TUNNELING_MESSAGE_STORAGE_H + +#include +#include + +struct aws_secure_tunnel_message_data_storage { + struct aws_allocator *allocator; + struct aws_secure_tunnel_message_data_view storage_view; + + int32_t stream_id; + struct aws_byte_cursor service_id; + struct aws_byte_cursor payload; + + struct aws_byte_buf storage; +}; + +struct aws_secure_tunnel_message_stream_storage { + struct aws_allocator *allocator; + struct aws_secure_tunnel_message_stream_view storage_view; + + int32_t stream_id; + struct aws_byte_cursor service_id; + + struct aws_byte_buf storage; +}; + +AWS_EXTERN_C_BEGIN + +/* Data */ + +AWS_IOTDEVICE_API +int aws_secure_tunnel_message_data_storage_init( + struct aws_secure_tunnel_message_data_storage *data_storage, + struct aws_allocator *allocator, + const struct aws_secure_tunnel_message_data_view *data_options); + +AWS_IOTDEVICE_API +int aws_secure_tunnel_message_data_storage_init_from_external_storage( + struct aws_secure_tunnel_message_data_storage *data_storage, + struct aws_allocator *allocator); + +AWS_IOTDEVICE_API +void aws_secure_tunnel_message_data_storage_clean_up(struct aws_secure_tunnel_message_data_storage *data_storage); + +/* Stream */ + +AWS_IOTDEVICE_API +int aws_secure_tunnel_message_stream_storage_init( + struct aws_secure_tunnel_message_stream_storage *stream_storage, + struct aws_allocator *allocator, + const struct aws_secure_tunnel_message_stream_view *stream_options); + +AWS_IOTDEVICE_API +int aws_secure_tunnel_message_stream_storage_init_from_external_storage( + struct aws_secure_tunnel_message_stream_storage *stream_storage, + struct aws_allocator *allocator); + +AWS_IOTDEVICE_API +void aws_secure_tunnel_message_stream_storage_clean_up(struct aws_secure_tunnel_message_stream_storage *stream_storage); + +AWS_EXTERN_C_END + +#endif /* AWS_IOTDEVICE_SECURE_TUNNELING_MESSAGE_STORAGE_H */ diff --git a/source/iotdevice.c b/source/iotdevice.c index a9c9765d..37a48b50 100644 --- a/source/iotdevice.c +++ b/source/iotdevice.c @@ -16,25 +16,47 @@ static struct aws_error_info s_errors[] = { AWS_DEFINE_ERROR_INFO_IOTDEVICE( AWS_ERROR_IOTDEVICE_DEFENDER_INVALID_REPORT_INTERVAL, - "Invalid defender task reporting interval. Must be greater than 5 minutes"), + "Invalid defender task reporting interval. Must be greater than 5 minutes."), AWS_DEFINE_ERROR_INFO_IOTDEVICE( AWS_ERROR_IOTDEVICE_DEFENDER_UNSUPPORTED_REPORT_FORMAT, - "Unknown format value selected for defender reporting task"), + "Unknown format value selected for defender reporting task."), AWS_DEFINE_ERROR_INFO_IOTDEVICE( AWS_ERROR_IOTDEVICE_DEFENDER_REPORT_SERIALIZATION_FAILURE, - "Error serializing report for publishing"), + "Error serializing report for publishing."), AWS_DEFINE_ERROR_INFO_IOTDEVICE( AWS_ERROR_IOTDEVICE_DEFENDER_UNKNOWN_CUSTOM_METRIC_TYPE, - "Unknown custom metric type found in reporting task"), + "Unknown custom metric type found in reporting task."), AWS_DEFINE_ERROR_INFO_IOTDEVICE( AWS_ERROR_IOTDEVICE_DEFENDER_INVALID_TASK_CONFIG, - "Invalid configuration detected in defender reporting task config. Check prior errors"), + "Invalid configuration detected in defender reporting task config. Check prior errors."), AWS_DEFINE_ERROR_INFO_IOTDEVICE( AWS_ERROR_IOTDEVICE_DEFENDER_PUBLISH_FAILURE, - "Mqtt client error while attempting to publish defender report"), + "Mqtt client error while attempting to publish defender report."), AWS_DEFINE_ERROR_INFO_IOTDEVICE( AWS_ERROR_IOTDEVICE_DEFENDER_UNKNOWN_TASK_STATUS, - "Device defender task was invoked with an unknown task status"), + "Device defender task was invoked with an unknown task status."), + + AWS_DEFINE_ERROR_INFO_IOTDEVICE( + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_STREAM, + "Secure Tunnel invalid stream id."), + AWS_DEFINE_ERROR_INFO_IOTDEVICE( + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INCORRECT_MODE, + "Secure Tunnel stream cannot be started while in Destination Mode."), + AWS_DEFINE_ERROR_INFO_IOTDEVICE( + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_BAD_SERVICE_ID, + "Secure Tunnel stream start request with bad service id."), + AWS_DEFINE_ERROR_INFO_IOTDEVICE( + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_OPTIONS_VALIDATION, + "Invalid Secure Tunnel data message options value."), + AWS_DEFINE_ERROR_INFO_IOTDEVICE( + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_STREAM_OPTIONS_VALIDATION, + "Invalid Secure Tunnel stream options value."), + AWS_DEFINE_ERROR_INFO_IOTDEVICE( + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_SECURE_TUNNEL_TERMINATED, + "Secure Tunnel terminated by user request."), + AWS_DEFINE_ERROR_INFO_IOTDEVICE( + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_OPERATION_FAILED_DUE_TO_DISCONNECTION, + "Secure Tunnel operation failed due to disconnected state."), }; /* clang-format on */ #undef AWS_DEFINE_ERROR_INFO_IOTDEVICE diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index df9c7fdc..db0b858f 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -4,7 +4,10 @@ */ #include +#include +#include +#include #include #include #include @@ -14,6 +17,7 @@ #include #include #include +#include #include #define MAX_WEBSOCKET_PAYLOAD 131076 @@ -21,17 +25,67 @@ #define PAYLOAD_BYTE_LENGTH_PREFIX 2 #define PING_TASK_INTERVAL ((uint64_t)20 * 1000000000) -struct aws_secure_tunnel_options_storage { - struct aws_secure_tunnel_options options; +// Steve TODO check if these need to be implemented or re-organized +static void s_change_current_state(struct aws_secure_tunnel *secure_tunnel, enum aws_secure_tunnel_state next_state); +int aws_secure_tunnel_bind_stream_id( + struct aws_secure_tunnel *secure_tunnel, + struct aws_secure_tunnel_operation_data *operation); +static int s_secure_tunneling_send( + struct aws_secure_tunnel *secure_tunnel, + const struct aws_byte_cursor *payload_data, + const struct aws_byte_cursor *service_id_data, + enum aws_secure_tunnel_message_type type); +static int s_websocket_connect(struct aws_secure_tunnel *secure_tunnel) { + return 0; +} +static int s_submit_operation(struct aws_secure_tunnel *secure_tunnel, struct aws_secure_tunnel_operation *operation); +static void s_reevaluate_service_task(struct aws_secure_tunnel *secure_tunnel); - /* backup */ - struct aws_socket_options socket_options; - struct aws_http_proxy_options http_proxy_options; - struct aws_http_proxy_config *http_proxy_config; - struct aws_byte_buf cursor_storage; - struct aws_string *root_ca; -}; +const char *aws_secure_tunnel_state_to_c_string(enum aws_secure_tunnel_state state) { + switch (state) { + case AWS_STS_STOPPED: + return "STOPPED"; + + case AWS_STS_CONNECTING: + return "CONNECTING"; + + case AWS_STS_SECURE_TUNNEL_CONNECT: + return "SECURE_TUNNEL_CONNECT"; + + case AWS_STS_CONNECTED: + return "CONNECTED"; + + case AWS_STS_CLEAN_DISCONNECT: + return "CLEAN_DISCONNECT"; + + case AWS_STS_CHANNEL_SHUTDOWN: + return "CHANNEL_SHUTDOWN"; + + case AWS_STS_PENDING_RECONNECT: + return "PENDING_RECONNECT"; + + case AWS_STS_TERMINATED: + return "TERMINATED"; + + 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"; + } + return "destination"; +} + +/********************************************************************************************************************* + * Options Storage + ********************************************************************************************************************/ + +/* + * Validation of options on creation of a new secure tunnel + */ int aws_secure_tunnel_options_validate(const struct aws_secure_tunnel_options *options) { AWS_ASSERT(options && options->allocator); if (options->bootstrap == NULL) { @@ -54,18 +108,28 @@ int aws_secure_tunnel_options_validate(const struct aws_secure_tunnel_options *o return AWS_OP_SUCCESS; } +/* + * Clean up stored secure tunnel config + */ void aws_secure_tunnel_options_storage_destroy(struct aws_secure_tunnel_options_storage *storage) { if (storage == NULL) { return; } - aws_client_bootstrap_release(storage->options.bootstrap); + aws_client_bootstrap_release(storage->bootstrap); aws_http_proxy_config_destroy(storage->http_proxy_config); aws_byte_buf_clean_up(&storage->cursor_storage); + aws_string_destroy(storage->endpoint_host); aws_string_destroy(storage->root_ca); - aws_mem_release(storage->options.allocator, storage); + aws_string_destroy(storage->service_id_1); + aws_string_destroy(storage->service_id_2); + aws_string_destroy(storage->service_id_3); + aws_mem_release(storage->allocator, storage); } +/* + * Copy and store secure tunnel options + */ struct aws_secure_tunnel_options_storage *aws_secure_tunnel_options_storage_new( const struct aws_secure_tunnel_options *src) { @@ -73,215 +137,241 @@ struct aws_secure_tunnel_options_storage *aws_secure_tunnel_options_storage_new( return NULL; } - struct aws_allocator *alloc = src->allocator; + struct aws_allocator *allocator = src->allocator; struct aws_secure_tunnel_options_storage *storage = - aws_mem_calloc(alloc, 1, sizeof(struct aws_secure_tunnel_options_storage)); + aws_mem_calloc(allocator, 1, sizeof(struct aws_secure_tunnel_options_storage)); /* shallow-copy everything that's shallow-copy-able */ - storage->options = *src; + // storage->options = *src; + + storage->allocator = allocator; + + storage->local_proxy_mode = src->local_proxy_mode; /* acquire reference to everything that's ref-counted */ - aws_client_bootstrap_acquire(storage->options.bootstrap); + storage->bootstrap = aws_client_bootstrap_acquire(src->bootstrap); /* deep-copy anything that needs deep-copying */ storage->socket_options = *src->socket_options; - storage->options.socket_options = &storage->socket_options; + // storage->options.socket_options = &storage->socket_options; /* deep-copy the http-proxy-options to http_proxy_config */ if (src->http_proxy_options != NULL) { storage->http_proxy_config = - aws_http_proxy_config_new_tunneling_from_proxy_options(alloc, src->http_proxy_options); + aws_http_proxy_config_new_tunneling_from_proxy_options(allocator, src->http_proxy_options); if (storage->http_proxy_config == NULL) { goto error; } /* Make a copy of http_proxy_options and point to it */ aws_http_proxy_options_init_from_config(&storage->http_proxy_options, storage->http_proxy_config); - storage->options.http_proxy_options = &storage->http_proxy_options; + // storage->options.http_proxy_options = &storage->http_proxy_options; } /* Store contents of all cursors within single buffer (and update cursors to point into it) */ aws_byte_buf_init_cache_and_update_cursors( - &storage->cursor_storage, alloc, &storage->options.access_token, &storage->options.endpoint_host, NULL); + // &storage->cursor_storage, allocator, &storage->options.access_token, &storage->options.endpoint_host, NULL); + &storage->cursor_storage, + allocator, + &storage->access_token, + NULL); + + storage->endpoint_host = aws_string_new_from_cursor(allocator, &src->endpoint_host); + if (storage->endpoint_host == NULL) { + goto error; + } if (src->root_ca != NULL) { - storage->root_ca = aws_string_new_from_c_str(alloc, src->root_ca); - storage->options.root_ca = aws_string_c_str(storage->root_ca); + storage->root_ca = aws_string_new_from_c_str(allocator, src->root_ca); + // storage->options.root_ca = aws_string_c_str(storage->root_ca); } - return storage; - -error: - aws_secure_tunnel_options_storage_destroy(storage); - return NULL; -} - -typedef int( - websocket_send_frame)(struct aws_websocket *websocket, const struct aws_websocket_send_frame_options *options); - -static void s_send_websocket_ping(struct aws_websocket *websocket, websocket_send_frame *send_frame) { - if (!websocket) { - return; + if (src->service_id_1 != NULL) { + storage->service_id_1 = aws_string_new_from_c_str(allocator, src->service_id_1); + // storage->options.service_id_1 = aws_string_c_str(storage->service_id_1); } - struct aws_websocket_send_frame_options frame_options; - AWS_ZERO_STRUCT(frame_options); - frame_options.opcode = AWS_WEBSOCKET_OPCODE_PING; - frame_options.fin = true; - send_frame(websocket, &frame_options); -} - -struct ping_task_context { - struct aws_allocator *allocator; - struct aws_event_loop *event_loop; - - struct aws_task ping_task; - struct aws_atomic_var task_cancelled; - struct aws_websocket *websocket; - - /* The ping_task shares the vtable function used by the secure tunnel to send frames over the websocket. */ - websocket_send_frame *send_frame; -}; - -static void s_ping_task(struct aws_task *task, void *user_data, enum aws_task_status task_status) { - AWS_LOGF_TRACE(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "s_ping_task"); - - struct ping_task_context *ping_task_context = user_data; - - if (task_status == AWS_TASK_STATUS_CANCELED) { - AWS_LOGF_INFO( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, "task_status is AWS_TASK_STATUS_CANCELED. Cleaning up ping task."); - aws_mem_release(ping_task_context->allocator, ping_task_context); - return; + if (src->service_id_2 != NULL) { + storage->service_id_2 = aws_string_new_from_c_str(allocator, src->service_id_2); + // storage->options.service_id_2 = aws_string_c_str(storage->service_id_2); } - const size_t task_cancelled = aws_atomic_load_int(&ping_task_context->task_cancelled); - if (task_cancelled) { - AWS_LOGF_INFO(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "task_cancelled is true. Cleaning up ping task."); - aws_mem_release(ping_task_context->allocator, ping_task_context); - return; + if (src->service_id_3 != NULL) { + storage->service_id_3 = aws_string_new_from_c_str(allocator, src->service_id_3); + // storage->options.service_id_3 = aws_string_c_str(storage->service_id_3); } - s_send_websocket_ping(ping_task_context->websocket, ping_task_context->send_frame); + return storage; - /* Schedule the next task */ - uint64_t now; - aws_event_loop_current_clock_time(ping_task_context->event_loop, &now); - aws_event_loop_schedule_task_future(ping_task_context->event_loop, task, now + PING_TASK_INTERVAL); +error: + aws_secure_tunnel_options_storage_destroy(storage); + return NULL; } -static void s_on_websocket_setup( - struct aws_websocket *websocket, - int error_code, - int handshake_response_status, - const struct aws_http_header *handshake_response_header_array, - size_t num_handshake_response_headers, - void *user_data) { - - (void)handshake_response_status; - (void)handshake_response_header_array; - (void)num_handshake_response_headers; - - /* Setup callback contract is: if error_code is non-zero then websocket is NULL. */ - AWS_FATAL_ASSERT((error_code != 0) == (websocket == NULL)); - - struct aws_secure_tunnel *secure_tunnel = user_data; - aws_http_message_release(secure_tunnel->handshake_request); - secure_tunnel->handshake_request = NULL; - - secure_tunnel->connection_error_code = error_code; - secure_tunnel->options->on_connection_complete(secure_tunnel->options->user_data); - - if (websocket) { - secure_tunnel->websocket = websocket; - struct ping_task_context *ping_task_context = - aws_mem_acquire(secure_tunnel->alloc, sizeof(struct ping_task_context)); - secure_tunnel->ping_task_context = ping_task_context; - AWS_ZERO_STRUCT(*ping_task_context); - ping_task_context->allocator = secure_tunnel->alloc; - ping_task_context->event_loop = - aws_event_loop_group_get_next_loop(secure_tunnel->options->bootstrap->event_loop_group); - aws_atomic_store_int(&ping_task_context->task_cancelled, 0); - ping_task_context->websocket = websocket; - ping_task_context->send_frame = secure_tunnel->websocket_vtable.send_frame; +/***************************************************************************************************************** + * MESSAGE HANDLING + *****************************************************************************************************************/ - aws_task_init(&ping_task_context->ping_task, s_ping_task, ping_task_context, "SecureTunnelingPingTask"); - aws_event_loop_schedule_task_now(ping_task_context->event_loop, &ping_task_context->ping_task); - } +/* + * Close and reset all stream ids + */ +static void s_reset_secure_tunnel(struct aws_secure_tunnel *secure_tunnel) { + secure_tunnel->config->stream_id = INVALID_STREAM_ID; + secure_tunnel->config->service_id_1_stream_id = INVALID_STREAM_ID; + secure_tunnel->config->service_id_2_stream_id = INVALID_STREAM_ID; + secure_tunnel->config->service_id_3_stream_id = INVALID_STREAM_ID; + // Steve TODO we may not want to drop data because we don't know which stream the data is for. + secure_tunnel->received_data.len = 0; /* Drop any incomplete secure tunnel frame */ } -static void s_on_websocket_shutdown(struct aws_websocket *websocket, int error_code, void *user_data) { - (void)websocket; - (void)error_code; - - struct aws_secure_tunnel *secure_tunnel = user_data; - aws_atomic_store_int(&secure_tunnel->ping_task_context->task_cancelled, 1); - secure_tunnel->ping_task_context->websocket = NULL; - secure_tunnel->options->on_connection_shutdown(secure_tunnel->options->user_data); -} +static void s_handle_data(struct aws_secure_tunnel *secure_tunnel, struct aws_iot_st_msg *st_msg) { + // Old way of handling a data message + secure_tunnel->options->on_data_receive(&st_msg->payload, secure_tunnel->options->user_data); -static bool s_on_websocket_incoming_frame_begin( - struct aws_websocket *websocket, - const struct aws_websocket_incoming_frame *frame, - void *user_data) { - (void)websocket; - (void)frame; - (void)user_data; - return true; + /* Steve TODO sort the message against service ids */ } static void s_handle_stream_start(struct aws_secure_tunnel *secure_tunnel, struct aws_iot_st_msg *st_msg) { + if (secure_tunnel->options->local_proxy_mode == AWS_SECURE_TUNNELING_SOURCE_MODE) { /* Source mode tunnel clients SHOULD treat receiving StreamStart as an error and close the active data stream * and WebSocket connection. */ - AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Received StreamStart in source mode. Closing the tunnel."); + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Received StreamStart in source mode. Closing the tunnel.", + (void *)secure_tunnel); secure_tunnel->vtable.close(secure_tunnel); } else { AWS_LOGF_INFO( AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "Received StreamStart in destination mode. stream_id=%d", + "id=%p: Received StreamStart in destination mode. service_id= " PRInSTR " stream_id=%d", + (void *)secure_tunnel, + AWS_BYTE_CURSOR_PRI(aws_byte_cursor_from_buf(&st_msg->service_id)), st_msg->stream_id); - secure_tunnel->stream_id = st_msg->stream_id; + + // Steve TODO there may be a better way to check if a service_id is being used in the StreamStart + if (st_msg->service_id.len > 0) { + struct aws_string *service_id = NULL; + service_id = aws_string_new_from_buf(secure_tunnel->allocator, &st_msg->service_id); + + if (service_id == NULL) { + aws_string_destroy(service_id); + /* Steve TODO log error? */ + return; + } + + // Check if stream_id is valid and then set the stream_id + // Steve TODO a Stream Reset MAY be sent by the destination to source for replaced stream ID if the stream + // previously existed. On the fence about whether to do this or not since it's optional in the protocol + if (secure_tunnel->config->service_id_1 != NULL && + aws_string_compare(secure_tunnel->config->service_id_1, service_id) == 0) { + secure_tunnel->config->service_id_1_stream_id = st_msg->stream_id; + } else if ( + secure_tunnel->config->service_id_2 != NULL && + aws_string_compare(secure_tunnel->config->service_id_2, service_id) == 0) { + secure_tunnel->config->service_id_2_stream_id = st_msg->stream_id; + } else if ( + secure_tunnel->config->service_id_3 != NULL && + aws_string_compare(secure_tunnel->config->service_id_3, service_id) == 0) { + secure_tunnel->config->service_id_3_stream_id = st_msg->stream_id; + } else { + // service ID is not supported Steve TODO log error + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Received StreamStart request for unsupported service_id: " PRInSTR, + (void *)secure_tunnel, + AWS_BYTE_CURSOR_PRI(aws_byte_cursor_from_buf(&st_msg->service_id))); + return; + } + + } else { + // No Service Id used indicating V1 Protocol or no multiplexing + secure_tunnel->config->stream_id = st_msg->stream_id; + } + // Steve TODO There doesn't really seem to be a point in calling this callback because it doesn't send anything + // Useful. We should have added a struct that contains the service_id and stream_id. secure_tunnel->options->on_stream_start(secure_tunnel->options->user_data); } } -static void s_reset_secure_tunnel(struct aws_secure_tunnel *secure_tunnel) { - secure_tunnel->stream_id = INVALID_STREAM_ID; - secure_tunnel->received_data.len = 0; /* Drop any incomplete secure tunnel frame */ -} - static void s_handle_stream_reset(struct aws_secure_tunnel *secure_tunnel, struct aws_iot_st_msg *st_msg) { - if (secure_tunnel->stream_id == INVALID_STREAM_ID || secure_tunnel->stream_id != st_msg->stream_id) { - AWS_LOGF_WARN( + + if (st_msg->service_id.len > 0) { + struct aws_string *service_id = NULL; + service_id = aws_string_new_from_buf(secure_tunnel->allocator, &st_msg->service_id); + + if (service_id == NULL) { + // Steve TODO destroy not necessary on a NULL service_id? + aws_string_destroy(service_id); + /* Steve TODO log error? */ + return; + } + + // Check if stream_id is valid and then reset the stream_id + if (secure_tunnel->config->service_id_1 != NULL && + aws_string_compare(secure_tunnel->config->service_id_1, service_id) == 0) { + secure_tunnel->config->service_id_1_stream_id = INVALID_STREAM_ID; + } else if ( + secure_tunnel->config->service_id_2 != NULL && + aws_string_compare(secure_tunnel->config->service_id_2, service_id) == 0) { + secure_tunnel->config->service_id_2_stream_id = INVALID_STREAM_ID; + } else if ( + secure_tunnel->config->service_id_3 != NULL && + aws_string_compare(secure_tunnel->config->service_id_3, service_id) == 0) { + secure_tunnel->config->service_id_3_stream_id = INVALID_STREAM_ID; + } else { + // service ID is not supported Steve TODO log error + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Received StreamReset request for unsupported service_id: " PRInSTR, + (void *)secure_tunnel, + AWS_BYTE_CURSOR_PRI(aws_byte_cursor_from_buf(&st_msg->service_id))); + return; + } + + AWS_LOGF_INFO( AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "Received StreamReset with stream_id different than the active stream_id. Ignoring. st_msg->stream_id=%d " - "secure_tunnel->stream_id=%d", - st_msg->stream_id, - secure_tunnel->stream_id); - return; + "id=%p: Service Id: " PRInSTR " stream reset.", + (void *)secure_tunnel, + AWS_BYTE_CURSOR_PRI(aws_byte_cursor_from_buf(&st_msg->service_id))); + } else { + // No Service ID provided, assuming this is a non-multiplexing request + if (secure_tunnel->config->stream_id == INVALID_STREAM_ID || + secure_tunnel->config->stream_id != st_msg->stream_id) { + AWS_LOGF_WARN( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "Received StreamReset with stream_id different than the active stream_id. Ignoring. " + "st_msg->stream_id=%d " + "secure_tunnel->config->stream_id=%d", + st_msg->stream_id, + secure_tunnel->config->stream_id); + return; + } + + secure_tunnel->config->stream_id = INVALID_STREAM_ID; + // s_reset_secure_tunnel(secure_tunnel); + AWS_LOGF_INFO(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: Stream reset.", (void *)secure_tunnel); } + // Steve TODO Indicate that a stream has been reset. secure_tunnel->options->on_stream_reset(secure_tunnel->options->user_data); - s_reset_secure_tunnel(secure_tunnel); } static void s_handle_session_reset(struct aws_secure_tunnel *secure_tunnel) { - if (secure_tunnel->stream_id == INVALID_STREAM_ID) { /* Session reset does not need to check stream id */ - return; - } + s_reset_secure_tunnel(secure_tunnel); secure_tunnel->options->on_session_reset(secure_tunnel->options->user_data); - s_reset_secure_tunnel(secure_tunnel); } static void s_process_iot_st_msg(struct aws_secure_tunnel *secure_tunnel, struct aws_iot_st_msg *st_msg) { + fprintf(stdout, "\ns_process_iot_st_msg() called\n"); /* TODO: Check stream_id, send reset? */ switch (st_msg->type) { case AWS_SECURE_TUNNEL_MT_DATA: - secure_tunnel->options->on_data_receive(&st_msg->payload, secure_tunnel->options->user_data); + s_handle_data(secure_tunnel, st_msg); + // secure_tunnel->options->on_data_receive(&st_msg->payload, secure_tunnel->options->user_data); break; case AWS_SECURE_TUNNEL_MT_STREAM_START: s_handle_stream_start(secure_tunnel, st_msg); @@ -324,7 +414,7 @@ static void s_process_received_data(struct aws_secure_tunnel *secure_tunnel) { tmp_cursor = cursor; struct aws_iot_st_msg st_msg; - aws_iot_st_msg_deserialize_from_cursor(&st_msg, &st_frame, secure_tunnel->alloc); + aws_iot_st_msg_deserialize_from_cursor(&st_msg, &st_frame, secure_tunnel->allocator); s_process_iot_st_msg(secure_tunnel, &st_msg); if (st_msg.type == AWS_SECURE_TUNNEL_MT_DATA) { @@ -342,8 +432,10 @@ static void s_process_received_data(struct aws_secure_tunnel *secure_tunnel) { } /***************************************************************************************************************** - * SECURE TUNNEL DECODING + * Websocket *****************************************************************************************************************/ +typedef int( + websocket_send_frame)(struct aws_websocket *websocket, const struct aws_websocket_send_frame_options *options); bool on_websocket_incoming_frame_payload( struct aws_websocket *websocket, @@ -378,108 +470,802 @@ static bool s_on_websocket_incoming_frame_complete( return true; } -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"; +// Steve TODO remove this for operational ping? Or use it on the task of websocket ping? +static void s_send_websocket_ping(struct aws_websocket *websocket, websocket_send_frame *send_frame) { + if (!websocket) { + return; } - return "destination"; + struct aws_websocket_send_frame_options frame_options; + AWS_ZERO_STRUCT(frame_options); + frame_options.opcode = AWS_WEBSOCKET_OPCODE_PING; + frame_options.fin = true; + send_frame(websocket, &frame_options); } -static struct aws_http_message *s_new_handshake_request(const struct aws_secure_tunnel *secure_tunnel) { - char path[50]; - snprintf( - path, - sizeof(path), - "/tunnel?local-proxy-mode=%s", - s_get_proxy_mode_string(secure_tunnel->options->local_proxy_mode)); - struct aws_http_message *handshake_request = aws_http_message_new_websocket_handshake_request( - secure_tunnel->alloc, aws_byte_cursor_from_c_str(path), secure_tunnel->options->endpoint_host); +// Steve TODO this can probably be removed as soon as pings are set up as operations +// struct ping_task_context { +// struct aws_allocator *allocator; +// struct aws_event_loop *event_loop; +// struct aws_task ping_task; +// struct aws_atomic_var task_cancelled; +// struct aws_websocket *websocket; +// /* The ping_task shares the vtable function used by the secure tunnel to send frames over the websocket. */ +// websocket_send_frame *send_frame; +// }; + +// Steve Commented s_ping_task. Probably unnecessary +// static void s_ping_task(struct aws_task *task, void *user_data, enum aws_task_status task_status) { +// AWS_LOGF_TRACE(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "s_ping_task"); +// struct ping_task_context *ping_task_context = user_data; +// if (task_status == AWS_TASK_STATUS_CANCELED) { +// AWS_LOGF_INFO( +// AWS_LS_IOTDEVICE_SECURE_TUNNELING, "task_status is AWS_TASK_STATUS_CANCELED. Cleaning up ping task."); +// aws_mem_release(ping_task_context->allocator, ping_task_context); +// return; +// } +// const size_t task_cancelled = aws_atomic_load_int(&ping_task_context->task_cancelled); +// if (task_cancelled) { +// AWS_LOGF_INFO(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "task_cancelled is true. Cleaning up ping task."); +// aws_mem_release(ping_task_context->allocator, ping_task_context); +// return; +// } +// s_send_websocket_ping(ping_task_context->websocket, ping_task_context->send_frame); +// /* Schedule the next task */ +// uint64_t now; +// aws_event_loop_current_clock_time(ping_task_context->event_loop, &now); +// aws_event_loop_schedule_task_future(ping_task_context->event_loop, task, now + PING_TASK_INTERVAL); +// } + +static void s_secure_tunnel_shutdown( + struct aws_client_bootstrap *bootstrap, + int error_code, + struct aws_channel *channel, + void *user_data) { - struct aws_http_header extra_headers[] = { - { - .name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Sec-WebSocket-Protocol"), - .value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("aws.iot.securetunneling-1.0"), - }, - { - .name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("access-token"), - .value = secure_tunnel->options->access_token, - }, - }; - for (size_t i = 0; i < AWS_ARRAY_SIZE(extra_headers); ++i) { - aws_http_message_add_header(handshake_request, extra_headers[i]); + (void)bootstrap; + (void)channel; + + struct aws_secure_tunnel *secure_tunnel = user_data; + + if (error_code == AWS_ERROR_SUCCESS) { + error_code = AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_UNEXPECTED_HANGUP; } - return handshake_request; + AWS_LOGF_INFO( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: channel tore down with error code %d(%s)", + (void *)secure_tunnel, + error_code, + aws_error_debug_str(error_code)); + + if (secure_tunnel->slot) { + aws_channel_slot_remove(secure_tunnel->slot); + AWS_LOGF_TRACE(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: slot removed successfully", (void *)secure_tunnel); + secure_tunnel->slot = NULL; + } + + /* Fail all queued operations */ + s_complete_operation_list( + secure_tunnel, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_OPERATION_FAILED_DUE_TO_OFFLINE_QUEUE_POLICY); + + if (secure_tunnel->desired_state == AWS_STS_CONNECTED) { + s_change_current_state(secure_tunnel, AWS_STS_PENDING_RECONNECT); + } else { + s_change_current_state(secure_tunnel, AWS_STS_STOPPED); + } } -void init_websocket_client_connection_options( - struct aws_secure_tunnel *secure_tunnel, - struct aws_websocket_client_connection_options *websocket_options) { +static void s_secure_tunnel_setup( + struct aws_client_bootstrap *bootstrap, + int error_code, + struct aws_channel *channel, + void *user_data) { - AWS_ZERO_STRUCT(*websocket_options); - websocket_options->allocator = secure_tunnel->alloc; - websocket_options->bootstrap = secure_tunnel->options->bootstrap; - websocket_options->socket_options = secure_tunnel->options->socket_options; - websocket_options->tls_options = &secure_tunnel->tls_con_opt; - websocket_options->host = secure_tunnel->options->endpoint_host; - websocket_options->port = 443; - websocket_options->handshake_request = s_new_handshake_request(secure_tunnel); - websocket_options->initial_window_size = MAX_WEBSOCKET_PAYLOAD; /* TODO: followup */ - websocket_options->user_data = secure_tunnel; - websocket_options->proxy_options = secure_tunnel->options->http_proxy_options; - websocket_options->on_connection_setup = s_on_websocket_setup; - websocket_options->on_connection_shutdown = s_on_websocket_shutdown; - websocket_options->on_incoming_frame_begin = s_on_websocket_incoming_frame_begin; - websocket_options->on_incoming_frame_payload = on_websocket_incoming_frame_payload; - websocket_options->on_incoming_frame_complete = s_on_websocket_incoming_frame_complete; - websocket_options->manual_window_management = false; + (void)bootstrap; - /* Save handshake_request to release it later */ - secure_tunnel->handshake_request = websocket_options->handshake_request; -} + /* Setup callback contract is: if error_code is non-zero then channel is NULL. */ + AWS_FATAL_ASSERT((error_code != 0) == (channel == NULL)); + struct aws_secure_tunnel *secure_tunnel = user_data; -static int s_secure_tunneling_connect(struct aws_secure_tunnel *secure_tunnel) { - if (secure_tunnel == NULL || secure_tunnel->stream_id != INVALID_STREAM_ID) { - return AWS_OP_ERR; + AWS_FATAL_ASSERT(secure_tunnel->current_state == AWS_STS_CONNECTING); + + if (error_code != AWS_OP_SUCCESS) { + /* secure tunnel shutdown already handles this case, so just call that. */ + s_secure_tunnel_shutdown(bootstrap, error_code, channel, user_data); + return; } - struct aws_websocket_client_connection_options websocket_options; - init_websocket_client_connection_options(secure_tunnel, &websocket_options); - if (secure_tunnel->websocket_vtable.client_connect(&websocket_options)) { - return AWS_OP_ERR; + if (secure_tunnel->desired_state != AWS_STS_CONNECTED) { + aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_USER_REQUESTED_STOP); + goto error; } - return AWS_OP_SUCCESS; + secure_tunnel->slot = aws_channel_slot_new(channel); /* allocs or crashes */ + + if (aws_channel_slot_insert_end(channel, secure_tunnel->slot)) { + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Failed to insert slot into channel %p, error %d (%s).", + (void *)secure_tunnel, + (void *)channel, + aws_last_error(), + aws_error_name(aws_last_error())); + goto error; + } + + if (aws_channel_slot_set_handler(secure_tunnel->slot, &secure_tunnel->handler)) { + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Failed to set MQTT handler into slot on channel %p, error %d (%s).", + (void *)secure_tunnel, + (void *)channel, + aws_last_error(), + aws_error_name(aws_last_error())); + + goto error; + } + + s_change_current_state(secure_tunnel, AWS_STS_SECURE_TUNNEL_CONNECT); + + return; + +error: + + s_change_current_state(secure_tunnel, AWS_STS_CHANNEL_SHUTDOWN); + (*secure_tunnel->vtable.channel_shutdown_fn)(channel, aws_last_error()); } -static int s_secure_tunneling_close(struct aws_secure_tunnel *secure_tunnel) { - if (secure_tunnel == NULL) { - return AWS_OP_ERR; +static void s_on_websocket_shutdown(struct aws_websocket *websocket, int error_code, void *user_data) { + struct aws_secure_tunnel *secure_tunnel = user_data; + + struct aws_channel *channel = secure_tunnel->slot ? secure_tunnel->slot->channel : NULL; + + s_secure_tunnel_shutdown(secure_tunnel->config->bootstrap, error_code, channel, secure_tunnel); + + if (websocket) { + aws_websocket_release(websocket); } - s_reset_secure_tunnel(secure_tunnel); - if (secure_tunnel->websocket != NULL) { - secure_tunnel->websocket_vtable.close(secure_tunnel->websocket, false); - secure_tunnel->websocket_vtable.release(secure_tunnel->websocket); - secure_tunnel->websocket = NULL; + /* Callback for user to indicate websocket has shutdown */ + if (secure_tunnel->options->on_connection_shutdown != NULL) { + secure_tunnel->options->on_connection_shutdown(secure_tunnel->options->user_data); } - return AWS_OP_SUCCESS; } -static void s_secure_tunneling_on_send_data_complete_callback( +static void s_on_websocket_setup( struct aws_websocket *websocket, int error_code, + int handshake_response_status, + const struct aws_http_header *handshake_response_header_array, + size_t num_handshake_response_headers, void *user_data) { - (void)websocket; - struct data_tunnel_pair *pair = user_data; - struct aws_secure_tunnel *secure_tunnel = (struct aws_secure_tunnel *)pair->secure_tunnel; - secure_tunnel->options->on_send_data_complete(error_code, pair->secure_tunnel->options->user_data); - aws_byte_buf_clean_up(&pair->buf); - aws_mem_release(secure_tunnel->alloc, pair); -} -bool secure_tunneling_send_data_call(struct aws_websocket *websocket, struct aws_byte_buf *out_buf, void *user_data) { + (void)handshake_response_status; + (void)handshake_response_header_array; + (void)num_handshake_response_headers; + + struct aws_secure_tunnel *secure_tunnel = user_data; + secure_tunnel->handshake_request = aws_http_message_release(secure_tunnel->handshake_request); + + /* Setup callback contract is: if error_code is non-zero then websocket is NULL. */ + AWS_FATAL_ASSERT((error_code != 0) == (websocket == NULL)); + + struct aws_channel *channel = NULL; + + if (websocket) { + channel = aws_websocket_get_channel(websocket); + AWS_ASSERT(channel); + + /* Websocket must be "converted" before the secure tunnel handler can be installed next to it. */ + if (aws_websocket_convert_to_midchannel_handler(websocket)) { + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Failed converting websocket, error %d (%s)", + (void *)secure_tunnel, + aws_last_error(), + aws_error_name(aws_last_error())); + + (*secure_tunnel->vtable.channel_shutdown_fn)(channel, aws_last_error()); + return; + } + + // secure_tunnel->websocket = websocket; + // struct ping_task_context *ping_task_context = + // aws_mem_acquire(secure_tunnel->allocator, sizeof(struct ping_task_context)); + // secure_tunnel->ping_task_context = ping_task_context; + // AWS_ZERO_STRUCT(*ping_task_context); + // ping_task_context->allocator = secure_tunnel->allocator; + // ping_task_context->event_loop = + // aws_event_loop_group_get_next_loop(secure_tunnel->options->bootstrap->event_loop_group); + // aws_atomic_store_int(&ping_task_context->task_cancelled, 0); + // ping_task_context->websocket = websocket; + // ping_task_context->send_frame = secure_tunnel->websocket_vtable.send_frame; + + // aws_task_init(&ping_task_context->ping_task, s_ping_task, ping_task_context, "SecureTunnelingPingTask"); + // aws_event_loop_schedule_task_now(ping_task_context->event_loop, &ping_task_context->ping_task); + } + + s_secure_tunnel_setup(secure_tunnel->config->bootstrap, error_code, channel, secure_tunnel); + + /* Existing connection_complete callback doesn't resturn an error code. Workaround to provide it. */ + /* Previous implementation, may be removed if tasked websocket setups succeeeds */ + // secure_tunnel->connection_error_code = error_code; + // s_secure_tunnel_setup(secure_tunnel->config->bootstrap, error_code, channel, secure_tunnel); + // secure_tunnel->options->on_connection_complete(secure_tunnel->options->user_data); +} + +struct aws_secure_tunnel_websocket_transform_complete_task { + struct aws_task task; + struct aws_allocator *allocator; + struct aws_secure_tunnel *secure_tunnel; + int error_code; + struct aws_http_message *handshake; +} + +void s_websocket_transform_complete_task_fn(struct aws_task *task, void *arg, enum aws_task_status status) { + (void)task; + + struct aws_secure_tunnel_websocket_transform_complete_task *websocket_transform_complete_task = arg; + if (status != AWS_TASK_STATUS_RUN_READY) { + goto done; + } + + struct aws_secure_tunnel *secure_tunnel = websocket_transform_complete_task->secure_tunnel; + + aws_http_message_release(secure_tunnel->handshake_request); + secure_tunnel->handshake_request = aws_http_message_acquire(websocket_transform_complete_task->handshake); + + int error_code = websocket_transform_complete_task->error_code; + if (error_code == 0 && secure_tunnel->desired_state == AWS_STS_CONNECTED) { + struct aws_websocket_client_connection_options websocket_options = { + .allocator = secure_tunnel->allocator, + .bootstrap = secure_tunnel->config->bootstrap, + .socket_options = &secure_tunnel->config->socket_options, + .tls_options = &secure_tunnel->tls_con_opt, + .host = aws_byte_cursor_from_string(secure_tunnel->config->endpoint_host), + .port = 443, + .handshake_request = websocket_transform_complete_task->handshake, + /* This may need to be set to MAX_WEBSOCKET_PAYLOAD */ + .initial_window_size = + 0, /* Prevent websocket data from arriving before the secure_tunnel handler is installed */ + .user_data = secure_tunnel, + .on_connection_setup = s_on_websocket_setup, + .on_connection_shutdown = s_on_websocket_shutdown, + .requested_event_loop = secure_tunnel->loop, /* STEVE TODO LEFT OFF RIGHT HERE */ + }; + + /* + websocket_options->allocator = secure_tunnel->allocator; + websocket_options->bootstrap = secure_tunnel->options->bootstrap; + websocket_options->socket_options = secure_tunnel->options->socket_options; + websocket_options->tls_options = &secure_tunnel->tls_con_opt; + websocket_options->host = secure_tunnel->options->endpoint_host; + websocket_options->port = 443; + websocket_options->handshake_request = s_new_handshake_request(secure_tunnel); + websocket_options->initial_window_size = MAX_WEBSOCKET_PAYLOAD; + websocket_options->user_data = secure_tunnel; + websocket_options->proxy_options = secure_tunnel->options->http_proxy_options; + websocket_options->on_connection_setup = s_on_websocket_setup; + websocket_options->on_connection_shutdown = s_on_websocket_shutdown; + websocket_options->on_incoming_frame_begin = s_on_websocket_incoming_frame_begin; + websocket_options->on_incoming_frame_payload = on_websocket_incoming_frame_payload; + websocket_options->on_incoming_frame_complete = s_on_websocket_incoming_frame_complete; + websocket_options->manual_window_management = false; + */ + + if (secure_tunnel->config->http_proxy_config != NULL) { + websocket_options.proxy_options = &secure_tunnel->config->http_proxy_options; + } + + if (secure_tunnel->vtable.websocket_connect_fn(&websocket_options)) { + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Failed to initiate websocket connection.", + (void *)secure_tunnel); + error_code = aws_last_error(); + goto error; + } + + goto done; + } else { + if (error_code == AWS_ERROR_SUCCESS) { + AWS_ASSERT(secure_tunnel->desired_state != AWS_STS_CONNECTED); + error_code = AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_USER_REQUESTED_STOP; + } + } + +error: + s_on_websocket_setup(NULL, error_code, -1, NULL, 0, secure_tunnel); + +done: + + aws_http_message_release(websocket_transform_complete_task->handshake); + aws_secure_tunnel_release(websocket_transform_complete_task->secure_tunnel); + + aws_mem_release(websocket_transform_complete_task->allocator, websocket_transform_complete_task); +} + +static void s_websocket_handshake_transform_complete( + struct aws_http_message *handshake_request, + int error_code, + void *complete_ctx) { + + struct aws_secure_tunnel *secure_tunnel = complete_ctx; + + struct aws_secure_tunnel_websocket_transform_complete_task *task = + aws_mem_calloc(secure_tunnel->allocator, 1, sizeof(struct aws_secure_tunnel_websocket_transform_complete_task)); + + aws_task_init( + &task->task, s_websocket_transform_complete_task_fn, (void *)task, "WebsocketHandshakeTransformComplete"); + + task->allocator = secure_tunnel->allocator; + task->secure_tunnel = aws_secure_tunnel_acquire(secure_tunnel); + task->error_code = error_code; + task->handshake = handshake_request; + + aws_event_loop_schedule_task_now(secure_tunnel->loop, &task->task); + + aws_secure_tunnel_release(secure_tunnel); +} + +static int s_websocket_connect(struct aws_secure_tunnel *secure_tunnel) { + AWS_ASSERT(secure_tunnel); + AWS_ASSERT(secure_tunnel->config->websocket_handshake_transform); + + /* Build websocket handshake request */ + char path[50]; + snprintf( + path, + sizeof(path), + "/tunnel?local-proxy-mode=%s", + s_get_proxy_mode_string(secure_tunnel->options->local_proxy_mode)); + + struct aws_http_message *handshake = aws_http_message_new_websocket_handshake_request( + secure_tunnel->allocator, + aws_byte_cursor_from_c_str(path), + aws_byte_cursor_from_string(secure_tunnel->config->endpoint_host)); + + /* Secure Tunnel specific headers */ + // Steve TODO implement client token connection headers here + struct aws_http_header extra_headers[] = { + { + .name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Sec-WebSocket-Protocol"), + .value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("aws.iot.securetunneling-2.0"), + }, + { + .name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("access-token"), + .value = secure_tunnel->options->access_token, + }, + }; + + if (handshake == NULL) { + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Transforming websocket handshake request.", + (void *)secure_tunnel); + goto error; + } + AWS_LOGF_TRACE( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: Transforming websocket handshake request.", (void *)secure_tunnel); + + aws_secure_tunnel_acquire(secure_tunnel); + secure_tunnel->config->websocket_handshake_transform( + handshake, + secure_tunnel->config->websocket_handshake_transform_user_data, + s_websocket_handshake_transform_complete, + secure_tunnel); + + return AWS_OP_SUCCESS; + +error: + aws_http_message_release(handshake); + + return AWS_OP_ERR; +} + +static bool s_on_websocket_incoming_frame_begin( + struct aws_websocket *websocket, + const struct aws_websocket_incoming_frame *frame, + void *user_data) { + (void)websocket; + (void)frame; + (void)user_data; + return true; +} + +static struct aws_http_message *s_new_handshake_request(const struct aws_secure_tunnel *secure_tunnel) { + char path[50]; + snprintf( + path, + sizeof(path), + "/tunnel?local-proxy-mode=%s", + s_get_proxy_mode_string(secure_tunnel->options->local_proxy_mode)); + struct aws_http_message *handshake_request = aws_http_message_new_websocket_handshake_request( + secure_tunnel->allocator, aws_byte_cursor_from_c_str(path), secure_tunnel->options->endpoint_host); + + // Steve TODO implement client token connection here + + struct aws_http_header extra_headers[] = { + { + .name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Sec-WebSocket-Protocol"), + .value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("aws.iot.securetunneling-2.0"), + }, + { + .name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("access-token"), + .value = secure_tunnel->options->access_token, + }, + }; + for (size_t i = 0; i < AWS_ARRAY_SIZE(extra_headers); ++i) { + aws_http_message_add_header(handshake_request, extra_headers[i]); + } + + return handshake_request; +} + +// Steve TODO remove this +void init_websocket_client_connection_options( + struct aws_secure_tunnel *secure_tunnel, + struct aws_websocket_client_connection_options *websocket_options) { + + AWS_ZERO_STRUCT(*websocket_options); + websocket_options->allocator = secure_tunnel->allocator; + websocket_options->bootstrap = secure_tunnel->options->bootstrap; + websocket_options->socket_options = secure_tunnel->options->socket_options; + websocket_options->tls_options = &secure_tunnel->tls_con_opt; + websocket_options->host = secure_tunnel->options->endpoint_host; + websocket_options->port = 443; + websocket_options->handshake_request = s_new_handshake_request(secure_tunnel); + websocket_options->initial_window_size = MAX_WEBSOCKET_PAYLOAD; /* TODO: followup */ + websocket_options->user_data = secure_tunnel; + websocket_options->proxy_options = secure_tunnel->options->http_proxy_options; + websocket_options->on_connection_setup = s_on_websocket_setup; + websocket_options->on_connection_shutdown = s_on_websocket_shutdown; + websocket_options->on_incoming_frame_begin = s_on_websocket_incoming_frame_begin; + websocket_options->on_incoming_frame_payload = on_websocket_incoming_frame_payload; + websocket_options->on_incoming_frame_complete = s_on_websocket_incoming_frame_complete; + websocket_options->manual_window_management = false; + + /* Save handshake_request to release it later */ + secure_tunnel->handshake_request = websocket_options->handshake_request; +} + +/********************************************************************************************************************* + * CHANNEL + ********************************************************************************************************************/ + +static void s_aws_secure_tunnel_shutdown_channel(struct aws_secure_tunnel *secure_tunnel, int error_code) { + if (error_code == AWS_ERROR_SUCCESS) { + error_code = AWS_ERROR_UNKNOWN; + } + if (secure_tunnel->current_state != AWS_STS_SECURE_TUNNEL_CONNECT && + secure_tunnel->current_state != AWS_STS_CONNECTED && secure_tunnel->current_state != AWS_STS_CLEAN_DISCONNECT) { + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: secure tunnel channel shutdown invoked from unexpected state %d(%s)", + (void *)secure_tunnel, + (int)secure_tunnel->current_state, + aws_secure_tunnel_state_to_c_string(secure_tunnel->current_state)); + return; + } + + if (secure_tunnel->slot == NULL || secure_tunnel->slot->channel == NULL) { + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: secure tunnel channel shutdown invoked without a channel", + (void *)secure_tunnel); + return; + } + + s_change_current_state(secure_tunnel, AWS_STS_CHANNEL_SHUTDOWN); + (*secure_tunnel->vtable.channel_shutdown_fn)(secure_tunnel->slot->channel, error_code); +} + +/********************************************************************************************************************* + * State Related + ********************************************************************************************************************/ +static void s_complete_operation_list(struct aws_secure_tunnel *secure_tunnel, int error_code); + +static void s_change_current_state_to_stopped(struct aws_secure_tunnel *secure_tunnel) { + secure_tunnel->current_state = AWS_STS_STOPPED; + + s_complete_operation_list(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); +} + +static void s_change_current_state_to_connecting(struct aws_secure_tunnel *secure_tunnel) { + AWS_ASSERT( + secure_tunnel->current_state == AWS_STS_STOPPED || secure_tunnel->current_state == AWS_STS_PENDING_RECONNECT); + + secure_tunnel->current_state = AWS_STS_CONNECTING; + secure_tunnel->clean_disconnect_error_code = AWS_ERROR_SUCCESS; + + int result = 0; + if (secure_tunnel->config->websocket_handshake_transform != NULL) { + result = s_websocket_connect(secure_tunnel); + } else { + struct aws_socket_channel_bootstrap_options channel_options; + AWS_ZERO_STRUCT(channel_options); + channel_options.bootstrap = secure_tunnel->config->bootstrap; + channel_options.host_name = aws_string_c_str(secure_tunnel->config->endpoint_host); + channel_options.port = 443; + channel_options.socket_options = &secure_tunnel->config->socket_options; + channel_options.tls_options = &secure_tunnel->tls_con_opt; + channel_options.setup_callback = &s_secure_tunnel_setup; + channel_options.shutdown_callback = &s_secure_tunnel_shutdown; + channel_options.user_data = secure_tunnel; + channel_options.requested_event_loop = secure_tunnel->loop; + + if (secure_tunnel->config->http_proxy_config == NULL) { + result = (*secure_tunnel->vtable.client_bootstrap_new_socket_channel_fn)(&channel_options); + } else { + result = (*secure_tunnel->vtable.http_proxy_new_socket_channel_fn)( + &channel_options, &secure_tunnel->config->http_proxy_options); + } + } + + if (result) { + int error_code = aws_last_error(); + AWS_LOGF_INFO( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: failed to kick off connection with error %d(%s)", + (void *)secure_tunnel, + error_code, + aws_error_debug_str(error_code)); + + s_change_current_state(secure_tunnel, AWS_STS_PENDING_RECONNECT); + } +} + +// Steve TODO implement these state changes +static void s_change_current_state_to_secure_tunnel_connect(struct aws_secure_tunnel *secure_tunnel) { + (void)secure_tunnel; +} +static void s_change_current_state_to_connected(struct aws_secure_tunnel *secure_tunnel) { + (void)secure_tunnel; +} +static void s_change_current_state_to_clean_disconnect(struct aws_secure_tunnel *secure_tunnel) { + (void)secure_tunnel; +} +static void s_change_current_state_to_channel_shutdown(struct aws_secure_tunnel *secure_tunnel) { + (void)secure_tunnel; +} +static void s_change_current_state_to_pending_reconnect(struct aws_secure_tunnel *secure_tunnel) { + (void)secure_tunnel; +} +static void s_change_current_state_to_terminated(struct aws_secure_tunnel *secure_tunnel) { + (void)secure_tunnel; +} + +static void s_change_current_state(struct aws_secure_tunnel *secure_tunnel, enum aws_secure_tunnel_state next_state) { + AWS_ASSERT(next_state != secure_tunnel->current_state); + if (next_state == secure_tunnel->current_state) { + return; + } + + AWS_LOGF_DEBUG( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: switching current state from %s to %s", + (void *)secure_tunnel, + aws_secure_tunnel_state_to_c_string(secure_tunnel->current_state), + aws_secure_tunnel_state_to_c_string(next_state)); + + switch (next_state) { + case AWS_STS_STOPPED: + s_change_current_state_to_stopped(secure_tunnel); + break; + case AWS_STS_CONNECTING: + s_change_current_state_to_connecting(secure_tunnel); + break; + case AWS_STS_SECURE_TUNNEL_CONNECT: + s_change_current_state_to_secure_tunnel_connect(secure_tunnel); + break; + case AWS_STS_CONNECTED: + s_change_current_state_to_connected(secure_tunnel); + break; + case AWS_STS_CLEAN_DISCONNECT: + s_change_current_state_to_clean_disconnect(secure_tunnel); + break; + case AWS_STS_CHANNEL_SHUTDOWN: + s_change_current_state_to_channel_shutdown(secure_tunnel); + break; + case AWS_STS_PENDING_RECONNECT: + s_change_current_state_to_pending_reconnect(secure_tunnel); + break; + case AWS_STS_TERMINATED: + s_change_current_state_to_terminated(secure_tunnel); + return; + } + + s_reevaluate_service_task(secure_tunnel); +} + +/********************************************************************************************************************* + * vtable functions + ********************************************************************************************************************/ + +static uint64_t s_aws_high_res_clock_get_ticks_proxy(void) { + uint64_t current_time = 0; + AWS_FATAL_ASSERT(aws_high_res_clock_get_ticks(¤t_time) == AWS_OP_SUCCESS); + + return current_time; +} + +static int s_secure_tunneling_connect(struct aws_secure_tunnel *secure_tunnel) { + // Steve TODO this is checking the stream_id and not other service id streams. + if (secure_tunnel == NULL || secure_tunnel->config->stream_id != INVALID_STREAM_ID) { + return AWS_OP_ERR; + } + + struct aws_websocket_client_connection_options websocket_options; + init_websocket_client_connection_options(secure_tunnel, &websocket_options); + if (secure_tunnel->websocket_vtable.client_connect(&websocket_options)) { + return AWS_OP_ERR; + } + + return AWS_OP_SUCCESS; +} + +static int s_secure_tunneling_close(struct aws_secure_tunnel *secure_tunnel) { + if (secure_tunnel == NULL) { + return AWS_OP_ERR; + } + + s_reset_secure_tunnel(secure_tunnel); + + if (secure_tunnel->websocket != NULL) { + secure_tunnel->websocket_vtable.close(secure_tunnel->websocket, false); + secure_tunnel->websocket_vtable.release(secure_tunnel->websocket); + secure_tunnel->websocket = NULL; + } + return AWS_OP_SUCCESS; +} + +static int s_secure_tunneling_send_data( + struct aws_secure_tunnel *secure_tunnel, + const struct aws_byte_cursor *payload_data) { + if (secure_tunnel->config->stream_id == INVALID_STREAM_ID) { + AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Invalid Stream Id"); + return AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_STREAM; + } + struct aws_byte_cursor new_data = *payload_data; + while (new_data.len) { + size_t bytes_max = new_data.len; + size_t amount_to_send = bytes_max < AWS_IOT_ST_SPLIT_MESSAGE_SIZE ? bytes_max : AWS_IOT_ST_SPLIT_MESSAGE_SIZE; + + struct aws_byte_cursor send_cursor = aws_byte_cursor_advance(&new_data, amount_to_send); + AWS_FATAL_ASSERT(send_cursor.len > 0); + if (send_cursor.len) { + if (s_secure_tunneling_send(secure_tunnel, &send_cursor, NULL, AWS_SECURE_TUNNEL_MT_DATA) != + AWS_OP_SUCCESS) { + AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Failure writing data to out_buf"); + return AWS_OP_ERR; + } + } + } + return AWS_OP_SUCCESS; +} + +static int s_secure_tunneling_send_data_v2( + struct aws_secure_tunnel *secure_tunnel, + const struct aws_secure_tunnel_message_data_view *data_options) { + AWS_PRECONDITION(secure_tunnel != NULL); + AWS_PRECONDITION(data_options != NULL); + + /* STEVE TODO TEMP LOGGING */ + aws_secure_tunnel_message_data_view_log(data_options, AWS_LL_DEBUG); + + struct aws_secure_tunnel_operation_data *data_op = + aws_secure_tunnel_operation_data_new(secure_tunnel->allocator, secure_tunnel, data_options); + + if (data_op == NULL) { + return AWS_OP_ERR; + } + + if (aws_secure_tunnel_bind_stream_id(secure_tunnel, data_op)) { + int error_code = aws_last_error(); + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: failed to bind secure tunnel stream id for data message operation, with error %d(%s)", + (void *)secure_tunnel, + error_code, + aws_error_debug_str(error_code)); + } + + if (s_submit_operation(secure_tunnel, &data_op->base)) { + goto error; + } + +error: + + aws_secure_tunnel_operation_release(&data_op->base); + return AWS_OP_ERR; + + // if (secure_tunnel->config->stream_id == INVALID_STREAM_ID) { + // AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Invalid Stream Id"); + // return AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_STREAM; + // } + // struct aws_byte_cursor new_data = *payload_data; + // struct aws_byte_cursor service_id_cursor = *service_id_data; + // size_t service_id_len = 0; + // if (service_id_data) { + // service_id_len = service_id_data->len; + // } + // while (new_data.len) { + // size_t bytes_max = new_data.len; + // size_t amount_to_send = bytes_max + service_id_len < AWS_IOT_ST_SPLIT_MESSAGE_SIZE + // ? bytes_max + // : AWS_IOT_ST_SPLIT_MESSAGE_SIZE - service_id_len; + + // struct aws_byte_cursor send_cursor = aws_byte_cursor_advance(&new_data, amount_to_send); + // AWS_FATAL_ASSERT(send_cursor.len > 0); + // if (send_cursor.len) { + // if (s_secure_tunneling_send(secure_tunnel, &send_cursor, &service_id_cursor, AWS_SECURE_TUNNEL_MT_DATA) + // != + // AWS_OP_SUCCESS) { + // AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Failure writing data to out_buf"); + // return AWS_OP_ERR; + // } + // } + // } + return AWS_OP_SUCCESS; +} + +static int s_secure_tunneling_send_stream_start(struct aws_secure_tunnel *secure_tunnel) { + if (secure_tunnel->options->local_proxy_mode == AWS_SECURE_TUNNELING_DESTINATION_MODE) { + AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Start can only be sent from src mode"); + return AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INCORRECT_MODE; + } + secure_tunnel->config->stream_id += 1; + if (secure_tunnel->config->stream_id == 0) { + secure_tunnel->config->stream_id += 1; + } + return s_secure_tunneling_send(secure_tunnel, NULL, NULL, AWS_SECURE_TUNNEL_MT_STREAM_START); +} + +static int s_secure_tunneling_send_stream_start_v2( + struct aws_secure_tunnel *secure_tunnel, + const struct aws_byte_cursor *service_id_data) { + if (secure_tunnel->options->local_proxy_mode == AWS_SECURE_TUNNELING_DESTINATION_MODE) { + AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Start can only be sent from src mode"); + return AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INCORRECT_MODE; + } + + secure_tunnel->config->stream_id += 1; + if (secure_tunnel->config->stream_id == 0) { + secure_tunnel->config->stream_id += 1; + } + + /* STEVE TODO Secure Tunnel Stream Start needs to include Service Id */ + return s_secure_tunneling_send(secure_tunnel, NULL, service_id_data, AWS_SECURE_TUNNEL_MT_STREAM_START); +} + +static int s_secure_tunneling_send_stream_reset(struct aws_secure_tunnel *secure_tunnel) { + if (secure_tunnel->config->stream_id == INVALID_STREAM_ID) { + AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Invalid Stream Id"); + return AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_STREAM; + } + + int result = s_secure_tunneling_send(secure_tunnel, NULL, NULL, AWS_SECURE_TUNNEL_MT_STREAM_RESET); + s_reset_secure_tunnel(secure_tunnel); + return result; +} + +static void s_secure_tunneling_on_send_data_complete_callback( + struct aws_websocket *websocket, + int error_code, + void *user_data) { + (void)websocket; + struct data_tunnel_pair *pair = user_data; + struct aws_secure_tunnel *secure_tunnel = (struct aws_secure_tunnel *)pair->secure_tunnel; + secure_tunnel->options->on_send_data_complete(error_code, pair->secure_tunnel->options->user_data); + aws_byte_buf_clean_up(&pair->buf); + aws_mem_release(secure_tunnel->allocator, pair); +} + +bool secure_tunneling_send_data_call(struct aws_websocket *websocket, struct aws_byte_buf *out_buf, void *user_data) { (void)websocket; struct data_tunnel_pair *pair = user_data; size_t space_available = out_buf->capacity - out_buf->len; @@ -522,25 +1308,35 @@ static void s_init_websocket_send_frame_options( static int s_init_data_tunnel_pair( struct data_tunnel_pair *pair, struct aws_secure_tunnel *secure_tunnel, - const struct aws_byte_cursor *data, + const struct aws_byte_cursor *payload_data, + const struct aws_byte_cursor *service_id_data, enum aws_secure_tunnel_message_type type) { struct aws_iot_st_msg message; - message.stream_id = secure_tunnel->stream_id; + message.stream_id = secure_tunnel->config->stream_id; message.ignorable = 0; message.type = type; - if (data != NULL) { - message.payload.buffer = data->ptr; - message.payload.len = data->len; + if (payload_data != NULL) { + message.payload.buffer = payload_data->ptr; + message.payload.len = payload_data->len; } else { message.payload.buffer = NULL; message.payload.len = 0; } + if (service_id_data != NULL) { + message.service_id.buffer = service_id_data->ptr; + message.service_id.len = service_id_data->len; + } else { + message.service_id.buffer = NULL; + message.service_id.len = 0; + } + message.connection_id = 0; pair->secure_tunnel = secure_tunnel; pair->length_prefix_written = false; - if (aws_iot_st_msg_serialize_from_struct(&pair->buf, secure_tunnel->alloc, message) != AWS_OP_SUCCESS) { + if (aws_iot_st_msg_serialize_from_struct(&pair->buf, secure_tunnel->allocator, message) != AWS_OP_SUCCESS) { AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Failure serializing message"); goto cleanup; } + fprintf(stdout, "\npair->buf.len: %zu\n", pair->buf.len); 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 cleanup; @@ -549,18 +1345,19 @@ static int s_init_data_tunnel_pair( return AWS_OP_SUCCESS; cleanup: aws_byte_buf_clean_up(&pair->buf); - aws_mem_release(pair->secure_tunnel->alloc, (void *)pair); + aws_mem_release(pair->secure_tunnel->allocator, (void *)pair); return AWS_OP_ERR; } int secure_tunneling_init_send_frame( struct aws_websocket_send_frame_options *frame_options, struct aws_secure_tunnel *secure_tunnel, - const struct aws_byte_cursor *data, + const struct aws_byte_cursor *payload_data, + const struct aws_byte_cursor *service_id_data, enum aws_secure_tunnel_message_type type) { struct data_tunnel_pair *pair = - (struct data_tunnel_pair *)aws_mem_acquire(secure_tunnel->alloc, sizeof(struct data_tunnel_pair)); - if (s_init_data_tunnel_pair(pair, secure_tunnel, data, type) != AWS_OP_SUCCESS) { + (struct data_tunnel_pair *)aws_mem_acquire(secure_tunnel->allocator, sizeof(struct data_tunnel_pair)); + if (s_init_data_tunnel_pair(pair, secure_tunnel, payload_data, service_id_data, type) != AWS_OP_SUCCESS) { return AWS_OP_ERR; } s_init_websocket_send_frame_options(frame_options, pair); @@ -569,80 +1366,522 @@ int secure_tunneling_init_send_frame( static int s_secure_tunneling_send( struct aws_secure_tunnel *secure_tunnel, - const struct aws_byte_cursor *data, + const struct aws_byte_cursor *payload_data, + const struct aws_byte_cursor *service_id_data, enum aws_secure_tunnel_message_type type) { + fprintf(stdout, "\ns_secure_tunneling_send() called\n"); + struct aws_websocket_send_frame_options frame_options; - if (secure_tunneling_init_send_frame(&frame_options, secure_tunnel, data, type) != AWS_OP_SUCCESS) { + if (secure_tunneling_init_send_frame(&frame_options, secure_tunnel, payload_data, service_id_data, type) != + AWS_OP_SUCCESS) { return AWS_OP_ERR; } return secure_tunnel->websocket_vtable.send_frame(secure_tunnel->websocket, &frame_options); } -static int s_secure_tunneling_send_data(struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *data) { - if (secure_tunnel->stream_id == INVALID_STREAM_ID) { - AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Invalid Stream Id"); - return AWS_ERROR_IOTDEVICE_SECUTRE_TUNNELING_INVALID_STREAM; +/********************************************************************************************************************* + * Operations + ********************************************************************************************************************/ + +int aws_secure_tunnel_get_stream_id_for_service(struct aws_secure_tunnel *secure_tunnel) {} + +int aws_secure_tunnel_operation_bind_stream_id( + struct aws_secure_tunnel_operation *operation, + struct aws_secure_tunnel *secure_tunnel) { + // Steve TODO check the stream id binding for any messages that need stream ids to be set. + switch (operation->operation_type) { + case AWS_STOT_DATA: + operation->vtable->aws_secure_tunnel_operation_set_stream_id_fn(operation, secure_tunnel); + return AWS_OP_SUCCESS; + + case AWS_STOT_NONE: + case AWS_STOT_CONNECT: + case AWS_STOT_PING: + case AWS_STOT_STREAM_RESET: + case AWS_STOT_STREAM_START: + return AWS_OP_SUCCESS; } - struct aws_byte_cursor new_data = *data; - while (new_data.len) { - size_t bytes_max = new_data.len; - size_t amount_to_send = bytes_max < AWS_IOT_ST_SPLIT_MESSAGE_SIZE ? bytes_max : AWS_IOT_ST_SPLIT_MESSAGE_SIZE; + if (operation->operation_type == AWS_STOT_DATA) { - struct aws_byte_cursor send_cursor = aws_byte_cursor_advance(&new_data, amount_to_send); - AWS_FATAL_ASSERT(send_cursor.len > 0); - if (send_cursor.len) { - if (s_secure_tunneling_send(secure_tunnel, &send_cursor, AWS_SECURE_TUNNEL_MT_DATA) != AWS_OP_SUCCESS) { - AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Failure writing data to out_buf"); - return AWS_OP_ERR; - } - } + return AWS_OP_SUCCESS; + } + + aws_raise_error(AWS_ERROR_INVALID_STATE); + return AWS_OP_ERR; +} + +static int s_aws_secure_tunnel_set_current_operation( + struct aws_secure_tunnel *secure_tunnel, + struct aws_secure_tunnel_operation *operation) { + if (aws_secure_tunnel_operation_bind_stream_id(operation, secure_tunnel)) { + int error_code = aws_last_error(); + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: failed to append stream id for current operation with error %d(%s)", + (void *)secure_tunnel, + error_code, + aws_error_debug_str(error_code)); + return AWS_OP_ERR; } + + secure_tunnel->current_operation = operation; + return AWS_OP_SUCCESS; } -static int s_secure_tunneling_send_stream_start(struct aws_secure_tunnel *secure_tunnel) { - if (secure_tunnel->options->local_proxy_mode == AWS_SECURE_TUNNELING_DESTINATION_MODE) { - AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Start can only be sent from src mode"); - return AWS_ERROR_IOTDEVICE_SECUTRE_TUNNELING_INCORRECT_MODE; +static void s_reset_ping(struct aws_secure_tunnel *secure_tunnel) { + uint64_t now = (*secure_tunnel->vtable.get_current_time_fn)(); + // Steve TODO do we want a keep alive time in seconds to try to keep the secure tunnel connected? + uint64_t keep_alive_seconds = 10; // store and get from -> secure_tunnel->config->server_keep_alive; + + uint64_t keep_alive_interval_nanos = + aws_timestamp_convert(keep_alive_seconds, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL); + secure_tunnel->next_ping_time = aws_add_u64_saturating(now, keep_alive_interval_nanos); + + AWS_LOGF_DEBUG( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: next PING scheduled for time %" PRIu64, + (void *)secure_tunnel, + secure_tunnel->next_ping_time); +} + +static void s_aws_secure_tunnel_on_socket_write_completion_secure_tunnel_connect( + struct aws_secure_tunnel *secure_tunnel, + int error_code) { + if (error_code != AWS_ERROR_SUCCESS) { + s_aws_secure_tunnel_shutdown_channel(secure_tunnel, error_code); + return; + } +} // Steve TODO THIS IS WHERE I LEFT OFF BEFORE MQTT5 MIGRATION. + +/* + * Check whether secure tunnel currently has work left to do based on its current state + */ +static bool s_aws_secure_tunnel_has_pending_operational_work(const struct aws_secure_tunnel *secure_tunnel) { + if (aws_linked_list_empty(&secure_tunnel->queued_operations)) { + 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_SECURE_TUNNEL_CONNECT: + /* Only allowed to CONNECT to a WebSocket in this state */ + return next_operation->operation_type == AWS_STOT_CONNECT; + + 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; + + default: + return false; + } +} + +/* + * + */ +static uint64_t s_aws_secure_tunnel_compute_operational_state_service_time( + struct aws_secure_tunnel *secure_tunnel, + uint64_t now) { + + /* If an io message is in transit down the channel, then wait for it to complete */ + if (secure_tunnel->pending_write_completion) { + return 0; } - secure_tunnel->stream_id += 1; - if (secure_tunnel->stream_id == 0) { - secure_tunnel->stream_id += 1; + + /* If we're in the middle of something, keep going */ + if (secure_tunnel->current_operation != NULL) { + return now; + } + + /* If nothing is queued, there's nothing to do */ + if (!s_aws_secure_tunnel_has_pending_operational_work(secure_tunnel)) { + return 0; + } + + AWS_FATAL_ASSERT(!aws_linked_list_empty(&secure_tunnel->queued_operations)); + + switch (secure_tunnel->current_state) { + case AWS_STS_SECURE_TUNNEL_CONNECT: + case AWS_STS_CLEAN_DISCONNECT: + case AWS_STS_CONNECTED: + return now; + + default: + /* no outbound traffic is allowed outside of the above states */ + return 0; } - return s_secure_tunneling_send(secure_tunnel, NULL, AWS_SECURE_TUNNEL_MT_STREAM_START); } -static int s_secure_tunneling_send_stream_reset(struct aws_secure_tunnel *secure_tunnel) { - if (secure_tunnel->stream_id == INVALID_STREAM_ID) { - AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Invalid Stream Id"); - return AWS_ERROR_IOTDEVICE_SECUTRE_TUNNELING_INVALID_STREAM; +static bool s_aws_secure_tunnel_should_service_operational_state( + struct aws_secure_tunnel *secure_tunnel, + uint64_t now) { + return now == s_aws_secure_tunnel_compute_operational_state_service_time(secure_tunnel, now); +} + +int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure_tunnel) { + struct aws_channel_slot *slot = secure_tunnel->slot; + // steve TODO voiding slot for warning atm + (void)slot; +} + +static void s_complete_operation( + struct aws_secure_tunnel *secure_tunnel, + struct aws_secure_tunnel_operation *operation, + int error_code, + const void *view) { + // Steve TODO keep this for now in case we want to do some form of stats tracking + (void)secure_tunnel; + + aws_secure_tunnel_operation_complete(operation, error_code, view); + aws_secure_tunnel_operation_release(operation); +} + +static void s_complete_operation_list(struct aws_secure_tunnel *secure_tunnel, int error_code) { + + struct aws_linked_list_node *node = aws_linked_list_begin(&secure_tunnel->queued_operations); + while (node != aws_linked_list_end(&secure_tunnel->queued_operations)) { + struct aws_secure_tunnel_operation *operation = + AWS_CONTAINER_OF(node, struct aws_secure_tunnel_operation, node); + + node = aws_linked_list_next(node); + + s_complete_operation(secure_tunnel, operation, error_code, NULL); } - int result = s_secure_tunneling_send(secure_tunnel, NULL, AWS_SECURE_TUNNEL_MT_STREAM_RESET); - s_reset_secure_tunnel(secure_tunnel); - return result; + /* we've released everything, so reset the list to empty */ + aws_linked_list_init(&secure_tunnel->queued_operations); } -static void s_secure_tunnel_destroy(void *user_data); +static void s_check_ping_timeout(struct aws_secure_tunnel *secure_tunnel, uint64_t now) { + // Steve TODO check the ping operation for timeout + // mqtt5_client uses s_check_timeouts() + (void)secure_tunnel; + (void)now; +} -struct aws_secure_tunnel *aws_secure_tunnel_new(const struct aws_secure_tunnel_options *options) { +static uint64_t s_min_non_0_64(uint64_t a, uint64_t b) { + if (a == 0) { + return b; + } - struct aws_tls_ctx_options tls_ctx_opt; - AWS_ZERO_STRUCT(tls_ctx_opt); + if (b == 0) { + return a; + } + + return aws_min_u64(a, b); +} + +/* + * next_service_time == 0 means to not service the secure tunnel, i.e. a state that only cares about external events + * + * This includes connecting and channel shutdown. Terminated is also included, but it's a state that only exists + * instantaneously before final destruction. + */ +static uint64_t s_compute_next_service_time_secure_tunnel_stopped( + struct aws_secure_tunnel *secure_tunnel, + uint64_t now) { + /* have we been told to connect or terminate? */ + if (secure_tunnel->desired_state != AWS_STS_STOPPED) { + return now; + } + + return 0; +} + +static uint64_t s_compute_next_service_time_secure_tunnel_connect( + struct aws_secure_tunnel *secure_tunnel, + uint64_t now) { + /* This state is interruptable by a stop/terminate */ + if (secure_tunnel->desired_state != AWS_STS_CONNECTED) { + return now; + } + + uint64_t operation_processing_time = s_aws_secure_tunnel_compute_operational_state_service_time(secure_tunnel, now); + if (operation_processing_time == 0) { + // Steve todo need to set up this connect timeout for websocket timing + return secure_tunnel->next_secure_tunnel_websocket_connect_timeout_time; + } + + return aws_min_u64(secure_tunnel->next_secure_tunnel_websocket_connect_timeout_time, operation_processing_time); +} + +static uint64_t s_compute_next_service_time_secure_tunnel_connected( + struct aws_secure_tunnel *secure_tunnel, + uint64_t now) { + /* ping and ping timeout */ + uint64_t next_service_time = secure_tunnel->next_ping_time; + if (secure_tunnel->next_ping_timeout_time != 0) { + next_service_time = aws_min_u64(next_service_time, secure_tunnel->next_ping_timeout_time); + } + + if (secure_tunnel->desired_state != AWS_STS_CONNECTED) { + next_service_time = now; + } + + uint64_t operation_processing_time = s_aws_secure_tunnel_compute_operational_state_service_time(secure_tunnel, now); + + next_service_time = s_min_non_0_64(operation_processing_time, next_service_time); + + /* reset reconnect delay interval */ + next_service_time = s_min_non_0_64(secure_tunnel->next_reconnect_delay_reset_time_ns, next_service_time); + + return next_service_time; +} + +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); +} + +static uint64_t s_compute_next_service_time_secure_tunnel_pending_reconnect( + struct aws_secure_tunnel *secure_tunnel, + uint64_t now) { + if (secure_tunnel->desired_state != AWS_STS_CONNECTED) { + return now; + } + + return secure_tunnel->next_reconnect_time_ns; +} + +static uint64_t s_compute_next_service_time_by_current_state(struct aws_secure_tunnel *secure_tunnel, uint64_t now) { + + switch (secure_tunnel->current_state) { + case AWS_STS_STOPPED: + return s_compute_next_service_time_secure_tunnel_stopped(secure_tunnel, now); + case AWS_STS_SECURE_TUNNEL_CONNECT: + return s_compute_next_service_time_secure_tunnel_connect(secure_tunnel, now); + case AWS_STS_CONNECTED: + return s_compute_next_service_time_secure_tunnel_connected(secure_tunnel, now); + case AWS_STS_CLEAN_DISCONNECT: + return s_compute_next_service_time_secure_tunnel_clean_disconnect(secure_tunnel, now); + case AWS_STS_PENDING_RECONNECT: + return s_compute_next_service_time_secure_tunnel_pending_reconnect(secure_tunnel, now); + case AWS_STS_CONNECTING: + case AWS_STS_CHANNEL_SHUTDOWN: + case AWS_STS_TERMINATED: + return 0; + } + + return 0; +} + +static void s_reevaluate_service_task(struct aws_secure_tunnel *secure_tunnel) { + /* + * This causes the secure_tunnel to only reevaluate service schedule time at the end of the service call or in + * a callback from an external event. + */ + if (secure_tunnel->in_service) { + return; + } + + uint64_t now = (*secure_tunnel->vtable.get_current_time_fn)(); + uint64_t next_service_time = s_compute_next_service_time_by_current_state(secure_tunnel, now); + + /* + * This catches both the case when there's an existing service schedule and we either want to not + * perform it (next_service_time == 0) or need to run service at a different time than the current scheduled time. + */ + if (next_service_time != secure_tunnel->next_service_task_run_time && + secure_tunnel->next_service_task_run_time > 0) { + aws_event_loop_cancel_task(secure_tunnel->loop, &secure_tunnel->service_task); + secure_tunnel->next_service_task_run_time = 0; + + AWS_LOGF_TRACE( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: cancelling previously scheduled service task", + (void *)secure_tunnel); + } + + if (next_service_time > 0 && (next_service_time < secure_tunnel->next_service_task_run_time || + secure_tunnel->next_service_task_run_time == 0)) { + aws_event_loop_schedule_task_future(secure_tunnel->loop, &secure_tunnel->service_task, next_service_time); + + AWS_LOGF_TRACE( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: scheduled service task for time %" PRIu64, + (void *)secure_tunnel, + next_service_time); + } + + secure_tunnel->next_service_task_run_time = next_service_time; +} + +static void s_enqueue_operation_back( + struct aws_secure_tunnel *secure_tunnel, + struct aws_secure_tunnel_operation *operation) { + AWS_LOGF_DEBUG( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: enqueuing %s operation to back", + (void *)secure_tunnel, + aws_secure_tunnel_operation_type_to_c_string(operation->operation_type)); + + aws_linked_list_push_back(&secure_tunnel->queued_operations, &operation->node); + + s_reevaluate_service_task(secure_tunnel); +} + +static void s_enqueue_operation_front( + struct aws_secure_tunnel *secure_tunnel, + struct aws_secure_tunnel_operation *operation) { + AWS_LOGF_DEBUG( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: enqueuing %s operation to front", + (void *)secure_tunnel, + aws_secure_tunnel_operation_type_to_c_string(operation->operation_type)); + + aws_linked_list_push_front(&secure_tunnel->queued_operations, &operation->node); + + s_reevaluate_service_task(secure_tunnel); +} +struct aws_secure_tunnel_submit_operation_task { + struct aws_task task; + struct aws_allocator *allocator; + struct aws_secure_tunnel *secure_tunnel; + struct aws_secure_tunnel_operation *operation; +}; + +static void s_secure_tunnel_submit_operation_task_fn(struct aws_task *task, void *arg, enum aws_task_status status) { + (void)task; + + int completion_error_code = AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_SECURE_TUNNEL_TERMINATED; + struct aws_secure_tunnel_submit_operation_task *submit_operation_task = arg; + + /* + * Take a ref to the operation that represents the secure tunnel taking ownership + * If we subsequently reject it (task cancel), then the operation completion + * will undo this ref acquisition. + */ + aws_secure_tunnel_operation_acquire(submit_operation_task->operation); + + if (status != AWS_TASK_STATUS_RUN_READY) { + goto error; + } + + /* + * If we're offline and this operation doesn't meet the requirements of the offline queue retention policy, + * fail it immediately. + */ + struct aws_secure_tunnel *secure_tunnel = submit_operation_task->secure_tunnel; + struct aws_secure_tunnel_operation *operation = submit_operation_task->operation; + if (!secure_tunnel->isConnected) { + completion_error_code = AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_OPERATION_FAILED_DUE_TO_DISCONNECTION; + goto error; + } + + /* Steve TODO check stream id here? */ + /* newly-submitted operations must have a 0 packet id */ + // aws_secure_tunnel_operation_set_packet_id(submit_operation_task->operation, 0); + + s_enqueue_operation_back(submit_operation_task->secure_tunnel, submit_operation_task->operation); + // aws_secure_tunnel_client_statistics_change_operation_statistic_state( + // submit_operation_task->client, submit_operation_task->operation, AWS_SECURE_TUNNEL_OSS_INCOMPLETE); + + goto done; + +error: + + aws_ref_count_release(&operation->ref_count); + +done: + + aws_secure_tunnel_operation_release(submit_operation_task->operation); + aws_secure_tunnel_release(submit_operation_task->secure_tunnel); + + aws_mem_release(submit_operation_task->allocator, submit_operation_task); +} + +static int s_submit_operation(struct aws_secure_tunnel *secure_tunnel, struct aws_secure_tunnel_operation *operation) { + struct aws_secure_tunnel_submit_operation_task *submit_task = + aws_mem_calloc(secure_tunnel->allocator, 1, sizeof(struct aws_secure_tunnel_submit_operation_task)); + if (submit_task == NULL) { + return AWS_OP_ERR; + } + + aws_task_init( + &submit_task->task, s_secure_tunnel_submit_operation_task_fn, submit_task, "SecureTunnelSubmitOperation"); + submit_task->allocator = secure_tunnel->allocator; + submit_task->secure_tunnel = aws_secure_tunnel_acquire(secure_tunnel); + submit_task->operation = operation; + + aws_event_loop_schedule_task_now(secure_tunnel->loop, &submit_task->task); + + return AWS_OP_SUCCESS; +} + +int aws_secure_tunnel_bind_stream_id( + struct aws_secure_tunnel *secure_tunnel, + struct aws_secure_tunnel_operation_data *operation) { + + return AWS_OP_ERR; +} + +static void s_secure_tunnel_destroy(void *user_data) { + struct aws_secure_tunnel *secure_tunnel = user_data; + + aws_secure_tunneling_on_termination_complete_fn *on_termination_complete = NULL; + void *termination_complete_user_data = NULL; + if (secure_tunnel->options != NULL) { + on_termination_complete = secure_tunnel->options->on_termination_complete; + termination_complete_user_data = secure_tunnel->options->user_data; + } + + /* Clean up pending operations */ + AWS_ASSERT(secure_tunnel->current_operation == NULL); + s_complete_operation_list(secure_tunnel, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_SECURE_TUNNEL_TERMINATED); + + /* Clean up all memory */ + aws_secure_tunnel_options_storage_destroy(secure_tunnel->config); + aws_byte_buf_clean_up(&secure_tunnel->received_data); + 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); + + if (on_termination_complete != NULL) { + (*on_termination_complete)(termination_complete_user_data); + } +} + +/********************************************************************************************************************* + * API Calls + ********************************************************************************************************************/ + +struct aws_secure_tunnel *aws_secure_tunnel_new(const struct aws_secure_tunnel_options *options) { + fprintf(stdout, "\nNEW SECURE TUNNEL\n"); struct aws_secure_tunnel *secure_tunnel = aws_mem_calloc(options->allocator, 1, sizeof(struct aws_secure_tunnel)); - secure_tunnel->alloc = options->allocator; + secure_tunnel->allocator = options->allocator; aws_ref_count_init(&secure_tunnel->ref_count, secure_tunnel, s_secure_tunnel_destroy); + aws_linked_list_init(&secure_tunnel->queued_operations); + secure_tunnel->current_operation = NULL; + /* store options */ - secure_tunnel->options_storage = aws_secure_tunnel_options_storage_new(options); - if (secure_tunnel->options_storage == NULL) { + secure_tunnel->config = aws_secure_tunnel_options_storage_new(options); + if (secure_tunnel->config == NULL) { + goto error; + } + secure_tunnel->config->secure_tunnel = secure_tunnel; + // secure_tunnel->options = &secure_tunnel->config->options; + + /* all secure tunnel activity will take place on this event loop */ + secure_tunnel->loop = aws_event_loop_group_get_next_loop(secure_tunnel->options->bootstrap->event_loop_group); + if (secure_tunnel->loop == NULL) { goto error; } - secure_tunnel->options = &secure_tunnel->options_storage->options; /* tls_ctx */ + struct aws_tls_ctx_options tls_ctx_opt; + AWS_ZERO_STRUCT(tls_ctx_opt); aws_tls_ctx_options_init_default_client(&tls_ctx_opt, options->allocator); if (options->root_ca != NULL) { @@ -666,10 +1905,18 @@ struct aws_secure_tunnel *aws_secure_tunnel_new(const struct aws_secure_tunnel_o aws_tls_ctx_options_clean_up(&tls_ctx_opt); /* Setup vtables here. */ + secure_tunnel->vtable.get_current_time_fn = s_aws_high_res_clock_get_ticks_proxy; + secure_tunnel->vtable.channel_shutdown_fn = aws_channel_shutdown; + secure_tunnel->vtable.websocket_connect_fn = aws_websocket_client_connect, + secure_tunnel->vtable.client_bootstrap_new_socket_channel_fn = aws_client_bootstrap_new_socket_channel; + secure_tunnel->vtable.http_proxy_new_socket_channel_fn = aws_http_proxy_new_socket_channel; + secure_tunnel->vtable.connect = s_secure_tunneling_connect; secure_tunnel->vtable.close = s_secure_tunneling_close; secure_tunnel->vtable.send_data = s_secure_tunneling_send_data; + secure_tunnel->vtable.send_data_v2 = s_secure_tunneling_send_data_v2; secure_tunnel->vtable.send_stream_start = s_secure_tunneling_send_stream_start; + secure_tunnel->vtable.send_stream_start_v2 = s_secure_tunneling_send_stream_start_v2; secure_tunnel->vtable.send_stream_reset = s_secure_tunneling_send_stream_reset; secure_tunnel->websocket_vtable.client_connect = aws_websocket_client_connect; @@ -678,7 +1925,10 @@ struct aws_secure_tunnel *aws_secure_tunnel_new(const struct aws_secure_tunnel_o secure_tunnel->websocket_vtable.release = aws_websocket_release; secure_tunnel->handshake_request = NULL; - secure_tunnel->stream_id = INVALID_STREAM_ID; + secure_tunnel->config->stream_id = INVALID_STREAM_ID; + secure_tunnel->config->service_id_1_stream_id = INVALID_STREAM_ID; + secure_tunnel->config->service_id_2_stream_id = INVALID_STREAM_ID; + secure_tunnel->config->service_id_3_stream_id = INVALID_STREAM_ID; secure_tunnel->websocket = NULL; /* TODO: Release this buffer when there is no data to hold */ @@ -693,36 +1943,22 @@ struct aws_secure_tunnel *aws_secure_tunnel_new(const struct aws_secure_tunnel_o } struct aws_secure_tunnel *aws_secure_tunnel_acquire(struct aws_secure_tunnel *secure_tunnel) { - aws_ref_count_acquire(&secure_tunnel->ref_count); + if (secure_tunnel != NULL) { + aws_ref_count_acquire(&secure_tunnel->ref_count); + } + return secure_tunnel; } void aws_secure_tunnel_release(struct aws_secure_tunnel *secure_tunnel) { - if (secure_tunnel == NULL) { - return; + if (secure_tunnel != NULL) { + aws_ref_count_release(&secure_tunnel->ref_count); } - aws_ref_count_release(&secure_tunnel->ref_count); } -static void s_secure_tunnel_destroy(void *user_data) { - struct aws_secure_tunnel *secure_tunnel = user_data; - - aws_secure_tunneling_on_termination_complete_fn *on_termination_complete = NULL; - void *termination_complete_user_data = NULL; - if (secure_tunnel->options != NULL) { - on_termination_complete = secure_tunnel->options->on_termination_complete; - termination_complete_user_data = secure_tunnel->options->user_data; - } - - aws_secure_tunnel_options_storage_destroy(secure_tunnel->options_storage); - aws_byte_buf_clean_up(&secure_tunnel->received_data); - aws_tls_connection_options_clean_up(&secure_tunnel->tls_con_opt); - aws_tls_ctx_release(secure_tunnel->tls_ctx); - aws_mem_release(secure_tunnel->alloc, secure_tunnel); - - if (on_termination_complete != NULL) { - (*on_termination_complete)(termination_complete_user_data); - } +// Steve TODO this isn't required on destination side, remove after testing +int aws_secure_tunnel_get_connection_error_code(struct aws_secure_tunnel *secure_tunnel) { + return secure_tunnel->connection_error_code; } int aws_secure_tunnel_connect(struct aws_secure_tunnel *secure_tunnel) { @@ -737,10 +1973,25 @@ int aws_secure_tunnel_send_data(struct aws_secure_tunnel *secure_tunnel, const s return secure_tunnel->vtable.send_data(secure_tunnel, data); } +int aws_secure_tunnel_send_data_v2( + struct aws_secure_tunnel *secure_tunnel, + const struct aws_secure_tunnel_message_data_view *data_options) { + return secure_tunnel->vtable.send_data_v2(secure_tunnel, data_options); +} + +/* Steve Todo unecessary for destination devices as they never attempt to start a stream. Can be depricated */ int aws_secure_tunnel_stream_start(struct aws_secure_tunnel *secure_tunnel) { return secure_tunnel->vtable.send_stream_start(secure_tunnel); } +/* Steve Todo can remove after testing. Not required for Destination devices */ +int aws_secure_tunnel_stream_start_v2( + struct aws_secure_tunnel *secure_tunnel, + const struct aws_byte_cursor *service_id_data) { + return secure_tunnel->vtable.send_stream_start_v2(secure_tunnel, service_id_data); +} + +/* Steve Todo a V2/V3 version is required to reset on a failed stream start attempt */ int aws_secure_tunnel_stream_reset(struct aws_secure_tunnel *secure_tunnel) { return secure_tunnel->vtable.send_stream_reset(secure_tunnel); } diff --git a/source/secure_tunneling_operations.c b/source/secure_tunneling_operations.c new file mode 100644 index 00000000..c7a200fa --- /dev/null +++ b/source/secure_tunneling_operations.c @@ -0,0 +1,324 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include +#include +#include +#include +#include + +#define MAX_PAYLOAD_SIZE 64512 +/********************************************************************************************************************* + * Operation base + ********************************************************************************************************************/ + +struct aws_secure_tunnel_operation *aws_secure_tunnel_operation_acquire(struct aws_secure_tunnel_operation *operation) { + if (operation == NULL) { + return NULL; + } + + aws_ref_count_acquire(&operation->ref_count); + + return operation; +} + +struct aws_secure_tunnel_operation *aws_secure_tunnel_operation_release(struct aws_secure_tunnel_operation *operation) { + if (operation != NULL) { + aws_ref_count_release(&operation->ref_count); + } + + return NULL; +} + +void *aws_secure_tunnel_operation_complete( + struct aws_secure_tunnel_operation *operation, + int error_code, + const void *associated_view) { + AWS_FATAL_ASSERT(operation->vtable != NULL); + + if (operation->vtable->aws_secure_tunnel_operation_completion_fn != NULL) { + (*operation->vtable->aws_secure_tunnel_operation_completion_fn)(operation, error_code, associated_view); + } +} + +/* STEVE TODO set the stream_id based on the service id */ +void aws_secure_tunnel_operation_set_stream_id( + struct aws_secure_tunnel_operation *operation, + struct aws_secure_tunnel *secure_tunnel) { + AWS_FATAL_ASSERT(operation->vtable != NULL); + if (operation->vtable->aws_secure_tunnel_operation_set_stream_id_fn != NULL) { + (*operation->vtable->aws_secure_tunnel_operation_set_stream_id_fn)(operation, secure_tunnel); + } +} + +int32_t aws_secure_tunnel_operation_get_stream_id(const struct aws_secure_tunnel_operation *operation) { + AWS_FATAL_ASSERT(operation->vtable != NULL); + if (operation->vtable->aws_secure_tunnel_operation_get_stream_id_address_fn != NULL) { + int32_t *stream_id_ptr = (*operation->vtable->aws_secure_tunnel_operation_get_stream_id_address_fn)(operation); + if (stream_id_ptr != NULL) { + return *stream_id_ptr; + } + } + + return 0; +} + +int32_t *aws_secure_tunnel_operation_get_stream_id_address(const struct aws_secure_tunnel_operation *operation) { + AWS_FATAL_ASSERT(operation->vtable != NULL); + if (operation->vtable->aws_secure_tunnel_operation_get_stream_id_address_fn != NULL) { + return (*operation->vtable->aws_secure_tunnel_operation_get_stream_id_address_fn)(operation); + } + + return NULL; +} + +// static struct aws_secure_tunnel_operation_vtable s_empty_operation_vtable = { +// .aws_secure_tunnel_operation_completion_fn = NULL, +// .aws_secure_tunnel_operation_set_stream_id_fn = NULL, +// .aws_secure_tunnel_operation_get_stream_id_address_fn = NULL, +// }; + +/********************************************************************************************************************* + * data + ********************************************************************************************************************/ + +static void s_aws_secure_tunnel_operation_data_set_stream_id( + struct aws_secure_tunnel_operation *operation, + struct aws_secure_tunnel *secure_tunnel) { + + struct aws_secure_tunnel_operation_data *data_op = operation->impl; + int32_t stream_id = 0; + + struct aws_secure_tunnel_message_data_storage *data_storage = &data_op->options_storage; + + if (data_storage->service_id.len > 0) { + struct aws_string *service_id = NULL; + service_id = aws_string_new_from_cursor(secure_tunnel->allocator, &data_storage->service_id); + + if (secure_tunnel->config->service_id_1 != NULL && + aws_string_compare(secure_tunnel->config->service_id_1, service_id) == 0) { + stream_id = secure_tunnel->config->service_id_1_stream_id; + } else if ( + secure_tunnel->config->service_id_2 != NULL && + aws_string_compare(secure_tunnel->config->service_id_2, service_id) == 0) { + stream_id = secure_tunnel->config->service_id_2_stream_id; + } else if ( + secure_tunnel->config->service_id_3 != NULL && + aws_string_compare(secure_tunnel->config->service_id_3, service_id) == 0) { + stream_id = secure_tunnel->config->service_id_3_stream_id; + } + } else { + stream_id = secure_tunnel->config->stream_id; + } + + data_op->options_storage.storage_view.stream_id = stream_id; +} + +static int32_t *s_aws_secure_tunnel_operation_data_get_stream_id_address_fn( + const struct aws_secure_tunnel_operation *operation) { + struct aws_secure_tunnel_operation_data *data_op = operation->impl; + return &data_op->options_storage.storage_view.stream_id; +} + +static struct aws_secure_tunnel_operation_vtable s_data_operation_vtable = { + .aws_secure_tunnel_operation_set_stream_id_fn = s_aws_secure_tunnel_operation_data_set_stream_id, + .aws_secure_tunnel_operation_get_stream_id_address_fn = s_aws_secure_tunnel_operation_data_get_stream_id_address_fn, +}; + +int aws_secure_tunnel_message_data_view_validate(const struct aws_secure_tunnel_message_data_view *data_view) { + if (data_view == NULL) { + AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "null DATA message options"); + return aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_OPTIONS_VALIDATION); + } + + if (data_view->stream_id != 0) { + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: aws_secure_tunnel_message_data_view - stream id must be 0", + (void *)data_view); + return aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_OPTIONS_VALIDATION); + } + + if (data_view->service_id.len <= 0) { + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: aws_secure_tunnel_message_data_view - Service Id cannot be empty", + (void *)data_view); + return aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_OPTIONS_VALIDATION); + } + + if (data_view->payload.len > MAX_PAYLOAD_SIZE) { + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: aws_secure_tunnel_message_data_view - payload too long", + (void *)data_view); + return aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_OPTIONS_VALIDATION); + } + + return AWS_OP_SUCCESS; +} + +void aws_secure_tunnel_message_data_view_log( + const struct aws_secure_tunnel_message_data_view *data_view, + enum aws_log_level level) { + + struct aws_logger *temp_logger = aws_logger_get(); + if (temp_logger == NULL || + temp_logger->vtable->get_log_level(temp_logger, AWS_LS_IOTDEVICE_SECURE_TUNNELING) < level) { + return; + } + + AWS_LOGF( + level, + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "(%p) aws_secure_tunnel_message_data_view stream id set to %d", + (void *)data_view, + (int)data_view->stream_id); + + AWS_LOGF( + level, + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: aws_secure_tunnel_message_data_view service id set to \"" PRInSTR "\"", + (void *)data_view, + AWS_BYTE_CURSOR_PRI(data_view->service_id)); +} + +static size_t s_aws_secure_tunnel_message_data_compute_storage_size( + const struct aws_secure_tunnel_message_data_view *data_view) { + size_t storage_size = data_view->payload.len; + storage_size += data_view->service_id.len; + + return storage_size; +} + +int aws_secure_tunnel_message_data_storage_init( + struct aws_secure_tunnel_message_data_storage *data_storage, + struct aws_allocator *allocator, + const struct aws_secure_tunnel_message_data_view *data_options) { + + AWS_ZERO_STRUCT(*data_storage); + size_t storage_capacity = s_aws_secure_tunnel_message_data_compute_storage_size(data_options); + if (aws_byte_buf_init(&data_storage->storage, allocator, storage_capacity)) { + return AWS_OP_ERR; + } + + struct aws_secure_tunnel_message_data_view *storage_view = &data_storage->storage_view; + + storage_view->stream_id = data_options->stream_id; + + storage_view->service_id = data_options->service_id; + if (aws_byte_buf_append_and_update(&data_storage->storage, &storage_view->service_id)) { + return AWS_OP_ERR; + } + + storage_view->payload = data_options->payload; + if (aws_byte_buf_append_and_update(&data_storage->storage, &storage_view->payload)) { + return AWS_OP_ERR; + } + + return AWS_OP_SUCCESS; +} + +void aws_secure_tunnel_message_data_storage_clean_up(struct aws_secure_tunnel_message_data_storage *data_storage) { + aws_byte_buf_clean_up(&data_storage->storage); +} + +static void s_destroy_operation_data(void *object) { + if (object == NULL) { + return; + } + + struct aws_secure_tunnel_operation_data *data_op = object; + + aws_secure_tunnel_message_data_storage_clean_up(&data_op->options_storage); + + aws_mem_release(data_op->allocator, data_op); +} + +struct aws_secure_tunnel_operation_data *aws_secure_tunnel_operation_data_new( + struct aws_allocator *allocator, + const struct aws_secure_tunnel *secure_tunnel, + const struct aws_secure_tunnel_message_data_view *data_options) { + (void)secure_tunnel; + AWS_PRECONDITION(allocator != NULL); + AWS_PRECONDITION(data_options != NULL); + + if (aws_secure_tunnel_message_data_view_validate(data_options)) { + return NULL; + } + + struct aws_secure_tunnel_operation_data *data_op = + aws_mem_calloc(allocator, 1, sizeof(struct aws_secure_tunnel_operation_data)); + if (data_op == NULL) { + return NULL; + } + + data_op->allocator = allocator; + data_op->base.vtable = &s_data_operation_vtable; + aws_ref_count_init(&data_op->base.ref_count, data_op, s_destroy_operation_data); + data_op->base.impl = data_op; + + if (aws_secure_tunnel_message_data_storage_init(&data_op->options_storage, allocator, data_options)) { + goto error; + } + + data_op->base.message_view = &data_op->options_storage.storage_view; + + return data_op; + +error: + + aws_secure_tunnel_operation_release(&data_op->base); + + return NULL; +} +const char *aws_secure_tunnel_operation_type_to_c_string(enum aws_secure_tunnel_operation_type operation_type) { + switch (operation_type) { + case AWS_STOT_NONE: + return "NONE"; + case AWS_STOT_CONNECT: + return "CONNECT"; + case AWS_STOT_PING: + return "PING"; + case AWS_STOT_DATA: + return "DATA"; + case AWS_STOT_STREAM_RESET: + return "STREAM RESET"; + case AWS_STOT_STREAM_START: + return "STREAM START"; + default: + return "UNKNOWN"; + } +} + +// int aws_secure_tunnel_packet_data_storage_init_from_external_storage( +// struct aws_secure_tunnel_packet_data_storage *data_storage, +// struct aws_allocator *allocator) { +// AWS_ZERO_STRUCT(*data_storage); + +// if (aws_secure_tunnel_user_property_set_init(&publish_storage->user_properties, allocator)) { +// return AWS_OP_ERR; +// } + +// if (aws_array_list_init_dynamic(&publish_storage->subscription_identifiers, allocator, 0, sizeof(uint32_t))) { +// return AWS_OP_ERR; +// } + +// return AWS_OP_SUCCESS; +// } + +// AWS_IOTDEVICE_API +// int aws_secure_tunnel_packet_stream_storage_init( +// struct aws_secure_tunnel_packet_stream_storage *stream_storage, +// struct aws_allocator *allocator, +// const struct aws_secure_tunnel_packet_stream_view *stream_options); + +// AWS_IOTDEVICE_API +// int aws_secure_tunnel_packet_stream_storage_init_from_external_storage( +// struct aws_secure_tunnel_packet_stream_storage *stream_storage, +// struct aws_allocator *allocator); + +// AWS_IOTDEVICE_API +// int aws_secure_tunnel_packet_stream_storage_clean_up(struct aws_secure_tunnel_packet_stream_storage *stream_storage); diff --git a/source/serializer.c b/source/serializer.c index 74c2aaa3..e98a688f 100644 --- a/source/serializer.c +++ b/source/serializer.c @@ -131,6 +131,7 @@ static int s_iot_st_get_varint_size(size_t value, size_t *encode_size) { } static int s_iot_st_compute_message_length(const struct aws_iot_st_msg *message, size_t *message_length) { + fprintf(stdout, "\ns_iot_st_compute_message_length()\ntype: %d\n", message->type); size_t local_length = 0; /* @@ -151,6 +152,7 @@ static int s_iot_st_compute_message_length(const struct aws_iot_st_msg *message, } local_length += (1 + stream_id_length); + fprintf(stdout, "adding stream_id:%d total:%zu\n", message->stream_id, local_length); } if (message->ignorable != 0) { @@ -159,24 +161,35 @@ static int s_iot_st_compute_message_length(const struct aws_iot_st_msg *message, * 1 byte ignorable varint */ local_length += 2; + fprintf(stdout, "adding ignorable total:%zu\n", local_length); } if (message->payload.len != 0) { /* * 1 byte key + * 1-4 byte payload length varint * n bytes payload.len */ - - local_length += (1 + message->payload.len); + size_t payload_length = 0; + if (s_iot_st_get_varint_size((uint32_t)message->payload.len, &payload_length)) { + return AWS_OP_ERR; + } + local_length += (1 + message->payload.len + payload_length); + fprintf(stdout, "adding message total:%zu\n", local_length); } if (message->service_id.len != 0) { /* * 1 byte key + * 1-4 byte payload length varint * n bytes service_id.len */ - - local_length += (1 + message->service_id.len); + size_t service_id_length = 0; + if (s_iot_st_get_varint_size((uint32_t)message->service_id.len, &service_id_length)) { + return AWS_OP_ERR; + } + local_length += (1 + message->service_id.len + service_id_length); + fprintf(stdout, "adding service_id total:%zu\n", local_length); } if (message->connection_id != 0) { @@ -191,6 +204,7 @@ static int s_iot_st_compute_message_length(const struct aws_iot_st_msg *message, } local_length += (1 + connection_id_length); + fprintf(stdout, "adding connection_id total:%zu\n", local_length); } *message_length = local_length; @@ -201,6 +215,7 @@ int aws_iot_st_msg_serialize_from_struct( struct aws_byte_buf *buffer, struct aws_allocator *allocator, struct aws_iot_st_msg message) { + fprintf(stdout, "\naws_iot_st_msg_serialize_from_struct()\n"); size_t message_total_length = 0; if (s_iot_st_compute_message_length(&message, &message_total_length)) { @@ -217,35 +232,42 @@ int aws_iot_st_msg_serialize_from_struct( } } + fprintf(stdout, "message type encoded. buf length: %zu\n", buffer->len); + if (message.stream_id != 0) { if (s_iot_st_encode_stream_id(message.stream_id, buffer)) { goto cleanup; } } + fprintf(stdout, "stream id encoded. buf length: %zu\n", buffer->len); if (message.ignorable != 0) { if (s_iot_st_encode_ignorable(message.ignorable, buffer)) { goto cleanup; } } + fprintf(stdout, "ignorable encoded. buf length: %zu\n", buffer->len); if (message.payload.len != 0) { if (s_iot_st_encode_payload(&message.payload, buffer)) { goto cleanup; } } + fprintf(stdout, "payload encoded. buf length: %zu\n", buffer->len); if (message.service_id.len != 0) { if (s_iot_st_encode_service_id(&message.service_id, buffer)) { goto cleanup; } } + fprintf(stdout, "service id encoded. buf length: %zu\n", buffer->len); if (message.connection_id != 0) { if (s_iot_st_encode_connection_id(message.connection_id, buffer)) { goto cleanup; } } + fprintf(stdout, "connection id encoded. buf length: %zu\n", buffer->len); AWS_RETURN_ERROR_IF2(buffer->capacity < AWS_IOT_ST_MAX_MESSAGE_SIZE, AWS_ERROR_INVALID_BUFFER_SIZE); return AWS_OP_SUCCESS; @@ -290,6 +312,7 @@ int aws_iot_st_msg_deserialize_from_cursor( struct aws_iot_st_msg *message, struct aws_byte_cursor *cursor, struct aws_allocator *allocator) { + fprintf(stdout, "\naws_iot_st_msg_deserialize_from_cursor()"); 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; @@ -378,3 +401,34 @@ int aws_iot_st_msg_deserialize_from_cursor( aws_byte_buf_clean_up(&message->service_id); return AWS_OP_ERR; } + +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 "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"; + } +} diff --git a/tests/secure_tunneling_tests.c b/tests/secure_tunneling_tests.c index 627202e4..6f601b6b 100644 --- a/tests/secure_tunneling_tests.c +++ b/tests/secure_tunneling_tests.c @@ -128,14 +128,14 @@ int s_mock_aws_websocket_send_frame( /* Deserialize the wire format to obtain original payload. */ struct aws_iot_st_msg message; - int rc = aws_iot_st_msg_deserialize_from_cursor(&message, &cursor, s_test_context.secure_tunnel->alloc); + int rc = aws_iot_st_msg_deserialize_from_cursor(&message, &cursor, s_test_context.secure_tunnel->allocator); ASSERT_INT_EQUALS(AWS_OP_SUCCESS, rc); s_mock_aws_websocket_send_frame_payload_len += message.payload.len; aws_byte_buf_clean_up(&message.payload); /* Deallocate memory for the buffer holding the wire protocol data and the tunnel context. */ aws_byte_buf_clean_up(buf); - aws_mem_release(s_test_context.secure_tunnel->alloc, pair); + aws_mem_release(s_test_context.secure_tunnel->allocator, pair); return AWS_OP_SUCCESS; } @@ -317,9 +317,10 @@ static int s_test_sent_data( struct aws_byte_cursor cur = aws_byte_cursor_from_c_str(expected_payload); struct aws_websocket_send_frame_options frame_options; - ASSERT_INT_EQUALS( - AWS_OP_SUCCESS, - secure_tunneling_init_send_frame(&frame_options, test_context->secure_tunnel, &cur, message.type)); + // Steve TODO fix secure tunnel tests + // ASSERT_INT_EQUALS( + // AWS_OP_SUCCESS, + // secure_tunneling_init_send_frame(&frame_options, test_context->secure_tunnel, &cur, message.type)); ASSERT_INT_EQUALS(serialized_st_msg.len + prefix_bytes, frame_options.payload_length); @@ -379,7 +380,7 @@ static int s_secure_tunneling_handle_stream_start_test(struct aws_allocator *all s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); ASSERT_TRUE(s_on_stream_start_called); - ASSERT_INT_EQUALS(STREAM_ID, test_context->secure_tunnel->stream_id); + ASSERT_INT_EQUALS(STREAM_ID, test_context->secure_tunnel->config->stream_id); ASSERT_UINT_EQUALS(0, test_context->secure_tunnel->received_data.len); return AWS_OP_SUCCESS; @@ -416,7 +417,7 @@ static int s_secure_tunneling_handle_data_receive_test(struct aws_allocator *all s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); ASSERT_TRUE(s_on_data_receive_correct_payload); - ASSERT_INT_EQUALS(STREAM_ID, test_context->secure_tunnel->stream_id); + ASSERT_INT_EQUALS(STREAM_ID, test_context->secure_tunnel->config->stream_id); ASSERT_UINT_EQUALS(0, test_context->secure_tunnel->received_data.len); return AWS_OP_SUCCESS; @@ -452,7 +453,7 @@ static int s_secure_tunneling_handle_stream_reset_test(struct aws_allocator *all s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); ASSERT_TRUE(s_on_stream_reset_called); - ASSERT_INT_EQUALS(INVALID_STREAM_ID, test_context->secure_tunnel->stream_id); + ASSERT_INT_EQUALS(INVALID_STREAM_ID, test_context->secure_tunnel->config->stream_id); ASSERT_UINT_EQUALS(0, test_context->secure_tunnel->received_data.len); return AWS_OP_SUCCESS; @@ -488,7 +489,7 @@ static int s_secure_tunneling_handle_session_reset_test(struct aws_allocator *al s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); ASSERT_TRUE(s_on_session_reset_called); - ASSERT_INT_EQUALS(INVALID_STREAM_ID, test_context->secure_tunnel->stream_id); + ASSERT_INT_EQUALS(INVALID_STREAM_ID, test_context->secure_tunnel->config->stream_id); ASSERT_UINT_EQUALS(0, test_context->secure_tunnel->received_data.len); return AWS_OP_SUCCESS; @@ -517,7 +518,7 @@ static int s_secure_tunneling_handle_session_reset_no_stream_test(struct aws_all s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); ASSERT_FALSE(s_on_session_reset_called); - ASSERT_INT_EQUALS(INVALID_STREAM_ID, test_context->secure_tunnel->stream_id); + ASSERT_INT_EQUALS(INVALID_STREAM_ID, test_context->secure_tunnel->config->stream_id); ASSERT_UINT_EQUALS(0, test_context->secure_tunnel->received_data.len); return AWS_OP_SUCCESS; @@ -599,7 +600,7 @@ static int s_secure_tunneling_handle_send_data(struct aws_allocator *allocator, struct secure_tunneling_test_context *test_context = ctx; test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_SOURCE_MODE; - test_context->secure_tunnel->stream_id = expected_stream_id; + test_context->secure_tunnel->config->stream_id = expected_stream_id; s_test_sent_data(test_context, expected_payload, expected_stream_id, prefix_bytes, type); @@ -626,7 +627,7 @@ static int s_secure_tunneling_handle_send_data_stream_start(struct aws_allocator struct secure_tunneling_test_context *test_context = ctx; test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_SOURCE_MODE; - test_context->secure_tunnel->stream_id = expected_stream_id; + test_context->secure_tunnel->config->stream_id = expected_stream_id; s_test_sent_data(test_context, expected_payload, expected_stream_id, prefix_bytes, type); @@ -653,7 +654,7 @@ static int s_secure_tunneling_handle_send_data_stream_reset(struct aws_allocator struct secure_tunneling_test_context *test_context = ctx; test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_SOURCE_MODE; - test_context->secure_tunnel->stream_id = expected_stream_id; + test_context->secure_tunnel->config->stream_id = expected_stream_id; s_test_sent_data(test_context, expected_payload, expected_stream_id, prefix_bytes, type); From da4a6bb32d57336aba0a4b8fd305326f240d8562 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 5 Dec 2022 11:36:08 -0800 Subject: [PATCH 04/69] WIP on hold to work on mqtt5 canary --- include/aws/iotdevice/iotdevice.h | 1 + .../iotdevice/private/secure_tunneling_impl.h | 3 +- include/aws/iotdevice/secure_tunneling.h | 13 + source/secure_tunneling.c | 261 ++++++++++++++++-- 4 files changed, 256 insertions(+), 22 deletions(-) diff --git a/include/aws/iotdevice/iotdevice.h b/include/aws/iotdevice/iotdevice.h index 0e70148c..bf3984a5 100644 --- a/include/aws/iotdevice/iotdevice.h +++ b/include/aws/iotdevice/iotdevice.h @@ -31,6 +31,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_TERMINATED, AWS_ERROR_END_IOTDEVICE_RANGE = AWS_ERROR_ENUM_END_RANGE(AWS_C_IOTDEVICE_PACKAGE_ID), }; diff --git a/include/aws/iotdevice/private/secure_tunneling_impl.h b/include/aws/iotdevice/private/secure_tunneling_impl.h index 8dd2e97d..3c98f9f2 100644 --- a/include/aws/iotdevice/private/secure_tunneling_impl.h +++ b/include/aws/iotdevice/private/secure_tunneling_impl.h @@ -223,7 +223,7 @@ struct aws_secure_tunnel { struct aws_tls_ctx *tls_ctx; struct aws_tls_connection_options tls_con_opt; - struct aws_secure_tunnel_vtable vtable; + const struct aws_secure_tunnel_vtable vtable; struct aws_websocket_vtable websocket_vtable; struct aws_ref_count ref_count; @@ -310,6 +310,7 @@ struct aws_secure_tunnel { bool in_service; struct aws_linked_list queued_operations; + struct aws_linked_list write_completion_operations; struct aws_secure_tunnel_operation *current_operation; /* diff --git a/include/aws/iotdevice/secure_tunneling.h b/include/aws/iotdevice/secure_tunneling.h index 10082922..dca064bc 100644 --- a/include/aws/iotdevice/secure_tunneling.h +++ b/include/aws/iotdevice/secure_tunneling.h @@ -83,6 +83,19 @@ struct aws_secure_tunnel_options { void *user_data; }; +/** + * Signature of callback to invoke when a DISCONNECT is fully written to the socket (or fails to be) + */ +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; +} + /* deprecated: "_config" is renamed "_options" for consistency with similar code in the aws-c libraries */ #define aws_secure_tunneling_connection_config aws_secure_tunnel_options diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index db0b858f..5a04cdaa 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -35,9 +35,9 @@ static int s_secure_tunneling_send( const struct aws_byte_cursor *payload_data, const struct aws_byte_cursor *service_id_data, enum aws_secure_tunnel_message_type type); -static int s_websocket_connect(struct aws_secure_tunnel *secure_tunnel) { - return 0; -} + +static int s_websocket_connect(struct aws_secure_tunnel *secure_tunnel){}; + static int s_submit_operation(struct aws_secure_tunnel *secure_tunnel, struct aws_secure_tunnel_operation *operation); static void s_reevaluate_service_task(struct aws_secure_tunnel *secure_tunnel); @@ -545,9 +545,7 @@ static void s_secure_tunnel_shutdown( secure_tunnel->slot = NULL; } - /* Fail all queued operations */ - s_complete_operation_list( - secure_tunnel, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_OPERATION_FAILED_DUE_TO_OFFLINE_QUEUE_POLICY); + aws_secure_tunnel_on_disconnection_update_operational_state(secure_tunnel); if (secure_tunnel->desired_state == AWS_STS_CONNECTED) { s_change_current_state(secure_tunnel, AWS_STS_PENDING_RECONNECT); @@ -701,7 +699,7 @@ struct aws_secure_tunnel_websocket_transform_complete_task { struct aws_secure_tunnel *secure_tunnel; int error_code; struct aws_http_message *handshake; -} +}; void s_websocket_transform_complete_task_fn(struct aws_task *task, void *arg, enum aws_task_status status) { (void)task; @@ -964,12 +962,16 @@ static void s_aws_secure_tunnel_shutdown_channel(struct aws_secure_tunnel *secur /********************************************************************************************************************* * State Related ********************************************************************************************************************/ -static void s_complete_operation_list(struct aws_secure_tunnel *secure_tunnel, int error_code); +static void s_complete_operation_list( + struct aws_secure_tunnel *secure_tunnel, + struct aws_linked_list *operation_list, + int error_code); static void s_change_current_state_to_stopped(struct aws_secure_tunnel *secure_tunnel) { secure_tunnel->current_state = AWS_STS_STOPPED; - s_complete_operation_list(secure_tunnel, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_USER_REQUESTED_STOP); + s_aws_secure_tunnel_operational_state_reset( + 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); @@ -1082,6 +1084,120 @@ static void s_change_current_state(struct aws_secure_tunnel *secure_tunnel, enum s_reevaluate_service_task(secure_tunnel); } +static bool s_is_valid_desired_state(enum aws_secure_tunnel_state desired_state) { + switch (desired_state) { + case AWS_STS_STOPPED: + case AWS_STS_CONNECTED: + case AWS_STS_TERMINATED: + return true; + default: + return false; + } +} + +static void s_change_state_task_fn(struct aws_task *task, void *arg, enum aws_task_status status) { + (void)task; + + struct aws_secure_tunnel_change_desired_state_task *change_state_task = arg; + struct aws_secure_tunnel *secure_tunnel = change_state_task->secure_tunnel; + enum aws_secure_tunnel_state desired_state = change_state_task->desired_state; + if (status != AWS_TASK_STATUS_RUN_READY) { + goto done; + } + + if (secure_tunnel->desired_state != desired_state) { + AWS_LOGF_INFO( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: changing desired secure_tunnel state from %s to %s", + (void *)secure_tunnel, + aws_secure_tunnel_state_to_c_string(secure_tunnel->desired_state), + aws_secure_tunnel_state_to_c_string(desired_state)); + + secure_tunnel->desired_state = desired_state; + + struct aws_secure_tunnel_operation_disconnect *disconnect_op = change_state_task->disconnect_operation; + if (desired_state == AWS_STS_STOPPED && disconnect_op != NULL) { + s_aws_secure_tunnel_shutdown_channel_with_disconnect( + secure_tunnel, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_USER_REQUESTED_STOP, disconnect_op); + } + + s_reevaluate_service_task(secure_tunnel); + } + +done: + + aws_secure_tunnel_operation_disconnect_release(change_state_task->disconnect_operation); + if (desired_state != AWS_STS_TERMINATED) { + aws_secure_tunnel_release(secure_tunnel); + } + + aws_mem_release(change_state_task->allocator, change_state_task); +} + +static struct aws_secure_tunnel_change_desired_state_task *s_aws_secure_tunnel_change_desired_state_task_new( + struct aws_allocator *allocator, + struct aws_secure_tunnel *secure_tunnel, + enum aws_secure_tunnel_state desired_state, + struct aws_secure_tunnel_operation_disconnect *disconnect_operation) { + + struct aws_secure_tunnel_change_desired_state_task *change_state_task = + aws_mem_calloc(allocator, 1, sizeof(struct aws_secure_tunnel_change_desired_state_task)); + if (change_state_task == NULL) { + return NULL; + } + + aws_task_init(&change_state_task->task, s_change_state_task_fn, (void *)change_state_task, "ChangeStateTask"); + change_state_task->allocator = secure_tunnel->allocator; + change_state_task->secure_tunnel = + (desired_state == AWS_STS_TERMINATED) ? secure_tunnel : aws_secure_tunnel_acquire(secure_tunnel); + change_state_task->desired_state = desired_state; + change_state_task->disconnect_operation = aws_secure_tunnel_operation_disconnect_acquire(disconnect_operation); + + return change_state_task; +} + +struct aws_secure_tunnel_change_desired_state_task { + struct aws_task task; + struct aws_allocator *allocator; + struct aws_secure_tunnel *secure_tunnel; + enum aws_secure_tunnel_state desired_state; + struct aws_secure_tunnel_operation_disconnect *disconnect_operation; +}; + +static int s_aws_secure_tunnel_change_desired_state( + struct aws_secure_tunnel *secure_tunnel, + enum aws_secure_tunnel_state desired_state, + struct aws_secure_tunnel_operation_disconnect *disconnect_operation) { + AWS_FATAL_ASSERT(secure_tunnel != NULL); + AWS_FATAL_ASSERT(secure_tunnel->loop != NULL); + AWS_FATAL_ASSERT(disconnect_operation == NULL || desired_state == AWS_STS_STOPPED); + + if (!s_is_valid_desired_state(desired_state)) { + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: invalid desired state argument %d(%s)", + (void *)secure_tunnel, + (int)desired_state, + aws_secure_tunnel_state_to_c_string(desired_state)); + + return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + } + + struct aws_secure_tunnel_change_desired_state_task *task = s_aws_secure_tunnel_change_desired_state_task_new( + secure_tunnel->allocator, secure_tunnel, desired_state, disconnect_operation); + 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 ********************************************************************************************************************/ @@ -1384,7 +1500,10 @@ static int s_secure_tunneling_send( * Operations ********************************************************************************************************************/ -int aws_secure_tunnel_get_stream_id_for_service(struct aws_secure_tunnel *secure_tunnel) {} +int aws_secure_tunnel_get_stream_id_for_service(struct aws_secure_tunnel *secure_tunnel) { + /* Todo Steve Implement this */ + (void)secure_tunnel; +} int aws_secure_tunnel_operation_bind_stream_id( struct aws_secure_tunnel_operation *operation, @@ -1453,7 +1572,82 @@ static void s_aws_secure_tunnel_on_socket_write_completion_secure_tunnel_connect s_aws_secure_tunnel_shutdown_channel(secure_tunnel, error_code); return; } -} // Steve TODO THIS IS WHERE I LEFT OFF BEFORE MQTT5 MIGRATION. + + s_reevaluate_service_task(secure_tunnel); +} + +static void s_awssecure_tunnel_on_socket_write_completion_connected( + struct aws_secure_tunnel *secure_tunnel, + int error_code) { + if (error_code != AWS_ERROR_SUCCESS) { + s_aws_secure_tunnel_shutdown_channel(secure_tunnel, error_code); + return; + } + + s_reevaluate_service_task(secure_tunnel); +} + +static void s_aws_secure_tunnel_on_socket_write_completion( + struct aws_channel *channel, + struct aws_io_message *message, + int error_code, + void *user_data) { + + (void)channel; + (void)message; + + struct aws_secure_tunnel *secure_tunnel = user_data; + secure_tunnel->pending_write_completion = false; + + if (error_code != AWS_ERROR_SUCCESS) { + AWS_LOGF_INFO( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: socket write completion invoked with error %d(%s)", + (void *)secure_tunnel, + error_code, + aws_error_debug_str(error_code)); + } + + switch (secure_tunnel->current_state) { + case AWS_STS_SECURE_TUNNEL_CONNECT: + s_aws_secure_tunnel_on_socket_write_completion_secure_tunnel_connect(secure_tunnel, error_code); + break; + + case AWS_STS_CONNECTED: + s_aws_secure_tunnel_on_socket_write_completion_connected(secure_tunnel, error_code); + break; + + case AWS_STS_CLEAN_DISCONNECT: + /* the CONNECTED callback works just fine for CLEAN_DISCONNECT */ + s_aws_secure_tunnel_on_socket_write_completion_connected(secure_tunnel, error_code); + break; + + default: + break; + } + + s_complete_operation_list(secure_tunnel, &secure_tunnel->write_completion_operations, error_code); +} + +void aws_secure_tunnel_on_disconnection_update_operational_state(struct aws_secure_tunnel *secure_tunnel) { + /* move current operation to the head of the queue */ + if (secure_tunnel->current_operation != NULL) { + aws_linked_list_push_front(&secure_tunnel->queued_operations, &secure_tunnel->current_operation->node); + secure_tunnel->current_operation = NULL; + } + + /* fail everything in pending write completion */ + s_complete_operation_list( + secure_tunnel, + &secure_tunnel->write_completion_operations, + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_OPERATION_FAILED_DUE_TO_OFFLINE_QUEUE_POLICY); + + /* fail everything in the pending operations list */ + s_complete_operation_list( + secure_tunnel, + &secure_tunnel->queued_operations, + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_OPERATION_FAILED_DUE_TO_OFFLINE_QUEUE_POLICY); +} /* * Check whether secure tunnel currently has work left to do based on its current state @@ -1528,6 +1722,8 @@ static bool s_aws_secure_tunnel_should_service_operational_state( int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure_tunnel) { struct aws_channel_slot *slot = secure_tunnel->slot; + const struct aws_secure_tunnel_vtable *vtable = secure_tunnel->vtable; + // steve TODO voiding slot for warning atm (void)slot; } @@ -1544,10 +1740,13 @@ static void s_complete_operation( aws_secure_tunnel_operation_release(operation); } -static void s_complete_operation_list(struct aws_secure_tunnel *secure_tunnel, int error_code) { +static void s_complete_operation_list( + struct aws_secure_tunnel *secure_tunnel, + struct aws_linked_list *operation_list, + int error_code) { - struct aws_linked_list_node *node = aws_linked_list_begin(&secure_tunnel->queued_operations); - while (node != aws_linked_list_end(&secure_tunnel->queued_operations)) { + struct aws_linked_list_node *node = aws_linked_list_begin(operation_list); + while (node != aws_linked_list_end(operation_list)) { struct aws_secure_tunnel_operation *operation = AWS_CONTAINER_OF(node, struct aws_secure_tunnel_operation, node); @@ -1557,7 +1756,13 @@ static void s_complete_operation_list(struct aws_secure_tunnel *secure_tunnel, i } /* we've released everything, so reset the list to empty */ - aws_linked_list_init(&secure_tunnel->queued_operations); + aws_linked_list_init(operation_list); +} + +void aws_secure_tunnel_operational_state_clean_up(struct aws_secure_tunnel *secure_tunnel) { + AWS_ASSERT(secure_tunnel->current_operation == NULL); + + s_aws_secure_tunnel_operational_state_reset(secure_tunnel, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_TERMINATED); } static void s_check_ping_timeout(struct aws_secure_tunnel *secure_tunnel, uint64_t now) { @@ -1743,6 +1948,13 @@ static void s_enqueue_operation_front( s_reevaluate_service_task(secure_tunnel); } + +static void s_aws_secure_tunnel_operational_state_reset( + struct aws_secure_tunnel *secure_tunnel, + int completion_error_code) { + s_complete_operation_list(secure_tunnel, &secure_tunnel->queued_operations, completion_error_code); + s_complete_operation_list(secure_tunnel, &secure_tunnel->write_completion_operations, completion_error_code); +} struct aws_secure_tunnel_submit_operation_task { struct aws_task task; struct aws_allocator *allocator; @@ -1825,8 +2037,10 @@ int aws_secure_tunnel_bind_stream_id( return AWS_OP_ERR; } -static void s_secure_tunnel_destroy(void *user_data) { - struct aws_secure_tunnel *secure_tunnel = user_data; +static void s_secure_tunnel_final_destroy(struct aws_secure_tunnel *secure_tunnel) { + if (secure_tunnel == NULL) { + return; + } aws_secure_tunneling_on_termination_complete_fn *on_termination_complete = NULL; void *termination_complete_user_data = NULL; @@ -1835,12 +2049,11 @@ static void s_secure_tunnel_destroy(void *user_data) { termination_complete_user_data = secure_tunnel->options->user_data; } - /* Clean up pending operations */ - AWS_ASSERT(secure_tunnel->current_operation == NULL); - s_complete_operation_list(secure_tunnel, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_SECURE_TUNNEL_TERMINATED); + aws_secure_tunnel_operational_state_clean_up(secure_tunnel); /* Clean up all memory */ 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_tls_connection_options_clean_up(&secure_tunnel->tls_con_opt); aws_tls_ctx_release(secure_tunnel->tls_ctx); @@ -1851,6 +2064,12 @@ static void s_secure_tunnel_destroy(void *user_data) { } } +static void s_on_secure_tunnel_zero_ref_count(void *user_data) { + struct aws_secure_tunnel *secure_tunnel = user_data; + + s_aws_secure_tunnel_client_change_desired_state(secure_tunnel, AWS_STS_TERMINATED, NULL); +} + /********************************************************************************************************************* * API Calls ********************************************************************************************************************/ @@ -1860,7 +2079,7 @@ struct aws_secure_tunnel *aws_secure_tunnel_new(const struct aws_secure_tunnel_o struct aws_secure_tunnel *secure_tunnel = aws_mem_calloc(options->allocator, 1, sizeof(struct aws_secure_tunnel)); secure_tunnel->allocator = options->allocator; - aws_ref_count_init(&secure_tunnel->ref_count, secure_tunnel, s_secure_tunnel_destroy); + aws_ref_count_init(&secure_tunnel->ref_count, secure_tunnel, s_on_secure_tunnel_zero_ref_count); aws_linked_list_init(&secure_tunnel->queued_operations); secure_tunnel->current_operation = NULL; From 97f1731a61faa0684e5e6435a141bea9659ea3bf Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 5 Dec 2022 13:34:37 -0800 Subject: [PATCH 05/69] more wip saves --- .../private/secure_tunneling_operations.h | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/include/aws/iotdevice/private/secure_tunneling_operations.h b/include/aws/iotdevice/private/secure_tunneling_operations.h index 80c96ac8..16f9e965 100644 --- a/include/aws/iotdevice/private/secure_tunneling_operations.h +++ b/include/aws/iotdevice/private/secure_tunneling_operations.h @@ -69,12 +69,22 @@ struct aws_secure_tunnel_operation_data { struct aws_secure_tunnel_message_data_storage options_storage; }; +struct aws_secure_tunnel_operation_disconnect { + struct aws_secure_tunnel_operation base; + struct aws_allocator *allocator; + + struct aws_secure_tunnel_packet_disconnect_storage options_storage; + + struct aws_secure_tunnel_disconnect_completion_options external_completion_options; + struct aws_secure_tunnel_disconnect_completion_options internal_completion_options; +} + AWS_EXTERN_C_BEGIN -/* Operation Base */ + /* Operation Base */ -AWS_IOTDEVICE_API struct aws_secure_tunnel_operation *aws_secure_tunnel_operation_acquire( - struct aws_secure_tunnel_operation *operation); + AWS_IOTDEVICE_API struct aws_secure_tunnel_operation * + aws_secure_tunnel_operation_acquire(struct aws_secure_tunnel_operation *operation); AWS_IOTDEVICE_API struct aws_secure_tunnel_operation *aws_secure_tunnel_operation_release( struct aws_secure_tunnel_operation *operation); From 6caf6d5931cb37dc237b01d47a78d6ae3aef2ed3 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 11 Jan 2023 16:35:18 -0800 Subject: [PATCH 06/69] revamp api and operations --- .../iotdevice/private/secure_tunneling_impl.h | 37 -- .../private/secure_tunneling_operations.h | 115 +++- include/aws/iotdevice/secure_tunneling.h | 117 ++-- .../secure_tunneling_message_storage.h | 69 --- source/secure_tunneling.c | 131 ----- source/secure_tunneling_operations.c | 545 +++++++++++++----- 6 files changed, 569 insertions(+), 445 deletions(-) delete mode 100644 include/aws/iotdevice/secure_tunneling_message_storage.h diff --git a/include/aws/iotdevice/private/secure_tunneling_impl.h b/include/aws/iotdevice/private/secure_tunneling_impl.h index 3c98f9f2..39636cc4 100644 --- a/include/aws/iotdevice/private/secure_tunneling_impl.h +++ b/include/aws/iotdevice/private/secure_tunneling_impl.h @@ -124,43 +124,6 @@ typedef void(aws_secure_tunnel_transform_websocket_handshake_fn)( aws_secure_tunnel_transform_websocket_handshake_complete_fn *complete_fn, void *complete_ctx); -/* - * Secure tunnel configuration - */ -struct aws_secure_tunnel_options_storage { - - // struct aws_secure_tunnel_options options; - struct aws_allocator *allocator; - struct aws_secure_tunnel *secure_tunnel; - - /* backup */ - - struct aws_client_bootstrap *bootstrap; - struct aws_socket_options socket_options; - struct aws_http_proxy_options http_proxy_options; - struct aws_http_proxy_config *http_proxy_config; - struct aws_byte_cursor access_token; - - aws_secure_tunnel_transform_websocket_handshake_fn *websocket_handshake_transform; - void *websocket_handshake_transform_user_data; - enum aws_secure_tunneling_local_proxy_mode local_proxy_mode; - - struct aws_string *endpoint_host; - struct aws_string *root_ca; - - /* Store contents of all aws_byte_cursors within single buffer */ - struct aws_byte_buf cursor_storage; - - /* Stream related info */ - int32_t stream_id; - struct aws_string *service_id_1; - int32_t service_id_1_stream_id; - struct aws_string *service_id_2; - int32_t service_id_2_stream_id; - struct aws_string *service_id_3; - int32_t service_id_3_stream_id; -}; - struct data_tunnel_pair { struct aws_byte_buf buf; struct aws_byte_cursor cur; diff --git a/include/aws/iotdevice/private/secure_tunneling_operations.h b/include/aws/iotdevice/private/secure_tunneling_operations.h index 16f9e965..220b3058 100644 --- a/include/aws/iotdevice/private/secure_tunneling_operations.h +++ b/include/aws/iotdevice/private/secure_tunneling_operations.h @@ -11,7 +11,6 @@ #include #include #include -#include /********************************************************************************************************************* * Operations @@ -28,6 +27,17 @@ enum aws_secure_tunnel_operation_type { AWS_STOT_STREAM_START }; +struct aws_secure_tunnel_message_storage { + struct aws_allocator *allocator; + struct aws_secure_tunnel_message_view storage_view; + + int32_t stream_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)( @@ -62,29 +72,64 @@ struct aws_secure_tunnel_operation { void *impl; }; -struct aws_secure_tunnel_operation_data { +struct aws_secure_tunnel_operation_message { struct aws_secure_tunnel_operation base; struct aws_allocator *allocator; - struct aws_secure_tunnel_message_data_storage options_storage; + struct aws_secure_tunnel_message_storage options_storage; }; struct aws_secure_tunnel_operation_disconnect { struct aws_secure_tunnel_operation base; struct aws_allocator *allocator; - struct aws_secure_tunnel_packet_disconnect_storage options_storage; + /* STEVE TODO disconnect shouldn't be necessary */ + // struct aws_secure_tunnel_packet_disconnect_storage options_storage; struct aws_secure_tunnel_disconnect_completion_options external_completion_options; struct aws_secure_tunnel_disconnect_completion_options internal_completion_options; -} +}; + +/* + * Secure tunnel configuration + */ +struct aws_secure_tunnel_options_storage { + + // struct aws_secure_tunnel_options options; + struct aws_allocator *allocator; + struct aws_secure_tunnel *secure_tunnel; + + /* backup */ + + struct aws_client_bootstrap *bootstrap; + struct aws_socket_options socket_options; + struct aws_http_proxy_options http_proxy_options; + struct aws_http_proxy_config *http_proxy_config; + struct aws_string *access_token; + + aws_secure_tunnel_transform_websocket_handshake_fn *websocket_handshake_transform; + void *websocket_handshake_transform_user_data; + enum aws_secure_tunneling_local_proxy_mode local_proxy_mode; + + struct aws_string *endpoint_host; + struct aws_string *root_ca; + + /* Stream related info */ + int32_t stream_id; + struct aws_string *service_id_1; + int32_t service_id_1_stream_id; + struct aws_string *service_id_2; + int32_t service_id_2_stream_id; + struct aws_string *service_id_3; + int32_t service_id_3_stream_id; +}; AWS_EXTERN_C_BEGIN - /* Operation Base */ +/* Operation Base */ - AWS_IOTDEVICE_API struct aws_secure_tunnel_operation * - aws_secure_tunnel_operation_acquire(struct aws_secure_tunnel_operation *operation); +AWS_IOTDEVICE_API struct aws_secure_tunnel_operation *aws_secure_tunnel_operation_acquire( + struct aws_secure_tunnel_operation *operation); AWS_IOTDEVICE_API struct aws_secure_tunnel_operation *aws_secure_tunnel_operation_release( struct aws_secure_tunnel_operation *operation); @@ -104,18 +149,62 @@ AWS_IOTDEVICE_API int32_t AWS_IOTDEVICE_API int32_t *aws_secure_tunnel_operation_get_stream_id_address( const struct aws_secure_tunnel_operation *operation); -/* Data */ +/* Message */ AWS_IOTDEVICE_API -void aws_secure_tunnel_message_data_view_log( - const struct aws_secure_tunnel_message_data_view *data_view, +int aws_secure_tunnel_message_view_validate(const struct aws_secure_tunnel_message_view *message_view); + +AWS_IOTDEVICE_API +void aws_secure_tunnel_message_view_log( + const struct aws_secure_tunnel_message_view *message_view, enum aws_log_level level); AWS_IOTDEVICE_API -struct aws_secure_tunnel_operation_data *aws_secure_tunnel_operation_data_new( +int aws_secure_tunnel_message_storage_init( + struct aws_secure_tunnel_message_storage *message_storage, + struct aws_allocator *allocator, + const struct aws_secure_tunnel_message_view *message_options); + +AWS_IOTDEVICE_API +void aws_secure_tunnel_message_storage_clean_up(struct aws_secure_tunnel_message_storage *message_storage); + +AWS_IOTDEVICE_API +struct aws_secure_tunnel_operation_message *aws_secure_tunnel_operation_message_new( struct aws_allocator *allocator, const struct aws_secure_tunnel *secure_tunnel, - const struct aws_secure_tunnel_message_data_view *data_options); + const struct aws_secure_tunnel_message_view *message_options); + +/* Secure Tunnel Storage Options */ + +/** + * Raises exception and returns AWS_OP_ERR if options are missing required parameters. + */ +AWS_IOTDEVICE_API +int aws_secure_tunnel_options_validate(const struct aws_secure_tunnel_options *options); + +AWS_IOTDEVICE_API +void aws_secure_tunnel_options_storage_log( + const struct aws_secure_tunnel_options_storage *options_storage, + enum aws_log_level level); + +/** + * Destroy options storage, and release any references held. + */ +AWS_IOTDEVICE_API +void aws_secure_tunnel_options_storage_destroy(struct aws_secure_tunnel_options_storage *storage); + +/** + * Create persistent storage for aws_secure_tunnel_options. + * Makes a deep copy of (or acquires reference to) any data referenced by options, + */ +AWS_IOTDEVICE_API +struct aws_secure_tunnel_options_storage *aws_secure_tunnel_options_storage_new( + const struct aws_secure_tunnel_options *options); + +AWS_IOTDEVICE_API +void aws_secure_tunnel_options_storage_log( + const struct aws_secure_tunnel_options_storage *options_storage, + enum aws_log_level level); AWS_IOTDEVICE_API const char *aws_secure_tunnel_operation_type_to_c_string(enum aws_secure_tunnel_operation_type operation_type); diff --git a/include/aws/iotdevice/secure_tunneling.h b/include/aws/iotdevice/secure_tunneling.h index dca064bc..586520cc 100644 --- a/include/aws/iotdevice/secure_tunneling.h +++ b/include/aws/iotdevice/secure_tunneling.h @@ -23,34 +23,22 @@ struct aws_http_proxy_options; */ /** - * Read-only snapshot of Data Message + * Read-only snapshot of secure tunnel Message */ -struct aws_secure_tunnel_message_data_view { +struct aws_secure_tunnel_message_view { int32_t stream_id; struct aws_byte_cursor service_id; struct aws_byte_cursor payload; }; -/** - * Read-only snapshot of Stream Message - * Used with Stream Start and Stream Reset message types - */ -struct aws_secure_tunnel_message_stream_view { - int32_t stream_id; - const struct aws_byte_cursor *service_id; -}; - /* Callbacks */ typedef void(aws_secure_tunneling_on_connection_complete_fn)(void *user_data); typedef void(aws_secure_tunneling_on_connection_shutdown_fn)(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_data_receive_fn)(const struct aws_byte_buf *data, void *user_data); -typedef void(aws_secure_tunneling_on_data_receive_v3_fn)( - const struct aws_byte_cursor service_id, - int connection_id, - const struct aws_byte_buf *data, - void *user_data); +typedef void( + aws_secure_tunneling_on_data_receive_new_fn)(const struct aws_secure_tunnel_message_view *message, void *user_data); typedef void(aws_secure_tunneling_on_stream_start_fn)(void *user_data); typedef void(aws_secure_tunneling_on_stream_reset_fn)(void *user_data); typedef void(aws_secure_tunneling_on_session_reset_fn)(void *user_data); @@ -64,6 +52,7 @@ struct aws_secure_tunnel_options { struct aws_byte_cursor access_token; struct aws_byte_cursor endpoint_host; + /* Steve TODO we only support destination mode so this can be removed outside of testing */ enum aws_secure_tunneling_local_proxy_mode local_proxy_mode; const char *root_ca; const char *service_id_1; @@ -74,7 +63,7 @@ struct aws_secure_tunnel_options { 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_data_receive_fn *on_data_receive; - aws_secure_tunneling_on_data_receive_v3_fn *on_data_receive_v3; + aws_secure_tunneling_on_data_receive_new_fn *on_data_receive_new; 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; @@ -94,7 +83,7 @@ typedef void(aws_secure_tunnel_disconnect_completion_fn)(int error_code, void *c struct aws_secure_tunnel_disconnect_completion_options { aws_secure_tunnel_disconnect_completion_fn *completion_callback; void *completion_user_data; -} +}; /* deprecated: "_config" is renamed "_options" for consistency with similar code in the aws-c libraries */ #define aws_secure_tunneling_connection_config aws_secure_tunnel_options @@ -106,69 +95,89 @@ struct aws_secure_tunnel_options_storage; AWS_EXTERN_C_BEGIN +/** + * Creates a new secure tunnel + * + * @param options secure tunnel configuration + * @return a new secure tunnel or NULL + */ AWS_IOTDEVICE_API struct aws_secure_tunnel *aws_secure_tunnel_new(const struct aws_secure_tunnel_options *options); +/** + * Acquires a reference to a secure tunnel + * + * @param secure_tunnel secure tunnel to acquire a reference to. May be NULL + * @return what was passed in as the secure tunnel (a client or NULL) + */ AWS_IOTDEVICE_API struct aws_secure_tunnel *aws_secure_tunnel_acquire(struct aws_secure_tunnel *secure_tunnel); +/** + * Release a reference to a secure tunnel. When the secure tunnel ref count drops to zero, the secure tunnel + * will automatically trigger a stop and once the stop completes, the secure tunnel will delete itself. + * + * @param secure_tunnel secure tunnel to release a reference to. May be NULL + * @return NULL + */ +AWS_IOTDEVICE_API +void aws_secure_tunnel_release(struct aws_secure_tunnel *secure_tunnel); + +/* TODO STEVE NEW replace aws_secure_tunnel_connect and put it in a state where it wants to be connected */ +/** + * Asynchronous notify to the secure tunnel that you want it to attempt to connect. + * The secure tunnel will attempt to stay connected. + * + * @param secure_tunnel secure tunnel to start + * @return success/failure in the synchronous logic that kicks off the start process + */ AWS_IOTDEVICE_API -int aws_secure_tunnel_get_connection_error_code(struct aws_secure_tunnel *secure_tunnel); +int aws_secure_tunnel_start(struct aws_secure_tunnel *secure_tunnel); +/* TODO STEVE NEW replace aws_secure_tunnel_close and put it in a state where it wants to be disconnected */ +/** + * Asynchronous notify to the secure tunnel that you want it to transition to the stopped state. When the + * secure tunnel reaches the stopped state, all session state is erased. + * + * @param secure_tunnel secure tunnel to stop + * @return success/failure in the synchronous logic that kicks off the start process + */ AWS_IOTDEVICE_API -void aws_secure_tunnel_release(struct aws_secure_tunnel *secure_tunnel); +int aws_secure_tunnel_stop(struct aws_secure_tunnel *secure_tunnel); +/* TODO STEVE depricate in favor of aws_secure_tunnel_start */ AWS_IOTDEVICE_API int aws_secure_tunnel_connect(struct aws_secure_tunnel *secure_tunnel); +/* TODO STEVE depricate in favor of aws_secure_tunnel_stop */ AWS_IOTDEVICE_API int aws_secure_tunnel_close(struct aws_secure_tunnel *secure_tunnel); +/* TODO STEVE depricate/replace with new API below */ AWS_IOTDEVICE_API int aws_secure_tunnel_send_data(struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *data); +/** + * Queues a message operation in a secure tunnel + * + * @param secure_tunnel secure tunnel to queue a message for + * @param message_options configuration options for the message operation + * @return success/failure in the synchronous logic that kicks off the message operation + */ AWS_IOTDEVICE_API -int aws_secure_tunnel_send_data_v2( +int aws_secure_tunnel_send_message_new( struct aws_secure_tunnel *secure_tunnel, - const struct aws_secure_tunnel_message_data_view *data_options); + const struct aws_secure_tunnel_message_view *message_options); +/* TODO STEVE depricate/remove. Destination device does not send a stream start. Keep only for internal testing */ AWS_IOTDEVICE_API int aws_secure_tunnel_stream_start(struct aws_secure_tunnel *secure_tunnel); +/* TODO STEVE see above. Remove or leave solely for testing purposes */ AWS_IOTDEVICE_API -int aws_secure_tunnel_stream_start_v2( +int aws_secure_tunnel_stream_start_new( struct aws_secure_tunnel *secure_tunnel, - const struct aws_byte_cursor *service_id_data); - -AWS_IOTDEVICE_API -int aws_secure_tunnel_stream_reset(struct aws_secure_tunnel *secure_tunnel); - -/** - * Raises exception and returns AWS_OP_ERR if options are missing required parameters. - */ -AWS_IOTDEVICE_API -int aws_secure_tunnel_options_validate(const struct aws_secure_tunnel_options *options); - -/** - * Create persistent storage for aws_secure_tunnel_options. - * Makes a deep copy of (or acquires reference to) any data referenced by options, - */ -AWS_IOTDEVICE_API -struct aws_secure_tunnel_options_storage *aws_secure_tunnel_options_storage_new( - const struct aws_secure_tunnel_options *options); - -/** - * Destroy options storage, and release any references held. - */ -AWS_IOTDEVICE_API -void aws_secure_tunnel_options_storage_destroy(struct aws_secure_tunnel_options_storage *storage); - -/** - * Return pointer to options struct stored within. - */ -AWS_IOTDEVICE_API -const struct aws_secure_tunnel_options *aws_secure_tunnel_options_storage_get( - const struct aws_secure_tunnel_options_storage *storage); + const struct aws_secure_tunnel_message_view *message_options); /* Making this exposed public to verify testing in the sdk layer */ AWS_IOTDEVICE_API diff --git a/include/aws/iotdevice/secure_tunneling_message_storage.h b/include/aws/iotdevice/secure_tunneling_message_storage.h deleted file mode 100644 index 3c707684..00000000 --- a/include/aws/iotdevice/secure_tunneling_message_storage.h +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -#ifndef AWS_IOTDEVICE_SECURE_TUNNELING_MESSAGE_STORAGE_H -#define AWS_IOTDEVICE_SECURE_TUNNELING_MESSAGE_STORAGE_H - -#include -#include - -struct aws_secure_tunnel_message_data_storage { - struct aws_allocator *allocator; - struct aws_secure_tunnel_message_data_view storage_view; - - int32_t stream_id; - struct aws_byte_cursor service_id; - struct aws_byte_cursor payload; - - struct aws_byte_buf storage; -}; - -struct aws_secure_tunnel_message_stream_storage { - struct aws_allocator *allocator; - struct aws_secure_tunnel_message_stream_view storage_view; - - int32_t stream_id; - struct aws_byte_cursor service_id; - - struct aws_byte_buf storage; -}; - -AWS_EXTERN_C_BEGIN - -/* Data */ - -AWS_IOTDEVICE_API -int aws_secure_tunnel_message_data_storage_init( - struct aws_secure_tunnel_message_data_storage *data_storage, - struct aws_allocator *allocator, - const struct aws_secure_tunnel_message_data_view *data_options); - -AWS_IOTDEVICE_API -int aws_secure_tunnel_message_data_storage_init_from_external_storage( - struct aws_secure_tunnel_message_data_storage *data_storage, - struct aws_allocator *allocator); - -AWS_IOTDEVICE_API -void aws_secure_tunnel_message_data_storage_clean_up(struct aws_secure_tunnel_message_data_storage *data_storage); - -/* Stream */ - -AWS_IOTDEVICE_API -int aws_secure_tunnel_message_stream_storage_init( - struct aws_secure_tunnel_message_stream_storage *stream_storage, - struct aws_allocator *allocator, - const struct aws_secure_tunnel_message_stream_view *stream_options); - -AWS_IOTDEVICE_API -int aws_secure_tunnel_message_stream_storage_init_from_external_storage( - struct aws_secure_tunnel_message_stream_storage *stream_storage, - struct aws_allocator *allocator); - -AWS_IOTDEVICE_API -void aws_secure_tunnel_message_stream_storage_clean_up(struct aws_secure_tunnel_message_stream_storage *stream_storage); - -AWS_EXTERN_C_END - -#endif /* AWS_IOTDEVICE_SECURE_TUNNELING_MESSAGE_STORAGE_H */ diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 5a04cdaa..9b6a88ea 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -5,7 +5,6 @@ #include #include -#include #include #include @@ -79,136 +78,6 @@ static const char *s_get_proxy_mode_string(enum aws_secure_tunneling_local_proxy return "destination"; } -/********************************************************************************************************************* - * Options Storage - ********************************************************************************************************************/ - -/* - * Validation of options on creation of a new secure tunnel - */ -int aws_secure_tunnel_options_validate(const struct aws_secure_tunnel_options *options) { - AWS_ASSERT(options && options->allocator); - if (options->bootstrap == NULL) { - AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "bootstrap cannot be NULL"); - return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); - } - if (options->socket_options == NULL) { - AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "socket options cannot be NULL"); - return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); - } - if (options->access_token.len == 0) { - AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "access token is required"); - return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); - } - if (options->endpoint_host.len == 0) { - AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "endpoint host is required"); - return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); - } - - return AWS_OP_SUCCESS; -} - -/* - * Clean up stored secure tunnel config - */ -void aws_secure_tunnel_options_storage_destroy(struct aws_secure_tunnel_options_storage *storage) { - if (storage == NULL) { - return; - } - - aws_client_bootstrap_release(storage->bootstrap); - aws_http_proxy_config_destroy(storage->http_proxy_config); - aws_byte_buf_clean_up(&storage->cursor_storage); - aws_string_destroy(storage->endpoint_host); - aws_string_destroy(storage->root_ca); - aws_string_destroy(storage->service_id_1); - aws_string_destroy(storage->service_id_2); - aws_string_destroy(storage->service_id_3); - aws_mem_release(storage->allocator, storage); -} - -/* - * Copy and store secure tunnel options - */ -struct aws_secure_tunnel_options_storage *aws_secure_tunnel_options_storage_new( - const struct aws_secure_tunnel_options *src) { - - if (aws_secure_tunnel_options_validate(src)) { - return NULL; - } - - struct aws_allocator *allocator = src->allocator; - - struct aws_secure_tunnel_options_storage *storage = - aws_mem_calloc(allocator, 1, sizeof(struct aws_secure_tunnel_options_storage)); - - /* shallow-copy everything that's shallow-copy-able */ - // storage->options = *src; - - storage->allocator = allocator; - - storage->local_proxy_mode = src->local_proxy_mode; - - /* acquire reference to everything that's ref-counted */ - storage->bootstrap = aws_client_bootstrap_acquire(src->bootstrap); - - /* deep-copy anything that needs deep-copying */ - storage->socket_options = *src->socket_options; - // storage->options.socket_options = &storage->socket_options; - - /* deep-copy the http-proxy-options to http_proxy_config */ - if (src->http_proxy_options != NULL) { - storage->http_proxy_config = - aws_http_proxy_config_new_tunneling_from_proxy_options(allocator, src->http_proxy_options); - if (storage->http_proxy_config == NULL) { - goto error; - } - - /* Make a copy of http_proxy_options and point to it */ - aws_http_proxy_options_init_from_config(&storage->http_proxy_options, storage->http_proxy_config); - // storage->options.http_proxy_options = &storage->http_proxy_options; - } - - /* Store contents of all cursors within single buffer (and update cursors to point into it) */ - aws_byte_buf_init_cache_and_update_cursors( - // &storage->cursor_storage, allocator, &storage->options.access_token, &storage->options.endpoint_host, NULL); - &storage->cursor_storage, - allocator, - &storage->access_token, - NULL); - - storage->endpoint_host = aws_string_new_from_cursor(allocator, &src->endpoint_host); - if (storage->endpoint_host == NULL) { - goto error; - } - - if (src->root_ca != NULL) { - storage->root_ca = aws_string_new_from_c_str(allocator, src->root_ca); - // storage->options.root_ca = aws_string_c_str(storage->root_ca); - } - - if (src->service_id_1 != NULL) { - storage->service_id_1 = aws_string_new_from_c_str(allocator, src->service_id_1); - // storage->options.service_id_1 = aws_string_c_str(storage->service_id_1); - } - - if (src->service_id_2 != NULL) { - storage->service_id_2 = aws_string_new_from_c_str(allocator, src->service_id_2); - // storage->options.service_id_2 = aws_string_c_str(storage->service_id_2); - } - - if (src->service_id_3 != NULL) { - storage->service_id_3 = aws_string_new_from_c_str(allocator, src->service_id_3); - // storage->options.service_id_3 = aws_string_c_str(storage->service_id_3); - } - - return storage; - -error: - aws_secure_tunnel_options_storage_destroy(storage); - return NULL; -} - /***************************************************************************************************************** * MESSAGE HANDLING *****************************************************************************************************************/ diff --git a/source/secure_tunneling_operations.c b/source/secure_tunneling_operations.c index c7a200fa..33b9b178 100644 --- a/source/secure_tunneling_operations.c +++ b/source/secure_tunneling_operations.c @@ -8,6 +8,7 @@ #include #include #include +#include #define MAX_PAYLOAD_SIZE 64512 /********************************************************************************************************************* @@ -43,7 +44,6 @@ void *aws_secure_tunnel_operation_complete( } } -/* STEVE TODO set the stream_id based on the service id */ void aws_secure_tunnel_operation_set_stream_id( struct aws_secure_tunnel_operation *operation, struct aws_secure_tunnel *secure_tunnel) { @@ -74,28 +74,140 @@ int32_t *aws_secure_tunnel_operation_get_stream_id_address(const struct aws_secu return NULL; } -// static struct aws_secure_tunnel_operation_vtable s_empty_operation_vtable = { -// .aws_secure_tunnel_operation_completion_fn = NULL, -// .aws_secure_tunnel_operation_set_stream_id_fn = NULL, -// .aws_secure_tunnel_operation_get_stream_id_address_fn = NULL, -// }; +static struct aws_secure_tunnel_operation_vtable s_empty_operation_vtable = { + .aws_secure_tunnel_operation_completion_fn = NULL, + .aws_secure_tunnel_operation_set_stream_id_fn = NULL, + .aws_secure_tunnel_operation_get_stream_id_address_fn = NULL, +}; + +/********************************************************************************************************************* + * Connect + ********************************************************************************************************************/ +/* STEVE TODO Connect Operation Implementation */ /********************************************************************************************************************* - * data + * Message ********************************************************************************************************************/ -static void s_aws_secure_tunnel_operation_data_set_stream_id( +int aws_secure_tunnel_message_view_validate(const struct aws_secure_tunnel_message_view *message_view) { + if (message_view == NULL) { + AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "null message options"); + return aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_OPTIONS_VALIDATION); + } + + if (message_view->stream_id != 0) { + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: aws_secure_tunnel_message_view - stream id must be 0", + (void *)message_view); + return aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_OPTIONS_VALIDATION); + } + + if (message_view->payload.len > MAX_PAYLOAD_SIZE) { + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: aws_secure_tunnel_message_view - payload too long", + (void *)message_view); + return aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_OPTIONS_VALIDATION); + } + + return AWS_OP_SUCCESS; +} + +void aws_secure_tunnel_message_view_log( + const struct aws_secure_tunnel_message_view *message_view, + enum aws_log_level level) { + struct aws_logger *log_handle = aws_logger_get_conditional(AWS_LS_IOTDEVICE_SECURE_TUNNELING, level); + if (log_handle == NULL) { + return; + } + + 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->service_id.len > 0) { + 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 payload set containing %zu bytes", + (void *)message_view, + (int)message_view->payload.len); +} + +static size_t s_aws_secure_tunnel_message_compute_storage_size( + const struct aws_secure_tunnel_message_view *message_view) { + size_t storage_size = message_view->payload.len; + storage_size += message_view->service_id.len; + + return storage_size; +} + +int aws_secure_tunnel_message_storage_init( + struct aws_secure_tunnel_message_storage *message_storage, + struct aws_allocator *allocator, + const struct aws_secure_tunnel_message_view *message_options) { + + AWS_ZERO_STRUCT(*message_storage); + size_t storage_capacity = s_aws_secure_tunnel_message_compute_storage_size(message_options); + if (aws_byte_buf_init(&message_storage->storage, allocator, storage_capacity)) { + return AWS_OP_ERR; + } + + struct aws_secure_tunnel_message_view *storage_view = &message_storage->storage_view; + + storage_view->stream_id = message_options->stream_id; + + storage_view->service_id = message_options->service_id; + if (aws_byte_buf_append_and_update(&message_storage->storage, &storage_view->service_id)) { + return AWS_OP_ERR; + } + + storage_view->payload = message_options->payload; + if (aws_byte_buf_append_and_update(&message_storage->storage, &storage_view->payload)) { + return AWS_OP_ERR; + } + + return AWS_OP_SUCCESS; +} + +void aws_secure_tunnel_message_storage_clean_up(struct aws_secure_tunnel_message_storage *message_storage) { + aws_byte_buf_clean_up(&message_storage->storage); +} + +static void s_aws_secure_tunnel_operation_message_set_stream_id( struct aws_secure_tunnel_operation *operation, struct aws_secure_tunnel *secure_tunnel) { - struct aws_secure_tunnel_operation_data *data_op = operation->impl; + struct aws_secure_tunnel_operation_message *message_op = operation->impl; int32_t stream_id = 0; - struct aws_secure_tunnel_message_data_storage *data_storage = &data_op->options_storage; + struct aws_secure_tunnel_message_storage *message_storage = &message_op->options_storage; - if (data_storage->service_id.len > 0) { + if (message_storage->service_id.len > 0) { struct aws_string *service_id = NULL; - service_id = aws_string_new_from_cursor(secure_tunnel->allocator, &data_storage->service_id); + service_id = aws_string_new_from_cursor(secure_tunnel->allocator, &message_storage->service_id); if (secure_tunnel->config->service_id_1 != NULL && aws_string_compare(secure_tunnel->config->service_id_1, service_id) == 0) { @@ -108,172 +220,353 @@ static void s_aws_secure_tunnel_operation_data_set_stream_id( secure_tunnel->config->service_id_3 != NULL && aws_string_compare(secure_tunnel->config->service_id_3, service_id) == 0) { stream_id = secure_tunnel->config->service_id_3_stream_id; + } else { + /* service_id doesn't match any existing service id*/ + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: aws_message_storage - invalid service_id:%s", + (void *)message_storage, + aws_string_c_str(service_id)); + /* STEVE TODO should we throw something or just log the error here? */ } + aws_string_destroy(service_id); } else { stream_id = secure_tunnel->config->stream_id; } - data_op->options_storage.storage_view.stream_id = stream_id; + message_op->options_storage.storage_view.stream_id = stream_id; } -static int32_t *s_aws_secure_tunnel_operation_data_get_stream_id_address_fn( +static int32_t *s_aws_secure_tunnel_operation_message_get_stream_id_address_fn( const struct aws_secure_tunnel_operation *operation) { - struct aws_secure_tunnel_operation_data *data_op = operation->impl; - return &data_op->options_storage.storage_view.stream_id; + struct aws_secure_tunnel_operation_message *message_op = operation->impl; + return &message_op->options_storage.storage_view.stream_id; } -static struct aws_secure_tunnel_operation_vtable s_data_operation_vtable = { - .aws_secure_tunnel_operation_set_stream_id_fn = s_aws_secure_tunnel_operation_data_set_stream_id, - .aws_secure_tunnel_operation_get_stream_id_address_fn = s_aws_secure_tunnel_operation_data_get_stream_id_address_fn, +static struct aws_secure_tunnel_operation_vtable s_message_operation_vtable = { + .aws_secure_tunnel_operation_set_stream_id_fn = s_aws_secure_tunnel_operation_message_set_stream_id, + .aws_secure_tunnel_operation_get_stream_id_address_fn = + s_aws_secure_tunnel_operation_message_get_stream_id_address_fn, }; -int aws_secure_tunnel_message_data_view_validate(const struct aws_secure_tunnel_message_data_view *data_view) { - if (data_view == NULL) { - AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "null DATA message options"); - return aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_OPTIONS_VALIDATION); +static void s_destroy_operation_message(void *object) { + if (object == NULL) { + return; } - if (data_view->stream_id != 0) { - AWS_LOGF_ERROR( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: aws_secure_tunnel_message_data_view - stream id must be 0", - (void *)data_view); - return aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_OPTIONS_VALIDATION); + struct aws_secure_tunnel_operation_message *message_op = object; + + aws_secure_tunnel_message_storage_clean_up(&message_op->options_storage); + + aws_mem_release(message_op->allocator, message_op); +} + +struct aws_secure_tunnel_operation_message *aws_secure_tunnel_operation_message_new( + struct aws_allocator *allocator, + const struct aws_secure_tunnel *secure_tunnel, + const struct aws_secure_tunnel_message_view *message_options) { + (void)secure_tunnel; + AWS_PRECONDITION(allocator != NULL); + AWS_PRECONDITION(message_options != NULL); + + if (aws_secure_tunnel_message_message_view_validate(message_options)) { + return NULL; } - if (data_view->service_id.len <= 0) { - AWS_LOGF_ERROR( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: aws_secure_tunnel_message_data_view - Service Id cannot be empty", - (void *)data_view); - return aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_OPTIONS_VALIDATION); + struct aws_secure_tunnel_operation_message *message_op = + aws_mem_calloc(allocator, 1, sizeof(struct aws_secure_tunnel_operation_message)); + if (message_op == NULL) { + return NULL; } - if (data_view->payload.len > MAX_PAYLOAD_SIZE) { - AWS_LOGF_ERROR( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: aws_secure_tunnel_message_data_view - payload too long", - (void *)data_view); - return aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_OPTIONS_VALIDATION); + message_op->allocator = allocator; + message_op->base.vtable = &s_message_operation_vtable; + aws_ref_count_init(&message_op->base.ref_count, message_op, s_destroy_operation_message); + message_op->base.impl = message_op; + + if (aws_secure_tunnel_message_message_storage_init(&message_op->options_storage, allocator, message_options)) { + goto error; + } + + message_op->base.message_view = &message_op->options_storage.storage_view; + + return message_op; + +error: + + aws_secure_tunnel_operation_release(&message_op->base); + + return NULL; +} + +/********************************************************************************************************************* + * Secure Tunnel Storage Options + ********************************************************************************************************************/ + +/* + * Validation of options on creation of a new secure tunnel + */ +int aws_secure_tunnel_options_validate(const struct aws_secure_tunnel_options *options) { + AWS_ASSERT(options && options->allocator); + if (options->bootstrap == NULL) { + AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "bootstrap cannot be NULL"); + return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + } + if (options->socket_options == NULL) { + AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "socket options cannot be NULL"); + return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + } + if (options->access_token.len == 0) { + AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "access token is required"); + return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + } + if (options->endpoint_host.len == 0) { + AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "endpoint host is required"); + return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); } return AWS_OP_SUCCESS; } -void aws_secure_tunnel_message_data_view_log( - const struct aws_secure_tunnel_message_data_view *data_view, +void aws_secure_tunnel_options_storage_log( + const struct aws_secure_tunnel_options_storage *options_storage, enum aws_log_level level) { - - struct aws_logger *temp_logger = aws_logger_get(); - if (temp_logger == NULL || - temp_logger->vtable->get_log_level(temp_logger, AWS_LS_IOTDEVICE_SECURE_TUNNELING) < level) { + struct aws_logger *log_handle = aws_logger_get_conditional(AWS_LS_IOTDEVICE_SECURE_TUNNELING, level); + if (log_handle == NULL) { return; } - AWS_LOGF( + AWS_LOGUF( + log_handle, level, AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "(%p) aws_secure_tunnel_message_data_view stream id set to %d", - (void *)data_view, - (int)data_view->stream_id); + "id=%p: aws_secure_tunnel_options_storage host name set to %s", + (void *)options_storage, + aws_string_c_str(options_storage->endpoint_host)); - AWS_LOGF( + AWS_LOGUF( + log_handle, level, AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: aws_secure_tunnel_message_data_view service id set to \"" PRInSTR "\"", - (void *)data_view, - AWS_BYTE_CURSOR_PRI(data_view->service_id)); -} + "id=%p: aws_secure_tunnel_options_storage bootstrap set to (%p)", + (void *)options_storage, + (void *)options_storage->bootstrap); -static size_t s_aws_secure_tunnel_message_data_compute_storage_size( - const struct aws_secure_tunnel_message_data_view *data_view) { - size_t storage_size = data_view->payload.len; - storage_size += data_view->service_id.len; + AWS_LOGUF( + log_handle, + level, + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: aws_secure_tunnel_options_storage socket options set to: type = %d, domain = %d, connect_timeout_ms = " + "%" PRIu32, + (void *)options_storage, + (int)options_storage->socket_options.type, + (int)options_storage->socket_options.domain, + options_storage->socket_options.connect_timeout_ms); + + if (options_storage->socket_options.keepalive) { + AWS_LOGUF( + log_handle, + level, + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: aws_secure_tunnel_options_storage socket keepalive options set to: keep_alive_interval_sec = " + "%" PRIu16 ", " + "keep_alive_timeout_sec = %" PRIu16 ", keep_alive_max_failed_probes = %" PRIu16, + (void *)options_storage, + options_storage->socket_options.keep_alive_interval_sec, + options_storage->socket_options.keep_alive_timeout_sec, + options_storage->socket_options.keep_alive_max_failed_probes); + } - return storage_size; -} + if (options_storage->http_proxy_config != NULL) { + AWS_LOGUF( + log_handle, + level, + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: aws_secure_tunnel_options_storage using http proxy:", + (void *)options_storage); -int aws_secure_tunnel_message_data_storage_init( - struct aws_secure_tunnel_message_data_storage *data_storage, - struct aws_allocator *allocator, - const struct aws_secure_tunnel_message_data_view *data_options) { + AWS_LOGUF( + log_handle, + level, + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: aws_secure_tunnel_options_storage http proxy host name set to " PRInSTR, + (void *)options_storage, + AWS_BYTE_CURSOR_PRI(options_storage->http_proxy_options.host)); - AWS_ZERO_STRUCT(*data_storage); - size_t storage_capacity = s_aws_secure_tunnel_message_data_compute_storage_size(data_options); - if (aws_byte_buf_init(&data_storage->storage, allocator, storage_capacity)) { - return AWS_OP_ERR; + AWS_LOGUF( + log_handle, + level, + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: aws_secure_tunnel_options_storage http proxy port set to %" PRIu16, + (void *)options_storage, + options_storage->http_proxy_options.port); + + if (options_storage->http_proxy_options.proxy_strategy != NULL) { + AWS_LOGUF( + log_handle, + level, + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: aws_secure_tunnel_options_storage http proxy strategy set to (%p)", + (void *)options_storage, + (void *)options_storage->http_proxy_options.proxy_strategy); + } } - struct aws_secure_tunnel_message_data_view *storage_view = &data_storage->storage_view; + if (options_storage->websocket_handshake_transform != NULL) { + AWS_LOGUF( + log_handle, + level, + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: aws_secure_tunnel_options_storage enabling websockets", + (void *)options_storage); + + AWS_LOGUF( + log_handle, + level, + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: aws_secure_tunnel_options_storage websocket handshake transform user data set to (%p)", + (void *)options_storage, + options_storage->websocket_handshake_transform_user_data); + } else { + AWS_LOGUF( + log_handle, + level, + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: aws_secure_tunnel_options_storage disabling websockets", + (void *)options_storage); + } - storage_view->stream_id = data_options->stream_id; + bool is_service_id_used = false; - storage_view->service_id = data_options->service_id; - if (aws_byte_buf_append_and_update(&data_storage->storage, &storage_view->service_id)) { - return AWS_OP_ERR; + if (options_storage->service_id_1 != NULL) { + is_service_id_used = true; + AWS_LOGUF( + log_handle, + level, + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: aws_secure_tunnel_options_storage service id 1:%s", + (void *)options_storage, + aws_string_c_str(options_storage->service_id_1)); } - storage_view->payload = data_options->payload; - if (aws_byte_buf_append_and_update(&data_storage->storage, &storage_view->payload)) { - return AWS_OP_ERR; + if (options_storage->service_id_2 != NULL) { + is_service_id_used = true; + AWS_LOGUF( + log_handle, + level, + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: aws_secure_tunnel_options_storage service id 2:%s", + (void *)options_storage, + aws_string_c_str(options_storage->service_id_2)); } - return AWS_OP_SUCCESS; -} + if (options_storage->service_id_3 != NULL) { + is_service_id_used = true; + AWS_LOGUF( + log_handle, + level, + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: aws_secure_tunnel_options_storage service id 3:%s", + (void *)options_storage, + aws_string_c_str(options_storage->service_id_3)); + } -void aws_secure_tunnel_message_data_storage_clean_up(struct aws_secure_tunnel_message_data_storage *data_storage) { - aws_byte_buf_clean_up(&data_storage->storage); + if (!is_service_id_used) { + AWS_LOGUF( + log_handle, + level, + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: aws_secure_tunnel_options_storage no service id set", + (void *)options_storage); + } } -static void s_destroy_operation_data(void *object) { - if (object == NULL) { +/* + * Clean up stored secure tunnel config + */ +void aws_secure_tunnel_options_storage_destroy(struct aws_secure_tunnel_options_storage *storage) { + if (storage == NULL) { return; } - struct aws_secure_tunnel_operation_data *data_op = object; - - aws_secure_tunnel_message_data_storage_clean_up(&data_op->options_storage); - - aws_mem_release(data_op->allocator, data_op); + 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->root_ca); + aws_string_destroy(storage->service_id_1); + aws_string_destroy(storage->service_id_2); + aws_string_destroy(storage->service_id_3); + aws_mem_release(storage->allocator, storage); } -struct aws_secure_tunnel_operation_data *aws_secure_tunnel_operation_data_new( - struct aws_allocator *allocator, - const struct aws_secure_tunnel *secure_tunnel, - const struct aws_secure_tunnel_message_data_view *data_options) { - (void)secure_tunnel; - AWS_PRECONDITION(allocator != NULL); - AWS_PRECONDITION(data_options != NULL); +/* + * Copy and store secure tunnel options + */ +struct aws_secure_tunnel_options_storage *aws_secure_tunnel_options_storage_new( + const struct aws_secure_tunnel_options *options) { + AWS_PRECONDITION(options != NULL); + AWS_PRECONDITION(options->allocator != NULL); - if (aws_secure_tunnel_message_data_view_validate(data_options)) { + if (aws_secure_tunnel_options_validate(options)) { return NULL; } - struct aws_secure_tunnel_operation_data *data_op = - aws_mem_calloc(allocator, 1, sizeof(struct aws_secure_tunnel_operation_data)); - if (data_op == NULL) { - return NULL; - } + struct aws_allocator *allocator = options->allocator; - data_op->allocator = allocator; - data_op->base.vtable = &s_data_operation_vtable; - aws_ref_count_init(&data_op->base.ref_count, data_op, s_destroy_operation_data); - data_op->base.impl = data_op; + struct aws_secure_tunnel_options_storage *storage = + aws_mem_calloc(allocator, 1, sizeof(struct aws_secure_tunnel_options_storage)); - if (aws_secure_tunnel_message_data_storage_init(&data_op->options_storage, allocator, data_options)) { + storage->allocator = options->allocator; + storage->socket_options = *options->socket_options; + storage->endpoint_host = aws_string_new_from_cursor(allocator, &options->endpoint_host); + if (storage->endpoint_host == NULL) { goto error; } - data_op->base.message_view = &data_op->options_storage.storage_view; + storage->access_token = aws_string_new_from_cursor(allocator, &options->access_token); + if (storage->access_token == NULL) { + goto error; + } - return data_op; + /* STEVE TODO can be removed except for testing */ + storage->local_proxy_mode = options->local_proxy_mode; -error: + /* acquire reference to everything that's ref-counted */ + storage->bootstrap = aws_client_bootstrap_acquire(options->bootstrap); - aws_secure_tunnel_operation_release(&data_op->base); + if (options->http_proxy_options != NULL) { + storage->http_proxy_config = + aws_http_proxy_config_new_from_proxy_options(allocator, options->http_proxy_options); + if (storage->http_proxy_config == NULL) { + goto error; + } + + aws_http_proxy_options_init_from_config(&storage->http_proxy_options, storage->http_proxy_config); + } + + if (options->root_ca != NULL) { + storage->root_ca = aws_string_new_from_c_str(allocator, &options->root_ca); + } + if (options->service_id_1 != NULL) { + storage->service_id_1 = aws_string_new_from_c_str(allocator, options->service_id_1); + } + + if (options->service_id_2 != NULL) { + storage->service_id_2 = aws_string_new_from_c_str(allocator, options->service_id_2); + } + + if (options->service_id_3 != NULL) { + storage->service_id_3 = aws_string_new_from_c_str(allocator, options->service_id_3); + } + return storage; + +error: + aws_secure_tunnel_options_storage_destroy(storage); return NULL; } + const char *aws_secure_tunnel_operation_type_to_c_string(enum aws_secure_tunnel_operation_type operation_type) { switch (operation_type) { case AWS_STOT_NONE: @@ -292,33 +585,3 @@ const char *aws_secure_tunnel_operation_type_to_c_string(enum aws_secure_tunnel_ return "UNKNOWN"; } } - -// int aws_secure_tunnel_packet_data_storage_init_from_external_storage( -// struct aws_secure_tunnel_packet_data_storage *data_storage, -// struct aws_allocator *allocator) { -// AWS_ZERO_STRUCT(*data_storage); - -// if (aws_secure_tunnel_user_property_set_init(&publish_storage->user_properties, allocator)) { -// return AWS_OP_ERR; -// } - -// if (aws_array_list_init_dynamic(&publish_storage->subscription_identifiers, allocator, 0, sizeof(uint32_t))) { -// return AWS_OP_ERR; -// } - -// return AWS_OP_SUCCESS; -// } - -// AWS_IOTDEVICE_API -// int aws_secure_tunnel_packet_stream_storage_init( -// struct aws_secure_tunnel_packet_stream_storage *stream_storage, -// struct aws_allocator *allocator, -// const struct aws_secure_tunnel_packet_stream_view *stream_options); - -// AWS_IOTDEVICE_API -// int aws_secure_tunnel_packet_stream_storage_init_from_external_storage( -// struct aws_secure_tunnel_packet_stream_storage *stream_storage, -// struct aws_allocator *allocator); - -// AWS_IOTDEVICE_API -// int aws_secure_tunnel_packet_stream_storage_clean_up(struct aws_secure_tunnel_packet_stream_storage *stream_storage); From 6ea5be80fb9a7087b83752cca26c0d94eceb71d2 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Thu, 12 Jan 2023 15:01:42 -0800 Subject: [PATCH 07/69] refactored message handling --- .../iotdevice/private/secure_tunneling_impl.h | 4 +- .../private/secure_tunneling_operations.h | 19 +- include/aws/iotdevice/private/serializer.h | 14 +- include/aws/iotdevice/secure_tunneling.h | 107 +++++-- source/secure_tunneling.c | 262 +++++++----------- source/secure_tunneling_operations.c | 20 +- source/serializer.c | 46 +-- 7 files changed, 246 insertions(+), 226 deletions(-) diff --git a/include/aws/iotdevice/private/secure_tunneling_impl.h b/include/aws/iotdevice/private/secure_tunneling_impl.h index 39636cc4..c954eaac 100644 --- a/include/aws/iotdevice/private/secure_tunneling_impl.h +++ b/include/aws/iotdevice/private/secure_tunneling_impl.h @@ -182,11 +182,10 @@ struct aws_secure_tunnel { /* Static settings */ struct aws_allocator *allocator; struct aws_secure_tunnel_options_storage *config; - struct aws_secure_tunnel_options *options; struct aws_tls_ctx *tls_ctx; struct aws_tls_connection_options tls_con_opt; - const struct aws_secure_tunnel_vtable vtable; + struct aws_secure_tunnel_vtable vtable; struct aws_websocket_vtable websocket_vtable; struct aws_ref_count ref_count; @@ -202,6 +201,7 @@ struct aws_secure_tunnel { * that we'd like to shutdown the channel with while CLEAN_DISCONNECT is processed. */ int clean_disconnect_error_code; + /* * handshake_request exists between the transform completion timepoint and the websocket setup callback. */ diff --git a/include/aws/iotdevice/private/secure_tunneling_operations.h b/include/aws/iotdevice/private/secure_tunneling_operations.h index 220b3058..ae480f83 100644 --- a/include/aws/iotdevice/private/secure_tunneling_operations.h +++ b/include/aws/iotdevice/private/secure_tunneling_operations.h @@ -31,6 +31,8 @@ struct aws_secure_tunnel_message_storage { struct aws_allocator *allocator; struct aws_secure_tunnel_message_view storage_view; + enum aws_secure_tunnel_message_type type; + bool ignorable; int32_t stream_id; struct aws_byte_cursor service_id; struct aws_byte_cursor payload; @@ -112,7 +114,6 @@ struct aws_secure_tunnel_options_storage { enum aws_secure_tunneling_local_proxy_mode local_proxy_mode; struct aws_string *endpoint_host; - struct aws_string *root_ca; /* Stream related info */ int32_t stream_id; @@ -122,6 +123,22 @@ struct aws_secure_tunnel_options_storage { int32_t service_id_2_stream_id; struct aws_string *service_id_3; int32_t service_id_3_stream_id; + + /* Callbacks */ + aws_secure_tunnel_message_received_fn *on_message_received; + + void *user_data; + + /* STEVE TODO we can depricate/remove these. Client only supports destination mode */ + enum aws_secure_tunneling_local_proxy_mode local_proxy_mode; + 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_data_receive_fn *on_data_receive; + 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; + aws_secure_tunneling_on_termination_complete_fn *on_termination_complete; }; AWS_EXTERN_C_BEGIN diff --git a/include/aws/iotdevice/private/serializer.h b/include/aws/iotdevice/private/serializer.h index d8c2184a..1f6d1df3 100644 --- a/include/aws/iotdevice/private/serializer.h +++ b/include/aws/iotdevice/private/serializer.h @@ -6,6 +6,8 @@ #define AWS_IOTDEVICE_SERIALIZER_H #include +#include +#include #include @@ -88,6 +90,7 @@ enum aws_secure_tunnel_message_type { /** * A single IoT Secure Tunnel Message + * STEVE TODO remove this. replaced with aws_secure_tunnel_message_view */ struct aws_iot_st_msg { enum aws_secure_tunnel_message_type type; @@ -98,6 +101,10 @@ struct aws_iot_st_msg { uint32_t connection_id; }; +typedef void(aws_secure_tunnel_on_message_received_fn)( + struct aws_secure_tunnel *secure_tunnel, + struct aws_secure_tunnel_message_view *message_view); + AWS_EXTERN_C_BEGIN AWS_IOTDEVICE_API @@ -107,10 +114,11 @@ int aws_iot_st_msg_serialize_from_struct( struct aws_iot_st_msg message); AWS_IOTDEVICE_API -int aws_iot_st_msg_deserialize_from_cursor( - struct aws_iot_st_msg *message, +int aws_secure_tunnel_deserialize_message_from_cursor( + struct aws_secure_tunnel *secure_tunnel, + struct aws_secure_tunnel_message_view *message, struct aws_byte_cursor *cursor, - struct aws_allocator *allocator); + 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); diff --git a/include/aws/iotdevice/secure_tunneling.h b/include/aws/iotdevice/secure_tunneling.h index 586520cc..da3674db 100644 --- a/include/aws/iotdevice/secure_tunneling.h +++ b/include/aws/iotdevice/secure_tunneling.h @@ -11,6 +11,7 @@ #define AWS_IOT_ST_SPLIT_MESSAGE_SIZE 15000 +/* STEVE TODO remove or move to private. We only support Destination Mode */ enum aws_secure_tunneling_local_proxy_mode { AWS_SECURE_TUNNELING_SOURCE_MODE, AWS_SECURE_TUNNELING_DESTINATION_MODE }; struct aws_secure_tunnel; @@ -18,58 +19,104 @@ struct aws_websocket; struct aws_websocket_incoming_frame; struct aws_http_proxy_options; -/* - * Views - */ - /** - * Read-only snapshot of secure tunnel Message + * Read-only snapshot of a Secure Tunnel Message */ - struct aws_secure_tunnel_message_view { + + enum aws_secure_tunnel_message_type type; + + /** + * If a message is received and its type is unrecognized, and this field is set to true, it is ok for the tunnel + * client to ignore the message safely. If this field is unset, it must be considered as false. + */ + bool ignorable; + int32_t stream_id; + + /** + * Secure tunnel multiplexing identifier + */ struct aws_byte_cursor service_id; struct aws_byte_cursor payload; }; /* Callbacks */ + +/** + * Signature of callback to invoke on received messages + */ +typedef void( + aws_secure_tunnel_message_received_fn)(const struct aws_secure_tunnel_message_view *message, void *user_data); + +/* STEVE TODO Old callbacks can probably be removed */ typedef void(aws_secure_tunneling_on_connection_complete_fn)(void *user_data); typedef void(aws_secure_tunneling_on_connection_shutdown_fn)(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_data_receive_fn)(const struct aws_byte_buf *data, void *user_data); -typedef void( - aws_secure_tunneling_on_data_receive_new_fn)(const struct aws_secure_tunnel_message_view *message, void *user_data); -typedef void(aws_secure_tunneling_on_stream_start_fn)(void *user_data); -typedef void(aws_secure_tunneling_on_stream_reset_fn)(void *user_data); +typedef void(aws_secure_tunneling_on_stream_start_fn)( + const struct aws_secure_tunnel_message_view *message, + int error_code, + void *user_data); +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_session_reset_fn)(void *user_data); typedef void(aws_secure_tunneling_on_termination_complete_fn)(void *user_data); +/** + * Basic Secure Tunnel configuration struct. + * + * Contains connection properties for the creation of a Secure Tunnel + */ struct aws_secure_tunnel_options { struct aws_allocator *allocator; + + /** + * Host to establish Secure Tunnel connection to + */ + struct aws_byte_cursor endpoint_host; + + /** + * Secure Tunnel bootstrap to use whenever Secure Tunnel establishes a connection + */ struct aws_client_bootstrap *bootstrap; + + /** + * Socket options to use whenever this Secure Tunnel establishes a connection + */ const struct aws_socket_options *socket_options; + + /** + * (Optional) Http proxy options to use whenever this Secure Tunnel establishes a connection + */ const struct aws_http_proxy_options *http_proxy_options; + /** + * Access Token used to establish a Secure Tunnel connection + */ struct aws_byte_cursor access_token; - struct aws_byte_cursor endpoint_host; - /* Steve TODO we only support destination mode so this can be removed outside of testing */ - enum aws_secure_tunneling_local_proxy_mode local_proxy_mode; + const char *root_ca; const char *service_id_1; const char *service_id_2; const char *service_id_3; + aws_secure_tunnel_message_received_fn *on_message_received; + + void *user_data; + + /* Steve TODO we only support destination mode so this can be removed outside of testing */ + enum aws_secure_tunneling_local_proxy_mode local_proxy_mode; 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_data_receive_fn *on_data_receive; - aws_secure_tunneling_on_data_receive_new_fn *on_data_receive_new; 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; aws_secure_tunneling_on_termination_complete_fn *on_termination_complete; - - void *user_data; }; /** @@ -145,18 +192,6 @@ int aws_secure_tunnel_start(struct aws_secure_tunnel *secure_tunnel); AWS_IOTDEVICE_API int aws_secure_tunnel_stop(struct aws_secure_tunnel *secure_tunnel); -/* TODO STEVE depricate in favor of aws_secure_tunnel_start */ -AWS_IOTDEVICE_API -int aws_secure_tunnel_connect(struct aws_secure_tunnel *secure_tunnel); - -/* TODO STEVE depricate in favor of aws_secure_tunnel_stop */ -AWS_IOTDEVICE_API -int aws_secure_tunnel_close(struct aws_secure_tunnel *secure_tunnel); - -/* TODO STEVE depricate/replace with new API below */ -AWS_IOTDEVICE_API -int aws_secure_tunnel_send_data(struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *data); - /** * Queues a message operation in a secure tunnel * @@ -169,6 +204,22 @@ int aws_secure_tunnel_send_message_new( struct aws_secure_tunnel *secure_tunnel, const struct aws_secure_tunnel_message_view *message_options); +//*********************************************************************************************************************** +/* STEVE TODO REMOVE OR HIDE ALL BELOW */ +//*********************************************************************************************************************** + +/* TODO STEVE depricate in favor of aws_secure_tunnel_start */ +AWS_IOTDEVICE_API +int aws_secure_tunnel_connect(struct aws_secure_tunnel *secure_tunnel); + +/* TODO STEVE depricate in favor of aws_secure_tunnel_stop */ +AWS_IOTDEVICE_API +int aws_secure_tunnel_close(struct aws_secure_tunnel *secure_tunnel); + +/* TODO STEVE depricate/replace new aws_secure_tunnel_send_message_new */ +AWS_IOTDEVICE_API +int aws_secure_tunnel_send_data(struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *data); + /* TODO STEVE depricate/remove. Destination device does not send a stream start. Keep only for internal testing */ AWS_IOTDEVICE_API int aws_secure_tunnel_stream_start(struct aws_secure_tunnel *secure_tunnel); diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 9b6a88ea..43ab8efc 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -26,9 +26,11 @@ // Steve TODO check if these need to be implemented or re-organized static void s_change_current_state(struct aws_secure_tunnel *secure_tunnel, enum aws_secure_tunnel_state next_state); + int aws_secure_tunnel_bind_stream_id( struct aws_secure_tunnel *secure_tunnel, struct aws_secure_tunnel_operation_data *operation); + static int s_secure_tunneling_send( struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *payload_data, @@ -86,6 +88,8 @@ static const char *s_get_proxy_mode_string(enum aws_secure_tunneling_local_proxy * Close and reset all stream ids */ 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->stream_id = INVALID_STREAM_ID; secure_tunnel->config->service_id_1_stream_id = INVALID_STREAM_ID; secure_tunnel->config->service_id_2_stream_id = INVALID_STREAM_ID; @@ -94,162 +98,92 @@ static void s_reset_secure_tunnel(struct aws_secure_tunnel *secure_tunnel) { secure_tunnel->received_data.len = 0; /* Drop any incomplete secure tunnel frame */ } -static void s_handle_data(struct aws_secure_tunnel *secure_tunnel, struct aws_iot_st_msg *st_msg) { - // Old way of handling a data message - secure_tunnel->options->on_data_receive(&st_msg->payload, secure_tunnel->options->user_data); - - /* Steve TODO sort the message against service ids */ -} - -static void s_handle_stream_start(struct aws_secure_tunnel *secure_tunnel, struct aws_iot_st_msg *st_msg) { - - if (secure_tunnel->options->local_proxy_mode == AWS_SECURE_TUNNELING_SOURCE_MODE) { - /* Source mode tunnel clients SHOULD treat receiving StreamStart as an error and close the active data stream - * and WebSocket connection. */ - AWS_LOGF_ERROR( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: Received StreamStart in source mode. Closing the tunnel.", - (void *)secure_tunnel); - secure_tunnel->vtable.close(secure_tunnel); - } else { +static int s_aws_secure_tunnel_set_stream_id( + struct aws_secure_tunnel *secure_tunnel, + struct aws_byte_cursor *service_id, + int32_t stream_id) { + /* No service id means V1 protocol is being used */ + if (service_id->len == 0) { + secure_tunnel->config->stream_id = stream_id; AWS_LOGF_INFO( AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: Received StreamStart in destination mode. service_id= " PRInSTR " stream_id=%d", + "id=%p: Secure tunnel stream_id set to %d", (void *)secure_tunnel, - AWS_BYTE_CURSOR_PRI(aws_byte_cursor_from_buf(&st_msg->service_id)), - st_msg->stream_id); - - // Steve TODO there may be a better way to check if a service_id is being used in the StreamStart - if (st_msg->service_id.len > 0) { - struct aws_string *service_id = NULL; - service_id = aws_string_new_from_buf(secure_tunnel->allocator, &st_msg->service_id); - - if (service_id == NULL) { - aws_string_destroy(service_id); - /* Steve TODO log error? */ - return; - } - - // Check if stream_id is valid and then set the stream_id - // Steve TODO a Stream Reset MAY be sent by the destination to source for replaced stream ID if the stream - // previously existed. On the fence about whether to do this or not since it's optional in the protocol - if (secure_tunnel->config->service_id_1 != NULL && - aws_string_compare(secure_tunnel->config->service_id_1, service_id) == 0) { - secure_tunnel->config->service_id_1_stream_id = st_msg->stream_id; - } else if ( - secure_tunnel->config->service_id_2 != NULL && - aws_string_compare(secure_tunnel->config->service_id_2, service_id) == 0) { - secure_tunnel->config->service_id_2_stream_id = st_msg->stream_id; - } else if ( - secure_tunnel->config->service_id_3 != NULL && - aws_string_compare(secure_tunnel->config->service_id_3, service_id) == 0) { - secure_tunnel->config->service_id_3_stream_id = st_msg->stream_id; - } else { - // service ID is not supported Steve TODO log error - AWS_LOGF_ERROR( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: Received StreamStart request for unsupported service_id: " PRInSTR, - (void *)secure_tunnel, - AWS_BYTE_CURSOR_PRI(aws_byte_cursor_from_buf(&st_msg->service_id))); - return; - } - - } else { - // No Service Id used indicating V1 Protocol or no multiplexing - secure_tunnel->config->stream_id = st_msg->stream_id; - } - // Steve TODO There doesn't really seem to be a point in calling this callback because it doesn't send anything - // Useful. We should have added a struct that contains the service_id and stream_id. - secure_tunnel->options->on_stream_start(secure_tunnel->options->user_data); + stream_id); + return AWS_OP_SUCCESS; } -} - -static void s_handle_stream_reset(struct aws_secure_tunnel *secure_tunnel, struct aws_iot_st_msg *st_msg) { - - if (st_msg->service_id.len > 0) { - struct aws_string *service_id = NULL; - service_id = aws_string_new_from_buf(secure_tunnel->allocator, &st_msg->service_id); - - if (service_id == NULL) { - // Steve TODO destroy not necessary on a NULL service_id? - aws_string_destroy(service_id); - /* Steve TODO log error? */ - return; - } - // Check if stream_id is valid and then reset the stream_id - if (secure_tunnel->config->service_id_1 != NULL && - aws_string_compare(secure_tunnel->config->service_id_1, service_id) == 0) { - secure_tunnel->config->service_id_1_stream_id = INVALID_STREAM_ID; - } else if ( - secure_tunnel->config->service_id_2 != NULL && - aws_string_compare(secure_tunnel->config->service_id_2, service_id) == 0) { - secure_tunnel->config->service_id_2_stream_id = INVALID_STREAM_ID; - } else if ( - secure_tunnel->config->service_id_3 != NULL && - aws_string_compare(secure_tunnel->config->service_id_3, service_id) == 0) { - secure_tunnel->config->service_id_3_stream_id = INVALID_STREAM_ID; - } else { - // service ID is not supported Steve TODO log error - AWS_LOGF_ERROR( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: Received StreamReset request for unsupported service_id: " PRInSTR, - (void *)secure_tunnel, - AWS_BYTE_CURSOR_PRI(aws_byte_cursor_from_buf(&st_msg->service_id))); - return; - } + struct aws_string *service_id_to_set = aws_string_new_from_buf(secure_tunnel->allocator, service_id); + if (service_id_to_set == NULL) { + return AWS_OP_ERR; + } - AWS_LOGF_INFO( + if (secure_tunnel->config->service_id_1 != NULL && + aws_string_compare(secure_tunnel->config->service_id_1, service_id_to_set) == 0) { + secure_tunnel->config->service_id_1_stream_id = stream_id; + } else if ( + secure_tunnel->config->service_id_2 != NULL && + aws_string_compare(secure_tunnel->config->service_id_2, service_id_to_set) == 0) { + secure_tunnel->config->service_id_2_stream_id = stream_id; + } else if ( + secure_tunnel->config->service_id_3 != NULL && + aws_string_compare(secure_tunnel->config->service_id_3, service_id_to_set) == 0) { + secure_tunnel->config->service_id_3_stream_id = stream_id; + } else { + AWS_LOGF_ERROR( AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: Service Id: " PRInSTR " stream reset.", + "id=%p: Secure tunnel request for unsupported service_id: " PRInSTR, (void *)secure_tunnel, - AWS_BYTE_CURSOR_PRI(aws_byte_cursor_from_buf(&st_msg->service_id))); - } else { - // No Service ID provided, assuming this is a non-multiplexing request - if (secure_tunnel->config->stream_id == INVALID_STREAM_ID || - secure_tunnel->config->stream_id != st_msg->stream_id) { - AWS_LOGF_WARN( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "Received StreamReset with stream_id different than the active stream_id. Ignoring. " - "st_msg->stream_id=%d " - "secure_tunnel->config->stream_id=%d", - st_msg->stream_id, - secure_tunnel->config->stream_id); - return; - } - - secure_tunnel->config->stream_id = INVALID_STREAM_ID; - // s_reset_secure_tunnel(secure_tunnel); - AWS_LOGF_INFO(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: Stream reset.", (void *)secure_tunnel); + AWS_BYTE_CURSOR_PRI(aws_byte_cursor_from_buf(service_id))); + aws_string_destroy(service_id_to_set); + return AWS_OP_ERR; } - // Steve TODO Indicate that a stream has been reset. - secure_tunnel->options->on_stream_reset(secure_tunnel->options->user_data); + 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(*service_id), + stream_id); + + aws_string_destroy(service_id_to_set); + return AWS_OP_SUCCESS; } -static void s_handle_session_reset(struct aws_secure_tunnel *secure_tunnel) { - s_reset_secure_tunnel(secure_tunnel); +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); + secure_tunnel->config->on_stream_start(message_view, result, secure_tunnel->config->user_data); +} - secure_tunnel->options->on_session_reset(secure_tunnel->options->user_data); +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 = s_aws_secure_tunnel_set_stream_id(secure_tunnel, &message_view->service_id, INVALID_STREAM_ID); + secure_tunnel->config->on_stream_reset(message_view, result, secure_tunnel->config->user_data); } -static void s_process_iot_st_msg(struct aws_secure_tunnel *secure_tunnel, struct aws_iot_st_msg *st_msg) { - fprintf(stdout, "\ns_process_iot_st_msg() called\n"); - /* TODO: Check stream_id, send reset? */ +static void s_aws_secure_tunnel_on_session_reset_received(struct aws_secure_tunnel *secure_tunnel) { + s_reset_secure_tunnel(secure_tunnel); + secure_tunnel->config->on_session_reset(secure_tunnel->config->user_data); +} - switch (st_msg->type) { +static void s_aws_secure_tunnel_connected_on_message_received( + struct aws_secure_tunnel *secure_tunnel, + struct aws_secure_tunnel_message_view *message_view) { + switch (message_view->type) { case AWS_SECURE_TUNNEL_MT_DATA: - s_handle_data(secure_tunnel, st_msg); - // secure_tunnel->options->on_data_receive(&st_msg->payload, secure_tunnel->options->user_data); + secure_tunnel->config->on_message_received(message_view, secure_tunnel); break; case AWS_SECURE_TUNNEL_MT_STREAM_START: - s_handle_stream_start(secure_tunnel, st_msg); + s_aws_secure_tunnel_on_stream_start_received(secure_tunnel, message_view); break; case AWS_SECURE_TUNNEL_MT_STREAM_RESET: - s_handle_stream_reset(secure_tunnel, st_msg); + s_aws_secure_tunnel_on_stream_reset_received(secure_tunnel, message_view); break; case AWS_SECURE_TUNNEL_MT_SESSION_RESET: - s_handle_session_reset(secure_tunnel); + s_aws_secure_tunnel_on_session_reset_received(secure_tunnel); break; /* Steve todo implement processing of new message types */ case AWS_SECURE_TUNNEL_MT_SERVICE_IDS: @@ -257,11 +191,11 @@ static void s_process_iot_st_msg(struct aws_secure_tunnel *secure_tunnel, struct case AWS_SECURE_TUNNEL_MT_CONNECTION_RESET: case AWS_SECURE_TUNNEL_MT_UNKNOWN: default: - if (!st_msg->ignorable) { + if (!message_view->ignorable) { AWS_LOGF_WARN( AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "Encountered an unknown but un-ignorable message. type=%d", - st_msg->type); + "Encountered an unknown but un-ignorable message. type=%s", + aws_secure_tunnel_operation_type_to_c_string(message_view->type)); } break; } @@ -272,9 +206,11 @@ static void 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; - struct aws_byte_cursor tmp_cursor = - 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`. */ + /* + * 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) { cursor = tmp_cursor; @@ -282,13 +218,10 @@ static void s_process_received_data(struct aws_secure_tunnel *secure_tunnel) { aws_byte_cursor_advance(&cursor, data_length); tmp_cursor = cursor; - struct aws_iot_st_msg st_msg; - aws_iot_st_msg_deserialize_from_cursor(&st_msg, &st_frame, secure_tunnel->allocator); - s_process_iot_st_msg(secure_tunnel, &st_msg); - - if (st_msg.type == AWS_SECURE_TUNNEL_MT_DATA) { - aws_byte_buf_clean_up(&st_msg.payload); - } + struct aws_secure_tunnel_message_view message_view; + AWS_ZERO_STRUCT(message_view); + aws_secure_tunnel_deserialize_message_from_cursor( + secure_tunnel, &message_view, &st_frame, &s_aws_secure_tunnel_connected_on_message_received); } if (cursor.ptr != received_data->buffer) { @@ -495,8 +428,8 @@ static void s_on_websocket_shutdown(struct aws_websocket *websocket, int error_c } /* Callback for user to indicate websocket has shutdown */ - if (secure_tunnel->options->on_connection_shutdown != NULL) { - secure_tunnel->options->on_connection_shutdown(secure_tunnel->options->user_data); + if (secure_tunnel->config->on_connection_shutdown != NULL) { + secure_tunnel->config->on_connection_shutdown(secure_tunnel->config->user_data); } } @@ -686,7 +619,7 @@ static int s_websocket_connect(struct aws_secure_tunnel *secure_tunnel) { path, sizeof(path), "/tunnel?local-proxy-mode=%s", - s_get_proxy_mode_string(secure_tunnel->options->local_proxy_mode)); + s_get_proxy_mode_string(secure_tunnel->config->local_proxy_mode)); struct aws_http_message *handshake = aws_http_message_new_websocket_handshake_request( secure_tunnel->allocator, @@ -702,7 +635,7 @@ static int s_websocket_connect(struct aws_secure_tunnel *secure_tunnel) { }, { .name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("access-token"), - .value = secure_tunnel->options->access_token, + .value = secure_tunnel->config->access_token, }, }; @@ -747,9 +680,9 @@ static struct aws_http_message *s_new_handshake_request(const struct aws_secure_ path, sizeof(path), "/tunnel?local-proxy-mode=%s", - s_get_proxy_mode_string(secure_tunnel->options->local_proxy_mode)); + s_get_proxy_mode_string(secure_tunnel->config->local_proxy_mode)); struct aws_http_message *handshake_request = aws_http_message_new_websocket_handshake_request( - secure_tunnel->allocator, aws_byte_cursor_from_c_str(path), secure_tunnel->options->endpoint_host); + secure_tunnel->allocator, aws_byte_cursor_from_c_str(path), secure_tunnel->config->endpoint_host); // Steve TODO implement client token connection here @@ -760,7 +693,7 @@ static struct aws_http_message *s_new_handshake_request(const struct aws_secure_ }, { .name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("access-token"), - .value = secure_tunnel->options->access_token, + .value = secure_tunnel->config->access_token, }, }; for (size_t i = 0; i < AWS_ARRAY_SIZE(extra_headers); ++i) { @@ -777,15 +710,15 @@ void init_websocket_client_connection_options( AWS_ZERO_STRUCT(*websocket_options); websocket_options->allocator = secure_tunnel->allocator; - websocket_options->bootstrap = secure_tunnel->options->bootstrap; - websocket_options->socket_options = secure_tunnel->options->socket_options; + websocket_options->bootstrap = secure_tunnel->config->bootstrap; + websocket_options->socket_options = secure_tunnel->config->socket_options; websocket_options->tls_options = &secure_tunnel->tls_con_opt; - websocket_options->host = secure_tunnel->options->endpoint_host; + websocket_options->host = secure_tunnel->config->endpoint_host; websocket_options->port = 443; websocket_options->handshake_request = s_new_handshake_request(secure_tunnel); websocket_options->initial_window_size = MAX_WEBSOCKET_PAYLOAD; /* TODO: followup */ websocket_options->user_data = secure_tunnel; - websocket_options->proxy_options = secure_tunnel->options->http_proxy_options; + websocket_options->proxy_options = secure_tunnel->config->http_proxy_options; websocket_options->on_connection_setup = s_on_websocket_setup; websocket_options->on_connection_shutdown = s_on_websocket_shutdown; websocket_options->on_incoming_frame_begin = s_on_websocket_incoming_frame_begin; @@ -1199,7 +1132,7 @@ static int s_secure_tunneling_send_data_v2( } static int s_secure_tunneling_send_stream_start(struct aws_secure_tunnel *secure_tunnel) { - if (secure_tunnel->options->local_proxy_mode == AWS_SECURE_TUNNELING_DESTINATION_MODE) { + if (secure_tunnel->config->local_proxy_mode == AWS_SECURE_TUNNELING_DESTINATION_MODE) { AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Start can only be sent from src mode"); return AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INCORRECT_MODE; } @@ -1213,7 +1146,7 @@ static int s_secure_tunneling_send_stream_start(struct aws_secure_tunnel *secure static int s_secure_tunneling_send_stream_start_v2( struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *service_id_data) { - if (secure_tunnel->options->local_proxy_mode == AWS_SECURE_TUNNELING_DESTINATION_MODE) { + if (secure_tunnel->config->local_proxy_mode == AWS_SECURE_TUNNELING_DESTINATION_MODE) { AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Start can only be sent from src mode"); return AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INCORRECT_MODE; } @@ -1245,7 +1178,7 @@ static void s_secure_tunneling_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; - secure_tunnel->options->on_send_data_complete(error_code, pair->secure_tunnel->options->user_data); + secure_tunnel->config->on_send_data_complete(error_code, pair->secure_tunnel->config->user_data); aws_byte_buf_clean_up(&pair->buf); aws_mem_release(secure_tunnel->allocator, pair); } @@ -1854,7 +1787,7 @@ static void s_secure_tunnel_submit_operation_task_fn(struct aws_task *task, void */ struct aws_secure_tunnel *secure_tunnel = submit_operation_task->secure_tunnel; struct aws_secure_tunnel_operation *operation = submit_operation_task->operation; - if (!secure_tunnel->isConnected) { + if (secure_tunnel->current_state != AWS_STS_CONNECTED) { completion_error_code = AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_OPERATION_FAILED_DUE_TO_DISCONNECTION; goto error; } @@ -1913,9 +1846,9 @@ static void s_secure_tunnel_final_destroy(struct aws_secure_tunnel *secure_tunne aws_secure_tunneling_on_termination_complete_fn *on_termination_complete = NULL; void *termination_complete_user_data = NULL; - if (secure_tunnel->options != NULL) { + if (secure_tunnel->config != NULL) { on_termination_complete = secure_tunnel->options->on_termination_complete; - termination_complete_user_data = secure_tunnel->options->user_data; + termination_complete_user_data = secure_tunnel->config->user_data; } aws_secure_tunnel_operational_state_clean_up(secure_tunnel); @@ -1959,10 +1892,9 @@ struct aws_secure_tunnel *aws_secure_tunnel_new(const struct aws_secure_tunnel_o goto error; } secure_tunnel->config->secure_tunnel = secure_tunnel; - // secure_tunnel->options = &secure_tunnel->config->options; /* all secure tunnel activity will take place on this event loop */ - secure_tunnel->loop = aws_event_loop_group_get_next_loop(secure_tunnel->options->bootstrap->event_loop_group); + secure_tunnel->loop = aws_event_loop_group_get_next_loop(secure_tunnel->config->bootstrap->event_loop_group); if (secure_tunnel->loop == NULL) { goto error; } diff --git a/source/secure_tunneling_operations.c b/source/secure_tunneling_operations.c index 33b9b178..35374564 100644 --- a/source/secure_tunneling_operations.c +++ b/source/secure_tunneling_operations.c @@ -177,6 +177,8 @@ int aws_secure_tunnel_message_storage_init( struct aws_secure_tunnel_message_view *storage_view = &message_storage->storage_view; + storage_view->type = message_options->type; + storage_view->ignorable = message_options->ignorable; storage_view->stream_id = message_options->stream_id; storage_view->service_id = message_options->service_id; @@ -493,7 +495,6 @@ void aws_secure_tunnel_options_storage_destroy(struct aws_secure_tunnel_options_ 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->root_ca); aws_string_destroy(storage->service_id_1); aws_string_destroy(storage->service_id_2); aws_string_destroy(storage->service_id_3); @@ -545,9 +546,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 (options->root_ca != NULL) { - storage->root_ca = aws_string_new_from_c_str(allocator, &options->root_ca); - } if (options->service_id_1 != NULL) { storage->service_id_1 = aws_string_new_from_c_str(allocator, options->service_id_1); } @@ -560,6 +558,20 @@ struct aws_secure_tunnel_options_storage *aws_secure_tunnel_options_storage_new( storage->service_id_3 = aws_string_new_from_c_str(allocator, options->service_id_3); } + storage->on_message_received = options->on_message_received; + storage->user_data = options->user_data; + + /* STEVE TODO these can probably be deprecated/removed as client only supports destination mode */ + 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_data_receive = options->on_data_receive; + storage->on_stream_start = options->on_stream_start; + storage->on_stream_reset = options->on_stream_reset; + storage->on_session_reset = options->on_session_reset; + storage->on_termination_complete = options->on_termination_complete; + return storage; error: diff --git a/source/serializer.c b/source/serializer.c index e98a688f..a0ec5a48 100644 --- a/source/serializer.c +++ b/source/serializer.c @@ -308,16 +308,19 @@ static int s_aws_st_decode_lengthdelim(struct aws_byte_cursor *cursor, struct aw return AWS_OP_SUCCESS; } -int aws_iot_st_msg_deserialize_from_cursor( - struct aws_iot_st_msg *message, +int aws_secure_tunnel_deserialize_message_from_cursor( + struct aws_secure_tunnel *secure_tunnel, + struct aws_secure_tunnel_message_view *message, struct aws_byte_cursor *cursor, - struct aws_allocator *allocator) { - fprintf(stdout, "\naws_iot_st_msg_deserialize_from_cursor()"); + aws_secure_tunnel_on_message_received_fn *on_message_received) { 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; - int payload_check = 0; - int service_id_check = 0; + struct aws_byte_buf payload_buf; + struct aws_byte_buf service_id_buf; + AWS_ZERO_STRUCT(payload_buf); + AWS_ZERO_STRUCT(service_id_buf); + while ((aws_byte_cursor_is_valid(cursor)) && (cursor->len > 0)) { // wire_type is only the first 3 bits, Zeroing out the first 5 // 0x07 == 00000111 @@ -325,6 +328,9 @@ int aws_iot_st_msg_deserialize_from_cursor( field_number = (*cursor->ptr) >> 3; aws_byte_cursor_advance(cursor, 1); + /* ignorable defaults to false unless set to true in the incoming message*/ + message->ignorable = false; + switch (wire_type) { case AWS_SECURE_TUNNEL_PBWT_VARINT: { uint32_t res = 0; @@ -341,8 +347,6 @@ int aws_iot_st_msg_deserialize_from_cursor( break; case AWS_SECURE_TUNNEL_FN_IGNORABLE: message->ignorable = res; - case AWS_SECURE_TUNNEL_FN_CONNECTION_ID: - message->connection_id = res; break; default: /* Unexpected field_number */ @@ -358,24 +362,24 @@ int aws_iot_st_msg_deserialize_from_cursor( switch (field_number) { case AWS_SECURE_TUNNEL_FN_PAYLOAD: - if (aws_byte_buf_init(&message->payload, allocator, length)) { + if (aws_byte_buf_init(&payload_buf, secure_tunnel->allocator, length)) { return AWS_OP_ERR; } - if (s_aws_st_decode_lengthdelim(cursor, &message->payload, length)) { + if (s_aws_st_decode_lengthdelim(cursor, &payload_buf, length)) { goto cleanup; } aws_byte_cursor_advance(cursor, length); - payload_check = 1; + message->payload = aws_byte_cursor_from_buf(&payload_buf); break; case AWS_SECURE_TUNNEL_FN_SERVICE_ID: - if (aws_byte_buf_init(&message->service_id, allocator, length)) { + if (aws_byte_buf_init(&service_id_buf, secure_tunnel->allocator, length)) { return AWS_OP_ERR; } - if (s_aws_st_decode_lengthdelim(cursor, &message->service_id, length)) { + if (s_aws_st_decode_lengthdelim(cursor, &service_id_buf, length)) { goto cleanup; } aws_byte_cursor_advance(cursor, length); - service_id_check = 1; + message->service_id = aws_byte_cursor_from_buf(&service_id_buf); break; } } break; @@ -389,16 +393,12 @@ int aws_iot_st_msg_deserialize_from_cursor( break; } } - if (payload_check == 0) { - AWS_ZERO_STRUCT(message->payload); - } - if (service_id_check == 0) { - AWS_ZERO_STRUCT(message->service_id); - } - return AWS_OP_SUCCESS; + + on_message_received(secure_tunnel, message); + cleanup: - aws_byte_buf_clean_up(&message->payload); - aws_byte_buf_clean_up(&message->service_id); + aws_byte_buf_clean_up(&payload_buf); + aws_byte_buf_clean_up(&service_id_buf); return AWS_OP_ERR; } From b7a5b00a560007ddadadd0e6aa36db9ee3240999 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Fri, 13 Jan 2023 16:49:25 -0800 Subject: [PATCH 08/69] continued implementation of operations and tasks --- include/aws/iotdevice/iotdevice.h | 3 + .../iotdevice/private/secure_tunneling_impl.h | 95 +- .../private/secure_tunneling_operations.h | 13 +- include/aws/iotdevice/private/serializer.h | 52 -- include/aws/iotdevice/secure_tunneling.h | 65 +- source/iotdevice.c | 21 + source/secure_tunneling.c | 860 +++++++++++------- source/secure_tunneling_operations.c | 36 +- 8 files changed, 733 insertions(+), 412 deletions(-) diff --git a/include/aws/iotdevice/iotdevice.h b/include/aws/iotdevice/iotdevice.h index bf3984a5..d0e6b4bb 100644 --- a/include/aws/iotdevice/iotdevice.h +++ b/include/aws/iotdevice/iotdevice.h @@ -27,7 +27,10 @@ enum aws_iotdevice_error { AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_OPTIONS_VALIDATION, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_STREAM_OPTIONS_VALIDATION, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_SECURE_TUNNEL_TERMINATED, + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_WEBSOCKET_TIMEOUT, + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_PING_RESPONSE_TIMEOUT, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_OPERATION_FAILED_DUE_TO_DISCONNECTION, + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_OPERATION_PROCESSING_FAILURE, 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, diff --git a/include/aws/iotdevice/private/secure_tunneling_impl.h b/include/aws/iotdevice/private/secure_tunneling_impl.h index c954eaac..0e42f73a 100644 --- a/include/aws/iotdevice/private/secure_tunneling_impl.h +++ b/include/aws/iotdevice/private/secure_tunneling_impl.h @@ -158,6 +158,7 @@ struct aws_secure_tunnel_vtable { int (*client_bootstrap_new_socket_channel_fn)(struct aws_socket_channel_bootstrap_options *options); int (*connect)(struct aws_secure_tunnel *secure_tunnel); + int (*close)(struct aws_secure_tunnel *secure_tunnel); int (*send_data)(struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *data); int (*send_data_v2)( struct aws_secure_tunnel *secure_tunnel, @@ -165,7 +166,22 @@ struct aws_secure_tunnel_vtable { int (*send_stream_start)(struct aws_secure_tunnel *secure_tunnel); int (*send_stream_start_v2)(struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *service_id_data); int (*send_stream_reset)(struct aws_secure_tunnel *secure_tunnel); - int (*close)(struct aws_secure_tunnel *secure_tunnel); + + /* aws_channel_acquire_message_from_pool */ + struct aws_io_message *(*aws_channel_acquire_message_from_pool_fn)( + struct aws_channel *channel, + enum aws_io_message_type message_type, + size_t size_hint, + void *user_data); + + /* aws_channel_slot_send_message */ + int (*aws_channel_slot_send_message_fn)( + struct aws_channel_slot *slot, + struct aws_io_message *message, + enum aws_channel_direction dir, + void *user_data); + + void *vtable_user_data; }; struct aws_websocket_client_connection_options; @@ -181,31 +197,35 @@ struct aws_websocket_vtable { struct aws_secure_tunnel { /* Static settings */ struct aws_allocator *allocator; + struct aws_ref_count ref_count; + + struct aws_secure_tunnel_vtable *vtable; + + /* + * Secure tunnel configuration + */ struct aws_secure_tunnel_options_storage *config; + struct aws_tls_ctx *tls_ctx; struct aws_tls_connection_options tls_con_opt; - struct aws_secure_tunnel_vtable vtable; - struct aws_websocket_vtable websocket_vtable; - - struct aws_ref_count ref_count; - - /* Used to check connection state */ - bool isConnected; + /* + * The recurrent task that runs all secure tunnel logic outside of external event callbacks. Bound to the secure + * tunnel's event loop. + */ + struct aws_task service_task; /* - * Temporary state-related data. - * - * clean_disconnect_error_code - the CLEAN_DISCONNECT state takes time to complete and we want to be able - * to pass an error code from a prior event to the channel shutdown. This holds the "override" error code - * that we'd like to shutdown the channel with while CLEAN_DISCONNECT is processed. + * Tracks when the secure tunnel's service task is next schedule to run. Is zero if the task is not scheduled to + * run or we are in the middle of a service (so technically not scheduled too). */ - int clean_disconnect_error_code; + uint64_t next_service_task_run_time; /* - * handshake_request exists between the transform completion timepoint and the websocket setup callback. + * True if the secure tunnel's service task is running. Used to skip service task reevaluation due to state changes + * while running the service task. Reevaluation will occur at the very end of the service. */ - struct aws_http_message *handshake_request; + bool in_service; /* * Event loop all the secure tunnel's tasks will be pinned to, ensuring serialization and @@ -213,12 +233,10 @@ struct aws_secure_tunnel { */ struct aws_event_loop *loop; - /* Channel handler information */ + /* Channel handler information */ /* STEVE TODO is this necessary for a websocket? */ struct aws_channel_handler handler; struct aws_channel_slot *slot; - /* Dynamic data */ - /* * What state is the secure tunnel working towards? */ @@ -229,25 +247,34 @@ struct aws_secure_tunnel { */ enum aws_secure_tunnel_state current_state; - struct aws_websocket *websocket; - - /* Stores what has been received but not processed */ - struct aws_byte_buf received_data; + struct aws_websocket_vtable websocket_vtable; - /* Error code of last connection attempt */ - int connection_error_code; + /* Used to check connection state */ + bool isConnected; /* - * The recurrent task that runs all secure tunnel's logic outside of external event callbacks. Bound to the secure - * tunnel's event loop. + * Temporary state-related data. + * + * clean_disconnect_error_code - the CLEAN_DISCONNECT state takes time to complete and we want to be able + * to pass an error code from a prior event to the channel shutdown. This holds the "override" error code + * that we'd like to shutdown the channel with while CLEAN_DISCONNECT is processed. */ - struct aws_task service_task; + int clean_disconnect_error_code; /* - * Tracks when the secure tunnel's service task is next schedule to run. Is zero if the task is not scheduled to - * run or we are in the middle of a service (so technically not scheduled too). + * handshake_request exists between the transform completion timepoint and the websocket setup callback. */ - uint64_t next_service_task_run_time; + struct aws_http_message *handshake_request; + + /* Dynamic data */ + + struct aws_websocket *websocket; + + /* Stores what has been received but not processed */ + struct aws_byte_buf received_data; + + /* Error code of last connection attempt */ + int connection_error_code; /* * When should the secure tunnel next attempt to reconnect? Only used by PENDING_RECONNECT state. @@ -266,12 +293,6 @@ struct aws_secure_tunnel { */ uint64_t next_secure_tunnel_websocket_connect_timeout_time; - /* - * True if the secure tunnel's service task is running. Used to skip service task reevaluation due to state changes - * while running the service task. Reevaluation will occur at the very end of the service. - */ - bool in_service; - struct aws_linked_list queued_operations; struct aws_linked_list write_completion_operations; struct aws_secure_tunnel_operation *current_operation; diff --git a/include/aws/iotdevice/private/secure_tunneling_operations.h b/include/aws/iotdevice/private/secure_tunneling_operations.h index ae480f83..c8d850fd 100644 --- a/include/aws/iotdevice/private/secure_tunneling_operations.h +++ b/include/aws/iotdevice/private/secure_tunneling_operations.h @@ -81,15 +81,9 @@ struct aws_secure_tunnel_operation_message { struct aws_secure_tunnel_message_storage options_storage; }; -struct aws_secure_tunnel_operation_disconnect { +struct aws_secure_tunnel_operation_pingreq { struct aws_secure_tunnel_operation base; struct aws_allocator *allocator; - - /* STEVE TODO disconnect shouldn't be necessary */ - // struct aws_secure_tunnel_packet_disconnect_storage options_storage; - - struct aws_secure_tunnel_disconnect_completion_options external_completion_options; - struct aws_secure_tunnel_disconnect_completion_options internal_completion_options; }; /* @@ -191,6 +185,11 @@ struct aws_secure_tunnel_operation_message *aws_secure_tunnel_operation_message_ const struct aws_secure_tunnel *secure_tunnel, const struct aws_secure_tunnel_message_view *message_options); +/* Ping */ + +AWS_IOTDEVICE_API +struct aws_secure_tunnel_operation_pingreq *aws_secure_tunnel_operation_pingreq_new(struct aws_allocator *allocator); + /* Secure Tunnel Storage Options */ /** diff --git a/include/aws/iotdevice/private/serializer.h b/include/aws/iotdevice/private/serializer.h index 1f6d1df3..b31fc257 100644 --- a/include/aws/iotdevice/private/serializer.h +++ b/include/aws/iotdevice/private/serializer.h @@ -36,58 +36,6 @@ enum aws_secure_tunnel_protocol_buffer_wire_type { AWS_SECURE_TUNNEL_PBWT_32_BIT = 5, /* fixed32, sfixed32, float */ }; -/** - * Type of IoT Secure Tunnel message. - * Enum values match IoT Secure Tunneling Local Proxy V3 Websocket Protocol Guide values. - * - * https://github.com/aws-samples/aws-iot-securetunneling-localproxy/blob/main/V3WebSocketProtocolGuide.md - */ -enum aws_secure_tunnel_message_type { - AWS_SECURE_TUNNEL_MT_UNKNOWN = 0, - - /** - * Data messages carry a payload with a sequence of bytes to write to the the active data stream - */ - AWS_SECURE_TUNNEL_MT_DATA = 1, - - /** - * StreamStart is the first message sent to start and establish a new and active data stream. This should only be - * sent from a Source to a Destination. - */ - AWS_SECURE_TUNNEL_MT_STREAM_START = 2, - - /** - * StreamReset messages convey that the data stream has ended, either in error, or closed intentionally for the - * tunnel peer. It is also sent to the source tunnel peer if an attempt to establish a new data stream fails on the - * destination side. - */ - AWS_SECURE_TUNNEL_MT_STREAM_RESET = 3, - - /** - * SessionReset messages can only originate from Secure Tunneling service if an internal data transmission error is - * detected. This will result in all active streams being closed. - */ - AWS_SECURE_TUNNEL_MT_SESSION_RESET = 4, - - /** - * ServiceIDs messages can only originate from the Secure Tunneling service and carry a list of unique service IDs - * used when opening a tunnel with services. - */ - AWS_SECURE_TUNNEL_MT_SERVICE_IDS = 5, - - /** - * ConnectionStart is the message sent to start and establish a new and active connection when the stream has been - * established and there's one active connection in the stream. - */ - AWS_SECURE_TUNNEL_MT_CONNECTION_START = 6, - - /** - * ConnectionReset messages convey that the connection has ended, either in error, or closed intentionally for the - * tunnel peer. - */ - AWS_SECURE_TUNNEL_MT_CONNECTION_RESET = 7 -}; - /** * A single IoT Secure Tunnel Message * STEVE TODO remove this. replaced with aws_secure_tunnel_message_view diff --git a/include/aws/iotdevice/secure_tunneling.h b/include/aws/iotdevice/secure_tunneling.h index da3674db..a0987ae8 100644 --- a/include/aws/iotdevice/secure_tunneling.h +++ b/include/aws/iotdevice/secure_tunneling.h @@ -11,14 +11,66 @@ #define AWS_IOT_ST_SPLIT_MESSAGE_SIZE 15000 -/* STEVE TODO remove or move to private. We only support Destination Mode */ -enum aws_secure_tunneling_local_proxy_mode { AWS_SECURE_TUNNELING_SOURCE_MODE, AWS_SECURE_TUNNELING_DESTINATION_MODE }; - struct aws_secure_tunnel; struct aws_websocket; struct aws_websocket_incoming_frame; struct aws_http_proxy_options; +/* STEVE TODO remove or move to private. We only support Destination Mode */ +enum aws_secure_tunneling_local_proxy_mode { AWS_SECURE_TUNNELING_SOURCE_MODE, AWS_SECURE_TUNNELING_DESTINATION_MODE }; + +/** + * Type of IoT Secure Tunnel message. + * Enum values match IoT Secure Tunneling Local Proxy V3 Websocket Protocol Guide values. + * + * https://github.com/aws-samples/aws-iot-securetunneling-localproxy/blob/main/V3WebSocketProtocolGuide.md + */ +enum aws_secure_tunnel_message_type { + AWS_SECURE_TUNNEL_MT_UNKNOWN = 0, + + /** + * Data messages carry a payload with a sequence of bytes to write to the the active data stream + */ + AWS_SECURE_TUNNEL_MT_DATA = 1, + + /** + * StreamStart is the first message sent to start and establish a new and active data stream. This should only be + * sent from a Source to a Destination. + */ + AWS_SECURE_TUNNEL_MT_STREAM_START = 2, + + /** + * StreamReset messages convey that the data stream has ended, either in error, or closed intentionally for the + * tunnel peer. It is also sent to the source tunnel peer if an attempt to establish a new data stream fails on the + * destination side. + */ + AWS_SECURE_TUNNEL_MT_STREAM_RESET = 3, + + /** + * SessionReset messages can only originate from Secure Tunneling service if an internal data transmission error is + * detected. This will result in all active streams being closed. + */ + AWS_SECURE_TUNNEL_MT_SESSION_RESET = 4, + + /** + * ServiceIDs messages can only originate from the Secure Tunneling service and carry a list of unique service IDs + * used when opening a tunnel with services. + */ + AWS_SECURE_TUNNEL_MT_SERVICE_IDS = 5, + + /** + * ConnectionStart is the message sent to start and establish a new and active connection when the stream has been + * established and there's one active connection in the stream. + */ + AWS_SECURE_TUNNEL_MT_CONNECTION_START = 6, + + /** + * ConnectionReset messages convey that the connection has ended, either in error, or closed intentionally for the + * tunnel peer. + */ + AWS_SECURE_TUNNEL_MT_CONNECTION_RESET = 7 +}; + /** * Read-only snapshot of a Secure Tunnel Message */ @@ -135,11 +187,6 @@ struct aws_secure_tunnel_disconnect_completion_options { /* deprecated: "_config" is renamed "_options" for consistency with similar code in the aws-c libraries */ #define aws_secure_tunneling_connection_config aws_secure_tunnel_options -/** - * Persistent storage for aws_secure_tunnel_options. - */ -struct aws_secure_tunnel_options_storage; - AWS_EXTERN_C_BEGIN /** @@ -200,7 +247,7 @@ int aws_secure_tunnel_stop(struct aws_secure_tunnel *secure_tunnel); * @return success/failure in the synchronous logic that kicks off the message operation */ AWS_IOTDEVICE_API -int aws_secure_tunnel_send_message_new( +int aws_secure_tunnel_send_message( struct aws_secure_tunnel *secure_tunnel, const struct aws_secure_tunnel_message_view *message_options); diff --git a/source/iotdevice.c b/source/iotdevice.c index 37a48b50..619c8f82 100644 --- a/source/iotdevice.c +++ b/source/iotdevice.c @@ -57,6 +57,27 @@ static struct aws_error_info s_errors[] = { AWS_DEFINE_ERROR_INFO_IOTDEVICE( AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_OPERATION_FAILED_DUE_TO_DISCONNECTION, "Secure Tunnel operation failed due to disconnected state."), + AWS_DEFINE_ERROR_INFO_IOTDEVICE( + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_WEBSOCKET_TIMEOUT, + "Remote endpoint did not respond to connect request before timeout exceeded."), + AWS_DEFINE_ERROR_INFO_IOTDEVICE( + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_PING_RESPONSE_TIMEOUT, + "Remote endpoint did not respond to a PINGREQ before timeout exceeded."), + AWS_DEFINE_ERROR_INFO_IOTDEVICE( + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_OPERATION_FAILED_DUE_TO_OFFLINE_QUEUE_POLICY, + "Error while processing secure tunnel operational state."), + AWS_DEFINE_ERROR_INFO_IOTDEVICE( + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_OPERATION_PROCESSING_FAILURE, + ""), + AWS_DEFINE_ERROR_INFO_IOTDEVICE( + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_UNEXPECTED_HANGUP, + "The connection was closed unexpectedly."), + 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_TERMINATED, + "Secure Tunnel terminated by user request."), }; /* clang-format on */ #undef AWS_DEFINE_ERROR_INFO_IOTDEVICE diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 43ab8efc..e54d24e1 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -20,6 +20,7 @@ #include #define MAX_WEBSOCKET_PAYLOAD 131076 +#define AWS_SECURE_TUNNEL_IO_MESSAGE_DEFAULT_LENGTH 4096 #define INVALID_STREAM_ID 0 #define PAYLOAD_BYTE_LENGTH_PREFIX 2 #define PING_TASK_INTERVAL ((uint64_t)20 * 1000000000) @@ -27,10 +28,6 @@ // Steve TODO check if these need to be implemented or re-organized static void s_change_current_state(struct aws_secure_tunnel *secure_tunnel, enum aws_secure_tunnel_state next_state); -int aws_secure_tunnel_bind_stream_id( - struct aws_secure_tunnel *secure_tunnel, - struct aws_secure_tunnel_operation_data *operation); - static int s_secure_tunneling_send( struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *payload_data, @@ -39,7 +36,6 @@ static int s_secure_tunneling_send( static int s_websocket_connect(struct aws_secure_tunnel *secure_tunnel){}; -static int s_submit_operation(struct aws_secure_tunnel *secure_tunnel, struct aws_secure_tunnel_operation *operation); 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) { @@ -81,7 +77,7 @@ static const char *s_get_proxy_mode_string(enum aws_secure_tunneling_local_proxy } /***************************************************************************************************************** - * MESSAGE HANDLING + * RECEIVED MESSAGE HANDLING *****************************************************************************************************************/ /* @@ -764,11 +760,19 @@ static void s_aws_secure_tunnel_shutdown_channel(struct aws_secure_tunnel *secur /********************************************************************************************************************* * State Related ********************************************************************************************************************/ + static void s_complete_operation_list( struct aws_secure_tunnel *secure_tunnel, struct aws_linked_list *operation_list, int error_code); +static void s_aws_secure_tunnel_operational_state_reset( + struct aws_secure_tunnel *secure_tunnel, + int completion_error_code) { + s_complete_operation_list(secure_tunnel, &secure_tunnel->queued_operations, completion_error_code); + s_complete_operation_list(secure_tunnel, &secure_tunnel->write_completion_operations, completion_error_code); +} + static void s_change_current_state_to_stopped(struct aws_secure_tunnel *secure_tunnel) { secure_tunnel->current_state = AWS_STS_STOPPED; @@ -897,6 +901,13 @@ static bool s_is_valid_desired_state(enum aws_secure_tunnel_state desired_state) } } +struct aws_secure_tunnel_change_desired_state_task { + struct aws_task task; + struct aws_allocator *allocator; + struct aws_secure_tunnel *secure_tunnel; + enum aws_secure_tunnel_state desired_state; +}; + static void s_change_state_task_fn(struct aws_task *task, void *arg, enum aws_task_status status) { (void)task; @@ -917,18 +928,11 @@ static void s_change_state_task_fn(struct aws_task *task, void *arg, enum aws_ta secure_tunnel->desired_state = desired_state; - struct aws_secure_tunnel_operation_disconnect *disconnect_op = change_state_task->disconnect_operation; - if (desired_state == AWS_STS_STOPPED && disconnect_op != NULL) { - s_aws_secure_tunnel_shutdown_channel_with_disconnect( - secure_tunnel, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_USER_REQUESTED_STOP, disconnect_op); - } - s_reevaluate_service_task(secure_tunnel); } done: - aws_secure_tunnel_operation_disconnect_release(change_state_task->disconnect_operation); if (desired_state != AWS_STS_TERMINATED) { aws_secure_tunnel_release(secure_tunnel); } @@ -939,8 +943,7 @@ static void s_change_state_task_fn(struct aws_task *task, void *arg, enum aws_ta static struct aws_secure_tunnel_change_desired_state_task *s_aws_secure_tunnel_change_desired_state_task_new( struct aws_allocator *allocator, struct aws_secure_tunnel *secure_tunnel, - enum aws_secure_tunnel_state desired_state, - struct aws_secure_tunnel_operation_disconnect *disconnect_operation) { + enum aws_secure_tunnel_state desired_state) { struct aws_secure_tunnel_change_desired_state_task *change_state_task = aws_mem_calloc(allocator, 1, sizeof(struct aws_secure_tunnel_change_desired_state_task)); @@ -953,26 +956,15 @@ static struct aws_secure_tunnel_change_desired_state_task *s_aws_secure_tunnel_c change_state_task->secure_tunnel = (desired_state == AWS_STS_TERMINATED) ? secure_tunnel : aws_secure_tunnel_acquire(secure_tunnel); change_state_task->desired_state = desired_state; - change_state_task->disconnect_operation = aws_secure_tunnel_operation_disconnect_acquire(disconnect_operation); return change_state_task; } -struct aws_secure_tunnel_change_desired_state_task { - struct aws_task task; - struct aws_allocator *allocator; - struct aws_secure_tunnel *secure_tunnel; - enum aws_secure_tunnel_state desired_state; - struct aws_secure_tunnel_operation_disconnect *disconnect_operation; -}; - static int s_aws_secure_tunnel_change_desired_state( struct aws_secure_tunnel *secure_tunnel, - enum aws_secure_tunnel_state desired_state, - struct aws_secure_tunnel_operation_disconnect *disconnect_operation) { + enum aws_secure_tunnel_state desired_state) { AWS_FATAL_ASSERT(secure_tunnel != NULL); AWS_FATAL_ASSERT(secure_tunnel->loop != NULL); - AWS_FATAL_ASSERT(disconnect_operation == NULL || desired_state == AWS_STS_STOPPED); if (!s_is_valid_desired_state(desired_state)) { AWS_LOGF_ERROR( @@ -985,9 +977,9 @@ static int s_aws_secure_tunnel_change_desired_state( return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); } - struct aws_secure_tunnel_change_desired_state_task *task = s_aws_secure_tunnel_change_desired_state_task_new( - secure_tunnel->allocator, secure_tunnel, desired_state, disconnect_operation); - if(task == NULL{ + struct aws_secure_tunnel_change_desired_state_task *task = + s_aws_secure_tunnel_change_desired_state_task_new(secure_tunnel->allocator, secure_tunnel, desired_state); + if (task == NULL) { AWS_LOGF_ERROR( AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: failed to create change desired state task", @@ -1066,71 +1058,6 @@ static int s_secure_tunneling_send_data( return AWS_OP_SUCCESS; } -static int s_secure_tunneling_send_data_v2( - struct aws_secure_tunnel *secure_tunnel, - const struct aws_secure_tunnel_message_data_view *data_options) { - AWS_PRECONDITION(secure_tunnel != NULL); - AWS_PRECONDITION(data_options != NULL); - - /* STEVE TODO TEMP LOGGING */ - aws_secure_tunnel_message_data_view_log(data_options, AWS_LL_DEBUG); - - struct aws_secure_tunnel_operation_data *data_op = - aws_secure_tunnel_operation_data_new(secure_tunnel->allocator, secure_tunnel, data_options); - - if (data_op == NULL) { - return AWS_OP_ERR; - } - - if (aws_secure_tunnel_bind_stream_id(secure_tunnel, data_op)) { - int error_code = aws_last_error(); - AWS_LOGF_ERROR( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: failed to bind secure tunnel stream id for data message operation, with error %d(%s)", - (void *)secure_tunnel, - error_code, - aws_error_debug_str(error_code)); - } - - if (s_submit_operation(secure_tunnel, &data_op->base)) { - goto error; - } - -error: - - aws_secure_tunnel_operation_release(&data_op->base); - return AWS_OP_ERR; - - // if (secure_tunnel->config->stream_id == INVALID_STREAM_ID) { - // AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Invalid Stream Id"); - // return AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_STREAM; - // } - // struct aws_byte_cursor new_data = *payload_data; - // struct aws_byte_cursor service_id_cursor = *service_id_data; - // size_t service_id_len = 0; - // if (service_id_data) { - // service_id_len = service_id_data->len; - // } - // while (new_data.len) { - // size_t bytes_max = new_data.len; - // size_t amount_to_send = bytes_max + service_id_len < AWS_IOT_ST_SPLIT_MESSAGE_SIZE - // ? bytes_max - // : AWS_IOT_ST_SPLIT_MESSAGE_SIZE - service_id_len; - - // struct aws_byte_cursor send_cursor = aws_byte_cursor_advance(&new_data, amount_to_send); - // AWS_FATAL_ASSERT(send_cursor.len > 0); - // if (send_cursor.len) { - // if (s_secure_tunneling_send(secure_tunnel, &send_cursor, &service_id_cursor, AWS_SECURE_TUNNEL_MT_DATA) - // != - // AWS_OP_SUCCESS) { - // AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Failure writing data to out_buf"); - // return AWS_OP_ERR; - // } - // } - // } - return AWS_OP_SUCCESS; -} - static int s_secure_tunneling_send_stream_start(struct aws_secure_tunnel *secure_tunnel) { if (secure_tunnel->config->local_proxy_mode == AWS_SECURE_TUNNELING_DESTINATION_MODE) { AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Start can only be sent from src mode"); @@ -1298,53 +1225,85 @@ static int s_secure_tunneling_send( return secure_tunnel->websocket_vtable.send_frame(secure_tunnel->websocket, &frame_options); } +struct aws_io_message *s_aws_channel_acquire_message_from_pool_default( + struct aws_channel *channel, + enum aws_io_message_type message_type, + size_t size_hint, + void *user_data) { + (void)user_data; + + return aws_channel_acquire_message_from_pool(channel, message_type, size_hint); +} + +static int s_aws_channel_slot_send_message_default( + struct aws_channel_slot *slot, + struct aws_io_message *message, + enum aws_channel_direction dir, + void *user_data) { + (void)user_data; + + return aws_channel_slot_send_message(slot, message, dir); +} + +static struct aws_secure_tunnel_vtable s_default_secure_tunnel_vtable = { + .get_current_time_fn = s_aws_high_res_clock_get_ticks_proxy, + .channel_shutdown_fn = aws_channel_shutdown, + .websocket_connect_fn = aws_websocket_client_connect, + .client_bootstrap_new_socket_channel_fn = aws_client_bootstrap_new_socket_channel, + .http_proxy_new_socket_channel_fn = aws_http_proxy_new_socket_channel, + + .aws_channel_acquire_message_from_pool_fn = s_aws_channel_acquire_message_from_pool_default, + .aws_channel_slot_send_message_fn = s_aws_channel_slot_send_message_default, + + .vtable_user_data = NULL, + + /* STEVE TODO remove these after changing the API to make these operations/tasks */ + .connect = s_secure_tunneling_connect, + .close = s_secure_tunneling_close, + .send_data = s_secure_tunneling_send_data, + .send_stream_start = s_secure_tunneling_send_stream_start, + .send_stream_start_v2 = s_secure_tunneling_send_stream_start_v2, + .send_stream_reset = s_secure_tunneling_send_stream_reset, +}; + /********************************************************************************************************************* * Operations ********************************************************************************************************************/ -int aws_secure_tunnel_get_stream_id_for_service(struct aws_secure_tunnel *secure_tunnel) { - /* Todo Steve Implement this */ +static void s_complete_operation( + struct aws_secure_tunnel *secure_tunnel, + struct aws_secure_tunnel_operation *operation, + int error_code, + const void *view) { + // Steve TODO Stat tracking can be added here in the future. (void)secure_tunnel; + + aws_secure_tunnel_operation_complete(operation, error_code, view); + aws_secure_tunnel_operation_release(operation); } -int aws_secure_tunnel_operation_bind_stream_id( - struct aws_secure_tunnel_operation *operation, - struct aws_secure_tunnel *secure_tunnel) { - // Steve TODO check the stream id binding for any messages that need stream ids to be set. - switch (operation->operation_type) { - case AWS_STOT_DATA: - operation->vtable->aws_secure_tunnel_operation_set_stream_id_fn(operation, secure_tunnel); - return AWS_OP_SUCCESS; - - case AWS_STOT_NONE: - case AWS_STOT_CONNECT: - case AWS_STOT_PING: - case AWS_STOT_STREAM_RESET: - case AWS_STOT_STREAM_START: - return AWS_OP_SUCCESS; - } - if (operation->operation_type == AWS_STOT_DATA) { +static void s_complete_operation_list( + struct aws_secure_tunnel *secure_tunnel, + struct aws_linked_list *operation_list, + int error_code) { - return AWS_OP_SUCCESS; + struct aws_linked_list_node *node = aws_linked_list_begin(operation_list); + while (node != aws_linked_list_end(operation_list)) { + struct aws_secure_tunnel_operation *operation = + AWS_CONTAINER_OF(node, struct aws_secure_tunnel_operation, node); + + node = aws_linked_list_next(node); + + s_complete_operation(secure_tunnel, operation, error_code, NULL); } - aws_raise_error(AWS_ERROR_INVALID_STATE); - return AWS_OP_ERR; + /* we've released everything, so reset the list to empty */ + aws_linked_list_init(operation_list); } static int s_aws_secure_tunnel_set_current_operation( struct aws_secure_tunnel *secure_tunnel, struct aws_secure_tunnel_operation *operation) { - if (aws_secure_tunnel_operation_bind_stream_id(operation, secure_tunnel)) { - int error_code = aws_last_error(); - AWS_LOGF_ERROR( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: failed to append stream id for current operation with error %d(%s)", - (void *)secure_tunnel, - error_code, - aws_error_debug_str(error_code)); - return AWS_OP_ERR; - } secure_tunnel->current_operation = operation; @@ -1480,9 +1439,6 @@ static bool s_aws_secure_tunnel_has_pending_operational_work(const struct aws_se } } -/* - * - */ static uint64_t s_aws_secure_tunnel_compute_operational_state_service_time( struct aws_secure_tunnel *secure_tunnel, uint64_t now) { @@ -1504,6 +1460,13 @@ static uint64_t s_aws_secure_tunnel_compute_operational_state_service_time( AWS_FATAL_ASSERT(!aws_linked_list_empty(&secure_tunnel->queued_operations)); + 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); + + AWS_FATAL_ASSERT(next_operation != NULL); + + /* now unless outside of allowed states */ switch (secure_tunnel->current_state) { case AWS_STS_SECURE_TUNNEL_CONNECT: case AWS_STS_CLEAN_DISCONNECT: @@ -1525,55 +1488,215 @@ static bool s_aws_secure_tunnel_should_service_operational_state( int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure_tunnel) { struct aws_channel_slot *slot = secure_tunnel->slot; const struct aws_secure_tunnel_vtable *vtable = secure_tunnel->vtable; + uint64_t now = (*vtable->get_current_time_fn)(); + + /* Should we write data? */ + bool should_service = s_aws_secure_tunnel_should_service_operational_state(secure_tunnel, now); + if (!should_service) { + return AWS_OP_SUCCESS; + } + + /* If we're going to write data, we need something to write to */ + struct aws_io_message *io_message = (*vtable->aws_channel_acquire_message_from_pool_fn)( + slot->channel, + AWS_IO_MESSAGE_APPLICATION_DATA, + AWS_SECURE_TUNNEL_IO_MESSAGE_DEFAULT_LENGTH, + vtable->vtable_user_data); + if (io_message == NULL) { + return AWS_OP_ERR; + } + + int operational_error_code = AWS_ERROR_SUCCESS; + + do { + /* if no current operation, pull one in and setup encode */ + if (secure_tunnel->current_operation == NULL) { + /* + * Loop through queued operations until we run out or find a good one. + */ + struct aws_secure_tunnel_operation *next_operation = NULL; + if (!aws_linked_list_empty(&secure_tunnel->queued_operations)) { + struct aws_linked_list_node *next_operation_node = + aws_linked_list_pop_front(&secure_tunnel->queued_operations); + + next_operation = AWS_CONTAINER_OF(next_operation_node, struct aws_secure_tunnel_operation, node); + + if (s_aws_secure_tunnel_set_current_operation(secure_tunnel, next_operation)) { + operational_error_code = AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_STREAM; + } + } + } + + struct aws_secure_tunnel_operation *current_operation = secure_tunnel->current_operation; + if (current_operation == NULL) { + break; + } + + /* write current operation to message, handle errors */ + /* STEVE TODO, encoding of message happens here */ + bool encoding_result = true; // This needs to be a result from the encoding of message + + /* + * if encoding finished: + * push to write completion + * clear current + * else (message full) + * break + */ + if (encoding_result) { - // steve TODO voiding slot for warning atm - (void)slot; + } else { + // AWS_FATAL_ASSERT(encoding_result == AWS_SECURE_TUNNEL_ER_OUT_OF_ROOM); + break; + } + + now = (*vtable->get_current_time_fn)(); + should_service = s_aws_secure_tunnel_should_service_operational_state(secure_tunnel, now); + } while (should_service); + + if (operational_error_code != AWS_ERROR_SUCCESS) { + aws_mem_release(io_message->allocator, io_message); + return aws_raise_error(operational_error_code); + } + + /* It's possible for there to be no data if we serviced operations that failed validation */ + if (io_message->message_data.len == 0) { + aws_mem_release(io_message->allocator, io_message); + return AWS_OP_SUCCESS; + } + + /* send io_message down channel in write direction, handle errors */ + io_message->on_completion = s_aws_secure_tunnel_on_socket_write_completion; + io_message->user_data = secure_tunnel; + secure_tunnel->pending_write_completion = true; + + if ((*vtable->aws_channel_slot_send_message_fn)( + slot, io_message, AWS_CHANNEL_DIR_WRITE, vtable->vtable_user_data)) { + secure_tunnel->pending_write_completion = false; + aws_mem_release(io_message->allocator, io_message); + return AWS_OP_ERR; + } + + return AWS_OP_SUCCESS; } -static void s_complete_operation( +void aws_secure_tunnel_operational_state_clean_up(struct aws_secure_tunnel *secure_tunnel) { + AWS_ASSERT(secure_tunnel->current_operation == NULL); + + s_aws_secure_tunnel_operational_state_reset(secure_tunnel, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_TERMINATED); +} + +static void s_enqueue_operation_back( struct aws_secure_tunnel *secure_tunnel, - struct aws_secure_tunnel_operation *operation, - int error_code, - const void *view) { - // Steve TODO keep this for now in case we want to do some form of stats tracking - (void)secure_tunnel; + struct aws_secure_tunnel_operation *operation) { + AWS_LOGF_DEBUG( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: enqueuing %s operation to back", + (void *)secure_tunnel, + aws_secure_tunnel_operation_type_to_c_string(operation->operation_type)); - aws_secure_tunnel_operation_complete(operation, error_code, view); - aws_secure_tunnel_operation_release(operation); + aws_linked_list_push_back(&secure_tunnel->queued_operations, &operation->node); + + s_reevaluate_service_task(secure_tunnel); } -static void s_complete_operation_list( +static void s_enqueue_operation_front( struct aws_secure_tunnel *secure_tunnel, - struct aws_linked_list *operation_list, - int error_code) { + struct aws_secure_tunnel_operation *operation) { + AWS_LOGF_DEBUG( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: enqueuing %s operation to front", + (void *)secure_tunnel, + aws_secure_tunnel_operation_type_to_c_string(operation->operation_type)); - struct aws_linked_list_node *node = aws_linked_list_begin(operation_list); - while (node != aws_linked_list_end(operation_list)) { - struct aws_secure_tunnel_operation *operation = - AWS_CONTAINER_OF(node, struct aws_secure_tunnel_operation, node); + aws_linked_list_push_front(&secure_tunnel->queued_operations, &operation->node); - node = aws_linked_list_next(node); + s_reevaluate_service_task(secure_tunnel); +} - s_complete_operation(secure_tunnel, operation, error_code, NULL); +struct aws_secure_tunnel_submit_operation_task { + struct aws_task task; + struct aws_allocator *allocator; + struct aws_secure_tunnel *secure_tunnel; + struct aws_secure_tunnel_operation *operation; +}; + +static void s_secure_tunnel_submit_operation_task_fn(struct aws_task *task, void *arg, enum aws_task_status status) { + (void)task; + + int completion_error_code = AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_SECURE_TUNNEL_TERMINATED; + struct aws_secure_tunnel_submit_operation_task *submit_operation_task = arg; + + /* + * Take a ref to the operation that represents the secure tunnel taking ownership + * If we subsequently reject it (task cancel), then the operation completion + * will undo this ref acquisition. + */ + aws_secure_tunnel_operation_acquire(submit_operation_task->operation); + + if (status != AWS_TASK_STATUS_RUN_READY) { + goto error; } - /* we've released everything, so reset the list to empty */ - aws_linked_list_init(operation_list); + /* + * If we're offline fail it immediately. + */ + struct aws_secure_tunnel *secure_tunnel = submit_operation_task->secure_tunnel; + struct aws_secure_tunnel_operation *operation = submit_operation_task->operation; + if (secure_tunnel->current_state != AWS_STS_CONNECTED) { + completion_error_code = AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_OPERATION_FAILED_DUE_TO_DISCONNECTION; + goto error; + } + + /* Set the stream ID of message */ + aws_secure_tunnel_operation_set_stream_id(operation, secure_tunnel); + + s_enqueue_operation_back(submit_operation_task->secure_tunnel, submit_operation_task->operation); + + goto done; + +error: + s_complete_operation(NULL, submit_operation_task->operation, completion_error_code, NULL); + +done: + aws_secure_tunnel_operation_release(submit_operation_task->operation); + aws_secure_tunnel_release(submit_operation_task->secure_tunnel); + + aws_mem_release(submit_operation_task->allocator, submit_operation_task); } -void aws_secure_tunnel_operational_state_clean_up(struct aws_secure_tunnel *secure_tunnel) { - AWS_ASSERT(secure_tunnel->current_operation == NULL); +static int s_submit_operation(struct aws_secure_tunnel *secure_tunnel, struct aws_secure_tunnel_operation *operation) { + struct aws_secure_tunnel_submit_operation_task *submit_task = + aws_mem_calloc(secure_tunnel->allocator, 1, sizeof(struct aws_secure_tunnel_submit_operation_task)); + if (submit_task == NULL) { + return AWS_OP_ERR; + } - s_aws_secure_tunnel_operational_state_reset(secure_tunnel, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_TERMINATED); + aws_task_init( + &submit_task->task, s_secure_tunnel_submit_operation_task_fn, submit_task, "SecureTunnelSubmitOperation"); + submit_task->allocator = secure_tunnel->allocator; + submit_task->secure_tunnel = aws_secure_tunnel_acquire(secure_tunnel); + submit_task->operation = operation; + + aws_event_loop_schedule_task_now(secure_tunnel->loop, &submit_task->task); + + return AWS_OP_SUCCESS; } static void s_check_ping_timeout(struct aws_secure_tunnel *secure_tunnel, uint64_t now) { // Steve TODO check the ping operation for timeout - // mqtt5_client uses s_check_timeouts() + // This is called during the CONNECTED state to check whether we need to initiate a disconnect due + // to a pingreq timeout. + // mqtt5_client uses s_check_timeouts() to check all unacked_operations. + // Currently the ping operation goes out but we don't check for its completion. (void)secure_tunnel; (void)now; } +/********************************************************************************************************************* + * Service Timing + ********************************************************************************************************************/ + static uint64_t s_min_non_0_64(uint64_t a, uint64_t b) { if (a == 0) { return b; @@ -1603,17 +1726,26 @@ static uint64_t s_compute_next_service_time_secure_tunnel_stopped( return 0; } -static uint64_t s_compute_next_service_time_secure_tunnel_connect( +static uint64_t s_compute_next_service_time_secure_tunnel_connecting( struct aws_secure_tunnel *secure_tunnel, uint64_t now) { - /* This state is interruptable by a stop/terminate */ - if (secure_tunnel->desired_state != AWS_STS_CONNECTED) { - return now; - } + (void)secure_tunnel; + (void)now; + + return 0; +} + +static uint64_t s_compute_next_service_time_secure_tunnel_connect( + struct aws_secure_tunnel *secure_tunnel, + uint64_t now) { + /* This state is interruptable by a stop/terminate */ + if (secure_tunnel->desired_state != AWS_STS_CONNECTED) { + return now; + } uint64_t operation_processing_time = s_aws_secure_tunnel_compute_operational_state_service_time(secure_tunnel, now); if (operation_processing_time == 0) { - // Steve todo need to set up this connect timeout for websocket timing + // STEVE TODO need to set up this connect timeout for websocket timing return secure_tunnel->next_secure_tunnel_websocket_connect_timeout_time; } @@ -1646,7 +1778,6 @@ 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); } @@ -1665,6 +1796,8 @@ static uint64_t s_compute_next_service_time_by_current_state(struct aws_secure_t switch (secure_tunnel->current_state) { case AWS_STS_STOPPED: return s_compute_next_service_time_secure_tunnel_stopped(secure_tunnel, now); + case AWS_STS_CONNECTING: + return s_compute_next_service_time_secure_tunnel_connecting(secure_tunnel, now); case AWS_STS_SECURE_TUNNEL_CONNECT: return s_compute_next_service_time_secure_tunnel_connect(secure_tunnel, now); case AWS_STS_CONNECTED: @@ -1673,7 +1806,6 @@ static uint64_t s_compute_next_service_time_by_current_state(struct aws_secure_t return s_compute_next_service_time_secure_tunnel_clean_disconnect(secure_tunnel, now); case AWS_STS_PENDING_RECONNECT: return s_compute_next_service_time_secure_tunnel_pending_reconnect(secure_tunnel, now); - case AWS_STS_CONNECTING: case AWS_STS_CHANNEL_SHUTDOWN: case AWS_STS_TERMINATED: return 0; @@ -1684,14 +1816,14 @@ static uint64_t s_compute_next_service_time_by_current_state(struct aws_secure_t static void s_reevaluate_service_task(struct aws_secure_tunnel *secure_tunnel) { /* - * This causes the secure_tunnel to only reevaluate service schedule time at the end of the service call or in + * This causes the secure tunnel to only reevaluate service schedule time at the end of the service call or in * a callback from an external event. */ if (secure_tunnel->in_service) { return; } - uint64_t now = (*secure_tunnel->vtable.get_current_time_fn)(); + uint64_t now = (*secure_tunnel->vtable->get_current_time_fn)(); uint64_t next_service_time = s_compute_next_service_time_by_current_state(secure_tunnel, now); /* @@ -1723,153 +1855,238 @@ static void s_reevaluate_service_task(struct aws_secure_tunnel *secure_tunnel) { secure_tunnel->next_service_task_run_time = next_service_time; } -static void s_enqueue_operation_back( - struct aws_secure_tunnel *secure_tunnel, - struct aws_secure_tunnel_operation *operation) { - AWS_LOGF_DEBUG( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: enqueuing %s operation to back", - (void *)secure_tunnel, - aws_secure_tunnel_operation_type_to_c_string(operation->operation_type)); +/********************************************************************************************************************* + * Secure Tunnel Clean Up + ********************************************************************************************************************/ - aws_linked_list_push_back(&secure_tunnel->queued_operations, &operation->node); +static void s_secure_tunnel_final_destroy(struct aws_secure_tunnel *secure_tunnel) { + if (secure_tunnel == NULL) { + return; + } - s_reevaluate_service_task(secure_tunnel); -} + aws_secure_tunneling_on_termination_complete_fn *on_termination_complete = NULL; + void *termination_complete_user_data = NULL; + if (secure_tunnel->config != NULL) { + on_termination_complete = secure_tunnel->config->on_termination_complete; + termination_complete_user_data = secure_tunnel->config->user_data; + } -static void s_enqueue_operation_front( - struct aws_secure_tunnel *secure_tunnel, - struct aws_secure_tunnel_operation *operation) { - AWS_LOGF_DEBUG( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: enqueuing %s operation to front", - (void *)secure_tunnel, - aws_secure_tunnel_operation_type_to_c_string(operation->operation_type)); + aws_secure_tunnel_operational_state_clean_up(secure_tunnel); - aws_linked_list_push_front(&secure_tunnel->queued_operations, &operation->node); + /* Clean up all memory */ + 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_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); - s_reevaluate_service_task(secure_tunnel); + if (on_termination_complete != NULL) { + (*on_termination_complete)(termination_complete_user_data); + } } -static void s_aws_secure_tunnel_operational_state_reset( - struct aws_secure_tunnel *secure_tunnel, - int completion_error_code) { - s_complete_operation_list(secure_tunnel, &secure_tunnel->queued_operations, completion_error_code); - s_complete_operation_list(secure_tunnel, &secure_tunnel->write_completion_operations, completion_error_code); +static void s_on_secure_tunnel_zero_ref_count(void *user_data) { + struct aws_secure_tunnel *secure_tunnel = user_data; + + s_aws_secure_tunnel_client_change_desired_state(secure_tunnel, AWS_STS_TERMINATED, NULL); } -struct aws_secure_tunnel_submit_operation_task { - struct aws_task task; - struct aws_allocator *allocator; - struct aws_secure_tunnel *secure_tunnel; - struct aws_secure_tunnel_operation *operation; -}; -static void s_secure_tunnel_submit_operation_task_fn(struct aws_task *task, void *arg, enum aws_task_status status) { - (void)task; +/********************************************************************************************************************* + * Update Loop + ********************************************************************************************************************/ - int completion_error_code = AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_SECURE_TUNNEL_TERMINATED; - struct aws_secure_tunnel_submit_operation_task *submit_operation_task = arg; +static int s_aws_secure_tunnel_queue_ping(struct aws_secure_tunnel *secure_tunnel) { + s_reset_ping(secure_tunnel); - /* - * Take a ref to the operation that represents the secure tunnel taking ownership - * If we subsequently reject it (task cancel), then the operation completion - * will undo this ref acquisition. - */ - aws_secure_tunnel_operation_acquire(submit_operation_task->operation); + AWS_LOGF_DEBUG(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: queuing PING", (void *)secure_tunnel); - if (status != AWS_TASK_STATUS_RUN_READY) { - goto error; + struct aws_secure_tunnel_operation_pingreq *pingreq_op = + aws_secure_tunnel_operation_pingreq_new(secure_tunnel->allocator); + s_enqueue_operation_front(secure_tunnel, &pingreq_op->base); + + return AWS_OP_SUCCESS; +} + +static bool s_service_state_stopped(struct aws_secure_tunnel *secure_tunnel) { + enum aws_secure_tunnel_state desired_state = secure_tunnel->desired_state; + if (desired_state == AWS_STS_CONNECTED) { + s_change_current_state(secure_tunnel, AWS_STS_CONNECTING); + } else if (desired_state == AWS_STS_TERMINATED) { + s_change_current_state(secure_tunnel, AWS_STS_TERMINATED); + return true; } + return false; +} - /* - * If we're offline and this operation doesn't meet the requirements of the offline queue retention policy, - * fail it immediately. - */ - struct aws_secure_tunnel *secure_tunnel = submit_operation_task->secure_tunnel; - struct aws_secure_tunnel_operation *operation = submit_operation_task->operation; - if (secure_tunnel->current_state != AWS_STS_CONNECTED) { - completion_error_code = AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_OPERATION_FAILED_DUE_TO_DISCONNECTION; - goto error; +static void s_service_state_connecting(struct aws_secure_tunnel *secure_tunnel) { + (void)secure_tunnel; +} + +static void s_service_state_secure_tunnel_connect(struct aws_secure_tunnel *secure_tunnel, uint64_t now) { + enum aws_secure_tunnel_state desired_state = secure_tunnel->desired_state; + if (desired_state != AWS_STS_CONNECTED) { + s_aws_secure_tunnel_shutdown_channel(secure_tunnel, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_USER_REQUESTED_STOP); + return; } - /* Steve TODO check stream id here? */ - /* newly-submitted operations must have a 0 packet id */ - // aws_secure_tunnel_operation_set_packet_id(submit_operation_task->operation, 0); + if (now >= secure_tunnel->next_secure_tunnel_websocket_connect_timeout_time) { + AWS_LOGF_INFO( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: shutting down channel due to websocket timeout", + (void *)secure_tunnel); - s_enqueue_operation_back(submit_operation_task->secure_tunnel, submit_operation_task->operation); - // aws_secure_tunnel_client_statistics_change_operation_statistic_state( - // submit_operation_task->client, submit_operation_task->operation, AWS_SECURE_TUNNEL_OSS_INCOMPLETE); + s_aws_secure_tunnel_shutdown_channel(secure_tunnel, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_WEBSOCKET_TIMEOUT); + return; + } - goto done; + if (aws_secure_tunnel_service_operational_state(secure_tunnel)) { + int error_code = aws_last_error(); + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: failed to service websocket connect to channel with error %d(%s)", + (void *)secure_tunnel, + error_code, + aws_error_debug_str(error_code)); -error: + s_aws_secure_tunnel_shutdown_channel(secure_tunnel, error_code); + return; + } +} - aws_ref_count_release(&operation->ref_count); +static void s_service_state_connected(struct aws_secure_tunnel *secure_tunnel, uint64_t 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_aws_secure_tunnel_shutdown_channel(secure_tunnel, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_USER_REQUESTED_STOP); + return; + } -done: + if (now >= secure_tunnel->next_ping_timeout_time && secure_tunnel->next_ping_timeout_time != 0) { + AWS_LOGF_INFO( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: channel shutdown due to PINGRESP timeout", + (void *)secure_tunnel); - aws_secure_tunnel_operation_release(submit_operation_task->operation); - aws_secure_tunnel_release(submit_operation_task->secure_tunnel); + s_aws_secure_tunnel_shutdown_channel(secure_tunnel, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_PING_RESPONSE_TIMEOUT); + return; + } - aws_mem_release(submit_operation_task->allocator, submit_operation_task); -} + if (now >= secure_tunnel->next_ping_time) { + if (s_aws_secure_tunnel_queue_ping(secure_tunnel)) { + int error_code = aws_last_error(); + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: failed to queue PINGREQ with error %d(%s)", + (void *)secure_tunnel, + error_code, + aws_error_debug_str(error_code)); -static int s_submit_operation(struct aws_secure_tunnel *secure_tunnel, struct aws_secure_tunnel_operation *operation) { - struct aws_secure_tunnel_submit_operation_task *submit_task = - aws_mem_calloc(secure_tunnel->allocator, 1, sizeof(struct aws_secure_tunnel_submit_operation_task)); - if (submit_task == NULL) { - return AWS_OP_ERR; + s_aws_secure_tunnel_shutdown_channel(secure_tunnel, error_code); + return; + } } - aws_task_init( - &submit_task->task, s_secure_tunnel_submit_operation_task_fn, submit_task, "SecureTunnelSubmitOperation"); - submit_task->allocator = secure_tunnel->allocator; - submit_task->secure_tunnel = aws_secure_tunnel_acquire(secure_tunnel); - submit_task->operation = operation; + s_check_ping_timeout(secure_tunnel, now); + /* STEVE TODO Reconnect logic and timer checks can go here */ - aws_event_loop_schedule_task_now(secure_tunnel->loop, &submit_task->task); + if (aws_secure_tunnel_service_operational_state(secure_tunnel)) { + int error_code = aws_last_error(); + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: failed to service CONNECTED operation queue with error %d(%s)", + (void *)secure_tunnel, + error_code, + aws_error_debug_str(error_code)); - return AWS_OP_SUCCESS; + s_aws_secure_tunnel_shutdown_channel(secure_tunnel, error_code); + return; + } } -int aws_secure_tunnel_bind_stream_id( - struct aws_secure_tunnel *secure_tunnel, - struct aws_secure_tunnel_operation_data *operation) { +static void s_service_state_clean_disconnect(struct aws_secure_tunnel *secure_tunnel, uint64_t now) { + if (aws_secure_tunnel_service_operational_state(secure_tunnel)) { + int error_code = aws_last_error(); + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: failed to service CLEAN_DISCONNECT operation queue with error %d(%s)", + (void *)secure_tunnel, + error_code, + aws_error_debug_str(error_code)); - return AWS_OP_ERR; + s_aws_secure_tunnel_shutdown_channel(secure_tunnel, error_code); + return; + } } -static void s_secure_tunnel_final_destroy(struct aws_secure_tunnel *secure_tunnel) { - if (secure_tunnel == NULL) { +static void s_service_state_channel_shutdown(struct aws_secure_tunnel *secure_tunnel) { + (void)secure_tunnel; +} + +static void s_service_state_pending_reconnect(struct aws_secure_tunnel *secure_tunnel, uint64_t now) { + if (secure_tunnel->desired_state != AWS_STS_CONNECTED) { + s_change_current_state(secure_tunnel, AWS_STS_STOPPED); return; } - aws_secure_tunneling_on_termination_complete_fn *on_termination_complete = NULL; - void *termination_complete_user_data = NULL; - if (secure_tunnel->config != NULL) { - on_termination_complete = secure_tunnel->options->on_termination_complete; - termination_complete_user_data = secure_tunnel->config->user_data; + if (now >= secure_tunnel->next_reconnect_time_ns) { + s_change_current_state(secure_tunnel, AWS_STS_CONNECTING); + return; } +} - aws_secure_tunnel_operational_state_clean_up(secure_tunnel); +static void s_secure_tunnel_service_task_fn(struct aws_task *task, void *arg, enum aws_task_status status) { + (void)task; + if (status != AWS_TASK_STATUS_RUN_READY) { + return; + } - /* Clean up all memory */ - 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_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); + struct aws_secure_tunnel *secure_tunnel = arg; + secure_tunnel->next_service_task_run_time = 0; + secure_tunnel->in_service = true; - if (on_termination_complete != NULL) { - (*on_termination_complete)(termination_complete_user_data); + uint64_t now = (*secure_tunnel->vtable->get_current_time_fn)(); + bool terminated = false; + switch (secure_tunnel->current_state) { + case AWS_STS_STOPPED: + terminated = s_service_state_stopped(secure_tunnel); + break; + case AWS_STS_CONNECTING: + s_service_state_connecting(secure_tunnel); + break; + case AWS_STS_SECURE_TUNNEL_CONNECT: + s_service_state_secure_tunnel_connect(secure_tunnel, now); + break; + case AWS_STS_CONNECTED: + s_service_state_connected(secure_tunnel, now); + break; + case AWS_STS_CLEAN_DISCONNECT: + s_service_state_clean_disconnect(secure_tunnel, now); + break; + case AWS_STS_CHANNEL_SHUTDOWN: + s_service_state_channel_shutdown(secure_tunnel); + break; + case AWS_STS_PENDING_RECONNECT: + s_service_state_pending_reconnect(secure_tunnel, now); + break; + default: + break; } -} -static void s_on_secure_tunnel_zero_ref_count(void *user_data) { - struct aws_secure_tunnel *secure_tunnel = user_data; + /* + * We can only enter the terminated state from stopped. If we do so, the secure tunnel memory is now freed and we + * will crash if we access anything anymore. + */ + if (terminated) { + return; + } - s_aws_secure_tunnel_client_change_desired_state(secure_tunnel, AWS_STS_TERMINATED, NULL); + /* we're not scheduled anymore, reschedule as needed */ + secure_tunnel->in_service = false; + s_reevaluate_service_task(secure_tunnel); } /********************************************************************************************************************* @@ -1877,10 +2094,19 @@ static void s_on_secure_tunnel_zero_ref_count(void *user_data) { ********************************************************************************************************************/ struct aws_secure_tunnel *aws_secure_tunnel_new(const struct aws_secure_tunnel_options *options) { - fprintf(stdout, "\nNEW SECURE TUNNEL\n"); + AWS_FATAL_ASSERT(options != NULL); + AWS_FATAL_ASSERT(options->allocator != NULL); struct aws_secure_tunnel *secure_tunnel = aws_mem_calloc(options->allocator, 1, sizeof(struct aws_secure_tunnel)); + if (secure_tunnel == NULL) { + return NULL; + } + + aws_task_init(&secure_tunnel->service_task, s_secure_tunnel_service_task_fn, secure_tunnel, "SecureTunnelService"); + secure_tunnel->allocator = options->allocator; + secure_tunnel->vtable = &s_default_secure_tunnel_vtable; + aws_ref_count_init(&secure_tunnel->ref_count, secure_tunnel, s_on_secure_tunnel_zero_ref_count); aws_linked_list_init(&secure_tunnel->queued_operations); @@ -1899,10 +2125,18 @@ struct aws_secure_tunnel *aws_secure_tunnel_new(const struct aws_secure_tunnel_o goto error; } - /* tls_ctx */ + secure_tunnel->desired_state = AWS_STS_STOPPED; + secure_tunnel->current_state = AWS_STS_STOPPED; + + // STEVE TODO Channel Handler may be uneccessary for a websocket secure tunnel + // secure_tunnel->handler.alloc = secure_tunnel->allocator; + // secure_tunnel->handler.vtable = &s_secure_tunnel_channel_handler_vtable; + // secure_tunnel->handler.impl = secure_tunnel; + + /* 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, options->allocator); + 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)) { @@ -1924,31 +2158,19 @@ struct aws_secure_tunnel *aws_secure_tunnel_new(const struct aws_secure_tunnel_o aws_tls_ctx_options_clean_up(&tls_ctx_opt); - /* Setup vtables here. */ - secure_tunnel->vtable.get_current_time_fn = s_aws_high_res_clock_get_ticks_proxy; - secure_tunnel->vtable.channel_shutdown_fn = aws_channel_shutdown; - secure_tunnel->vtable.websocket_connect_fn = aws_websocket_client_connect, - secure_tunnel->vtable.client_bootstrap_new_socket_channel_fn = aws_client_bootstrap_new_socket_channel; - secure_tunnel->vtable.http_proxy_new_socket_channel_fn = aws_http_proxy_new_socket_channel; - - secure_tunnel->vtable.connect = s_secure_tunneling_connect; - secure_tunnel->vtable.close = s_secure_tunneling_close; - secure_tunnel->vtable.send_data = s_secure_tunneling_send_data; - secure_tunnel->vtable.send_data_v2 = s_secure_tunneling_send_data_v2; - secure_tunnel->vtable.send_stream_start = s_secure_tunneling_send_stream_start; - secure_tunnel->vtable.send_stream_start_v2 = s_secure_tunneling_send_stream_start_v2; - secure_tunnel->vtable.send_stream_reset = s_secure_tunneling_send_stream_reset; + /* websocket vtable */ secure_tunnel->websocket_vtable.client_connect = aws_websocket_client_connect; secure_tunnel->websocket_vtable.send_frame = aws_websocket_send_frame; secure_tunnel->websocket_vtable.close = aws_websocket_close; secure_tunnel->websocket_vtable.release = aws_websocket_release; - secure_tunnel->handshake_request = NULL; + /* Connection reset */ secure_tunnel->config->stream_id = INVALID_STREAM_ID; secure_tunnel->config->service_id_1_stream_id = INVALID_STREAM_ID; secure_tunnel->config->service_id_2_stream_id = INVALID_STREAM_ID; secure_tunnel->config->service_id_3_stream_id = INVALID_STREAM_ID; + secure_tunnel->handshake_request = NULL; secure_tunnel->websocket = NULL; /* TODO: Release this buffer when there is no data to hold */ @@ -1966,7 +2188,6 @@ struct aws_secure_tunnel *aws_secure_tunnel_acquire(struct aws_secure_tunnel *se if (secure_tunnel != NULL) { aws_ref_count_acquire(&secure_tunnel->ref_count); } - return secure_tunnel; } @@ -1976,42 +2197,71 @@ void aws_secure_tunnel_release(struct aws_secure_tunnel *secure_tunnel) { } } -// Steve TODO this isn't required on destination side, remove after testing -int aws_secure_tunnel_get_connection_error_code(struct aws_secure_tunnel *secure_tunnel) { - return secure_tunnel->connection_error_code; +int aws_secure_tunnel_start(struct aws_secure_tunnel *secure_tunnel) { + return s_aws_secure_tunnel_change_desired_state(secure_tunnel, AWS_STS_CONNECTED); +} + +int aws_secure_tunnel_stop(struct aws_secure_tunnel *secure_tunnel) { + AWS_LOGF_DEBUG( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: Stopping secure tunnel immediately", (void *)secure_tunnel); + return s_aws_secure_tunnel_change_desired_state(secure_tunnel, AWS_STS_STOPPED); +} + +int aws_secure_tunnel_send_message( + 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); + + if (message_op == NULL) { + return AWS_OP_ERR; + } + + AWS_LOGF_DEBUG( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Submitting MESSAGE operation (%p)", + (void *)secure_tunnel, + (void *)message_op); + aws_secure_tunnel_message_view_log(message_op->base.message_view, AWS_LL_DEBUG); + + 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; } int aws_secure_tunnel_connect(struct aws_secure_tunnel *secure_tunnel) { - return secure_tunnel->vtable.connect(secure_tunnel); + return secure_tunnel->vtable->connect(secure_tunnel); } int aws_secure_tunnel_close(struct aws_secure_tunnel *secure_tunnel) { - return secure_tunnel->vtable.close(secure_tunnel); + return secure_tunnel->vtable->close(secure_tunnel); } int aws_secure_tunnel_send_data(struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *data) { - return secure_tunnel->vtable.send_data(secure_tunnel, data); -} - -int aws_secure_tunnel_send_data_v2( - struct aws_secure_tunnel *secure_tunnel, - const struct aws_secure_tunnel_message_data_view *data_options) { - return secure_tunnel->vtable.send_data_v2(secure_tunnel, data_options); + return secure_tunnel->vtable->send_data(secure_tunnel, data); } -/* Steve Todo unecessary for destination devices as they never attempt to start a stream. Can be depricated */ int aws_secure_tunnel_stream_start(struct aws_secure_tunnel *secure_tunnel) { - return secure_tunnel->vtable.send_stream_start(secure_tunnel); + return secure_tunnel->vtable->send_stream_start(secure_tunnel); } -/* Steve Todo can remove after testing. Not required for Destination devices */ int aws_secure_tunnel_stream_start_v2( struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *service_id_data) { - return secure_tunnel->vtable.send_stream_start_v2(secure_tunnel, service_id_data); + return secure_tunnel->vtable->send_stream_start_v2(secure_tunnel, service_id_data); } /* Steve Todo a V2/V3 version is required to reset on a failed stream start attempt */ int aws_secure_tunnel_stream_reset(struct aws_secure_tunnel *secure_tunnel) { - return secure_tunnel->vtable.send_stream_reset(secure_tunnel); + return secure_tunnel->vtable->send_stream_reset(secure_tunnel); } diff --git a/source/secure_tunneling_operations.c b/source/secure_tunneling_operations.c index 35374564..a9d92dfc 100644 --- a/source/secure_tunneling_operations.c +++ b/source/secure_tunneling_operations.c @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0. */ +#include #include #include #include @@ -11,6 +12,7 @@ #include #define MAX_PAYLOAD_SIZE 64512 +#define INVALID_STREAM_ID 0 /********************************************************************************************************************* * Operation base ********************************************************************************************************************/ @@ -230,6 +232,7 @@ static void s_aws_secure_tunnel_operation_message_set_stream_id( (void *)message_storage, aws_string_c_str(service_id)); /* STEVE TODO should we throw something or just log the error here? */ + stream_id = INVALID_STREAM_ID; } aws_string_destroy(service_id); } else { @@ -271,7 +274,7 @@ struct aws_secure_tunnel_operation_message *aws_secure_tunnel_operation_message_ AWS_PRECONDITION(allocator != NULL); AWS_PRECONDITION(message_options != NULL); - if (aws_secure_tunnel_message_message_view_validate(message_options)) { + if (aws_secure_tunnel_message_view_validate(message_options)) { return NULL; } @@ -286,7 +289,7 @@ struct aws_secure_tunnel_operation_message *aws_secure_tunnel_operation_message_ aws_ref_count_init(&message_op->base.ref_count, message_op, s_destroy_operation_message); message_op->base.impl = message_op; - if (aws_secure_tunnel_message_message_storage_init(&message_op->options_storage, allocator, message_options)) { + if (aws_secure_tunnel_message_storage_init(&message_op->options_storage, allocator, message_options)) { goto error; } @@ -301,6 +304,35 @@ struct aws_secure_tunnel_operation_message *aws_secure_tunnel_operation_message_ return NULL; } +/********************************************************************************************************************* + * Pingreq + ********************************************************************************************************************/ + +static void s_destroy_operation_pingreq(void *object) { + if (object == NULL) { + return; + } + + struct aws_secure_tunnel_operation_pingreq *pingreq_op = object; + aws_mem_release(pingreq_op->allocator, pingreq_op); +} + +struct aws_secure_tunnel_operation_pingreq *aws_secure_tunnel_operation_pingreq_new(struct aws_allocator *allocator) { + struct aws_secure_tunnel_operation_pingreq *pingreq_op = + aws_mem_calloc(allocator, 1, sizeof(struct aws_secure_tunnel_operation_pingreq)); + if (pingreq_op == NULL) { + return NULL; + } + + pingreq_op->allocator = allocator; + pingreq_op->base.vtable = &s_empty_operation_vtable; + pingreq_op->base.operation_type = AWS_STOT_PING; + aws_ref_count_init(&pingreq_op->base.ref_count, pingreq_op, s_destroy_operation_pingreq); + pingreq_op->base.impl = pingreq_op; + + return pingreq_op; +} + /********************************************************************************************************************* * Secure Tunnel Storage Options ********************************************************************************************************************/ From b1d85b38bf8b35ecf1c30b9cb9f03528a23fa07c Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Tue, 17 Jan 2023 13:18:09 -0800 Subject: [PATCH 09/69] stash --- include/aws/iotdevice/private/secure_tunneling_impl.h | 3 ++- include/aws/iotdevice/private/secure_tunneling_operations.h | 3 +-- source/secure_tunneling_operations.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/aws/iotdevice/private/secure_tunneling_impl.h b/include/aws/iotdevice/private/secure_tunneling_impl.h index 0e42f73a..3dc3f632 100644 --- a/include/aws/iotdevice/private/secure_tunneling_impl.h +++ b/include/aws/iotdevice/private/secure_tunneling_impl.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -162,7 +163,7 @@ struct aws_secure_tunnel_vtable { int (*send_data)(struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *data); int (*send_data_v2)( struct aws_secure_tunnel *secure_tunnel, - const struct aws_secure_tunnel_message_data_view *data_options); + const struct aws_secure_tunnel_message_view *message_options); int (*send_stream_start)(struct aws_secure_tunnel *secure_tunnel); int (*send_stream_start_v2)(struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *service_id_data); int (*send_stream_reset)(struct aws_secure_tunnel *secure_tunnel); diff --git a/include/aws/iotdevice/private/secure_tunneling_operations.h b/include/aws/iotdevice/private/secure_tunneling_operations.h index c8d850fd..b920303f 100644 --- a/include/aws/iotdevice/private/secure_tunneling_operations.h +++ b/include/aws/iotdevice/private/secure_tunneling_operations.h @@ -105,7 +105,6 @@ struct aws_secure_tunnel_options_storage { aws_secure_tunnel_transform_websocket_handshake_fn *websocket_handshake_transform; void *websocket_handshake_transform_user_data; - enum aws_secure_tunneling_local_proxy_mode local_proxy_mode; struct aws_string *endpoint_host; @@ -145,7 +144,7 @@ AWS_IOTDEVICE_API struct aws_secure_tunnel_operation *aws_secure_tunnel_operatio AWS_IOTDEVICE_API struct aws_secure_tunnel_operation *aws_secure_tunnel_operation_release( struct aws_secure_tunnel_operation *operation); -AWS_IOTDEVICE_API void *aws_secure_tunnel_operation_complete( +AWS_IOTDEVICE_API void aws_secure_tunnel_operation_complete( struct aws_secure_tunnel_operation *operation, int error_code, const void *associated_view); diff --git a/source/secure_tunneling_operations.c b/source/secure_tunneling_operations.c index a9d92dfc..e8d2bcb3 100644 --- a/source/secure_tunneling_operations.c +++ b/source/secure_tunneling_operations.c @@ -35,7 +35,7 @@ struct aws_secure_tunnel_operation *aws_secure_tunnel_operation_release(struct a return NULL; } -void *aws_secure_tunnel_operation_complete( +void aws_secure_tunnel_operation_complete( struct aws_secure_tunnel_operation *operation, int error_code, const void *associated_view) { From 449195cdeef26ba340fe1bee0c7c5ed5db823ee0 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 18 Jan 2023 10:23:08 -0800 Subject: [PATCH 10/69] test connection --- CMakeLists.txt | 1 + source/secure_tunneling.c | 253 ++++++++++--------- source/secure_tunneling_operations.c | 3 +- tests/aws_iot_secure_tunneling_client_test.c | 22 +- tests/secure_tunneling_tests.c | 115 +++++---- 5 files changed, 208 insertions(+), 186 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 811d3d2c..0cd6bdaf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -123,4 +123,5 @@ install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake" include(CTest) if (BUILD_TESTING) add_subdirectory(tests) + add_subdirectory(bin/securetunnel) endif () diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index e352ac82..5cefcf04 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -28,13 +28,20 @@ // Steve TODO check if these need to be implemented or re-organized static void s_change_current_state(struct aws_secure_tunnel *secure_tunnel, enum aws_secure_tunnel_state next_state); +/* STEVE TODO These forward declarations exist but may not be implemented */ +void aws_secure_tunnel_on_disconnection_update_operational_state(struct aws_secure_tunnel *secure_tunnel); +void aws_secure_tunnel_operational_state_clean_up(struct aws_secure_tunnel *secure_tunnel); +static int s_aws_secure_tunnel_change_desired_state( + struct aws_secure_tunnel *secure_tunnel, + enum aws_secure_tunnel_state desired_state); + static int s_secure_tunneling_send( struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *payload_data, const struct aws_byte_cursor *service_id_data, enum aws_secure_tunnel_message_type type); -static int s_websocket_connect(struct aws_secure_tunnel *secure_tunnel){}; +static int s_websocket_connect(struct aws_secure_tunnel *secure_tunnel); static void s_reevaluate_service_task(struct aws_secure_tunnel *secure_tunnel); @@ -76,6 +83,43 @@ static const char *s_get_proxy_mode_string(enum aws_secure_tunneling_local_proxy return "destination"; } +/********************************************************************************************************************* + * Secure Tunnel Clean Up + ********************************************************************************************************************/ + +static void s_secure_tunnel_final_destroy(struct aws_secure_tunnel *secure_tunnel) { + if (secure_tunnel == NULL) { + return; + } + + aws_secure_tunneling_on_termination_complete_fn *on_termination_complete = NULL; + void *termination_complete_user_data = NULL; + if (secure_tunnel->config != NULL) { + on_termination_complete = secure_tunnel->config->on_termination_complete; + termination_complete_user_data = secure_tunnel->config->user_data; + } + + aws_secure_tunnel_operational_state_clean_up(secure_tunnel); + + /* Clean up all memory */ + 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_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); + + if (on_termination_complete != NULL) { + (*on_termination_complete)(termination_complete_user_data); + } +} + +static void s_on_secure_tunnel_zero_ref_count(void *user_data) { + struct aws_secure_tunnel *secure_tunnel = user_data; + + s_aws_secure_tunnel_change_desired_state(secure_tunnel, AWS_STS_TERMINATED); +} + /***************************************************************************************************************** * RECEIVED MESSAGE HANDLING *****************************************************************************************************************/ @@ -109,7 +153,7 @@ static int s_aws_secure_tunnel_set_stream_id( return AWS_OP_SUCCESS; } - struct aws_string *service_id_to_set = aws_string_new_from_buf(secure_tunnel->allocator, service_id); + struct aws_string *service_id_to_set = aws_string_new_from_cursor(secure_tunnel->allocator, service_id); if (service_id_to_set == NULL) { return AWS_OP_ERR; } @@ -130,7 +174,7 @@ static int s_aws_secure_tunnel_set_stream_id( AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: Secure tunnel request for unsupported service_id: " PRInSTR, (void *)secure_tunnel, - AWS_BYTE_CURSOR_PRI(aws_byte_cursor_from_buf(service_id))); + AWS_BYTE_CURSOR_PRI(*service_id)); aws_string_destroy(service_id_to_set); return AWS_OP_ERR; } @@ -191,7 +235,7 @@ static void s_aws_secure_tunnel_connected_on_message_received( AWS_LOGF_WARN( AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Encountered an unknown but un-ignorable message. type=%s", - aws_secure_tunnel_operation_type_to_c_string(message_view->type)); + aws_secure_tunnel_message_type_to_c_string(message_view->type)); } break; } @@ -269,17 +313,17 @@ static bool s_on_websocket_incoming_frame_complete( } // Steve TODO remove this for operational ping? Or use it on the task of websocket ping? -static void s_send_websocket_ping(struct aws_websocket *websocket, websocket_send_frame *send_frame) { - if (!websocket) { - return; - } +// static void s_send_websocket_ping(struct aws_websocket *websocket, websocket_send_frame *send_frame) { +// if (!websocket) { +// return; +// } - struct aws_websocket_send_frame_options frame_options; - AWS_ZERO_STRUCT(frame_options); - frame_options.opcode = AWS_WEBSOCKET_OPCODE_PING; - frame_options.fin = true; - send_frame(websocket, &frame_options); -} +// struct aws_websocket_send_frame_options frame_options; +// AWS_ZERO_STRUCT(frame_options); +// frame_options.opcode = AWS_WEBSOCKET_OPCODE_PING; +// frame_options.fin = true; +// send_frame(websocket, &frame_options); +// } // Steve TODO this can probably be removed as soon as pings are set up as operations // struct ping_task_context { @@ -409,7 +453,7 @@ static void s_secure_tunnel_setup( error: s_change_current_state(secure_tunnel, AWS_STS_CHANNEL_SHUTDOWN); - (*secure_tunnel->vtable.channel_shutdown_fn)(channel, aws_last_error()); + (*secure_tunnel->vtable->channel_shutdown_fn)(channel, aws_last_error()); } static void s_on_websocket_shutdown(struct aws_websocket *websocket, int error_code, void *user_data) { @@ -429,60 +473,63 @@ static void s_on_websocket_shutdown(struct aws_websocket *websocket, int error_c } } -static void s_on_websocket_setup( - struct aws_websocket *websocket, - int error_code, - int handshake_response_status, - const struct aws_http_header *handshake_response_header_array, - size_t num_handshake_response_headers, - void *user_data) { - - (void)handshake_response_status; - (void)handshake_response_header_array; - (void)num_handshake_response_headers; - - struct aws_secure_tunnel *secure_tunnel = user_data; - secure_tunnel->handshake_request = aws_http_message_release(secure_tunnel->handshake_request); - - /* Setup callback contract is: if error_code is non-zero then websocket is NULL. */ - AWS_FATAL_ASSERT((error_code != 0) == (websocket == NULL)); - - struct aws_channel *channel = NULL; - - if (websocket) { - channel = aws_websocket_get_channel(websocket); - AWS_ASSERT(channel); - - /* Websocket must be "converted" before the secure tunnel handler can be installed next to it. */ - if (aws_websocket_convert_to_midchannel_handler(websocket)) { - AWS_LOGF_ERROR( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: Failed converting websocket, error %d (%s)", - (void *)secure_tunnel, - aws_last_error(), - aws_error_name(aws_last_error())); - - (*secure_tunnel->vtable.channel_shutdown_fn)(channel, aws_last_error()); - return; - } - - // secure_tunnel->websocket = websocket; - // struct ping_task_context *ping_task_context = - // aws_mem_acquire(secure_tunnel->allocator, sizeof(struct ping_task_context)); - // secure_tunnel->ping_task_context = ping_task_context; - // AWS_ZERO_STRUCT(*ping_task_context); - // ping_task_context->allocator = secure_tunnel->allocator; - // ping_task_context->event_loop = - // aws_event_loop_group_get_next_loop(secure_tunnel->options->bootstrap->event_loop_group); - // aws_atomic_store_int(&ping_task_context->task_cancelled, 0); - // ping_task_context->websocket = websocket; - // ping_task_context->send_frame = secure_tunnel->websocket_vtable.send_frame; +static void s_on_websocket_setup(const struct aws_websocket_on_connection_setup_data *setup, void *user_data) +// struct aws_websocket *websocket, +// int error_code, +// int handshake_response_status, +// const struct aws_http_header *handshake_response_header_array, +// size_t num_handshake_response_headers, +// void *user_data) +{ + (void)setup; + (void)user_data; - // aws_task_init(&ping_task_context->ping_task, s_ping_task, ping_task_context, "SecureTunnelingPingTask"); - // aws_event_loop_schedule_task_now(ping_task_context->event_loop, &ping_task_context->ping_task); - } + // (void)handshake_response_status; + // (void)handshake_response_header_array; + // (void)num_handshake_response_headers; + + // struct aws_secure_tunnel *secure_tunnel = user_data; + // secure_tunnel->handshake_request = aws_http_message_release(secure_tunnel->handshake_request); + + // /* Setup callback contract is: if error_code is non-zero then websocket is NULL. */ + // AWS_FATAL_ASSERT((error_code != 0) == (websocket == NULL)); + + // struct aws_channel *channel = NULL; + + // if (websocket) { + // channel = aws_websocket_get_channel(websocket); + // AWS_ASSERT(channel); + + // /* Websocket must be "converted" before the secure tunnel handler can be installed next to it. */ + // if (aws_websocket_convert_to_midchannel_handler(websocket)) { + // AWS_LOGF_ERROR( + // AWS_LS_IOTDEVICE_SECURE_TUNNELING, + // "id=%p: Failed converting websocket, error %d (%s)", + // (void *)secure_tunnel, + // aws_last_error(), + // aws_error_name(aws_last_error())); + + // (*secure_tunnel->vtable->channel_shutdown_fn)(channel, aws_last_error()); + // return; + // } + + // secure_tunnel->websocket = websocket; + // struct ping_task_context *ping_task_context = + // aws_mem_acquire(secure_tunnel->allocator, sizeof(struct ping_task_context)); + // secure_tunnel->ping_task_context = ping_task_context; + // AWS_ZERO_STRUCT(*ping_task_context); + // ping_task_context->allocator = secure_tunnel->allocator; + // ping_task_context->event_loop = + // aws_event_loop_group_get_next_loop(secure_tunnel->options->bootstrap->event_loop_group); + // aws_atomic_store_int(&ping_task_context->task_cancelled, 0); + // ping_task_context->websocket = websocket; + // ping_task_context->send_frame = secure_tunnel->websocket_vtable.send_frame; + + // aws_task_init(&ping_task_context->ping_task, s_ping_task, ping_task_context, "SecureTunnelingPingTask"); + // aws_event_loop_schedule_task_now(ping_task_context->event_loop, &ping_task_context->ping_task); + // } - s_secure_tunnel_setup(secure_tunnel->config->bootstrap, error_code, channel, secure_tunnel); + // s_secure_tunnel_setup(secure_tunnel->config->bootstrap, error_code, channel, secure_tunnel); /* Existing connection_complete callback doesn't resturn an error code. Workaround to provide it. */ /* Previous implementation, may be removed if tasked websocket setups succeeeds */ @@ -554,7 +601,7 @@ void s_websocket_transform_complete_task_fn(struct aws_task *task, void *arg, en websocket_options.proxy_options = &secure_tunnel->config->http_proxy_options; } - if (secure_tunnel->vtable.websocket_connect_fn(&websocket_options)) { + if (secure_tunnel->vtable->websocket_connect_fn(&websocket_options)) { AWS_LOGF_ERROR( AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: Failed to initiate websocket connection.", @@ -572,7 +619,7 @@ void s_websocket_transform_complete_task_fn(struct aws_task *task, void *arg, en } error: - s_on_websocket_setup(NULL, error_code, -1, NULL, 0, secure_tunnel); + // s_on_websocket_setup(NULL, error_code, -1, NULL, 0, secure_tunnel); done: @@ -606,6 +653,7 @@ static void s_websocket_handshake_transform_complete( } static int s_websocket_connect(struct aws_secure_tunnel *secure_tunnel) { + (void)secure_tunnel; AWS_ASSERT(secure_tunnel); AWS_ASSERT(secure_tunnel->config->websocket_handshake_transform); @@ -631,9 +679,10 @@ static int s_websocket_connect(struct aws_secure_tunnel *secure_tunnel) { }, { .name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("access-token"), - .value = secure_tunnel->config->access_token, + .value = aws_byte_cursor_from_string(secure_tunnel->config->access_token), }, }; + (void)extra_headers; if (handshake == NULL) { AWS_LOGF_ERROR( @@ -678,7 +727,9 @@ static struct aws_http_message *s_new_handshake_request(const struct aws_secure_ "/tunnel?local-proxy-mode=%s", s_get_proxy_mode_string(secure_tunnel->config->local_proxy_mode)); struct aws_http_message *handshake_request = aws_http_message_new_websocket_handshake_request( - secure_tunnel->allocator, aws_byte_cursor_from_c_str(path), secure_tunnel->config->endpoint_host); + secure_tunnel->allocator, + aws_byte_cursor_from_c_str(path), + aws_byte_cursor_from_string(secure_tunnel->config->endpoint_host)); // Steve TODO implement client token connection here @@ -689,7 +740,7 @@ static struct aws_http_message *s_new_handshake_request(const struct aws_secure_ }, { .name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("access-token"), - .value = secure_tunnel->config->access_token, + .value = aws_byte_cursor_from_string(secure_tunnel->config->access_token), }, }; for (size_t i = 0; i < AWS_ARRAY_SIZE(extra_headers); ++i) { @@ -707,14 +758,14 @@ void init_websocket_client_connection_options( AWS_ZERO_STRUCT(*websocket_options); websocket_options->allocator = secure_tunnel->allocator; websocket_options->bootstrap = secure_tunnel->config->bootstrap; - websocket_options->socket_options = secure_tunnel->config->socket_options; + websocket_options->socket_options = &secure_tunnel->config->socket_options; websocket_options->tls_options = &secure_tunnel->tls_con_opt; - websocket_options->host = secure_tunnel->config->endpoint_host; + websocket_options->host = aws_byte_cursor_from_string(secure_tunnel->config->endpoint_host); websocket_options->port = 443; websocket_options->handshake_request = s_new_handshake_request(secure_tunnel); websocket_options->initial_window_size = MAX_WEBSOCKET_PAYLOAD; /* TODO: followup */ websocket_options->user_data = secure_tunnel; - websocket_options->proxy_options = secure_tunnel->config->http_proxy_options; + websocket_options->proxy_options = &secure_tunnel->config->http_proxy_options; websocket_options->on_connection_setup = s_on_websocket_setup; websocket_options->on_connection_shutdown = s_on_websocket_shutdown; websocket_options->on_incoming_frame_begin = s_on_websocket_incoming_frame_begin; @@ -754,7 +805,7 @@ static void s_aws_secure_tunnel_shutdown_channel(struct aws_secure_tunnel *secur } s_change_current_state(secure_tunnel, AWS_STS_CHANNEL_SHUTDOWN); - (*secure_tunnel->vtable.channel_shutdown_fn)(secure_tunnel->slot->channel, error_code); + (*secure_tunnel->vtable->channel_shutdown_fn)(secure_tunnel->slot->channel, error_code); } /********************************************************************************************************************* @@ -807,9 +858,9 @@ static void s_change_current_state_to_connecting(struct aws_secure_tunnel *secur channel_options.requested_event_loop = secure_tunnel->loop; if (secure_tunnel->config->http_proxy_config == NULL) { - result = (*secure_tunnel->vtable.client_bootstrap_new_socket_channel_fn)(&channel_options); + result = (*secure_tunnel->vtable->client_bootstrap_new_socket_channel_fn)(&channel_options); } else { - result = (*secure_tunnel->vtable.http_proxy_new_socket_channel_fn)( + result = (*secure_tunnel->vtable->http_proxy_new_socket_channel_fn)( &channel_options, &secure_tunnel->config->http_proxy_options); } } @@ -844,7 +895,9 @@ static void s_change_current_state_to_pending_reconnect(struct aws_secure_tunnel (void)secure_tunnel; } static void s_change_current_state_to_terminated(struct aws_secure_tunnel *secure_tunnel) { - (void)secure_tunnel; + secure_tunnel->current_state = AWS_STS_TERMINATED; + + s_secure_tunnel_final_destroy(secure_tunnel); } static void s_change_current_state(struct aws_secure_tunnel *secure_tunnel, enum aws_secure_tunnel_state next_state) { @@ -1311,7 +1364,7 @@ static int s_aws_secure_tunnel_set_current_operation( } static void s_reset_ping(struct aws_secure_tunnel *secure_tunnel) { - uint64_t now = (*secure_tunnel->vtable.get_current_time_fn)(); + uint64_t now = (*secure_tunnel->vtable->get_current_time_fn)(); // Steve TODO do we want a keep alive time in seconds to try to keep the secure tunnel connected? uint64_t keep_alive_seconds = 10; // store and get from -> secure_tunnel->config->server_keep_alive; @@ -1337,7 +1390,7 @@ static void s_aws_secure_tunnel_on_socket_write_completion_secure_tunnel_connect s_reevaluate_service_task(secure_tunnel); } -static void s_awssecure_tunnel_on_socket_write_completion_connected( +static void s_aws_secure_tunnel_on_socket_write_completion_connected( struct aws_secure_tunnel *secure_tunnel, int error_code) { if (error_code != AWS_ERROR_SUCCESS) { @@ -1855,43 +1908,6 @@ static void s_reevaluate_service_task(struct aws_secure_tunnel *secure_tunnel) { secure_tunnel->next_service_task_run_time = next_service_time; } -/********************************************************************************************************************* - * Secure Tunnel Clean Up - ********************************************************************************************************************/ - -static void s_secure_tunnel_final_destroy(struct aws_secure_tunnel *secure_tunnel) { - if (secure_tunnel == NULL) { - return; - } - - aws_secure_tunneling_on_termination_complete_fn *on_termination_complete = NULL; - void *termination_complete_user_data = NULL; - if (secure_tunnel->config != NULL) { - on_termination_complete = secure_tunnel->config->on_termination_complete; - termination_complete_user_data = secure_tunnel->config->user_data; - } - - aws_secure_tunnel_operational_state_clean_up(secure_tunnel); - - /* Clean up all memory */ - 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_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); - - if (on_termination_complete != NULL) { - (*on_termination_complete)(termination_complete_user_data); - } -} - -static void s_on_secure_tunnel_zero_ref_count(void *user_data) { - struct aws_secure_tunnel *secure_tunnel = user_data; - - s_aws_secure_tunnel_client_change_desired_state(secure_tunnel, AWS_STS_TERMINATED, NULL); -} - /********************************************************************************************************************* * Update Loop ********************************************************************************************************************/ @@ -2008,6 +2024,7 @@ 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; if (aws_secure_tunnel_service_operational_state(secure_tunnel)) { int error_code = aws_last_error(); AWS_LOGF_ERROR( diff --git a/source/secure_tunneling_operations.c b/source/secure_tunneling_operations.c index e8d2bcb3..9586beb1 100644 --- a/source/secure_tunneling_operations.c +++ b/source/secure_tunneling_operations.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -155,7 +156,7 @@ void aws_secure_tunnel_message_view_log( AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: aws_secure_tunnel_message_view payload set containing %zu bytes", (void *)message_view, - (int)message_view->payload.len); + message_view->payload.len); } static size_t s_aws_secure_tunnel_message_compute_storage_size( diff --git a/tests/aws_iot_secure_tunneling_client_test.c b/tests/aws_iot_secure_tunneling_client_test.c index aced609f..4604cc74 100644 --- a/tests/aws_iot_secure_tunneling_client_test.c +++ b/tests/aws_iot_secure_tunneling_client_test.c @@ -51,15 +51,15 @@ static void s_on_data_receive(const struct aws_byte_buf *data, void *user_data) aws_byte_buf_clean_up(&data_to_print); } -static void s_on_stream_start(void *user_data) { - UNUSED(user_data); - AWS_LOGF_INFO(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Client received StreamStart."); -} +// static void s_on_stream_start(void *user_data) { +// UNUSED(user_data); +// AWS_LOGF_INFO(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Client received StreamStart."); +// } -static void s_on_stream_reset(void *user_data) { - UNUSED(user_data); - AWS_LOGF_INFO(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Client received StreamReset."); -} +// static void s_on_stream_reset(void *user_data) { +// UNUSED(user_data); +// AWS_LOGF_INFO(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Client received StreamReset."); +// } static void s_on_session_reset(void *user_data) { UNUSED(user_data); @@ -97,8 +97,8 @@ static void s_init_secure_tunneling_connection_config( config->on_connection_shutdown = s_on_connection_shutdown; config->on_send_data_complete = s_on_send_data_complete; config->on_data_receive = s_on_data_receive; - config->on_stream_start = s_on_stream_start; - config->on_stream_reset = s_on_stream_reset; + // config->on_stream_start = s_on_stream_start; + // config->on_stream_reset = s_on_stream_reset; config->on_session_reset = s_on_session_reset; config->user_data = allocator; @@ -174,7 +174,7 @@ int main(int argc, char **argv) { struct aws_byte_cursor cur = aws_byte_cursor_from_c_str(payload); AWS_RETURN_ERROR_IF2(aws_secure_tunnel_send_data(secure_tunnel, &cur) == AWS_OP_SUCCESS, AWS_OP_ERR); - AWS_RETURN_ERROR_IF2(aws_secure_tunnel_stream_reset(secure_tunnel) == AWS_OP_SUCCESS, AWS_OP_ERR); + // AWS_RETURN_ERROR_IF2(aws_secure_tunnel_stream_reset(secure_tunnel) == AWS_OP_SUCCESS, AWS_OP_ERR); ASSERT_SUCCESS(aws_condition_variable_wait(&condition_variable, &mutex)); } else if (local_proxy_mode == AWS_SECURE_TUNNELING_DESTINATION_MODE) { /* Wait a little for data to show up */ diff --git a/tests/secure_tunneling_tests.c b/tests/secure_tunneling_tests.c index 6f601b6b..7333387f 100644 --- a/tests/secure_tunneling_tests.c +++ b/tests/secure_tunneling_tests.c @@ -48,34 +48,34 @@ struct secure_tunneling_test_context { static struct secure_tunneling_test_context s_test_context = {.max_threads = 1}; static bool s_on_stream_start_called = false; -static void s_on_stream_start(void *user_data) { - UNUSED(user_data); - s_on_stream_start_called = true; -} +// static void s_on_stream_start(void *user_data) { +// UNUSED(user_data); +// s_on_stream_start_called = true; +// } static bool s_on_data_receive_correct_payload = false; -static void s_on_data_receive(const struct aws_byte_buf *data, void *user_data) { - UNUSED(user_data); - s_on_data_receive_correct_payload = aws_byte_buf_eq_c_str(data, PAYLOAD); -} +// static void s_on_data_receive(const struct aws_byte_buf *data, void *user_data) { +// UNUSED(user_data); +// s_on_data_receive_correct_payload = aws_byte_buf_eq_c_str(data, PAYLOAD); +// } static bool s_on_stream_reset_called = false; -static void s_on_stream_reset(void *user_data) { - UNUSED(user_data); - s_on_stream_reset_called = true; -} +// static void s_on_stream_reset(void *user_data) { +// UNUSED(user_data); +// s_on_stream_reset_called = true; +// } static bool s_on_session_reset_called = false; -static void s_on_session_reset(void *user_data) { - UNUSED(user_data); - s_on_session_reset_called = true; -} +// static void s_on_session_reset(void *user_data) { +// UNUSED(user_data); +// s_on_session_reset_called = true; +// } static bool s_on_termination_complete_called = false; -static void s_on_termination_complete(void *user_data) { - UNUSED(user_data); - s_on_termination_complete_called = true; -} +// static void s_on_termination_complete(void *user_data) { +// UNUSED(user_data); +// s_on_termination_complete_called = true; +// } static void s_init_secure_tunneling_connection_config( struct aws_allocator *allocator, @@ -95,11 +95,13 @@ static void s_init_secure_tunneling_connection_config( options->local_proxy_mode = local_proxy_mode; options->endpoint_host = aws_byte_cursor_from_c_str(endpoint); - options->on_stream_start = s_on_stream_start; - options->on_data_receive = s_on_data_receive; - options->on_stream_reset = s_on_stream_reset; - options->on_session_reset = s_on_session_reset; - options->on_termination_complete = s_on_termination_complete; + /* + options->on_stream_start = s_on_stream_start; + options->on_data_receive = s_on_data_receive; + options->on_stream_reset = s_on_stream_reset; + options->on_session_reset = s_on_session_reset; + options->on_termination_complete = s_on_termination_complete; + */ /* TODO: Initialize the rest of the callbacks */ } @@ -122,20 +124,20 @@ int s_mock_aws_websocket_send_frame( UNUSED(websocket); ++s_mock_aws_websocket_send_frame_call_count; - struct data_tunnel_pair *pair = (struct data_tunnel_pair *)options->user_data; - struct aws_byte_buf *buf = &pair->buf; - struct aws_byte_cursor cursor = aws_byte_cursor_from_buf(buf); + // struct data_tunnel_pair *pair = (struct data_tunnel_pair *)options->user_data; + // struct aws_byte_buf *buf = &pair->buf; + // struct aws_byte_cursor cursor = aws_byte_cursor_from_buf(buf); /* Deserialize the wire format to obtain original payload. */ struct aws_iot_st_msg message; - int rc = aws_iot_st_msg_deserialize_from_cursor(&message, &cursor, s_test_context.secure_tunnel->allocator); - ASSERT_INT_EQUALS(AWS_OP_SUCCESS, rc); + // int rc = aws_iot_st_msg_deserialize_from_cursor(&message, &cursor, s_test_context.secure_tunnel->allocator); + // ASSERT_INT_EQUALS(AWS_OP_SUCCESS, rc); s_mock_aws_websocket_send_frame_payload_len += message.payload.len; aws_byte_buf_clean_up(&message.payload); /* Deallocate memory for the buffer holding the wire protocol data and the tunnel context. */ - aws_byte_buf_clean_up(buf); - aws_mem_release(s_test_context.secure_tunnel->allocator, pair); + // aws_byte_buf_clean_up(buf); + // aws_mem_release(s_test_context.secure_tunnel->allocator, pair); return AWS_OP_SUCCESS; } @@ -313,9 +315,10 @@ static int s_test_sent_data( message.ignorable = 0; message.payload = aws_byte_buf_from_c_str(expected_payload); struct aws_byte_buf serialized_st_msg; - aws_iot_st_msg_serialize_from_struct(&serialized_st_msg, test_context->secure_tunnel->options->allocator, message); + // aws_iot_st_msg_serialize_from_struct(&serialized_st_msg, test_context->secure_tunnel->options->allocator, + // message); - struct aws_byte_cursor cur = aws_byte_cursor_from_c_str(expected_payload); + // struct aws_byte_cursor cur = aws_byte_cursor_from_c_str(expected_payload); struct aws_websocket_send_frame_options frame_options; // Steve TODO fix secure tunnel tests // ASSERT_INT_EQUALS( @@ -325,10 +328,10 @@ static int s_test_sent_data( ASSERT_INT_EQUALS(serialized_st_msg.len + prefix_bytes, frame_options.payload_length); struct aws_byte_buf out_buf; - ASSERT_INT_EQUALS( - AWS_OP_SUCCESS, - aws_byte_buf_init( - &out_buf, test_context->secure_tunnel->options->allocator, (size_t)frame_options.payload_length)); + // ASSERT_INT_EQUALS( + // AWS_OP_SUCCESS, + // aws_byte_buf_init( + // &out_buf, test_context->secure_tunnel->options->allocator, (size_t)frame_options.payload_length)); ASSERT_TRUE(secure_tunneling_send_data_call(NULL, &out_buf, frame_options.user_data)); struct aws_byte_cursor out_buf_cur = aws_byte_cursor_from_buf(&out_buf); @@ -342,7 +345,7 @@ static int s_test_sent_data( struct data_tunnel_pair *pair = frame_options.user_data; aws_byte_buf_clean_up(&pair->buf); - aws_mem_release(pair->secure_tunnel->options->allocator, (void *)pair); + // aws_mem_release(pair->secure_tunnel->options->allocator, (void *)pair); aws_byte_buf_clean_up(&serialized_st_msg); aws_byte_buf_clean_up(&out_buf); @@ -370,7 +373,7 @@ static int s_secure_tunneling_handle_stream_start_test(struct aws_allocator *all */ struct secure_tunneling_test_context *test_context = ctx; - test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_DESTINATION_MODE; + // test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_DESTINATION_MODE; struct aws_iot_st_msg st_msg; AWS_ZERO_STRUCT(st_msg); @@ -380,7 +383,7 @@ static int s_secure_tunneling_handle_stream_start_test(struct aws_allocator *all s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); ASSERT_TRUE(s_on_stream_start_called); - ASSERT_INT_EQUALS(STREAM_ID, test_context->secure_tunnel->config->stream_id); + // ASSERT_INT_EQUALS(STREAM_ID, test_context->secure_tunnel->config->stream_id); ASSERT_UINT_EQUALS(0, test_context->secure_tunnel->received_data.len); return AWS_OP_SUCCESS; @@ -399,7 +402,7 @@ static int s_secure_tunneling_handle_data_receive_test(struct aws_allocator *all */ struct secure_tunneling_test_context *test_context = ctx; - test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_DESTINATION_MODE; + // test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_DESTINATION_MODE; /* Send StreamStart first */ struct aws_iot_st_msg st_msg; @@ -417,7 +420,7 @@ static int s_secure_tunneling_handle_data_receive_test(struct aws_allocator *all s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); ASSERT_TRUE(s_on_data_receive_correct_payload); - ASSERT_INT_EQUALS(STREAM_ID, test_context->secure_tunnel->config->stream_id); + // ASSERT_INT_EQUALS(STREAM_ID, test_context->secure_tunnel->config->stream_id); ASSERT_UINT_EQUALS(0, test_context->secure_tunnel->received_data.len); return AWS_OP_SUCCESS; @@ -436,7 +439,7 @@ static int s_secure_tunneling_handle_stream_reset_test(struct aws_allocator *all */ struct secure_tunneling_test_context *test_context = ctx; - test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_DESTINATION_MODE; + // test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_DESTINATION_MODE; /* Send StreamStart first */ struct aws_iot_st_msg st_msg; @@ -453,7 +456,7 @@ static int s_secure_tunneling_handle_stream_reset_test(struct aws_allocator *all s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); ASSERT_TRUE(s_on_stream_reset_called); - ASSERT_INT_EQUALS(INVALID_STREAM_ID, test_context->secure_tunnel->config->stream_id); + // ASSERT_INT_EQUALS(INVALID_STREAM_ID, test_context->secure_tunnel->config->stream_id); ASSERT_UINT_EQUALS(0, test_context->secure_tunnel->received_data.len); return AWS_OP_SUCCESS; @@ -472,7 +475,7 @@ static int s_secure_tunneling_handle_session_reset_test(struct aws_allocator *al */ struct secure_tunneling_test_context *test_context = ctx; - test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_DESTINATION_MODE; + // test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_DESTINATION_MODE; /* Send StreamStart first */ struct aws_iot_st_msg st_msg; @@ -489,7 +492,7 @@ static int s_secure_tunneling_handle_session_reset_test(struct aws_allocator *al s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); ASSERT_TRUE(s_on_session_reset_called); - ASSERT_INT_EQUALS(INVALID_STREAM_ID, test_context->secure_tunnel->config->stream_id); + // ASSERT_INT_EQUALS(INVALID_STREAM_ID, test_context->secure_tunnel->config->stream_id); ASSERT_UINT_EQUALS(0, test_context->secure_tunnel->received_data.len); return AWS_OP_SUCCESS; @@ -508,7 +511,7 @@ static int s_secure_tunneling_handle_session_reset_no_stream_test(struct aws_all */ struct secure_tunneling_test_context *test_context = ctx; - test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_DESTINATION_MODE; + // test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_DESTINATION_MODE; /* Send StreamReset without existing stream */ struct aws_iot_st_msg st_msg; @@ -518,7 +521,7 @@ static int s_secure_tunneling_handle_session_reset_no_stream_test(struct aws_all s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); ASSERT_FALSE(s_on_session_reset_called); - ASSERT_INT_EQUALS(INVALID_STREAM_ID, test_context->secure_tunnel->config->stream_id); + // ASSERT_INT_EQUALS(INVALID_STREAM_ID, test_context->secure_tunnel->config->stream_id); ASSERT_UINT_EQUALS(0, test_context->secure_tunnel->received_data.len); return AWS_OP_SUCCESS; @@ -599,8 +602,8 @@ static int s_secure_tunneling_handle_send_data(struct aws_allocator *allocator, const enum aws_secure_tunnel_message_type type = AWS_SECURE_TUNNEL_MT_DATA; struct secure_tunneling_test_context *test_context = ctx; - test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_SOURCE_MODE; - test_context->secure_tunnel->config->stream_id = expected_stream_id; + // test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_SOURCE_MODE; + // test_context->secure_tunnel->config->stream_id = expected_stream_id; s_test_sent_data(test_context, expected_payload, expected_stream_id, prefix_bytes, type); @@ -626,8 +629,8 @@ static int s_secure_tunneling_handle_send_data_stream_start(struct aws_allocator const enum aws_secure_tunnel_message_type type = AWS_SECURE_TUNNEL_MT_STREAM_START; struct secure_tunneling_test_context *test_context = ctx; - test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_SOURCE_MODE; - test_context->secure_tunnel->config->stream_id = expected_stream_id; + // test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_SOURCE_MODE; + // test_context->secure_tunnel->config->stream_id = expected_stream_id; s_test_sent_data(test_context, expected_payload, expected_stream_id, prefix_bytes, type); @@ -653,8 +656,8 @@ static int s_secure_tunneling_handle_send_data_stream_reset(struct aws_allocator const enum aws_secure_tunnel_message_type type = AWS_SECURE_TUNNEL_MT_STREAM_RESET; struct secure_tunneling_test_context *test_context = ctx; - test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_SOURCE_MODE; - test_context->secure_tunnel->config->stream_id = expected_stream_id; + // test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_SOURCE_MODE; + // test_context->secure_tunnel->config->stream_id = expected_stream_id; s_test_sent_data(test_context, expected_payload, expected_stream_id, prefix_bytes, type); @@ -675,7 +678,7 @@ static int s_secure_tunneling_handle_send_data_public(struct aws_allocator *allo */ struct secure_tunneling_test_context *test_context = ctx; - test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_SOURCE_MODE; + // test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_SOURCE_MODE; /* Open the tunnel. */ int rc = aws_secure_tunnel_connect(test_context->secure_tunnel); From d7c42165b563e4eeeac547e95740f0d4182204f4 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Thu, 19 Jan 2023 16:37:04 -0800 Subject: [PATCH 11/69] progress before removing channel and slot for websocket use --- .../iotdevice/private/secure_tunneling_impl.h | 17 +- .../private/secure_tunneling_operations.h | 4 +- include/aws/iotdevice/secure_tunneling.h | 6 + source/iotdevice.c | 7 +- source/secure_tunneling.c | 564 ++++++++---------- source/secure_tunneling_operations.c | 51 +- tests/secure_tunneling_tests.c | 6 +- 7 files changed, 298 insertions(+), 357 deletions(-) diff --git a/include/aws/iotdevice/private/secure_tunneling_impl.h b/include/aws/iotdevice/private/secure_tunneling_impl.h index 3dc3f632..7ad23588 100644 --- a/include/aws/iotdevice/private/secure_tunneling_impl.h +++ b/include/aws/iotdevice/private/secure_tunneling_impl.h @@ -41,25 +41,14 @@ enum aws_secure_tunnel_state { * This state is not interruptible by any means other than channel setup completion. * * Next States: - * SECURE_TUNNEL_CONNECT - if the channel completes setup with no error and desired state is still CONNECTED - * CHANNEL_SHUTDOWN - if the channel completes setup with no error, but desired state is not CONNECTED + * CONNECTED - if WebSocket handshake is successful and desired state is still CONNECTED + * CHANNEL_SHUTDOWN - On unsuccessful WebSocket Handshake or if the channel completes setup with no error, but + * desired state is not CONNECTED * PENDING_RECONNECT - if the channel fails to complete setup and desired state is still CONNECTED * STOPPED - if the channel fails to complete setup and desired state is not CONNECTED */ AWS_STS_CONNECTING, - /* - * The secure tunnel is attempting to connect through the AWS Secure Tunnel Service via a WebSocket handshake. - * - * Next States: - * CONNECTED - if WebSocket handshake is successful and desired state is still CONNECTED - * CHANNEL_SHUTDOWN - On send/encode errors, read/decode errors, unsuccessful WebSocket Handshake, - * desired state is no longer CONNECTED - * PENDING_RECONNECT - unexpected channel shutdown completion and desired state still CONNECTED - * STOPPED - unexpected channel shutdown completion and desired state no longer CONNECTED - */ - AWS_STS_SECURE_TUNNEL_CONNECT, - /* * The secure tunnel is ready to perform user-requested operations. * diff --git a/include/aws/iotdevice/private/secure_tunneling_operations.h b/include/aws/iotdevice/private/secure_tunneling_operations.h index b920303f..119f404e 100644 --- a/include/aws/iotdevice/private/secure_tunneling_operations.h +++ b/include/aws/iotdevice/private/secure_tunneling_operations.h @@ -102,9 +102,7 @@ struct aws_secure_tunnel_options_storage { struct aws_http_proxy_options http_proxy_options; struct aws_http_proxy_config *http_proxy_config; struct aws_string *access_token; - - aws_secure_tunnel_transform_websocket_handshake_fn *websocket_handshake_transform; - void *websocket_handshake_transform_user_data; + struct aws_string *client_token; struct aws_string *endpoint_host; diff --git a/include/aws/iotdevice/secure_tunneling.h b/include/aws/iotdevice/secure_tunneling.h index a0987ae8..dbebdf30 100644 --- a/include/aws/iotdevice/secure_tunneling.h +++ b/include/aws/iotdevice/secure_tunneling.h @@ -150,6 +150,12 @@ struct aws_secure_tunnel_options { */ struct aws_byte_cursor access_token; + /** + * (Optional) Client Token used to re-establish a Secure Tunnel connection after the one-time use access token has + * been used. If one is not provided, it will automatically be generated and re-used on subsequent reconnects. + */ + struct aws_byte_cursor client_token; + const char *root_ca; const char *service_id_1; const char *service_id_2; diff --git a/source/iotdevice.c b/source/iotdevice.c index 619c8f82..b08e2d11 100644 --- a/source/iotdevice.c +++ b/source/iotdevice.c @@ -42,7 +42,7 @@ static struct aws_error_info s_errors[] = { AWS_DEFINE_ERROR_INFO_IOTDEVICE( AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INCORRECT_MODE, "Secure Tunnel stream cannot be started while in Destination Mode."), - AWS_DEFINE_ERROR_INFO_IOTDEVICE( + AWS_DEFINE_ERROR_INFO_IOTDEVICE( AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_BAD_SERVICE_ID, "Secure Tunnel stream start request with bad service id."), AWS_DEFINE_ERROR_INFO_IOTDEVICE( @@ -68,7 +68,7 @@ static struct aws_error_info s_errors[] = { "Error while processing secure tunnel operational state."), AWS_DEFINE_ERROR_INFO_IOTDEVICE( AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_OPERATION_PROCESSING_FAILURE, - ""), + "Error while processing secure tunnel operational state."), AWS_DEFINE_ERROR_INFO_IOTDEVICE( AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_UNEXPECTED_HANGUP, "The connection was closed unexpectedly."), @@ -104,8 +104,7 @@ static struct aws_log_subject_info_list s_logging_subjects_list = { static bool s_iotdevice_library_initialized = false; void aws_iotdevice_library_init(struct aws_allocator *allocator) { - AWS_PRECONDITION(aws_allocator_is_valid(allocator)); - (void)(allocator); // ignore unused warning + (void)(allocator); if (!s_iotdevice_library_initialized) { aws_register_error_info(&s_error_list); diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 5cefcf04..300d3c74 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -24,6 +24,10 @@ #define INVALID_STREAM_ID 0 #define PAYLOAD_BYTE_LENGTH_PREFIX 2 #define PING_TASK_INTERVAL ((uint64_t)20 * 1000000000) +#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" // Steve TODO check if these need to be implemented or re-organized static void s_change_current_state(struct aws_secure_tunnel *secure_tunnel, enum aws_secure_tunnel_state next_state); @@ -40,8 +44,10 @@ static int s_secure_tunneling_send( const struct aws_byte_cursor *payload_data, const struct aws_byte_cursor *service_id_data, enum aws_secure_tunnel_message_type type); +static struct aws_http_message *s_new_handshake_request(const struct aws_secure_tunnel *secure_tunnel); static int s_websocket_connect(struct aws_secure_tunnel *secure_tunnel); +static void s_reset_ping(struct aws_secure_tunnel *secure_tunnel); static void s_reevaluate_service_task(struct aws_secure_tunnel *secure_tunnel); @@ -53,9 +59,6 @@ const char *aws_secure_tunnel_state_to_c_string(enum aws_secure_tunnel_state sta case AWS_STS_CONNECTING: return "CONNECTING"; - case AWS_STS_SECURE_TUNNEL_CONNECT: - return "SECURE_TUNNEL_CONNECT"; - case AWS_STS_CONNECTED: return "CONNECTED"; @@ -170,7 +173,7 @@ static int s_aws_secure_tunnel_set_stream_id( aws_string_compare(secure_tunnel->config->service_id_3, service_id_to_set) == 0) { secure_tunnel->config->service_id_3_stream_id = stream_id; } else { - AWS_LOGF_ERROR( + AWS_LOGF_WARN( AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: Secure tunnel request for unsupported service_id: " PRInSTR, (void *)secure_tunnel, @@ -279,7 +282,17 @@ static void s_process_received_data(struct aws_secure_tunnel *secure_tunnel) { typedef int( websocket_send_frame)(struct aws_websocket *websocket, const struct aws_websocket_send_frame_options *options); -bool on_websocket_incoming_frame_payload( +static bool s_on_websocket_incoming_frame_begin( + struct aws_websocket *websocket, + const struct aws_websocket_incoming_frame *frame, + void *user_data) { + (void)websocket; + (void)frame; + (void)user_data; + return true; +} + +static bool s_on_websocket_incoming_frame_payload( struct aws_websocket *websocket, const struct aws_websocket_incoming_frame *frame, struct aws_byte_cursor data, @@ -402,26 +415,26 @@ static void s_secure_tunnel_setup( struct aws_channel *channel, void *user_data) { - (void)bootstrap; - /* Setup callback contract is: if error_code is non-zero then channel is NULL. */ AWS_FATAL_ASSERT((error_code != 0) == (channel == NULL)); struct aws_secure_tunnel *secure_tunnel = user_data; - AWS_FATAL_ASSERT(secure_tunnel->current_state == AWS_STS_CONNECTING); - if (error_code != AWS_OP_SUCCESS) { - /* secure tunnel shutdown already handles this case, so just call that. */ + /* secure tunnel already handles this case, so just call that. */ s_secure_tunnel_shutdown(bootstrap, error_code, channel, user_data); return; } + AWS_FATAL_ASSERT(secure_tunnel->current_state == AWS_STS_CONNECTING); + AWS_FATAL_ASSERT(aws_event_loop_thread_is_callers_thread(secure_tunnel->loop)); + if (secure_tunnel->desired_state != AWS_STS_CONNECTED) { aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_USER_REQUESTED_STOP); goto error; } - secure_tunnel->slot = aws_channel_slot_new(channel); /* allocs or crashes */ + /* allocs or crashes */ + secure_tunnel->slot = aws_channel_slot_new(channel); if (aws_channel_slot_insert_end(channel, secure_tunnel->slot)) { AWS_LOGF_ERROR( @@ -437,7 +450,7 @@ static void s_secure_tunnel_setup( if (aws_channel_slot_set_handler(secure_tunnel->slot, &secure_tunnel->handler)) { AWS_LOGF_ERROR( AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: Failed to set MQTT handler into slot on channel %p, error %d (%s).", + "id=%p: Failed to set secure tunnel handler into slot on channel %p, error %d (%s).", (void *)secure_tunnel, (void *)channel, aws_last_error(), @@ -446,12 +459,10 @@ static void s_secure_tunnel_setup( goto error; } - s_change_current_state(secure_tunnel, AWS_STS_SECURE_TUNNEL_CONNECT); + s_change_current_state(secure_tunnel, AWS_STS_CONNECTED); return; - error: - s_change_current_state(secure_tunnel, AWS_STS_CHANNEL_SHUTDOWN); (*secure_tunnel->vtable->channel_shutdown_fn)(channel, aws_last_error()); } @@ -466,76 +477,38 @@ static void s_on_websocket_shutdown(struct aws_websocket *websocket, int error_c if (websocket) { aws_websocket_release(websocket); } - - /* Callback for user to indicate websocket has shutdown */ - if (secure_tunnel->config->on_connection_shutdown != NULL) { - secure_tunnel->config->on_connection_shutdown(secure_tunnel->config->user_data); - } } -static void s_on_websocket_setup(const struct aws_websocket_on_connection_setup_data *setup, void *user_data) -// struct aws_websocket *websocket, -// int error_code, -// int handshake_response_status, -// const struct aws_http_header *handshake_response_header_array, -// size_t num_handshake_response_headers, -// void *user_data) -{ - (void)setup; - (void)user_data; - - // (void)handshake_response_status; - // (void)handshake_response_header_array; - // (void)num_handshake_response_headers; - - // struct aws_secure_tunnel *secure_tunnel = user_data; - // secure_tunnel->handshake_request = aws_http_message_release(secure_tunnel->handshake_request); - - // /* Setup callback contract is: if error_code is non-zero then websocket is NULL. */ - // AWS_FATAL_ASSERT((error_code != 0) == (websocket == NULL)); - - // struct aws_channel *channel = NULL; +static void s_on_websocket_setup(const struct aws_websocket_on_connection_setup_data *setup, void *user_data) { + struct aws_secure_tunnel *secure_tunnel = user_data; + secure_tunnel->handshake_request = aws_http_message_release(secure_tunnel->handshake_request); - // if (websocket) { - // channel = aws_websocket_get_channel(websocket); - // AWS_ASSERT(channel); + /* Setup callback contract is: if error_code is non-zero then websocket is NULL. */ + AWS_FATAL_ASSERT((setup->error_code != 0) == (setup->websocket == NULL)); - // /* Websocket must be "converted" before the secure tunnel handler can be installed next to it. */ - // if (aws_websocket_convert_to_midchannel_handler(websocket)) { - // AWS_LOGF_ERROR( - // AWS_LS_IOTDEVICE_SECURE_TUNNELING, - // "id=%p: Failed converting websocket, error %d (%s)", - // (void *)secure_tunnel, - // aws_last_error(), - // aws_error_name(aws_last_error())); + struct aws_channel *channel = NULL; - // (*secure_tunnel->vtable->channel_shutdown_fn)(channel, aws_last_error()); - // return; - // } + if (setup->websocket) { + channel = aws_websocket_get_channel(setup->websocket); + AWS_ASSERT(channel); - // secure_tunnel->websocket = websocket; - // struct ping_task_context *ping_task_context = - // aws_mem_acquire(secure_tunnel->allocator, sizeof(struct ping_task_context)); - // secure_tunnel->ping_task_context = ping_task_context; - // AWS_ZERO_STRUCT(*ping_task_context); - // ping_task_context->allocator = secure_tunnel->allocator; - // ping_task_context->event_loop = - // aws_event_loop_group_get_next_loop(secure_tunnel->options->bootstrap->event_loop_group); - // aws_atomic_store_int(&ping_task_context->task_cancelled, 0); - // ping_task_context->websocket = websocket; - // ping_task_context->send_frame = secure_tunnel->websocket_vtable.send_frame; - - // aws_task_init(&ping_task_context->ping_task, s_ping_task, ping_task_context, "SecureTunnelingPingTask"); - // aws_event_loop_schedule_task_now(ping_task_context->event_loop, &ping_task_context->ping_task); - // } + /* Websocket must be "converted" before the MQTT handler can be installed next to it. */ + // STEVE TODO temp removing the conversion + // if (aws_websocket_convert_to_midchannel_handler(setup->websocket)) { + // AWS_LOGF_ERROR( + // AWS_LS_IOTDEVICE_SECURE_TUNNELING, + // "id=%p: Failed converting websocket, error %d (%s)", + // (void *)secure_tunnel, + // aws_last_error(), + // aws_error_name(aws_last_error())); - // s_secure_tunnel_setup(secure_tunnel->config->bootstrap, error_code, channel, secure_tunnel); + // (*secure_tunnel->vtable->channel_shutdown_fn)(channel, aws_last_error()); + // return; + // } + } - /* Existing connection_complete callback doesn't resturn an error code. Workaround to provide it. */ - /* Previous implementation, may be removed if tasked websocket setups succeeeds */ - // secure_tunnel->connection_error_code = error_code; - // s_secure_tunnel_setup(secure_tunnel->config->bootstrap, error_code, channel, secure_tunnel); - // secure_tunnel->options->on_connection_complete(secure_tunnel->options->user_data); + /* Call into the channel-setup callback, the rest of the logic is the same. */ + s_secure_tunnel_setup(secure_tunnel->config->bootstrap, setup->error_code, channel, secure_tunnel); } struct aws_secure_tunnel_websocket_transform_complete_task { @@ -568,34 +541,19 @@ void s_websocket_transform_complete_task_fn(struct aws_task *task, void *arg, en .tls_options = &secure_tunnel->tls_con_opt, .host = aws_byte_cursor_from_string(secure_tunnel->config->endpoint_host), .port = 443, - .handshake_request = websocket_transform_complete_task->handshake, - /* This may need to be set to MAX_WEBSOCKET_PAYLOAD */ - .initial_window_size = - 0, /* Prevent websocket data from arriving before the secure_tunnel handler is installed */ + .handshake_request = secure_tunnel->handshake_request, + .initial_window_size = MAX_WEBSOCKET_PAYLOAD, /* Prevent websocket data from arriving before the + secure_tunnel handler is installed by setting this to 0 */ .user_data = secure_tunnel, .on_connection_setup = s_on_websocket_setup, .on_connection_shutdown = s_on_websocket_shutdown, - .requested_event_loop = secure_tunnel->loop, /* STEVE TODO LEFT OFF RIGHT HERE */ - }; + .requested_event_loop = secure_tunnel->loop, - /* - websocket_options->allocator = secure_tunnel->allocator; - websocket_options->bootstrap = secure_tunnel->options->bootstrap; - websocket_options->socket_options = secure_tunnel->options->socket_options; - websocket_options->tls_options = &secure_tunnel->tls_con_opt; - websocket_options->host = secure_tunnel->options->endpoint_host; - websocket_options->port = 443; - websocket_options->handshake_request = s_new_handshake_request(secure_tunnel); - websocket_options->initial_window_size = MAX_WEBSOCKET_PAYLOAD; - websocket_options->user_data = secure_tunnel; - websocket_options->proxy_options = secure_tunnel->options->http_proxy_options; - websocket_options->on_connection_setup = s_on_websocket_setup; - websocket_options->on_connection_shutdown = s_on_websocket_shutdown; - websocket_options->on_incoming_frame_begin = s_on_websocket_incoming_frame_begin; - websocket_options->on_incoming_frame_payload = on_websocket_incoming_frame_payload; - websocket_options->on_incoming_frame_complete = s_on_websocket_incoming_frame_complete; - websocket_options->manual_window_management = false; - */ + /* STEVE TODO These are the old functions that may need replacing */ + .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, + }; if (secure_tunnel->config->http_proxy_config != NULL) { websocket_options.proxy_options = &secure_tunnel->config->http_proxy_options; @@ -618,8 +576,9 @@ void s_websocket_transform_complete_task_fn(struct aws_task *task, void *arg, en } } -error: - // s_on_websocket_setup(NULL, error_code, -1, NULL, 0, secure_tunnel); +error:; + struct aws_websocket_on_connection_setup_data websocket_setup = {.error_code = error_code}; + s_on_websocket_setup(&websocket_setup, secure_tunnel); done: @@ -629,94 +588,53 @@ void s_websocket_transform_complete_task_fn(struct aws_task *task, void *arg, en aws_mem_release(websocket_transform_complete_task->allocator, websocket_transform_complete_task); } -static void s_websocket_handshake_transform_complete( - struct aws_http_message *handshake_request, - int error_code, - void *complete_ctx) { +static int s_websocket_connect(struct aws_secure_tunnel *secure_tunnel) { + AWS_ASSERT(secure_tunnel); + + struct aws_http_message *handshake = s_new_handshake_request(secure_tunnel); + if (handshake == NULL) { + goto error; + } - struct aws_secure_tunnel *secure_tunnel = complete_ctx; + AWS_LOGF_TRACE( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: Transforming websocket handshake request.", (void *)secure_tunnel); struct aws_secure_tunnel_websocket_transform_complete_task *task = aws_mem_calloc(secure_tunnel->allocator, 1, sizeof(struct aws_secure_tunnel_websocket_transform_complete_task)); aws_task_init( &task->task, s_websocket_transform_complete_task_fn, (void *)task, "WebsocketHandshakeTransformComplete"); - task->allocator = secure_tunnel->allocator; task->secure_tunnel = aws_secure_tunnel_acquire(secure_tunnel); - task->error_code = error_code; - task->handshake = handshake_request; + task->error_code = AWS_OP_SUCCESS; + task->handshake = handshake; aws_event_loop_schedule_task_now(secure_tunnel->loop, &task->task); - aws_secure_tunnel_release(secure_tunnel); -} - -static int s_websocket_connect(struct aws_secure_tunnel *secure_tunnel) { - (void)secure_tunnel; - AWS_ASSERT(secure_tunnel); - AWS_ASSERT(secure_tunnel->config->websocket_handshake_transform); - - /* Build websocket handshake request */ - char path[50]; - snprintf( - path, - sizeof(path), - "/tunnel?local-proxy-mode=%s", - s_get_proxy_mode_string(secure_tunnel->config->local_proxy_mode)); - - struct aws_http_message *handshake = aws_http_message_new_websocket_handshake_request( - secure_tunnel->allocator, - aws_byte_cursor_from_c_str(path), - aws_byte_cursor_from_string(secure_tunnel->config->endpoint_host)); + return AWS_OP_SUCCESS; - /* Secure Tunnel specific headers */ - // Steve TODO implement client token connection headers here - struct aws_http_header extra_headers[] = { - { - .name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Sec-WebSocket-Protocol"), - .value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("aws.iot.securetunneling-2.0"), - }, - { - .name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("access-token"), - .value = aws_byte_cursor_from_string(secure_tunnel->config->access_token), - }, - }; - (void)extra_headers; +error: + return AWS_OP_ERR; +} - if (handshake == NULL) { +static int s_handshake_add_header( + const struct aws_secure_tunnel *secure_tunnel, + struct aws_http_message *handshake, + struct aws_http_header header) { + if (aws_http_message_add_header(handshake, header)) { AWS_LOGF_ERROR( AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: Transforming websocket handshake request.", + "id=%p: Failed to add header to websocket handshake request", (void *)secure_tunnel); - goto error; + return AWS_OP_ERR; } AWS_LOGF_TRACE( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: Transforming websocket handshake request.", (void *)secure_tunnel); - - aws_secure_tunnel_acquire(secure_tunnel); - secure_tunnel->config->websocket_handshake_transform( - handshake, - secure_tunnel->config->websocket_handshake_transform_user_data, - s_websocket_handshake_transform_complete, - secure_tunnel); - + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Added header " PRInSTR " " PRInSTR " to websocket request", + (void *)secure_tunnel, + AWS_BYTE_CURSOR_PRI(header.name), + AWS_BYTE_CURSOR_PRI(header.value)); return AWS_OP_SUCCESS; - -error: - aws_http_message_release(handshake); - - return AWS_OP_ERR; -} - -static bool s_on_websocket_incoming_frame_begin( - struct aws_websocket *websocket, - const struct aws_websocket_incoming_frame *frame, - void *user_data) { - (void)websocket; - (void)frame; - (void)user_data; - return true; } static struct aws_http_message *s_new_handshake_request(const struct aws_secure_tunnel *secure_tunnel) { @@ -726,55 +644,50 @@ static struct aws_http_message *s_new_handshake_request(const struct aws_secure_ sizeof(path), "/tunnel?local-proxy-mode=%s", s_get_proxy_mode_string(secure_tunnel->config->local_proxy_mode)); - struct aws_http_message *handshake_request = aws_http_message_new_websocket_handshake_request( + + struct aws_http_message *handshake = aws_http_message_new_websocket_handshake_request( secure_tunnel->allocator, aws_byte_cursor_from_c_str(path), aws_byte_cursor_from_string(secure_tunnel->config->endpoint_host)); - // Steve TODO implement client token connection here - - struct aws_http_header extra_headers[] = { - { - .name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Sec-WebSocket-Protocol"), - .value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("aws.iot.securetunneling-2.0"), - }, - { - .name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("access-token"), - .value = aws_byte_cursor_from_string(secure_tunnel->config->access_token), - }, + if (handshake == NULL) { + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: Failed to generate handshake request.", (void *)secure_tunnel); + goto error; + } + + // /* Secure Tunnel specific headers */ + struct aws_http_header header_protocol = { + .name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL(WEBSOCKET_HEADER_NAME_PROTOCOL), + .value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL(WEBSOCKET_HEADER_PROTOCOL_VALUE), }; - for (size_t i = 0; i < AWS_ARRAY_SIZE(extra_headers); ++i) { - aws_http_message_add_header(handshake_request, extra_headers[i]); + if (s_handshake_add_header(secure_tunnel, handshake, header_protocol)) { + goto error; } - return handshake_request; -} + struct aws_http_header header_access_token = { + .name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL(WEBSOCKET_HEADER_NAME_ACCESS_TOKEN), + .value = aws_byte_cursor_from_string(secure_tunnel->config->access_token), + }; + if (s_handshake_add_header(secure_tunnel, handshake, header_access_token)) { + goto error; + } -// Steve TODO remove this -void init_websocket_client_connection_options( - struct aws_secure_tunnel *secure_tunnel, - struct aws_websocket_client_connection_options *websocket_options) { - - AWS_ZERO_STRUCT(*websocket_options); - websocket_options->allocator = secure_tunnel->allocator; - websocket_options->bootstrap = secure_tunnel->config->bootstrap; - websocket_options->socket_options = &secure_tunnel->config->socket_options; - websocket_options->tls_options = &secure_tunnel->tls_con_opt; - websocket_options->host = aws_byte_cursor_from_string(secure_tunnel->config->endpoint_host); - websocket_options->port = 443; - websocket_options->handshake_request = s_new_handshake_request(secure_tunnel); - websocket_options->initial_window_size = MAX_WEBSOCKET_PAYLOAD; /* TODO: followup */ - websocket_options->user_data = secure_tunnel; - websocket_options->proxy_options = &secure_tunnel->config->http_proxy_options; - websocket_options->on_connection_setup = s_on_websocket_setup; - websocket_options->on_connection_shutdown = s_on_websocket_shutdown; - websocket_options->on_incoming_frame_begin = s_on_websocket_incoming_frame_begin; - websocket_options->on_incoming_frame_payload = on_websocket_incoming_frame_payload; - websocket_options->on_incoming_frame_complete = s_on_websocket_incoming_frame_complete; - websocket_options->manual_window_management = false; - - /* Save handshake_request to release it later */ - secure_tunnel->handshake_request = websocket_options->handshake_request; + if (secure_tunnel->config->client_token) { + struct aws_http_header header_client_token = { + .name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL(WEBSOCKET_HEADER_NAME_CLIENT_TOKEN), + .value = aws_byte_cursor_from_string(secure_tunnel->config->client_token), + }; + if (s_handshake_add_header(secure_tunnel, handshake, header_client_token)) { + goto error; + } + } + + return handshake; + +error: + aws_http_message_release(handshake); + return NULL; } /********************************************************************************************************************* @@ -785,8 +698,7 @@ static void s_aws_secure_tunnel_shutdown_channel(struct aws_secure_tunnel *secur if (error_code == AWS_ERROR_SUCCESS) { error_code = AWS_ERROR_UNKNOWN; } - if (secure_tunnel->current_state != AWS_STS_SECURE_TUNNEL_CONNECT && - secure_tunnel->current_state != AWS_STS_CONNECTED && secure_tunnel->current_state != AWS_STS_CLEAN_DISCONNECT) { + if (secure_tunnel->current_state != AWS_STS_CONNECTED && secure_tunnel->current_state != AWS_STS_CLEAN_DISCONNECT) { AWS_LOGF_ERROR( AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: secure tunnel channel shutdown invoked from unexpected state %d(%s)", @@ -841,29 +753,7 @@ static void s_change_current_state_to_connecting(struct aws_secure_tunnel *secur secure_tunnel->current_state = AWS_STS_CONNECTING; secure_tunnel->clean_disconnect_error_code = AWS_ERROR_SUCCESS; - int result = 0; - if (secure_tunnel->config->websocket_handshake_transform != NULL) { - result = s_websocket_connect(secure_tunnel); - } else { - struct aws_socket_channel_bootstrap_options channel_options; - AWS_ZERO_STRUCT(channel_options); - channel_options.bootstrap = secure_tunnel->config->bootstrap; - channel_options.host_name = aws_string_c_str(secure_tunnel->config->endpoint_host); - channel_options.port = 443; - channel_options.socket_options = &secure_tunnel->config->socket_options; - channel_options.tls_options = &secure_tunnel->tls_con_opt; - channel_options.setup_callback = &s_secure_tunnel_setup; - channel_options.shutdown_callback = &s_secure_tunnel_shutdown; - channel_options.user_data = secure_tunnel; - channel_options.requested_event_loop = secure_tunnel->loop; - - if (secure_tunnel->config->http_proxy_config == NULL) { - result = (*secure_tunnel->vtable->client_bootstrap_new_socket_channel_fn)(&channel_options); - } else { - result = (*secure_tunnel->vtable->http_proxy_new_socket_channel_fn)( - &channel_options, &secure_tunnel->config->http_proxy_options); - } - } + int result = s_websocket_connect(secure_tunnel); if (result) { int error_code = aws_last_error(); @@ -879,18 +769,32 @@ static void s_change_current_state_to_connecting(struct aws_secure_tunnel *secur } // Steve TODO implement these state changes -static void s_change_current_state_to_secure_tunnel_connect(struct aws_secure_tunnel *secure_tunnel) { - (void)secure_tunnel; -} + static void s_change_current_state_to_connected(struct aws_secure_tunnel *secure_tunnel) { - (void)secure_tunnel; + AWS_FATAL_ASSERT(secure_tunnel->current_state == AWS_STS_CONNECTING); + + secure_tunnel->current_state = AWS_STS_CONNECTED; + + aws_secure_tunnel_on_disconnection_update_operational_state(secure_tunnel); + + secure_tunnel->next_ping_timeout_time = 0; + s_reset_ping(secure_tunnel); } + static void s_change_current_state_to_clean_disconnect(struct aws_secure_tunnel *secure_tunnel) { - (void)secure_tunnel; + AWS_FATAL_ASSERT(secure_tunnel->current_state == AWS_STS_CONNECTED); + + secure_tunnel->current_state = AWS_STS_CLEAN_DISCONNECT; } + static void s_change_current_state_to_channel_shutdown(struct aws_secure_tunnel *secure_tunnel) { - (void)secure_tunnel; + enum aws_secure_tunnel_state current_state = secure_tunnel->current_state; + AWS_FATAL_ASSERT( + current_state == AWS_STS_CONNECTING || current_state == AWS_STS_CONNECTED || + current_state == AWS_STS_CLEAN_DISCONNECT); + secure_tunnel->current_state = AWS_STS_CHANNEL_SHUTDOWN; } + static void s_change_current_state_to_pending_reconnect(struct aws_secure_tunnel *secure_tunnel) { (void)secure_tunnel; } @@ -920,9 +824,6 @@ static void s_change_current_state(struct aws_secure_tunnel *secure_tunnel, enum case AWS_STS_CONNECTING: s_change_current_state_to_connecting(secure_tunnel); break; - case AWS_STS_SECURE_TUNNEL_CONNECT: - s_change_current_state_to_secure_tunnel_connect(secure_tunnel); - break; case AWS_STS_CONNECTED: s_change_current_state_to_connected(secure_tunnel); break; @@ -1057,16 +958,17 @@ static uint64_t s_aws_high_res_clock_get_ticks_proxy(void) { } static int s_secure_tunneling_connect(struct aws_secure_tunnel *secure_tunnel) { + (void)secure_tunnel; // Steve TODO this is checking the stream_id and not other service id streams. - if (secure_tunnel == NULL || secure_tunnel->config->stream_id != INVALID_STREAM_ID) { - return AWS_OP_ERR; - } + // if (secure_tunnel == NULL || secure_tunnel->config->stream_id != INVALID_STREAM_ID) { + // return AWS_OP_ERR; + // } - struct aws_websocket_client_connection_options websocket_options; - init_websocket_client_connection_options(secure_tunnel, &websocket_options); - if (secure_tunnel->websocket_vtable.client_connect(&websocket_options)) { - return AWS_OP_ERR; - } + // struct aws_websocket_client_connection_options websocket_options; + // init_websocket_client_connection_options(secure_tunnel, &websocket_options); + // if (secure_tunnel->websocket_vtable.client_connect(&websocket_options)) { + // return AWS_OP_ERR; + // } return AWS_OP_SUCCESS; } @@ -1319,6 +1221,95 @@ static struct aws_secure_tunnel_vtable s_default_secure_tunnel_vtable = { .send_stream_reset = s_secure_tunneling_send_stream_reset, }; +static int s_process_read_message( + struct aws_channel_handler *handler, + struct aws_channel_slot *slot, + struct aws_io_message *message) { + + struct aws_secure_tunnel *secure_tunnel = handler->impl; + + if (message->message_type != AWS_IO_MESSAGE_APPLICATION_DATA) { + AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "id=%p: unexpected io message data", (void *)secure_tunnel); + return AWS_OP_ERR; + } + + AWS_LOGF_TRACE( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: processing read message of size %zu", + (void *)secure_tunnel, + message->message_data.len); + + struct aws_byte_cursor message_cursor = aws_byte_cursor_from_buf(&message->message_data); + + (void)message_cursor; + printf("\n\nSTEVE TODO DEGUB s_process_read_message() called here\n\n"); + + // int result = aws_mqtt5_decoder_on_data_received(&client->decoder, message_cursor); + // if (result != AWS_OP_SUCCESS) { + // int error_code = aws_last_error(); + // AWS_LOGF_ERROR( + // AWS_LS_MQTT5_CLIENT, + // "id=%p: decode failure with error %d(%s)", + // (void *)client, + // error_code, + // aws_error_debug_str(error_code)); + + // if (error_code == AWS_ERROR_MQTT5_DECODE_PROTOCOL_ERROR && s_should_client_disconnect_cleanly(client)) { + // s_aws_mqtt5_client_shutdown_channel_clean(client, error_code, AWS_MQTT5_DRC_PROTOCOL_ERROR); + // } else { + // s_aws_mqtt5_client_shutdown_channel(client, error_code); + // } + + // goto done; + // } + + aws_channel_slot_increment_read_window(slot, message->message_data.len); + + // done: + + aws_mem_release(message->allocator, message); + + return AWS_OP_SUCCESS; +} + +static size_t s_initial_window_size(struct aws_channel_handler *handler) { + (void)handler; + + return SIZE_MAX; +} + +static size_t s_message_overhead(struct aws_channel_handler *handler) { + (void)handler; + + return 0; +} + +static void s_destroy(struct aws_channel_handler *handler) { + (void)handler; +} + +static int s_shutdown( + struct aws_channel_handler *handler, + struct aws_channel_slot *slot, + enum aws_channel_direction dir, + int error_code, + bool free_scarce_resources_immediately) { + + (void)handler; + + return aws_channel_slot_on_handler_shutdown_complete(slot, dir, error_code, free_scarce_resources_immediately); +} + +static struct aws_channel_handler_vtable s_secure_tunnel_channel_handler_vtable = { + .process_read_message = &s_process_read_message, + .process_write_message = NULL, + .increment_read_window = NULL, + .shutdown = &s_shutdown, + .initial_window_size = &s_initial_window_size, + .message_overhead = &s_message_overhead, + .destroy = &s_destroy, +}; + /********************************************************************************************************************* * Operations ********************************************************************************************************************/ @@ -1379,17 +1370,6 @@ static void s_reset_ping(struct aws_secure_tunnel *secure_tunnel) { secure_tunnel->next_ping_time); } -static void s_aws_secure_tunnel_on_socket_write_completion_secure_tunnel_connect( - struct aws_secure_tunnel *secure_tunnel, - int error_code) { - if (error_code != AWS_ERROR_SUCCESS) { - s_aws_secure_tunnel_shutdown_channel(secure_tunnel, error_code); - return; - } - - s_reevaluate_service_task(secure_tunnel); -} - static void s_aws_secure_tunnel_on_socket_write_completion_connected( struct aws_secure_tunnel *secure_tunnel, int error_code) { @@ -1423,10 +1403,6 @@ static void s_aws_secure_tunnel_on_socket_write_completion( } switch (secure_tunnel->current_state) { - case AWS_STS_SECURE_TUNNEL_CONNECT: - s_aws_secure_tunnel_on_socket_write_completion_secure_tunnel_connect(secure_tunnel, error_code); - break; - case AWS_STS_CONNECTED: s_aws_secure_tunnel_on_socket_write_completion_connected(secure_tunnel, error_code); break; @@ -1476,10 +1452,6 @@ static bool s_aws_secure_tunnel_has_pending_operational_work(const struct aws_se AWS_CONTAINER_OF(next_operation_node, struct aws_secure_tunnel_operation, node); switch (secure_tunnel->current_state) { - case AWS_STS_SECURE_TUNNEL_CONNECT: - /* Only allowed to CONNECT to a WebSocket in this state */ - return next_operation->operation_type == AWS_STOT_CONNECT; - 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; @@ -1521,7 +1493,6 @@ static uint64_t s_aws_secure_tunnel_compute_operational_state_service_time( /* now unless outside of allowed states */ switch (secure_tunnel->current_state) { - case AWS_STS_SECURE_TUNNEL_CONNECT: case AWS_STS_CLEAN_DISCONNECT: case AWS_STS_CONNECTED: return now; @@ -1788,23 +1759,6 @@ static uint64_t s_compute_next_service_time_secure_tunnel_connecting( return 0; } -static uint64_t s_compute_next_service_time_secure_tunnel_connect( - struct aws_secure_tunnel *secure_tunnel, - uint64_t now) { - /* This state is interruptable by a stop/terminate */ - if (secure_tunnel->desired_state != AWS_STS_CONNECTED) { - return now; - } - - uint64_t operation_processing_time = s_aws_secure_tunnel_compute_operational_state_service_time(secure_tunnel, now); - if (operation_processing_time == 0) { - // STEVE TODO need to set up this connect timeout for websocket timing - return secure_tunnel->next_secure_tunnel_websocket_connect_timeout_time; - } - - return aws_min_u64(secure_tunnel->next_secure_tunnel_websocket_connect_timeout_time, operation_processing_time); -} - static uint64_t s_compute_next_service_time_secure_tunnel_connected( struct aws_secure_tunnel *secure_tunnel, uint64_t now) { @@ -1851,8 +1805,6 @@ static uint64_t s_compute_next_service_time_by_current_state(struct aws_secure_t return s_compute_next_service_time_secure_tunnel_stopped(secure_tunnel, now); case AWS_STS_CONNECTING: return s_compute_next_service_time_secure_tunnel_connecting(secure_tunnel, now); - case AWS_STS_SECURE_TUNNEL_CONNECT: - return s_compute_next_service_time_secure_tunnel_connect(secure_tunnel, now); case AWS_STS_CONNECTED: return s_compute_next_service_time_secure_tunnel_connected(secure_tunnel, now); case AWS_STS_CLEAN_DISCONNECT: @@ -1935,16 +1887,8 @@ static bool s_service_state_stopped(struct aws_secure_tunnel *secure_tunnel) { return false; } -static void s_service_state_connecting(struct aws_secure_tunnel *secure_tunnel) { +static void s_service_state_connecting(struct aws_secure_tunnel *secure_tunnel, uint64_t now) { (void)secure_tunnel; -} - -static void s_service_state_secure_tunnel_connect(struct aws_secure_tunnel *secure_tunnel, uint64_t now) { - enum aws_secure_tunnel_state desired_state = secure_tunnel->desired_state; - if (desired_state != AWS_STS_CONNECTED) { - s_aws_secure_tunnel_shutdown_channel(secure_tunnel, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_USER_REQUESTED_STOP); - return; - } if (now >= secure_tunnel->next_secure_tunnel_websocket_connect_timeout_time) { AWS_LOGF_INFO( @@ -2072,10 +2016,7 @@ static void s_secure_tunnel_service_task_fn(struct aws_task *task, void *arg, en terminated = s_service_state_stopped(secure_tunnel); break; case AWS_STS_CONNECTING: - s_service_state_connecting(secure_tunnel); - break; - case AWS_STS_SECURE_TUNNEL_CONNECT: - s_service_state_secure_tunnel_connect(secure_tunnel, now); + s_service_state_connecting(secure_tunnel, now); break; case AWS_STS_CONNECTED: s_service_state_connected(secure_tunnel, now); @@ -2127,6 +2068,7 @@ struct aws_secure_tunnel *aws_secure_tunnel_new(const struct aws_secure_tunnel_o aws_ref_count_init(&secure_tunnel->ref_count, secure_tunnel, s_on_secure_tunnel_zero_ref_count); aws_linked_list_init(&secure_tunnel->queued_operations); + aws_linked_list_init(&secure_tunnel->write_completion_operations); secure_tunnel->current_operation = NULL; /* store options */ @@ -2135,6 +2077,8 @@ struct aws_secure_tunnel *aws_secure_tunnel_new(const struct aws_secure_tunnel_o goto error; } secure_tunnel->config->secure_tunnel = secure_tunnel; + // secure_tunnel->config->websocket_handshake_transform + // secure_tunnel->config->websocket_handshake_transform_user_data = secure_tunnel; /* 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); @@ -2146,9 +2090,9 @@ struct aws_secure_tunnel *aws_secure_tunnel_new(const struct aws_secure_tunnel_o secure_tunnel->current_state = AWS_STS_STOPPED; // STEVE TODO Channel Handler may be uneccessary for a websocket secure tunnel - // secure_tunnel->handler.alloc = secure_tunnel->allocator; - // secure_tunnel->handler.vtable = &s_secure_tunnel_channel_handler_vtable; - // secure_tunnel->handler.impl = secure_tunnel; + secure_tunnel->handler.alloc = secure_tunnel->allocator; + secure_tunnel->handler.vtable = &s_secure_tunnel_channel_handler_vtable; + secure_tunnel->handler.impl = secure_tunnel; /* tls setup */ struct aws_tls_ctx_options tls_ctx_opt; @@ -2193,6 +2137,8 @@ struct aws_secure_tunnel *aws_secure_tunnel_new(const struct aws_secure_tunnel_o /* TODO: Release this buffer when there is no data to hold */ aws_byte_buf_init(&secure_tunnel->received_data, options->allocator, MAX_WEBSOCKET_PAYLOAD); + aws_secure_tunnel_options_storage_log(secure_tunnel->config, AWS_LL_DEBUG); + return secure_tunnel; error: diff --git a/source/secure_tunneling_operations.c b/source/secure_tunneling_operations.c index 9586beb1..17b6d440 100644 --- a/source/secure_tunneling_operations.c +++ b/source/secure_tunneling_operations.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -447,30 +448,6 @@ void aws_secure_tunnel_options_storage_log( } } - if (options_storage->websocket_handshake_transform != NULL) { - AWS_LOGUF( - log_handle, - level, - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: aws_secure_tunnel_options_storage enabling websockets", - (void *)options_storage); - - AWS_LOGUF( - log_handle, - level, - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: aws_secure_tunnel_options_storage websocket handshake transform user data set to (%p)", - (void *)options_storage, - options_storage->websocket_handshake_transform_user_data); - } else { - AWS_LOGUF( - log_handle, - level, - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: aws_secure_tunnel_options_storage disabling websockets", - (void *)options_storage); - } - bool is_service_id_used = false; if (options_storage->service_id_1 != NULL) { @@ -528,6 +505,7 @@ void aws_secure_tunnel_options_storage_destroy(struct aws_secure_tunnel_options_ 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_string_destroy(storage->service_id_1); aws_string_destroy(storage->service_id_2); aws_string_destroy(storage->service_id_3); @@ -563,6 +541,31 @@ struct aws_secure_tunnel_options_storage *aws_secure_tunnel_options_storage_new( goto error; } + if (options->client_token.len > 0) { + storage->client_token = aws_string_new_from_cursor(allocator, &options->client_token); + if (storage->client_token == NULL) { + goto error; + } + } else { + struct aws_uuid uuid; + if (aws_uuid_init(&uuid)) { + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "Failed to initiate an uuid struct: %s", + aws_error_str(aws_last_error())); + goto error; + } + char uuid_str[AWS_UUID_STR_LEN] = {0}; + struct aws_byte_buf uuid_buf = aws_byte_buf_from_array(uuid_str, sizeof(uuid_str)); + uuid_buf.len = 0; + if (aws_uuid_to_str(&uuid, &uuid_buf)) { + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Failed to stringify uuid: %s", aws_error_str(aws_last_error())); + goto error; + } + storage->client_token = aws_string_new_from_buf(allocator, &uuid_buf); + } + /* STEVE TODO can be removed except for testing */ storage->local_proxy_mode = options->local_proxy_mode; diff --git a/tests/secure_tunneling_tests.c b/tests/secure_tunneling_tests.c index 7333387f..94e402d8 100644 --- a/tests/secure_tunneling_tests.c +++ b/tests/secure_tunneling_tests.c @@ -269,7 +269,7 @@ static void s_send_secure_tunneling_frame_to_websocket( aws_byte_buf_append(&websocket_frame, &c); c = aws_byte_cursor_from_buf(&websocket_frame); - on_websocket_incoming_frame_payload(NULL, NULL, c, secure_tunnel); + // on_websocket_incoming_frame_payload(NULL, NULL, c, secure_tunnel); aws_byte_buf_clean_up(&serialized_st_msg); aws_byte_buf_clean_up(&websocket_frame); @@ -542,10 +542,10 @@ static int s_secure_tunneling_init_websocket_options_test(struct aws_allocator * UNUSED(allocator); - struct secure_tunneling_test_context *test_context = ctx; + // struct secure_tunneling_test_context *test_context = ctx; struct aws_websocket_client_connection_options websocket_options; - init_websocket_client_connection_options(test_context->secure_tunnel, &websocket_options); + // init_websocket_client_connection_options(test_context->secure_tunnel, &websocket_options); ASSERT_TRUE(aws_byte_cursor_eq_c_str(&websocket_options.host, ENDPOINT)); From 6dee25f1f5a649cdfe9a51980e05de86fea2e780 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 23 Jan 2023 08:56:11 -0800 Subject: [PATCH 12/69] stable before checking slot --- .../iotdevice/private/secure_tunneling_impl.h | 10 +- .../private/secure_tunneling_operations.h | 2 +- include/aws/iotdevice/secure_tunneling.h | 11 - source/secure_tunneling.c | 293 ++++++++---------- source/secure_tunneling_operations.c | 28 +- source/serializer.c | 71 ++++- tests/aws_iot_secure_tunneling_client_test.c | 4 +- tests/secure_tunneling_tests.c | 4 +- 8 files changed, 216 insertions(+), 207 deletions(-) diff --git a/include/aws/iotdevice/private/secure_tunneling_impl.h b/include/aws/iotdevice/private/secure_tunneling_impl.h index 7ad23588..52a0428e 100644 --- a/include/aws/iotdevice/private/secure_tunneling_impl.h +++ b/include/aws/iotdevice/private/secure_tunneling_impl.h @@ -53,8 +53,6 @@ enum aws_secure_tunnel_state { * The secure tunnel is ready to perform user-requested operations. * * Next States: - * CHANNEL_SHUTDOWN - On send/encode errors, read/decode errors, WebSocket disconnect, desired state - * no longer CONNECTED, PINGRESP timeout * PENDING_RECONNECT - unexpected channel shutdown completion and desired state still CONNECTED * STOPPED - unexpected channel shutdown completion and desired state no longer CONNECTED */ @@ -65,7 +63,6 @@ enum aws_secure_tunnel_state { * transmitting a STREAM RESET message to all open streams. * * Next States: - * CHANNEL_SHUTDOWN - on successful (or unsuccessful) send of STREAM RESET messages * PENDING_RECONNECT - unexpected channel shutdown completion and desired state still CONNECTED * STOPPED - unexpected channel shutdown completion and desired state no longer CONNECTED */ @@ -147,8 +144,6 @@ struct aws_secure_tunnel_vtable { /* aws_client_bootstrap_new_socket_channel */ int (*client_bootstrap_new_socket_channel_fn)(struct aws_socket_channel_bootstrap_options *options); - int (*connect)(struct aws_secure_tunnel *secure_tunnel); - int (*close)(struct aws_secure_tunnel *secure_tunnel); int (*send_data)(struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *data); int (*send_data_v2)( struct aws_secure_tunnel *secure_tunnel, @@ -277,6 +272,11 @@ struct aws_secure_tunnel { */ uint64_t next_reconnect_delay_reset_time_ns; + /* + * How many consecutive reconnect failures have we experienced? + */ + uint64_t reconnect_count; + /* * When should we shut down the channel due to failure to receive a websocket handshake? Only relevant during the * SECURE_TUNNEL_CONNECT state. diff --git a/include/aws/iotdevice/private/secure_tunneling_operations.h b/include/aws/iotdevice/private/secure_tunneling_operations.h index 119f404e..0d6bbb1f 100644 --- a/include/aws/iotdevice/private/secure_tunneling_operations.h +++ b/include/aws/iotdevice/private/secure_tunneling_operations.h @@ -48,7 +48,7 @@ struct aws_secure_tunnel_operation_vtable { const void *completion_view); /* Set the stream id of outgoing st_msg */ - void (*aws_secure_tunnel_operation_set_stream_id_fn)( + int (*aws_secure_tunnel_operation_set_stream_id_fn)( struct aws_secure_tunnel_operation *operation, struct aws_secure_tunnel *secure_tunnel); diff --git a/include/aws/iotdevice/secure_tunneling.h b/include/aws/iotdevice/secure_tunneling.h index dbebdf30..a8ed7a8c 100644 --- a/include/aws/iotdevice/secure_tunneling.h +++ b/include/aws/iotdevice/secure_tunneling.h @@ -157,9 +157,6 @@ struct aws_secure_tunnel_options { struct aws_byte_cursor client_token; const char *root_ca; - const char *service_id_1; - const char *service_id_2; - const char *service_id_3; aws_secure_tunnel_message_received_fn *on_message_received; @@ -261,14 +258,6 @@ int aws_secure_tunnel_send_message( /* STEVE TODO REMOVE OR HIDE ALL BELOW */ //*********************************************************************************************************************** -/* TODO STEVE depricate in favor of aws_secure_tunnel_start */ -AWS_IOTDEVICE_API -int aws_secure_tunnel_connect(struct aws_secure_tunnel *secure_tunnel); - -/* TODO STEVE depricate in favor of aws_secure_tunnel_stop */ -AWS_IOTDEVICE_API -int aws_secure_tunnel_close(struct aws_secure_tunnel *secure_tunnel); - /* TODO STEVE depricate/replace new aws_secure_tunnel_send_message_new */ AWS_IOTDEVICE_API int aws_secure_tunnel_send_data(struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *data); diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 300d3c74..957db56d 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -23,16 +23,16 @@ #define AWS_SECURE_TUNNEL_IO_MESSAGE_DEFAULT_LENGTH 4096 #define INVALID_STREAM_ID 0 #define PAYLOAD_BYTE_LENGTH_PREFIX 2 +#define MIN_RECONNECT_DELAY_MS 1000 +#define MAX_RECONNECT_DELAY_MS 120000 #define PING_TASK_INTERVAL ((uint64_t)20 * 1000000000) +#define DEFAULT_PING_TIMEOUT_MS 30000 #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" -// Steve TODO check if these need to be implemented or re-organized static void s_change_current_state(struct aws_secure_tunnel *secure_tunnel, enum aws_secure_tunnel_state next_state); - -/* STEVE TODO These forward declarations exist but may not be implemented */ void aws_secure_tunnel_on_disconnection_update_operational_state(struct aws_secure_tunnel *secure_tunnel); void aws_secure_tunnel_operational_state_clean_up(struct aws_secure_tunnel *secure_tunnel); static int s_aws_secure_tunnel_change_desired_state( @@ -44,10 +44,6 @@ static int s_secure_tunneling_send( const struct aws_byte_cursor *payload_data, const struct aws_byte_cursor *service_id_data, enum aws_secure_tunnel_message_type type); -static struct aws_http_message *s_new_handshake_request(const struct aws_secure_tunnel *secure_tunnel); - -static int s_websocket_connect(struct aws_secure_tunnel *secure_tunnel); -static void s_reset_ping(struct aws_secure_tunnel *secure_tunnel); static void s_reevaluate_service_task(struct aws_secure_tunnel *secure_tunnel); @@ -212,9 +208,34 @@ static void s_aws_secure_tunnel_on_session_reset_received(struct aws_secure_tunn secure_tunnel->config->on_session_reset(secure_tunnel->config->user_data); } +static void s_aws_secure_tunnel_on_service_ids_received(struct aws_secure_tunnel *secure_tunnel) { + if (secure_tunnel->config->service_id_1) { + AWS_LOGF_INFO( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: secure tunnel service id 1 set to: " PRInSTR, + (void *)secure_tunnel, + AWS_BYTE_CURSOR_PRI(aws_byte_cursor_from_string(secure_tunnel->config->service_id_1))); + } + if (secure_tunnel->config->service_id_2) { + AWS_LOGF_INFO( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: secure tunnel service id 2 set to: " PRInSTR, + (void *)secure_tunnel, + AWS_BYTE_CURSOR_PRI(aws_byte_cursor_from_string(secure_tunnel->config->service_id_2))); + } + if (secure_tunnel->config->service_id_3) { + AWS_LOGF_INFO( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: secure tunnel service id 3 set to: " PRInSTR, + (void *)secure_tunnel, + AWS_BYTE_CURSOR_PRI(aws_byte_cursor_from_string(secure_tunnel->config->service_id_3))); + } +} + static void s_aws_secure_tunnel_connected_on_message_received( struct aws_secure_tunnel *secure_tunnel, struct aws_secure_tunnel_message_view *message_view) { + printf("\n\ns_aws_secure_tunnel_connected_on_message_received() called \n\n"); switch (message_view->type) { case AWS_SECURE_TUNNEL_MT_DATA: secure_tunnel->config->on_message_received(message_view, secure_tunnel); @@ -230,6 +251,8 @@ static void s_aws_secure_tunnel_connected_on_message_received( break; /* Steve todo implement processing of new message types */ case AWS_SECURE_TUNNEL_MT_SERVICE_IDS: + s_aws_secure_tunnel_on_service_ids_received(secure_tunnel); + break; case AWS_SECURE_TUNNEL_MT_CONNECTION_START: case AWS_SECURE_TUNNEL_MT_CONNECTION_RESET: case AWS_SECURE_TUNNEL_MT_UNKNOWN: @@ -247,7 +270,7 @@ static void s_aws_secure_tunnel_connected_on_message_received( static void s_process_received_data(struct aws_secure_tunnel *secure_tunnel) { struct aws_byte_buf *received_data = &secure_tunnel->received_data; struct aws_byte_cursor cursor = aws_byte_cursor_from_buf(received_data); - + printf("\n\n PROCESSING RECEIVED WEBSOCKET DATA \n\n"); 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 @@ -289,6 +312,7 @@ static bool s_on_websocket_incoming_frame_begin( (void)websocket; (void)frame; (void)user_data; + printf("\n\nwebsocket frame begin\n\n"); return true; } @@ -319,59 +343,13 @@ static bool s_on_websocket_incoming_frame_complete( (void)frame; (void)error_code; (void)user_data; + printf("\n\nwebsocket frame end\n\n"); /* TODO: Check error_code */ return true; } -// Steve TODO remove this for operational ping? Or use it on the task of websocket ping? -// static void s_send_websocket_ping(struct aws_websocket *websocket, websocket_send_frame *send_frame) { -// if (!websocket) { -// return; -// } - -// struct aws_websocket_send_frame_options frame_options; -// AWS_ZERO_STRUCT(frame_options); -// frame_options.opcode = AWS_WEBSOCKET_OPCODE_PING; -// frame_options.fin = true; -// send_frame(websocket, &frame_options); -// } - -// Steve TODO this can probably be removed as soon as pings are set up as operations -// struct ping_task_context { -// struct aws_allocator *allocator; -// struct aws_event_loop *event_loop; -// struct aws_task ping_task; -// struct aws_atomic_var task_cancelled; -// struct aws_websocket *websocket; -// /* The ping_task shares the vtable function used by the secure tunnel to send frames over the websocket. */ -// websocket_send_frame *send_frame; -// }; - -// Steve Commented s_ping_task. Probably unnecessary -// static void s_ping_task(struct aws_task *task, void *user_data, enum aws_task_status task_status) { -// AWS_LOGF_TRACE(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "s_ping_task"); -// struct ping_task_context *ping_task_context = user_data; -// if (task_status == AWS_TASK_STATUS_CANCELED) { -// AWS_LOGF_INFO( -// AWS_LS_IOTDEVICE_SECURE_TUNNELING, "task_status is AWS_TASK_STATUS_CANCELED. Cleaning up ping task."); -// aws_mem_release(ping_task_context->allocator, ping_task_context); -// return; -// } -// const size_t task_cancelled = aws_atomic_load_int(&ping_task_context->task_cancelled); -// if (task_cancelled) { -// AWS_LOGF_INFO(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "task_cancelled is true. Cleaning up ping task."); -// aws_mem_release(ping_task_context->allocator, ping_task_context); -// return; -// } -// s_send_websocket_ping(ping_task_context->websocket, ping_task_context->send_frame); -// /* Schedule the next task */ -// uint64_t now; -// aws_event_loop_current_clock_time(ping_task_context->event_loop, &now); -// aws_event_loop_schedule_task_future(ping_task_context->event_loop, task, now + PING_TASK_INTERVAL); -// } - static void s_secure_tunnel_shutdown( struct aws_client_bootstrap *bootstrap, int error_code, @@ -489,6 +467,7 @@ static void s_on_websocket_setup(const struct aws_websocket_on_connection_setup_ struct aws_channel *channel = NULL; if (setup->websocket) { + secure_tunnel->websocket = setup->websocket; channel = aws_websocket_get_channel(setup->websocket); AWS_ASSERT(channel); @@ -588,35 +567,6 @@ error:; aws_mem_release(websocket_transform_complete_task->allocator, websocket_transform_complete_task); } -static int s_websocket_connect(struct aws_secure_tunnel *secure_tunnel) { - AWS_ASSERT(secure_tunnel); - - struct aws_http_message *handshake = s_new_handshake_request(secure_tunnel); - if (handshake == NULL) { - goto error; - } - - AWS_LOGF_TRACE( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: Transforming websocket handshake request.", (void *)secure_tunnel); - - struct aws_secure_tunnel_websocket_transform_complete_task *task = - aws_mem_calloc(secure_tunnel->allocator, 1, sizeof(struct aws_secure_tunnel_websocket_transform_complete_task)); - - aws_task_init( - &task->task, s_websocket_transform_complete_task_fn, (void *)task, "WebsocketHandshakeTransformComplete"); - task->allocator = secure_tunnel->allocator; - task->secure_tunnel = aws_secure_tunnel_acquire(secure_tunnel); - task->error_code = AWS_OP_SUCCESS; - task->handshake = handshake; - - aws_event_loop_schedule_task_now(secure_tunnel->loop, &task->task); - - return AWS_OP_SUCCESS; - -error: - return AWS_OP_ERR; -} - static int s_handshake_add_header( const struct aws_secure_tunnel *secure_tunnel, struct aws_http_message *handshake, @@ -636,7 +586,6 @@ static int s_handshake_add_header( AWS_BYTE_CURSOR_PRI(header.value)); return AWS_OP_SUCCESS; } - static struct aws_http_message *s_new_handshake_request(const struct aws_secure_tunnel *secure_tunnel) { char path[50]; snprintf( @@ -690,6 +639,46 @@ static struct aws_http_message *s_new_handshake_request(const struct aws_secure_ return NULL; } +static int s_websocket_connect(struct aws_secure_tunnel *secure_tunnel) { + AWS_ASSERT(secure_tunnel); + + struct aws_http_message *handshake = s_new_handshake_request(secure_tunnel); + if (handshake == NULL) { + goto error; + } + + AWS_LOGF_TRACE( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: Transforming websocket handshake request.", (void *)secure_tunnel); + + struct aws_secure_tunnel_websocket_transform_complete_task *task = + aws_mem_calloc(secure_tunnel->allocator, 1, sizeof(struct aws_secure_tunnel_websocket_transform_complete_task)); + + aws_task_init( + &task->task, s_websocket_transform_complete_task_fn, (void *)task, "WebsocketHandshakeTransformComplete"); + task->allocator = secure_tunnel->allocator; + task->secure_tunnel = aws_secure_tunnel_acquire(secure_tunnel); + task->error_code = AWS_OP_SUCCESS; + task->handshake = handshake; + + aws_event_loop_schedule_task_now(secure_tunnel->loop, &task->task); + + return AWS_OP_SUCCESS; + +error: + return AWS_OP_ERR; +} + +static void s_reset_ping(struct aws_secure_tunnel *secure_tunnel) { + uint64_t now = (*secure_tunnel->vtable->get_current_time_fn)(); + secure_tunnel->next_ping_time = aws_add_u64_saturating(now, PING_TASK_INTERVAL); + + AWS_LOGF_DEBUG( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: next PING scheduled for time %" PRIu64, + (void *)secure_tunnel, + secure_tunnel->next_ping_time); +} + /********************************************************************************************************************* * CHANNEL ********************************************************************************************************************/ @@ -795,8 +784,32 @@ static void s_change_current_state_to_channel_shutdown(struct aws_secure_tunnel secure_tunnel->current_state = AWS_STS_CHANNEL_SHUTDOWN; } +static void s_update_reconnect_delay_for_pending_reconnect(struct aws_secure_tunnel *secure_tunnel) { + + uint64_t delay_ms = MIN_RECONNECT_DELAY_MS; + for (int i = 0; i < (int)secure_tunnel->reconnect_count; ++i) { + delay_ms = delay_ms * delay_ms; + } + + 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++; +} + static void s_change_current_state_to_pending_reconnect(struct aws_secure_tunnel *secure_tunnel) { - (void)secure_tunnel; + secure_tunnel->current_state = AWS_STS_PENDING_RECONNECT; + + s_update_reconnect_delay_for_pending_reconnect(secure_tunnel); } static void s_change_current_state_to_terminated(struct aws_secure_tunnel *secure_tunnel) { secure_tunnel->current_state = AWS_STS_TERMINATED; @@ -957,37 +970,6 @@ static uint64_t s_aws_high_res_clock_get_ticks_proxy(void) { return current_time; } -static int s_secure_tunneling_connect(struct aws_secure_tunnel *secure_tunnel) { - (void)secure_tunnel; - // Steve TODO this is checking the stream_id and not other service id streams. - // if (secure_tunnel == NULL || secure_tunnel->config->stream_id != INVALID_STREAM_ID) { - // return AWS_OP_ERR; - // } - - // struct aws_websocket_client_connection_options websocket_options; - // init_websocket_client_connection_options(secure_tunnel, &websocket_options); - // if (secure_tunnel->websocket_vtable.client_connect(&websocket_options)) { - // return AWS_OP_ERR; - // } - - return AWS_OP_SUCCESS; -} - -static int s_secure_tunneling_close(struct aws_secure_tunnel *secure_tunnel) { - if (secure_tunnel == NULL) { - return AWS_OP_ERR; - } - - s_reset_secure_tunnel(secure_tunnel); - - if (secure_tunnel->websocket != NULL) { - secure_tunnel->websocket_vtable.close(secure_tunnel->websocket, false); - secure_tunnel->websocket_vtable.release(secure_tunnel->websocket); - secure_tunnel->websocket = NULL; - } - return AWS_OP_SUCCESS; -} - static int s_secure_tunneling_send_data( struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *payload_data) { @@ -1170,8 +1152,6 @@ static int s_secure_tunneling_send( const struct aws_byte_cursor *service_id_data, enum aws_secure_tunnel_message_type type) { - fprintf(stdout, "\ns_secure_tunneling_send() called\n"); - struct aws_websocket_send_frame_options frame_options; if (secure_tunneling_init_send_frame(&frame_options, secure_tunnel, payload_data, service_id_data, type) != AWS_OP_SUCCESS) { @@ -1213,8 +1193,6 @@ static struct aws_secure_tunnel_vtable s_default_secure_tunnel_vtable = { .vtable_user_data = NULL, /* STEVE TODO remove these after changing the API to make these operations/tasks */ - .connect = s_secure_tunneling_connect, - .close = s_secure_tunneling_close, .send_data = s_secure_tunneling_send_data, .send_stream_start = s_secure_tunneling_send_stream_start, .send_stream_start_v2 = s_secure_tunneling_send_stream_start_v2, @@ -1345,31 +1323,6 @@ static void s_complete_operation_list( aws_linked_list_init(operation_list); } -static int s_aws_secure_tunnel_set_current_operation( - struct aws_secure_tunnel *secure_tunnel, - struct aws_secure_tunnel_operation *operation) { - - secure_tunnel->current_operation = operation; - - return AWS_OP_SUCCESS; -} - -static void s_reset_ping(struct aws_secure_tunnel *secure_tunnel) { - uint64_t now = (*secure_tunnel->vtable->get_current_time_fn)(); - // Steve TODO do we want a keep alive time in seconds to try to keep the secure tunnel connected? - uint64_t keep_alive_seconds = 10; // store and get from -> secure_tunnel->config->server_keep_alive; - - uint64_t keep_alive_interval_nanos = - aws_timestamp_convert(keep_alive_seconds, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL); - secure_tunnel->next_ping_time = aws_add_u64_saturating(now, keep_alive_interval_nanos); - - AWS_LOGF_DEBUG( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: next PING scheduled for time %" PRIu64, - (void *)secure_tunnel, - secure_tunnel->next_ping_time); -} - static void s_aws_secure_tunnel_on_socket_write_completion_connected( struct aws_secure_tunnel *secure_tunnel, int error_code) { @@ -1539,15 +1492,24 @@ int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure * Loop through queued operations until we run out or find a good one. */ struct aws_secure_tunnel_operation *next_operation = NULL; - if (!aws_linked_list_empty(&secure_tunnel->queued_operations)) { + + while (!aws_linked_list_empty(&secure_tunnel->queued_operations)) { struct aws_linked_list_node *next_operation_node = aws_linked_list_pop_front(&secure_tunnel->queued_operations); next_operation = AWS_CONTAINER_OF(next_operation_node, struct aws_secure_tunnel_operation, node); - if (s_aws_secure_tunnel_set_current_operation(secure_tunnel, next_operation)) { - operational_error_code = AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_STREAM; + /* If a data message attempts to be sent on an unopen stream, discard it. */ + if (next_operation->operation_type == AWS_STOT_DATA) { + if ((*next_operation->vtable->aws_secure_tunnel_operation_set_stream_id_fn)( + next_operation, secure_tunnel)) { + int last_error_code = aws_last_error(); + s_complete_operation(secure_tunnel, next_operation, last_error_code, NULL); + } } + + secure_tunnel->current_operation = next_operation; + break; } } @@ -1574,6 +1536,26 @@ int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure break; } + if (current_operation->operation_type == AWS_STOT_PING) { + uint64_t ping_timeout_nanos = + aws_timestamp_convert(DEFAULT_PING_TIMEOUT_MS, AWS_TIMESTAMP_MILLIS, AWS_TIMESTAMP_NANOS, NULL); + secure_tunnel->next_ping_timeout_time = aws_add_u64_saturating(now, ping_timeout_nanos); + + if (secure_tunnel->websocket != NULL) { + printf("\n\nsecure_tunnel->websocket != NULL\n\n"); + struct aws_websocket_send_frame_options frame_options; + AWS_ZERO_STRUCT(frame_options); + frame_options.opcode = AWS_WEBSOCKET_OPCODE_PING; + frame_options.fin = true; + aws_websocket_send_frame(secure_tunnel->websocket, &frame_options); + } else { + printf("\n\nsecure_tunnel->websocket is NULL\n\n"); + } + + s_complete_operation(secure_tunnel, current_operation, AWS_OP_SUCCESS, NULL); + secure_tunnel->current_operation = NULL; + } + now = (*vtable->get_current_time_fn)(); should_service = s_aws_secure_tunnel_should_service_operational_state(secure_tunnel, now); } while (should_service); @@ -1952,6 +1934,7 @@ static void s_service_state_connected(struct aws_secure_tunnel *secure_tunnel, u s_check_ping_timeout(secure_tunnel, now); /* STEVE TODO Reconnect logic and timer checks can go here */ + secure_tunnel->reconnect_count = 0; if (aws_secure_tunnel_service_operational_state(secure_tunnel)) { int error_code = aws_last_error(); @@ -2121,10 +2104,10 @@ struct aws_secure_tunnel *aws_secure_tunnel_new(const struct aws_secure_tunnel_o /* websocket vtable */ - secure_tunnel->websocket_vtable.client_connect = aws_websocket_client_connect; + // secure_tunnel->websocket_vtable.client_connect = aws_websocket_client_connect; secure_tunnel->websocket_vtable.send_frame = aws_websocket_send_frame; secure_tunnel->websocket_vtable.close = aws_websocket_close; - secure_tunnel->websocket_vtable.release = aws_websocket_release; + // secure_tunnel->websocket_vtable.release = aws_websocket_release; /* Connection reset */ secure_tunnel->config->stream_id = INVALID_STREAM_ID; @@ -2202,14 +2185,6 @@ int aws_secure_tunnel_send_message( return AWS_OP_ERR; } -int aws_secure_tunnel_connect(struct aws_secure_tunnel *secure_tunnel) { - return secure_tunnel->vtable->connect(secure_tunnel); -} - -int aws_secure_tunnel_close(struct aws_secure_tunnel *secure_tunnel) { - return secure_tunnel->vtable->close(secure_tunnel); -} - int aws_secure_tunnel_send_data(struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *data) { return secure_tunnel->vtable->send_data(secure_tunnel, data); } diff --git a/source/secure_tunneling_operations.c b/source/secure_tunneling_operations.c index 17b6d440..afe0db94 100644 --- a/source/secure_tunneling_operations.c +++ b/source/secure_tunneling_operations.c @@ -202,12 +202,13 @@ void aws_secure_tunnel_message_storage_clean_up(struct aws_secure_tunnel_message aws_byte_buf_clean_up(&message_storage->storage); } -static void s_aws_secure_tunnel_operation_message_set_stream_id( +/* Sets the stream id on outbound messages*/ +static int s_aws_secure_tunnel_operation_message_set_stream_id( struct aws_secure_tunnel_operation *operation, struct aws_secure_tunnel *secure_tunnel) { struct aws_secure_tunnel_operation_message *message_op = operation->impl; - int32_t stream_id = 0; + int32_t stream_id = INVALID_STREAM_ID; struct aws_secure_tunnel_message_storage *message_storage = &message_op->options_storage; @@ -241,7 +242,12 @@ static void s_aws_secure_tunnel_operation_message_set_stream_id( stream_id = secure_tunnel->config->stream_id; } + if (stream_id == INVALID_STREAM_ID) { + return aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_STREAM); + } + message_op->options_storage.storage_view.stream_id = stream_id; + return AWS_OP_SUCCESS; } static int32_t *s_aws_secure_tunnel_operation_message_get_stream_id_address_fn( @@ -541,6 +547,12 @@ struct aws_secure_tunnel_options_storage *aws_secure_tunnel_options_storage_new( goto error; } + /* + * Client token is provided to the secure tunnel service alongside the access token. + * The access token is one-time use unless coupled with a client token. The pair can be used together + * for reconnects. If the user provides one, we will use that. If one is not provided, we will generate + * one for use with this access token to handle reconnecting on disconnections. + */ if (options->client_token.len > 0) { storage->client_token = aws_string_new_from_cursor(allocator, &options->client_token); if (storage->client_token == NULL) { @@ -582,18 +594,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 (options->service_id_1 != NULL) { - storage->service_id_1 = aws_string_new_from_c_str(allocator, options->service_id_1); - } - - if (options->service_id_2 != NULL) { - storage->service_id_2 = aws_string_new_from_c_str(allocator, options->service_id_2); - } - - if (options->service_id_3 != NULL) { - storage->service_id_3 = aws_string_new_from_c_str(allocator, options->service_id_3); - } - storage->on_message_received = options->on_message_received; storage->user_data = options->user_data; diff --git a/source/serializer.c b/source/serializer.c index a0ec5a48..e2fafe3c 100644 --- a/source/serializer.c +++ b/source/serializer.c @@ -2,6 +2,8 @@ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0. */ +#include +#include #include /***************************************************************************************************************** @@ -313,13 +315,17 @@ int aws_secure_tunnel_deserialize_message_from_cursor( struct aws_secure_tunnel_message_view *message, struct aws_byte_cursor *cursor, aws_secure_tunnel_on_message_received_fn *on_message_received) { + printf("\n\naws_secure_tunnel_deserialize_message_from_cursor()\n\n"); 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_byte_buf payload_buf; struct aws_byte_buf service_id_buf; + struct aws_byte_buf available_service_id_buf; + AWS_ZERO_STRUCT(payload_buf); AWS_ZERO_STRUCT(service_id_buf); + int service_ids_set = 0; while ((aws_byte_cursor_is_valid(cursor)) && (cursor->len > 0)) { // wire_type is only the first 3 bits, Zeroing out the first 5 @@ -357,30 +363,66 @@ int aws_secure_tunnel_deserialize_message_from_cursor( case AWS_SECURE_TUNNEL_PBWT_LENGTH_DELIMINTED: { uint32_t length = 0; if (s_iot_st_decode_varint_uint32_t(cursor, &length)) { - return AWS_OP_ERR; + goto error; } switch (field_number) { case AWS_SECURE_TUNNEL_FN_PAYLOAD: - if (aws_byte_buf_init(&payload_buf, secure_tunnel->allocator, length)) { - return AWS_OP_ERR; - } - if (s_aws_st_decode_lengthdelim(cursor, &payload_buf, length)) { - goto cleanup; + if (aws_byte_buf_init(&payload_buf, secure_tunnel->allocator, length) || + s_aws_st_decode_lengthdelim(cursor, &payload_buf, length)) { + goto error; } aws_byte_cursor_advance(cursor, length); message->payload = aws_byte_cursor_from_buf(&payload_buf); break; + case AWS_SECURE_TUNNEL_FN_SERVICE_ID: - if (aws_byte_buf_init(&service_id_buf, secure_tunnel->allocator, length)) { - return AWS_OP_ERR; - } - if (s_aws_st_decode_lengthdelim(cursor, &service_id_buf, length)) { - goto cleanup; + if (aws_byte_buf_init(&service_id_buf, secure_tunnel->allocator, length) || + s_aws_st_decode_lengthdelim(cursor, &service_id_buf, length)) { + goto error; } aws_byte_cursor_advance(cursor, length); message->service_id = aws_byte_cursor_from_buf(&service_id_buf); break; + + case AWS_SECURE_TUNNEL_FN_AVAILABLE_SERVICE_IDS: + AWS_ZERO_STRUCT(available_service_id_buf); + if (aws_byte_buf_init(&available_service_id_buf, secure_tunnel->allocator, length) || + s_aws_st_decode_lengthdelim(cursor, &available_service_id_buf, length)) { + goto error; + } + aws_byte_cursor_advance(cursor, length); + switch (service_ids_set) { + case 0: + if (secure_tunnel->config->service_id_1) { + aws_string_destroy(secure_tunnel->config->service_id_1); + } + secure_tunnel->config->service_id_1 = + aws_string_new_from_buf(secure_tunnel->allocator, &available_service_id_buf); + break; + case 1: + if (secure_tunnel->config->service_id_2) { + aws_string_destroy(secure_tunnel->config->service_id_2); + } + secure_tunnel->config->service_id_2 = + aws_string_new_from_buf(secure_tunnel->allocator, &available_service_id_buf); + break; + case 2: + if (secure_tunnel->config->service_id_3) { + aws_string_destroy(secure_tunnel->config->service_id_3); + } + secure_tunnel->config->service_id_3 = + aws_string_new_from_buf(secure_tunnel->allocator, &available_service_id_buf); + break; + default: + aws_byte_buf_clean_up(&available_service_id_buf); + goto error; + break; + } + + aws_byte_buf_clean_up(&available_service_id_buf); + service_ids_set++; + break; } } break; @@ -389,14 +431,17 @@ int aws_secure_tunnel_deserialize_message_from_cursor( case AWS_SECURE_TUNNEL_PBWT_START_GROUP: case AWS_SECURE_TUNNEL_PBWT_END_GROUP: case AWS_SECURE_TUNNEL_PBWT_32_BIT: - return AWS_OP_ERR; + goto error; break; } } on_message_received(secure_tunnel, message); + aws_byte_buf_clean_up(&payload_buf); + aws_byte_buf_clean_up(&service_id_buf); + return AWS_OP_SUCCESS; -cleanup: +error: aws_byte_buf_clean_up(&payload_buf); aws_byte_buf_clean_up(&service_id_buf); return AWS_OP_ERR; diff --git a/tests/aws_iot_secure_tunneling_client_test.c b/tests/aws_iot_secure_tunneling_client_test.c index 4604cc74..9f98809e 100644 --- a/tests/aws_iot_secure_tunneling_client_test.c +++ b/tests/aws_iot_secure_tunneling_client_test.c @@ -157,7 +157,7 @@ int main(int argc, char **argv) { /* Create a secure tunnel object and connect */ struct aws_secure_tunnel *secure_tunnel = aws_secure_tunnel_new(&config); - aws_secure_tunnel_connect(secure_tunnel); + aws_secure_tunnel_start(secure_tunnel); /* wait here until the connection is done */ aws_mutex_lock(&mutex); @@ -183,7 +183,7 @@ int main(int argc, char **argv) { aws_thread_current_sleep((uint64_t)60 * 60 * 1000000000); /* clean up */ - aws_secure_tunnel_close(secure_tunnel); + aws_secure_tunnel_stop(secure_tunnel); aws_secure_tunnel_release(secure_tunnel); aws_client_bootstrap_release(bootstrap); diff --git a/tests/secure_tunneling_tests.c b/tests/secure_tunneling_tests.c index 94e402d8..53a95218 100644 --- a/tests/secure_tunneling_tests.c +++ b/tests/secure_tunneling_tests.c @@ -681,7 +681,7 @@ static int s_secure_tunneling_handle_send_data_public(struct aws_allocator *allo // test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_SOURCE_MODE; /* Open the tunnel. */ - int rc = aws_secure_tunnel_connect(test_context->secure_tunnel); + int rc = aws_secure_tunnel_start(test_context->secure_tunnel); ASSERT_INT_EQUALS(AWS_OP_SUCCESS, rc); size_t buf_sizes[] = {10, 100, 1000, AWS_IOT_ST_SPLIT_MESSAGE_SIZE + 1, 2 * AWS_IOT_ST_SPLIT_MESSAGE_SIZE + 1}; @@ -717,7 +717,7 @@ static int s_secure_tunneling_handle_send_data_public(struct aws_allocator *allo } /* Close the tunnel. */ - aws_secure_tunnel_close(test_context->secure_tunnel); + aws_secure_tunnel_stop(test_context->secure_tunnel); return AWS_OP_SUCCESS; } From 5b6f269ffd143c667186caffcb0ec7ba9872950f Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Tue, 24 Jan 2023 13:31:57 -0800 Subject: [PATCH 13/69] pings updated to work with secure tunnel --- .../iotdevice/private/secure_tunneling_impl.h | 81 +-- source/secure_tunneling.c | 548 ++++-------------- source/serializer.c | 12 +- tests/secure_tunneling_tests.c | 8 +- 4 files changed, 160 insertions(+), 489 deletions(-) diff --git a/include/aws/iotdevice/private/secure_tunneling_impl.h b/include/aws/iotdevice/private/secure_tunneling_impl.h index 52a0428e..78262a42 100644 --- a/include/aws/iotdevice/private/secure_tunneling_impl.h +++ b/include/aws/iotdevice/private/secure_tunneling_impl.h @@ -23,7 +23,7 @@ * (2) external events. * * Most states are interruptible (in the sense of a change in desired state causing an immediate change in state) but - * CONNECTING and CHANNEL_SHUTDOWN cannot be interrupted due to waiting for an asynchronous callback (that has no + * CONNECTING cannot be interrupted due to waiting for an asynchronous callback (that has no * cancel) to complete. */ enum aws_secure_tunnel_state { @@ -42,10 +42,9 @@ enum aws_secure_tunnel_state { * * Next States: * CONNECTED - if WebSocket handshake is successful and desired state is still CONNECTED - * CHANNEL_SHUTDOWN - On unsuccessful WebSocket Handshake or if the channel completes setup with no error, but - * desired state is not CONNECTED - * PENDING_RECONNECT - if the channel fails to complete setup and desired state is still CONNECTED - * STOPPED - if the channel fails to complete setup and desired state is not CONNECTED + * WEBSOCKET_SHUTDOWN - if the websocket completes setup with no error but desired state is not CONNECTED + * PENDING_RECONNECT - if the websocket fails to complete setup and desired state is still CONNECTED + * STOPPED - if the websocket fails to complete setup and desired state is not CONNECTED */ AWS_STS_CONNECTING, @@ -53,29 +52,31 @@ enum aws_secure_tunnel_state { * The secure tunnel is ready to perform user-requested operations. * * Next States: - * PENDING_RECONNECT - unexpected channel shutdown completion and desired state still CONNECTED - * STOPPED - unexpected channel shutdown completion and desired state no longer CONNECTED + * WEBSOCKET_SHUTDOWN - desired state is no longer CONNECTED + * PENDING_RECONNECT - unexpected websocket shutdown completion and desired state still CONNECTED + * STOPPED - unexpected websocket shutdown completion and desired state no longer CONNECTED */ AWS_STS_CONNECTED, /* - * The secure tunnel is attempt to shut down a connection cleanly by finishing the current operation and then - * transmitting a STREAM RESET message to all open streams. + * The secure tunnel is attempt to shut down a websocket connection cleanly by finishing the current operation and + * then transmitting a STREAM RESET message to all open streams. * * Next States: - * PENDING_RECONNECT - unexpected channel shutdown completion and desired state still CONNECTED - * STOPPED - unexpected channel shutdown completion and desired state no longer CONNECTED + * WEBSOCKET_SHUTDOWN - on sucessful (or unsuccessful) disconnection + * PENDING_RECONNECT - unexpected websocket shutdown completion and desired state still CONNECTED + * STOPPED - unexpected websocket shutdown completion and desired state no longer CONNECTED */ AWS_STS_CLEAN_DISCONNECT, /* - * The secure tunnel is waiting for the io channel to completely shut down. This state is not interruptible. + * The secure tunnel is waiting for the websocket to completely shut down. This state is not interruptible. * * Next States: - * PENDING_RECONNECT - the io channel has shut down and desired state is still CONNECTED - * STOPPED - the io channel has shut down and desired state is not CONNECTED + * PENDING_RECONNECT - the websocket has shut down and desired state is still CONNECTED + * STOPPED - the websocket has shut down and desired state is not CONNECTED */ - AWS_STS_CHANNEL_SHUTDOWN, + AWS_STS_WEBSOCKET_SHUTDOWN, /* * The secure tunnel is waiting for the reconnect timer to expire before attempting to connect again. @@ -130,20 +131,11 @@ struct aws_secure_tunnel_vtable { /* aws_high_res_clock_get_ticks */ uint64_t (*get_current_time_fn)(void); - /* aws_channel_shutdown */ - int (*channel_shutdown_fn)(struct aws_channel *channel, int error_code); - - /* aws_websocket_client_connect */ - int (*websocket_connect_fn)(const struct aws_websocket_client_connection_options *options); - /* aws_http_proxy_new_socket_channel */ int (*http_proxy_new_socket_channel_fn)( struct aws_socket_channel_bootstrap_options *channel_options, const struct aws_http_proxy_options *proxy_options); - /* aws_client_bootstrap_new_socket_channel */ - int (*client_bootstrap_new_socket_channel_fn)(struct aws_socket_channel_bootstrap_options *options); - int (*send_data)(struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *data); int (*send_data_v2)( struct aws_secure_tunnel *secure_tunnel, @@ -152,32 +144,18 @@ struct aws_secure_tunnel_vtable { int (*send_stream_start_v2)(struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *service_id_data); int (*send_stream_reset)(struct aws_secure_tunnel *secure_tunnel); - /* aws_channel_acquire_message_from_pool */ - struct aws_io_message *(*aws_channel_acquire_message_from_pool_fn)( - struct aws_channel *channel, - enum aws_io_message_type message_type, - size_t size_hint, - void *user_data); - - /* aws_channel_slot_send_message */ - int (*aws_channel_slot_send_message_fn)( - struct aws_channel_slot *slot, - struct aws_io_message *message, - enum aws_channel_direction dir, - void *user_data); - void *vtable_user_data; }; -struct aws_websocket_client_connection_options; +// struct aws_websocket_client_connection_options; struct aws_websocket_send_frame_options; -struct aws_websocket_vtable { - int (*client_connect)(const struct aws_websocket_client_connection_options *options); - int (*send_frame)(struct aws_websocket *websocket, const struct aws_websocket_send_frame_options *options); - void (*close)(struct aws_websocket *websocket, bool free_scarce_resources_immediately); - void (*release)(struct aws_websocket *websocket); -}; +// struct aws_websocket_vtable { +// int (*client_connect)(const struct aws_websocket_client_connection_options *options); +// int (*send_frame)(struct aws_websocket *websocket, const struct aws_websocket_send_frame_options *options); +// void (*close)(struct aws_websocket *websocket, bool free_scarce_resources_immediately); +// void (*release)(struct aws_websocket *websocket); +// }; struct aws_secure_tunnel { /* Static settings */ @@ -218,10 +196,6 @@ struct aws_secure_tunnel { */ struct aws_event_loop *loop; - /* Channel handler information */ /* STEVE TODO is this necessary for a websocket? */ - struct aws_channel_handler handler; - struct aws_channel_slot *slot; - /* * What state is the secure tunnel working towards? */ @@ -232,7 +206,7 @@ struct aws_secure_tunnel { */ enum aws_secure_tunnel_state current_state; - struct aws_websocket_vtable websocket_vtable; + // struct aws_websocket_vtable websocket_vtable; /* Used to check connection state */ bool isConnected; @@ -284,7 +258,6 @@ struct aws_secure_tunnel { uint64_t next_secure_tunnel_websocket_connect_timeout_time; struct aws_linked_list queued_operations; - struct aws_linked_list write_completion_operations; struct aws_secure_tunnel_operation *current_operation; /* @@ -299,12 +272,6 @@ struct aws_secure_tunnel { */ uint64_t next_ping_time; - /* - * When should we shut down the channel due to failure to receive a PINGRESP? Only non-zero when an outstanding - * PINGREQ has not been answered. - */ - uint64_t next_ping_timeout_time; - /* Steve todo remove this and use next_ping_time above with tasks */ /* The secure tunneling endpoint ELB drops idle connect after 1 minute. We need to send a ping periodically to keep * the connection */ diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 957db56d..4ad599b6 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -26,18 +26,20 @@ #define MIN_RECONNECT_DELAY_MS 1000 #define MAX_RECONNECT_DELAY_MS 120000 #define PING_TASK_INTERVAL ((uint64_t)20 * 1000000000) -#define DEFAULT_PING_TIMEOUT_MS 30000 #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" static void s_change_current_state(struct aws_secure_tunnel *secure_tunnel, enum aws_secure_tunnel_state next_state); -void aws_secure_tunnel_on_disconnection_update_operational_state(struct aws_secure_tunnel *secure_tunnel); void aws_secure_tunnel_operational_state_clean_up(struct aws_secure_tunnel *secure_tunnel); static int s_aws_secure_tunnel_change_desired_state( struct aws_secure_tunnel *secure_tunnel, enum aws_secure_tunnel_state desired_state); +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, @@ -61,8 +63,8 @@ const char *aws_secure_tunnel_state_to_c_string(enum aws_secure_tunnel_state sta case AWS_STS_CLEAN_DISCONNECT: return "CLEAN_DISCONNECT"; - case AWS_STS_CHANNEL_SHUTDOWN: - return "CHANNEL_SHUTDOWN"; + case AWS_STS_WEBSOCKET_SHUTDOWN: + return "WEBSOCKET_SHUTDOWN"; case AWS_STS_PENDING_RECONNECT: return "PENDING_RECONNECT"; @@ -115,7 +117,6 @@ static void s_secure_tunnel_final_destroy(struct aws_secure_tunnel *secure_tunne static void s_on_secure_tunnel_zero_ref_count(void *user_data) { struct aws_secure_tunnel *secure_tunnel = user_data; - s_aws_secure_tunnel_change_desired_state(secure_tunnel, AWS_STS_TERMINATED); } @@ -236,6 +237,7 @@ static void s_aws_secure_tunnel_connected_on_message_received( struct aws_secure_tunnel *secure_tunnel, struct aws_secure_tunnel_message_view *message_view) { printf("\n\ns_aws_secure_tunnel_connected_on_message_received() called \n\n"); + printf("\nMessage Type: %s", aws_secure_tunnel_message_type_to_c_string(message_view->type)); switch (message_view->type) { case AWS_SECURE_TUNNEL_MT_DATA: secure_tunnel->config->on_message_received(message_view, secure_tunnel); @@ -270,7 +272,6 @@ static void s_aws_secure_tunnel_connected_on_message_received( static void s_process_received_data(struct aws_secure_tunnel *secure_tunnel) { struct aws_byte_buf *received_data = &secure_tunnel->received_data; struct aws_byte_cursor cursor = aws_byte_cursor_from_buf(received_data); - printf("\n\n PROCESSING RECEIVED WEBSOCKET DATA \n\n"); 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 @@ -291,8 +292,6 @@ static void s_process_received_data(struct aws_secure_tunnel *secure_tunnel) { } if (cursor.ptr != received_data->buffer) { - /* TODO: Consider better data structure that doesn't require moving bytes */ - /* Move unprocessed data to the beginning */ received_data->len = 0; aws_byte_buf_append(received_data, &cursor); @@ -312,7 +311,6 @@ static bool s_on_websocket_incoming_frame_begin( (void)websocket; (void)frame; (void)user_data; - printf("\n\nwebsocket frame begin\n\n"); return true; } @@ -343,42 +341,57 @@ static bool s_on_websocket_incoming_frame_complete( (void)frame; (void)error_code; (void)user_data; - printf("\n\nwebsocket frame end\n\n"); /* TODO: Check error_code */ return true; } -static void s_secure_tunnel_shutdown( - struct aws_client_bootstrap *bootstrap, - int error_code, - struct aws_channel *channel, - void *user_data) { - +static void s_secure_tunnel_shutdown(struct aws_client_bootstrap *bootstrap, int error_code, void *user_data) { (void)bootstrap; - (void)channel; - struct aws_secure_tunnel *secure_tunnel = user_data; if (error_code == AWS_ERROR_SUCCESS) { error_code = AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_UNEXPECTED_HANGUP; } - AWS_LOGF_INFO( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: channel tore down with error code %d(%s)", - (void *)secure_tunnel, - error_code, - aws_error_debug_str(error_code)); + /* fail current and all pending operations */ + if (secure_tunnel->current_operation != NULL) { + aws_linked_list_push_front(&secure_tunnel->queued_operations, &secure_tunnel->current_operation->node); + secure_tunnel->current_operation = NULL; + } - if (secure_tunnel->slot) { - aws_channel_slot_remove(secure_tunnel->slot); - AWS_LOGF_TRACE(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: slot removed successfully", (void *)secure_tunnel); - secure_tunnel->slot = NULL; + if (aws_linked_list_empty(&secure_tunnel->queued_operations)) { + s_complete_operation_list( + secure_tunnel, + &secure_tunnel->queued_operations, + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_OPERATION_FAILED_DUE_TO_OFFLINE_QUEUE_POLICY); } +} - aws_secure_tunnel_on_disconnection_update_operational_state(secure_tunnel); +/* Normal call to shutdown the websocket */ +static void s_secure_tunnel_shutdown_websocket(struct aws_secure_tunnel *secure_tunnel) { + if (secure_tunnel->current_state != AWS_STS_CONNECTED && secure_tunnel->current_state != AWS_STS_CLEAN_DISCONNECT) { + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: secure tunnel websocket shutdown invoked from unexpected state %d(%s)", + (void *)secure_tunnel, + (int)secure_tunnel->current_state, + aws_secure_tunnel_state_to_c_string(secure_tunnel->current_state)); + return; + } + + s_change_current_state(secure_tunnel, AWS_STS_WEBSOCKET_SHUTDOWN); +} + +/* Called by websocket when it's destroyed or manually on failed websocket creation */ +static void s_on_websocket_shutdown(struct aws_websocket *websocket, int error_code, void *user_data) { + struct aws_secure_tunnel *secure_tunnel = user_data; + s_secure_tunnel_shutdown(secure_tunnel->config->bootstrap, error_code, secure_tunnel); + + if (websocket) { + aws_websocket_release(websocket); + } if (secure_tunnel->desired_state == AWS_STS_CONNECTED) { s_change_current_state(secure_tunnel, AWS_STS_PENDING_RECONNECT); @@ -387,19 +400,12 @@ static void s_secure_tunnel_shutdown( } } -static void s_secure_tunnel_setup( - struct aws_client_bootstrap *bootstrap, - int error_code, - struct aws_channel *channel, - void *user_data) { - - /* Setup callback contract is: if error_code is non-zero then channel is NULL. */ - AWS_FATAL_ASSERT((error_code != 0) == (channel == NULL)); +static void s_secure_tunnel_setup(struct aws_client_bootstrap *bootstrap, int error_code, void *user_data) { + (void)bootstrap; struct aws_secure_tunnel *secure_tunnel = user_data; if (error_code != AWS_OP_SUCCESS) { - /* secure tunnel already handles this case, so just call that. */ - s_secure_tunnel_shutdown(bootstrap, error_code, channel, user_data); + s_on_websocket_shutdown(secure_tunnel->websocket, error_code, secure_tunnel); return; } @@ -411,52 +417,14 @@ static void s_secure_tunnel_setup( goto error; } - /* allocs or crashes */ - secure_tunnel->slot = aws_channel_slot_new(channel); - - if (aws_channel_slot_insert_end(channel, secure_tunnel->slot)) { - AWS_LOGF_ERROR( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: Failed to insert slot into channel %p, error %d (%s).", - (void *)secure_tunnel, - (void *)channel, - aws_last_error(), - aws_error_name(aws_last_error())); - goto error; - } - - if (aws_channel_slot_set_handler(secure_tunnel->slot, &secure_tunnel->handler)) { - AWS_LOGF_ERROR( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: Failed to set secure tunnel handler into slot on channel %p, error %d (%s).", - (void *)secure_tunnel, - (void *)channel, - aws_last_error(), - aws_error_name(aws_last_error())); - - goto error; - } - s_change_current_state(secure_tunnel, AWS_STS_CONNECTED); return; error: - s_change_current_state(secure_tunnel, AWS_STS_CHANNEL_SHUTDOWN); - (*secure_tunnel->vtable->channel_shutdown_fn)(channel, aws_last_error()); -} - -static void s_on_websocket_shutdown(struct aws_websocket *websocket, int error_code, void *user_data) { - struct aws_secure_tunnel *secure_tunnel = user_data; - - struct aws_channel *channel = secure_tunnel->slot ? secure_tunnel->slot->channel : NULL; - - s_secure_tunnel_shutdown(secure_tunnel->config->bootstrap, error_code, channel, secure_tunnel); - - if (websocket) { - aws_websocket_release(websocket); - } + s_on_websocket_shutdown(secure_tunnel->websocket, error_code, secure_tunnel); } +/* Called on successful or failed websocket setup attempt */ static void s_on_websocket_setup(const struct aws_websocket_on_connection_setup_data *setup, void *user_data) { struct aws_secure_tunnel *secure_tunnel = user_data; secure_tunnel->handshake_request = aws_http_message_release(secure_tunnel->handshake_request); @@ -464,30 +432,11 @@ static void s_on_websocket_setup(const struct aws_websocket_on_connection_setup_ /* Setup callback contract is: if error_code is non-zero then websocket is NULL. */ AWS_FATAL_ASSERT((setup->error_code != 0) == (setup->websocket == NULL)); - struct aws_channel *channel = NULL; - - if (setup->websocket) { - secure_tunnel->websocket = setup->websocket; - channel = aws_websocket_get_channel(setup->websocket); - AWS_ASSERT(channel); + secure_tunnel->websocket = setup->websocket; - /* Websocket must be "converted" before the MQTT handler can be installed next to it. */ - // STEVE TODO temp removing the conversion - // if (aws_websocket_convert_to_midchannel_handler(setup->websocket)) { - // AWS_LOGF_ERROR( - // AWS_LS_IOTDEVICE_SECURE_TUNNELING, - // "id=%p: Failed converting websocket, error %d (%s)", - // (void *)secure_tunnel, - // aws_last_error(), - // aws_error_name(aws_last_error())); - - // (*secure_tunnel->vtable->channel_shutdown_fn)(channel, aws_last_error()); - // return; - // } - } + /* Failed/Successful websocket creation and associated errors logged by "websocket-setup" */ - /* Call into the channel-setup callback, the rest of the logic is the same. */ - s_secure_tunnel_setup(secure_tunnel->config->bootstrap, setup->error_code, channel, secure_tunnel); + s_secure_tunnel_setup(secure_tunnel->config->bootstrap, setup->error_code, secure_tunnel); } struct aws_secure_tunnel_websocket_transform_complete_task { @@ -520,15 +469,15 @@ void s_websocket_transform_complete_task_fn(struct aws_task *task, void *arg, en .tls_options = &secure_tunnel->tls_con_opt, .host = aws_byte_cursor_from_string(secure_tunnel->config->endpoint_host), .port = 443, + .proxy_options = &secure_tunnel->config->http_proxy_options, .handshake_request = secure_tunnel->handshake_request, - .initial_window_size = MAX_WEBSOCKET_PAYLOAD, /* Prevent websocket data from arriving before the - secure_tunnel handler is installed by setting this to 0 */ + .initial_window_size = MAX_WEBSOCKET_PAYLOAD, + .manual_window_management = false, .user_data = secure_tunnel, - .on_connection_setup = s_on_websocket_setup, - .on_connection_shutdown = s_on_websocket_shutdown, .requested_event_loop = secure_tunnel->loop, - /* STEVE TODO These are the old functions that may need replacing */ + .on_connection_setup = s_on_websocket_setup, + .on_connection_shutdown = s_on_websocket_shutdown, .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, @@ -538,7 +487,7 @@ void s_websocket_transform_complete_task_fn(struct aws_task *task, void *arg, en websocket_options.proxy_options = &secure_tunnel->config->http_proxy_options; } - if (secure_tunnel->vtable->websocket_connect_fn(&websocket_options)) { + if (aws_websocket_client_connect(&websocket_options)) { AWS_LOGF_ERROR( AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: Failed to initiate websocket connection.", @@ -560,10 +509,8 @@ error:; s_on_websocket_setup(&websocket_setup, secure_tunnel); done: - aws_http_message_release(websocket_transform_complete_task->handshake); aws_secure_tunnel_release(websocket_transform_complete_task->secure_tunnel); - aws_mem_release(websocket_transform_complete_task->allocator, websocket_transform_complete_task); } @@ -586,6 +533,7 @@ static int s_handshake_add_header( AWS_BYTE_CURSOR_PRI(header.value)); return AWS_OP_SUCCESS; } + static struct aws_http_message *s_new_handshake_request(const struct aws_secure_tunnel *secure_tunnel) { char path[50]; snprintf( @@ -679,50 +627,14 @@ static void s_reset_ping(struct aws_secure_tunnel *secure_tunnel) { secure_tunnel->next_ping_time); } -/********************************************************************************************************************* - * CHANNEL - ********************************************************************************************************************/ - -static void s_aws_secure_tunnel_shutdown_channel(struct aws_secure_tunnel *secure_tunnel, int error_code) { - if (error_code == AWS_ERROR_SUCCESS) { - error_code = AWS_ERROR_UNKNOWN; - } - if (secure_tunnel->current_state != AWS_STS_CONNECTED && secure_tunnel->current_state != AWS_STS_CLEAN_DISCONNECT) { - AWS_LOGF_ERROR( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: secure tunnel channel shutdown invoked from unexpected state %d(%s)", - (void *)secure_tunnel, - (int)secure_tunnel->current_state, - aws_secure_tunnel_state_to_c_string(secure_tunnel->current_state)); - return; - } - - if (secure_tunnel->slot == NULL || secure_tunnel->slot->channel == NULL) { - AWS_LOGF_ERROR( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: secure tunnel channel shutdown invoked without a channel", - (void *)secure_tunnel); - return; - } - - s_change_current_state(secure_tunnel, AWS_STS_CHANNEL_SHUTDOWN); - (*secure_tunnel->vtable->channel_shutdown_fn)(secure_tunnel->slot->channel, error_code); -} - /********************************************************************************************************************* * State Related ********************************************************************************************************************/ -static void s_complete_operation_list( - struct aws_secure_tunnel *secure_tunnel, - struct aws_linked_list *operation_list, - int error_code); - static void s_aws_secure_tunnel_operational_state_reset( struct aws_secure_tunnel *secure_tunnel, int completion_error_code) { s_complete_operation_list(secure_tunnel, &secure_tunnel->queued_operations, completion_error_code); - s_complete_operation_list(secure_tunnel, &secure_tunnel->write_completion_operations, completion_error_code); } static void s_change_current_state_to_stopped(struct aws_secure_tunnel *secure_tunnel) { @@ -764,9 +676,11 @@ static void s_change_current_state_to_connected(struct aws_secure_tunnel *secure secure_tunnel->current_state = AWS_STS_CONNECTED; - aws_secure_tunnel_on_disconnection_update_operational_state(secure_tunnel); + /* + * Any rejoin logic can be implemented here. Secure Tunnel does not handle any rejoin state. + * We may opt to send disconnects to existing non-zero stream IDs to notify that the server has reconnected. + */ - secure_tunnel->next_ping_timeout_time = 0; s_reset_ping(secure_tunnel); } @@ -776,19 +690,26 @@ static void s_change_current_state_to_clean_disconnect(struct aws_secure_tunnel secure_tunnel->current_state = AWS_STS_CLEAN_DISCONNECT; } -static void s_change_current_state_to_channel_shutdown(struct aws_secure_tunnel *secure_tunnel) { +static void s_change_current_state_to_websocket_shutdown(struct aws_secure_tunnel *secure_tunnel) { enum aws_secure_tunnel_state current_state = secure_tunnel->current_state; AWS_FATAL_ASSERT( current_state == AWS_STS_CONNECTING || current_state == AWS_STS_CONNECTED || current_state == AWS_STS_CLEAN_DISCONNECT); - secure_tunnel->current_state = AWS_STS_CHANNEL_SHUTDOWN; + + if (secure_tunnel->websocket) { + aws_websocket_close(secure_tunnel->websocket, false); + } else { + s_on_websocket_shutdown(secure_tunnel->websocket, AWS_ERROR_UNKNOWN, secure_tunnel); + } + + secure_tunnel->current_state = AWS_STS_WEBSOCKET_SHUTDOWN; } static void s_update_reconnect_delay_for_pending_reconnect(struct aws_secure_tunnel *secure_tunnel) { uint64_t delay_ms = MIN_RECONNECT_DELAY_MS; for (int i = 0; i < (int)secure_tunnel->reconnect_count; ++i) { - delay_ms = delay_ms * delay_ms; + delay_ms = delay_ms * 2; } delay_ms = aws_min_u64(delay_ms, MAX_RECONNECT_DELAY_MS); @@ -843,8 +764,8 @@ static void s_change_current_state(struct aws_secure_tunnel *secure_tunnel, enum case AWS_STS_CLEAN_DISCONNECT: s_change_current_state_to_clean_disconnect(secure_tunnel); break; - case AWS_STS_CHANNEL_SHUTDOWN: - s_change_current_state_to_channel_shutdown(secure_tunnel); + case AWS_STS_WEBSOCKET_SHUTDOWN: + s_change_current_state_to_websocket_shutdown(secure_tunnel); break; case AWS_STS_PENDING_RECONNECT: s_change_current_state_to_pending_reconnect(secure_tunnel); @@ -1157,39 +1078,13 @@ static int s_secure_tunneling_send( AWS_OP_SUCCESS) { return AWS_OP_ERR; } - return secure_tunnel->websocket_vtable.send_frame(secure_tunnel->websocket, &frame_options); -} - -struct aws_io_message *s_aws_channel_acquire_message_from_pool_default( - struct aws_channel *channel, - enum aws_io_message_type message_type, - size_t size_hint, - void *user_data) { - (void)user_data; - - return aws_channel_acquire_message_from_pool(channel, message_type, size_hint); -} - -static int s_aws_channel_slot_send_message_default( - struct aws_channel_slot *slot, - struct aws_io_message *message, - enum aws_channel_direction dir, - void *user_data) { - (void)user_data; - - return aws_channel_slot_send_message(slot, message, dir); + return aws_websocket_send_frame(secure_tunnel->websocket, &frame_options); } static struct aws_secure_tunnel_vtable s_default_secure_tunnel_vtable = { .get_current_time_fn = s_aws_high_res_clock_get_ticks_proxy, - .channel_shutdown_fn = aws_channel_shutdown, - .websocket_connect_fn = aws_websocket_client_connect, - .client_bootstrap_new_socket_channel_fn = aws_client_bootstrap_new_socket_channel, .http_proxy_new_socket_channel_fn = aws_http_proxy_new_socket_channel, - .aws_channel_acquire_message_from_pool_fn = s_aws_channel_acquire_message_from_pool_default, - .aws_channel_slot_send_message_fn = s_aws_channel_slot_send_message_default, - .vtable_user_data = NULL, /* STEVE TODO remove these after changing the API to make these operations/tasks */ @@ -1199,95 +1094,6 @@ static struct aws_secure_tunnel_vtable s_default_secure_tunnel_vtable = { .send_stream_reset = s_secure_tunneling_send_stream_reset, }; -static int s_process_read_message( - struct aws_channel_handler *handler, - struct aws_channel_slot *slot, - struct aws_io_message *message) { - - struct aws_secure_tunnel *secure_tunnel = handler->impl; - - if (message->message_type != AWS_IO_MESSAGE_APPLICATION_DATA) { - AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "id=%p: unexpected io message data", (void *)secure_tunnel); - return AWS_OP_ERR; - } - - AWS_LOGF_TRACE( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: processing read message of size %zu", - (void *)secure_tunnel, - message->message_data.len); - - struct aws_byte_cursor message_cursor = aws_byte_cursor_from_buf(&message->message_data); - - (void)message_cursor; - printf("\n\nSTEVE TODO DEGUB s_process_read_message() called here\n\n"); - - // int result = aws_mqtt5_decoder_on_data_received(&client->decoder, message_cursor); - // if (result != AWS_OP_SUCCESS) { - // int error_code = aws_last_error(); - // AWS_LOGF_ERROR( - // AWS_LS_MQTT5_CLIENT, - // "id=%p: decode failure with error %d(%s)", - // (void *)client, - // error_code, - // aws_error_debug_str(error_code)); - - // if (error_code == AWS_ERROR_MQTT5_DECODE_PROTOCOL_ERROR && s_should_client_disconnect_cleanly(client)) { - // s_aws_mqtt5_client_shutdown_channel_clean(client, error_code, AWS_MQTT5_DRC_PROTOCOL_ERROR); - // } else { - // s_aws_mqtt5_client_shutdown_channel(client, error_code); - // } - - // goto done; - // } - - aws_channel_slot_increment_read_window(slot, message->message_data.len); - - // done: - - aws_mem_release(message->allocator, message); - - return AWS_OP_SUCCESS; -} - -static size_t s_initial_window_size(struct aws_channel_handler *handler) { - (void)handler; - - return SIZE_MAX; -} - -static size_t s_message_overhead(struct aws_channel_handler *handler) { - (void)handler; - - return 0; -} - -static void s_destroy(struct aws_channel_handler *handler) { - (void)handler; -} - -static int s_shutdown( - struct aws_channel_handler *handler, - struct aws_channel_slot *slot, - enum aws_channel_direction dir, - int error_code, - bool free_scarce_resources_immediately) { - - (void)handler; - - return aws_channel_slot_on_handler_shutdown_complete(slot, dir, error_code, free_scarce_resources_immediately); -} - -static struct aws_channel_handler_vtable s_secure_tunnel_channel_handler_vtable = { - .process_read_message = &s_process_read_message, - .process_write_message = NULL, - .increment_read_window = NULL, - .shutdown = &s_shutdown, - .initial_window_size = &s_initial_window_size, - .message_overhead = &s_message_overhead, - .destroy = &s_destroy, -}; - /********************************************************************************************************************* * Operations ********************************************************************************************************************/ @@ -1323,75 +1129,6 @@ static void s_complete_operation_list( aws_linked_list_init(operation_list); } -static void s_aws_secure_tunnel_on_socket_write_completion_connected( - struct aws_secure_tunnel *secure_tunnel, - int error_code) { - if (error_code != AWS_ERROR_SUCCESS) { - s_aws_secure_tunnel_shutdown_channel(secure_tunnel, error_code); - return; - } - - s_reevaluate_service_task(secure_tunnel); -} - -static void s_aws_secure_tunnel_on_socket_write_completion( - struct aws_channel *channel, - struct aws_io_message *message, - int error_code, - void *user_data) { - - (void)channel; - (void)message; - - struct aws_secure_tunnel *secure_tunnel = user_data; - secure_tunnel->pending_write_completion = false; - - if (error_code != AWS_ERROR_SUCCESS) { - AWS_LOGF_INFO( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: socket write completion invoked with error %d(%s)", - (void *)secure_tunnel, - error_code, - aws_error_debug_str(error_code)); - } - - switch (secure_tunnel->current_state) { - case AWS_STS_CONNECTED: - s_aws_secure_tunnel_on_socket_write_completion_connected(secure_tunnel, error_code); - break; - - case AWS_STS_CLEAN_DISCONNECT: - /* the CONNECTED callback works just fine for CLEAN_DISCONNECT */ - s_aws_secure_tunnel_on_socket_write_completion_connected(secure_tunnel, error_code); - break; - - default: - break; - } - - s_complete_operation_list(secure_tunnel, &secure_tunnel->write_completion_operations, error_code); -} - -void aws_secure_tunnel_on_disconnection_update_operational_state(struct aws_secure_tunnel *secure_tunnel) { - /* move current operation to the head of the queue */ - if (secure_tunnel->current_operation != NULL) { - aws_linked_list_push_front(&secure_tunnel->queued_operations, &secure_tunnel->current_operation->node); - secure_tunnel->current_operation = NULL; - } - - /* fail everything in pending write completion */ - s_complete_operation_list( - secure_tunnel, - &secure_tunnel->write_completion_operations, - AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_OPERATION_FAILED_DUE_TO_OFFLINE_QUEUE_POLICY); - - /* fail everything in the pending operations list */ - s_complete_operation_list( - secure_tunnel, - &secure_tunnel->queued_operations, - AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_OPERATION_FAILED_DUE_TO_OFFLINE_QUEUE_POLICY); -} - /* * Check whether secure tunnel currently has work left to do based on its current state */ @@ -1423,6 +1160,7 @@ static uint64_t s_aws_secure_tunnel_compute_operational_state_service_time( /* If an io message is in transit down the channel, then wait for it to complete */ if (secure_tunnel->pending_write_completion) { + printf("\n secure_tunnel->pending_write_completion = true \n"); return 0; } @@ -1463,7 +1201,6 @@ static bool s_aws_secure_tunnel_should_service_operational_state( } int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure_tunnel) { - struct aws_channel_slot *slot = secure_tunnel->slot; const struct aws_secure_tunnel_vtable *vtable = secure_tunnel->vtable; uint64_t now = (*vtable->get_current_time_fn)(); @@ -1473,16 +1210,6 @@ int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure return AWS_OP_SUCCESS; } - /* If we're going to write data, we need something to write to */ - struct aws_io_message *io_message = (*vtable->aws_channel_acquire_message_from_pool_fn)( - slot->channel, - AWS_IO_MESSAGE_APPLICATION_DATA, - AWS_SECURE_TUNNEL_IO_MESSAGE_DEFAULT_LENGTH, - vtable->vtable_user_data); - if (io_message == NULL) { - return AWS_OP_ERR; - } - int operational_error_code = AWS_ERROR_SUCCESS; do { @@ -1537,19 +1264,18 @@ int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure } if (current_operation->operation_type == AWS_STOT_PING) { - uint64_t ping_timeout_nanos = - aws_timestamp_convert(DEFAULT_PING_TIMEOUT_MS, AWS_TIMESTAMP_MILLIS, AWS_TIMESTAMP_NANOS, NULL); - secure_tunnel->next_ping_timeout_time = aws_add_u64_saturating(now, ping_timeout_nanos); - + /* + * 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. + */ if (secure_tunnel->websocket != NULL) { - printf("\n\nsecure_tunnel->websocket != NULL\n\n"); struct aws_websocket_send_frame_options frame_options; AWS_ZERO_STRUCT(frame_options); frame_options.opcode = AWS_WEBSOCKET_OPCODE_PING; frame_options.fin = true; aws_websocket_send_frame(secure_tunnel->websocket, &frame_options); - } else { - printf("\n\nsecure_tunnel->websocket is NULL\n\n"); } s_complete_operation(secure_tunnel, current_operation, AWS_OP_SUCCESS, NULL); @@ -1561,27 +1287,26 @@ int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure } while (should_service); if (operational_error_code != AWS_ERROR_SUCCESS) { - aws_mem_release(io_message->allocator, io_message); return aws_raise_error(operational_error_code); } /* It's possible for there to be no data if we serviced operations that failed validation */ - if (io_message->message_data.len == 0) { - aws_mem_release(io_message->allocator, io_message); - return AWS_OP_SUCCESS; - } + // if (io_message->message_data.len == 0) { + // aws_mem_release(io_message->allocator, io_message); + // return AWS_OP_SUCCESS; + // } /* send io_message down channel in write direction, handle errors */ - io_message->on_completion = s_aws_secure_tunnel_on_socket_write_completion; - io_message->user_data = secure_tunnel; - secure_tunnel->pending_write_completion = true; - - if ((*vtable->aws_channel_slot_send_message_fn)( - slot, io_message, AWS_CHANNEL_DIR_WRITE, vtable->vtable_user_data)) { - secure_tunnel->pending_write_completion = false; - aws_mem_release(io_message->allocator, io_message); - return AWS_OP_ERR; - } + // io_message->on_completion = s_aws_secure_tunnel_on_socket_write_completion; + // io_message->user_data = secure_tunnel; + // secure_tunnel->pending_write_completion = true; + + // if ((*vtable->aws_channel_slot_send_message_fn)( + // slot, io_message, AWS_CHANNEL_DIR_WRITE, vtable->vtable_user_data)) { + // secure_tunnel->pending_write_completion = false; + // aws_mem_release(io_message->allocator, io_message); + // return AWS_OP_ERR; + // } return AWS_OP_SUCCESS; } @@ -1689,15 +1414,15 @@ static int s_submit_operation(struct aws_secure_tunnel *secure_tunnel, struct aw return AWS_OP_SUCCESS; } -static void s_check_ping_timeout(struct aws_secure_tunnel *secure_tunnel, uint64_t now) { - // Steve TODO check the ping operation for timeout - // This is called during the CONNECTED state to check whether we need to initiate a disconnect due - // to a pingreq timeout. - // mqtt5_client uses s_check_timeouts() to check all unacked_operations. - // Currently the ping operation goes out but we don't check for its completion. - (void)secure_tunnel; - (void)now; -} +// static void s_check_ping_timeout(struct aws_secure_tunnel *secure_tunnel, uint64_t now) { +// // Steve TODO check the ping operation for timeout +// // This is called during the CONNECTED state to check whether we need to initiate a disconnect due +// // to a pingreq timeout. +// // mqtt5_client uses s_check_timeouts() to check all unacked_operations. +// // Currently the ping operation goes out but we don't check for its completion. +// (void)secure_tunnel; +// (void)now; +// } /********************************************************************************************************************* * Service Timing @@ -1746,9 +1471,6 @@ static uint64_t s_compute_next_service_time_secure_tunnel_connected( uint64_t now) { /* ping and ping timeout */ uint64_t next_service_time = secure_tunnel->next_ping_time; - if (secure_tunnel->next_ping_timeout_time != 0) { - next_service_time = aws_min_u64(next_service_time, secure_tunnel->next_ping_timeout_time); - } if (secure_tunnel->desired_state != AWS_STS_CONNECTED) { next_service_time = now; @@ -1770,6 +1492,15 @@ static uint64_t s_compute_next_service_time_secure_tunnel_clean_disconnect( return s_aws_secure_tunnel_compute_operational_state_service_time(secure_tunnel, now); } +static uint64_t s_compute_next_service_time_secure_tunnel_websocket_shutdown( + struct aws_secure_tunnel *secure_tunnel, + uint64_t now) { + (void)secure_tunnel; + (void)now; + + return 0; +} + static uint64_t s_compute_next_service_time_secure_tunnel_pending_reconnect( struct aws_secure_tunnel *secure_tunnel, uint64_t now) { @@ -1791,9 +1522,10 @@ static uint64_t s_compute_next_service_time_by_current_state(struct aws_secure_t return s_compute_next_service_time_secure_tunnel_connected(secure_tunnel, now); case AWS_STS_CLEAN_DISCONNECT: return s_compute_next_service_time_secure_tunnel_clean_disconnect(secure_tunnel, now); + case AWS_STS_WEBSOCKET_SHUTDOWN: + return s_compute_next_service_time_secure_tunnel_websocket_shutdown(secure_tunnel, now); case AWS_STS_PENDING_RECONNECT: return s_compute_next_service_time_secure_tunnel_pending_reconnect(secure_tunnel, now); - case AWS_STS_CHANNEL_SHUTDOWN: case AWS_STS_TERMINATED: return 0; } @@ -1875,10 +1607,9 @@ static void s_service_state_connecting(struct aws_secure_tunnel *secure_tunnel, if (now >= secure_tunnel->next_secure_tunnel_websocket_connect_timeout_time) { AWS_LOGF_INFO( AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: shutting down channel due to websocket timeout", + "id=%p: shutting down websocket due to connect timeout", (void *)secure_tunnel); - - s_aws_secure_tunnel_shutdown_channel(secure_tunnel, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_WEBSOCKET_TIMEOUT); + s_secure_tunnel_shutdown_websocket(secure_tunnel); return; } @@ -1890,8 +1621,7 @@ static void s_service_state_connecting(struct aws_secure_tunnel *secure_tunnel, (void *)secure_tunnel, error_code, aws_error_debug_str(error_code)); - - s_aws_secure_tunnel_shutdown_channel(secure_tunnel, error_code); + s_secure_tunnel_shutdown_websocket(secure_tunnel); return; } } @@ -1903,17 +1633,7 @@ static void s_service_state_connected(struct aws_secure_tunnel *secure_tunnel, u AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: channel shutdown due to user Stop request", (void *)secure_tunnel); - s_aws_secure_tunnel_shutdown_channel(secure_tunnel, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_USER_REQUESTED_STOP); - return; - } - - if (now >= secure_tunnel->next_ping_timeout_time && secure_tunnel->next_ping_timeout_time != 0) { - AWS_LOGF_INFO( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: channel shutdown due to PINGRESP timeout", - (void *)secure_tunnel); - - s_aws_secure_tunnel_shutdown_channel(secure_tunnel, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_PING_RESPONSE_TIMEOUT); + s_secure_tunnel_shutdown_websocket(secure_tunnel); return; } @@ -1926,13 +1646,11 @@ static void s_service_state_connected(struct aws_secure_tunnel *secure_tunnel, u (void *)secure_tunnel, error_code, aws_error_debug_str(error_code)); - - s_aws_secure_tunnel_shutdown_channel(secure_tunnel, error_code); + s_secure_tunnel_shutdown_websocket(secure_tunnel); return; } } - s_check_ping_timeout(secure_tunnel, now); /* STEVE TODO Reconnect logic and timer checks can go here */ secure_tunnel->reconnect_count = 0; @@ -1944,8 +1662,7 @@ static void s_service_state_connected(struct aws_secure_tunnel *secure_tunnel, u (void *)secure_tunnel, error_code, aws_error_debug_str(error_code)); - - s_aws_secure_tunnel_shutdown_channel(secure_tunnel, error_code); + s_secure_tunnel_shutdown_websocket(secure_tunnel); return; } } @@ -1960,16 +1677,11 @@ static void s_service_state_clean_disconnect(struct aws_secure_tunnel *secure_tu (void *)secure_tunnel, error_code, aws_error_debug_str(error_code)); - - s_aws_secure_tunnel_shutdown_channel(secure_tunnel, error_code); + s_secure_tunnel_shutdown_websocket(secure_tunnel); return; } } -static void s_service_state_channel_shutdown(struct aws_secure_tunnel *secure_tunnel) { - (void)secure_tunnel; -} - static void s_service_state_pending_reconnect(struct aws_secure_tunnel *secure_tunnel, uint64_t now) { if (secure_tunnel->desired_state != AWS_STS_CONNECTED) { s_change_current_state(secure_tunnel, AWS_STS_STOPPED); @@ -2007,9 +1719,6 @@ static void s_secure_tunnel_service_task_fn(struct aws_task *task, void *arg, en case AWS_STS_CLEAN_DISCONNECT: s_service_state_clean_disconnect(secure_tunnel, now); break; - case AWS_STS_CHANNEL_SHUTDOWN: - s_service_state_channel_shutdown(secure_tunnel); - break; case AWS_STS_PENDING_RECONNECT: s_service_state_pending_reconnect(secure_tunnel, now); break; @@ -2051,7 +1760,6 @@ struct aws_secure_tunnel *aws_secure_tunnel_new(const struct aws_secure_tunnel_o aws_ref_count_init(&secure_tunnel->ref_count, secure_tunnel, s_on_secure_tunnel_zero_ref_count); aws_linked_list_init(&secure_tunnel->queued_operations); - aws_linked_list_init(&secure_tunnel->write_completion_operations); secure_tunnel->current_operation = NULL; /* store options */ @@ -2072,11 +1780,6 @@ struct aws_secure_tunnel *aws_secure_tunnel_new(const struct aws_secure_tunnel_o secure_tunnel->desired_state = AWS_STS_STOPPED; secure_tunnel->current_state = AWS_STS_STOPPED; - // STEVE TODO Channel Handler may be uneccessary for a websocket secure tunnel - secure_tunnel->handler.alloc = secure_tunnel->allocator; - secure_tunnel->handler.vtable = &s_secure_tunnel_channel_handler_vtable; - secure_tunnel->handler.impl = secure_tunnel; - /* tls setup */ struct aws_tls_ctx_options tls_ctx_opt; AWS_ZERO_STRUCT(tls_ctx_opt); @@ -2102,13 +1805,6 @@ struct aws_secure_tunnel *aws_secure_tunnel_new(const struct aws_secure_tunnel_o aws_tls_ctx_options_clean_up(&tls_ctx_opt); - /* websocket vtable */ - - // secure_tunnel->websocket_vtable.client_connect = aws_websocket_client_connect; - secure_tunnel->websocket_vtable.send_frame = aws_websocket_send_frame; - secure_tunnel->websocket_vtable.close = aws_websocket_close; - // secure_tunnel->websocket_vtable.release = aws_websocket_release; - /* Connection reset */ secure_tunnel->config->stream_id = INVALID_STREAM_ID; secure_tunnel->config->service_id_1_stream_id = INVALID_STREAM_ID; diff --git a/source/serializer.c b/source/serializer.c index e2fafe3c..991a11a3 100644 --- a/source/serializer.c +++ b/source/serializer.c @@ -315,7 +315,6 @@ int aws_secure_tunnel_deserialize_message_from_cursor( struct aws_secure_tunnel_message_view *message, struct aws_byte_cursor *cursor, aws_secure_tunnel_on_message_received_fn *on_message_received) { - printf("\n\naws_secure_tunnel_deserialize_message_from_cursor()\n\n"); 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; @@ -343,7 +342,6 @@ int aws_secure_tunnel_deserialize_message_from_cursor( if (s_iot_st_decode_varint_uint32_t(cursor, &res)) { return AWS_OP_ERR; } - switch (field_number) { case AWS_SECURE_TUNNEL_FN_TYPE: message->type = res; @@ -391,6 +389,7 @@ int aws_secure_tunnel_deserialize_message_from_cursor( s_aws_st_decode_lengthdelim(cursor, &available_service_id_buf, length)) { goto error; } + aws_byte_cursor_advance(cursor, length); switch (service_ids_set) { case 0: @@ -439,6 +438,15 @@ int aws_secure_tunnel_deserialize_message_from_cursor( on_message_received(secure_tunnel, message); aws_byte_buf_clean_up(&payload_buf); aws_byte_buf_clean_up(&service_id_buf); + /* If any service ids were set, clear the ones that haven't been set by this message. */ + if (service_ids_set) { + switch (service_ids_set) { + case 1: + aws_string_destroy(secure_tunnel->config->service_id_2); + case 2: + aws_string_destroy(secure_tunnel->config->service_id_3); + } + } return AWS_OP_SUCCESS; error: diff --git a/tests/secure_tunneling_tests.c b/tests/secure_tunneling_tests.c index 53a95218..fbb0c5a3 100644 --- a/tests/secure_tunneling_tests.c +++ b/tests/secure_tunneling_tests.c @@ -159,10 +159,10 @@ static struct aws_secure_tunnel *s_secure_tunnel_new_mock(const struct aws_secur if (!secure_tunnel) { return secure_tunnel; } - secure_tunnel->websocket_vtable.client_connect = s_mock_aws_websocket_client_connect; - secure_tunnel->websocket_vtable.send_frame = s_mock_aws_websocket_send_frame; - secure_tunnel->websocket_vtable.close = s_mock_aws_websocket_close; - secure_tunnel->websocket_vtable.release = s_mock_aws_websocket_release; + // secure_tunnel->websocket_vtable.client_connect = s_mock_aws_websocket_client_connect; + // secure_tunnel->websocket_vtable.send_frame = s_mock_aws_websocket_send_frame; + // secure_tunnel->websocket_vtable.close = s_mock_aws_websocket_close; + // secure_tunnel->websocket_vtable.release = s_mock_aws_websocket_release; /* * Initialize a dummy websocket when the tunnel is created. From 39dcd7f9880c7253fbd4b1c4f40fe34afd44bfe7 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Tue, 24 Jan 2023 14:08:23 -0800 Subject: [PATCH 14/69] cleanup --- .../iotdevice/private/secure_tunneling_impl.h | 6 --- include/aws/iotdevice/secure_tunneling.h | 1 + source/secure_tunneling.c | 37 +++---------------- 3 files changed, 7 insertions(+), 37 deletions(-) diff --git a/include/aws/iotdevice/private/secure_tunneling_impl.h b/include/aws/iotdevice/private/secure_tunneling_impl.h index 78262a42..3ca2a090 100644 --- a/include/aws/iotdevice/private/secure_tunneling_impl.h +++ b/include/aws/iotdevice/private/secure_tunneling_impl.h @@ -251,12 +251,6 @@ struct aws_secure_tunnel { */ uint64_t reconnect_count; - /* - * When should we shut down the channel due to failure to receive a websocket handshake? Only relevant during the - * SECURE_TUNNEL_CONNECT state. - */ - uint64_t next_secure_tunnel_websocket_connect_timeout_time; - struct aws_linked_list queued_operations; struct aws_secure_tunnel_operation *current_operation; diff --git a/include/aws/iotdevice/secure_tunneling.h b/include/aws/iotdevice/secure_tunneling.h index a8ed7a8c..b7e7c9ee 100644 --- a/include/aws/iotdevice/secure_tunneling.h +++ b/include/aws/iotdevice/secure_tunneling.h @@ -164,6 +164,7 @@ struct aws_secure_tunnel_options { /* Steve TODO we only support destination mode so this can be removed outside of testing */ enum aws_secure_tunneling_local_proxy_mode local_proxy_mode; + 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; diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 4ad599b6..571d4fae 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -669,15 +669,14 @@ static void s_change_current_state_to_connecting(struct aws_secure_tunnel *secur } } -// Steve TODO implement these state changes - static void s_change_current_state_to_connected(struct aws_secure_tunnel *secure_tunnel) { AWS_FATAL_ASSERT(secure_tunnel->current_state == AWS_STS_CONNECTING); secure_tunnel->current_state = AWS_STS_CONNECTED; + secure_tunnel->reconnect_count = 0; /* - * Any rejoin logic can be implemented here. Secure Tunnel does not handle any rejoin state. + * TODO Any rejoin logic can be implemented here. Secure Tunnel does not handle any rejoin state. * We may opt to send disconnects to existing non-zero stream IDs to notify that the server has reconnected. */ @@ -732,6 +731,7 @@ static void s_change_current_state_to_pending_reconnect(struct aws_secure_tunnel s_update_reconnect_delay_for_pending_reconnect(secure_tunnel); } + static void s_change_current_state_to_terminated(struct aws_secure_tunnel *secure_tunnel) { secure_tunnel->current_state = AWS_STS_TERMINATED; @@ -867,6 +867,7 @@ static int s_aws_secure_tunnel_change_desired_state( struct aws_secure_tunnel_change_desired_state_task *task = s_aws_secure_tunnel_change_desired_state_task_new(secure_tunnel->allocator, secure_tunnel, desired_state); + if (task == NULL) { AWS_LOGF_ERROR( AWS_LS_IOTDEVICE_SECURE_TUNNELING, @@ -1103,7 +1104,6 @@ static void s_complete_operation( struct aws_secure_tunnel_operation *operation, int error_code, const void *view) { - // Steve TODO Stat tracking can be added here in the future. (void)secure_tunnel; aws_secure_tunnel_operation_complete(operation, error_code, view); @@ -1158,9 +1158,8 @@ static uint64_t s_aws_secure_tunnel_compute_operational_state_service_time( struct aws_secure_tunnel *secure_tunnel, uint64_t now) { - /* If an io message is in transit down the channel, then wait for it to complete */ + /* If a message is in transit down the websocket, then wait for it to complete */ if (secure_tunnel->pending_write_completion) { - printf("\n secure_tunnel->pending_write_completion = true \n"); return 0; } @@ -1414,16 +1413,6 @@ static int s_submit_operation(struct aws_secure_tunnel *secure_tunnel, struct aw return AWS_OP_SUCCESS; } -// static void s_check_ping_timeout(struct aws_secure_tunnel *secure_tunnel, uint64_t now) { -// // Steve TODO check the ping operation for timeout -// // This is called during the CONNECTED state to check whether we need to initiate a disconnect due -// // to a pingreq timeout. -// // mqtt5_client uses s_check_timeouts() to check all unacked_operations. -// // Currently the ping operation goes out but we don't check for its completion. -// (void)secure_tunnel; -// (void)now; -// } - /********************************************************************************************************************* * Service Timing ********************************************************************************************************************/ @@ -1469,7 +1458,7 @@ static uint64_t s_compute_next_service_time_secure_tunnel_connecting( static uint64_t s_compute_next_service_time_secure_tunnel_connected( struct aws_secure_tunnel *secure_tunnel, uint64_t now) { - /* ping and ping timeout */ + /* TODO check against ping timeout once pong is implemented by secure tunnel service */ uint64_t next_service_time = secure_tunnel->next_ping_time; if (secure_tunnel->desired_state != AWS_STS_CONNECTED) { @@ -1604,15 +1593,6 @@ static bool s_service_state_stopped(struct aws_secure_tunnel *secure_tunnel) { static void s_service_state_connecting(struct aws_secure_tunnel *secure_tunnel, uint64_t now) { (void)secure_tunnel; - if (now >= secure_tunnel->next_secure_tunnel_websocket_connect_timeout_time) { - AWS_LOGF_INFO( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: shutting down websocket due to connect timeout", - (void *)secure_tunnel); - s_secure_tunnel_shutdown_websocket(secure_tunnel); - return; - } - if (aws_secure_tunnel_service_operational_state(secure_tunnel)) { int error_code = aws_last_error(); AWS_LOGF_ERROR( @@ -1651,9 +1631,6 @@ static void s_service_state_connected(struct aws_secure_tunnel *secure_tunnel, u } } - /* STEVE TODO Reconnect logic and timer checks can go here */ - secure_tunnel->reconnect_count = 0; - if (aws_secure_tunnel_service_operational_state(secure_tunnel)) { int error_code = aws_last_error(); AWS_LOGF_ERROR( @@ -1768,8 +1745,6 @@ struct aws_secure_tunnel *aws_secure_tunnel_new(const struct aws_secure_tunnel_o goto error; } secure_tunnel->config->secure_tunnel = secure_tunnel; - // secure_tunnel->config->websocket_handshake_transform - // secure_tunnel->config->websocket_handshake_transform_user_data = secure_tunnel; /* 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); From 1ea9dbe3ca7e2b618d98585d58aa922a95a3e299 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Thu, 26 Jan 2023 09:06:07 -0800 Subject: [PATCH 15/69] encoding WIP --- .../iotdevice/private/secure_tunneling_impl.h | 7 - .../private/secure_tunneling_operations.h | 1 - include/aws/iotdevice/private/serializer.h | 6 + source/iotdevice.c | 13 +- source/secure_tunneling.c | 322 +++++++++--------- source/secure_tunneling_operations.c | 21 +- source/serializer.c | 159 ++++++++- tests/secure_tunneling_tests.c | 2 +- 8 files changed, 324 insertions(+), 207 deletions(-) diff --git a/include/aws/iotdevice/private/secure_tunneling_impl.h b/include/aws/iotdevice/private/secure_tunneling_impl.h index 3ca2a090..03867dbd 100644 --- a/include/aws/iotdevice/private/secure_tunneling_impl.h +++ b/include/aws/iotdevice/private/secure_tunneling_impl.h @@ -131,11 +131,6 @@ struct aws_secure_tunnel_vtable { /* aws_high_res_clock_get_ticks */ uint64_t (*get_current_time_fn)(void); - /* aws_http_proxy_new_socket_channel */ - int (*http_proxy_new_socket_channel_fn)( - struct aws_socket_channel_bootstrap_options *channel_options, - const struct aws_http_proxy_options *proxy_options); - int (*send_data)(struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *data); int (*send_data_v2)( struct aws_secure_tunnel *secure_tunnel, @@ -143,8 +138,6 @@ struct aws_secure_tunnel_vtable { int (*send_stream_start)(struct aws_secure_tunnel *secure_tunnel); int (*send_stream_start_v2)(struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *service_id_data); int (*send_stream_reset)(struct aws_secure_tunnel *secure_tunnel); - - void *vtable_user_data; }; // struct aws_websocket_client_connection_options; diff --git a/include/aws/iotdevice/private/secure_tunneling_operations.h b/include/aws/iotdevice/private/secure_tunneling_operations.h index 0d6bbb1f..b54b1627 100644 --- a/include/aws/iotdevice/private/secure_tunneling_operations.h +++ b/include/aws/iotdevice/private/secure_tunneling_operations.h @@ -31,7 +31,6 @@ struct aws_secure_tunnel_message_storage { struct aws_allocator *allocator; struct aws_secure_tunnel_message_view storage_view; - enum aws_secure_tunnel_message_type type; bool ignorable; int32_t stream_id; struct aws_byte_cursor service_id; diff --git a/include/aws/iotdevice/private/serializer.h b/include/aws/iotdevice/private/serializer.h index b31fc257..5e418a0a 100644 --- a/include/aws/iotdevice/private/serializer.h +++ b/include/aws/iotdevice/private/serializer.h @@ -61,6 +61,12 @@ int aws_iot_st_msg_serialize_from_struct( struct aws_allocator *allocator, struct aws_iot_st_msg message); +AWS_IOTDEVICE_API +int aws_iot_st_msg_serialize_from_view( + struct aws_byte_buf *buffer, + struct aws_allocator *allocator, + const struct aws_secure_tunnel_message_view *message_view); + AWS_IOTDEVICE_API int aws_secure_tunnel_deserialize_message_from_cursor( struct aws_secure_tunnel *secure_tunnel, diff --git a/source/iotdevice.c b/source/iotdevice.c index b08e2d11..fa625123 100644 --- a/source/iotdevice.c +++ b/source/iotdevice.c @@ -14,6 +14,9 @@ #define AWS_DEFINE_ERROR_INFO_IOTDEVICE(C, ES) AWS_DEFINE_ERROR_INFO(C, ES, "libaws-c-iotdevice") /* clang-format off */ static struct aws_error_info s_errors[] = { + AWS_DEFINE_ERROR_INFO_IOTDEVICE( + AWS_ERROR_IOTDEVICE_INVALID_RESERVED_BITS, + "Bits marked as reserved in the IoT Device spec were incorrectly set."), AWS_DEFINE_ERROR_INFO_IOTDEVICE( AWS_ERROR_IOTDEVICE_DEFENDER_INVALID_REPORT_INTERVAL, "Invalid defender task reporting interval. Must be greater than 5 minutes."), @@ -54,9 +57,6 @@ static struct aws_error_info s_errors[] = { AWS_DEFINE_ERROR_INFO_IOTDEVICE( AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_SECURE_TUNNEL_TERMINATED, "Secure Tunnel terminated by user request."), - AWS_DEFINE_ERROR_INFO_IOTDEVICE( - AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_OPERATION_FAILED_DUE_TO_DISCONNECTION, - "Secure Tunnel operation failed due to disconnected state."), AWS_DEFINE_ERROR_INFO_IOTDEVICE( AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_WEBSOCKET_TIMEOUT, "Remote endpoint did not respond to connect request before timeout exceeded."), @@ -64,11 +64,14 @@ static struct aws_error_info s_errors[] = { AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_PING_RESPONSE_TIMEOUT, "Remote endpoint did not respond to a PINGREQ before timeout exceeded."), AWS_DEFINE_ERROR_INFO_IOTDEVICE( - AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_OPERATION_FAILED_DUE_TO_OFFLINE_QUEUE_POLICY, - "Error while processing secure tunnel operational state."), + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_OPERATION_FAILED_DUE_TO_DISCONNECTION, + "Secure Tunnel operation failed due to disconnected state."), AWS_DEFINE_ERROR_INFO_IOTDEVICE( AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_OPERATION_PROCESSING_FAILURE, "Error while processing secure tunnel operational state."), + AWS_DEFINE_ERROR_INFO_IOTDEVICE( + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_OPERATION_FAILED_DUE_TO_OFFLINE_QUEUE_POLICY, + "Error while processing secure tunnel operational state."), AWS_DEFINE_ERROR_INFO_IOTDEVICE( AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_UNEXPECTED_HANGUP, "The connection was closed unexpectedly."), diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 571d4fae..fc6aa6a4 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -43,9 +43,7 @@ static void s_complete_operation_list( static int s_secure_tunneling_send( struct aws_secure_tunnel *secure_tunnel, - const struct aws_byte_cursor *payload_data, - const struct aws_byte_cursor *service_id_data, - enum aws_secure_tunnel_message_type type); + const struct aws_secure_tunnel_message_view *message_view); static void s_reevaluate_service_task(struct aws_secure_tunnel *secure_tunnel); @@ -469,7 +467,6 @@ void s_websocket_transform_complete_task_fn(struct aws_task *task, void *arg, en .tls_options = &secure_tunnel->tls_con_opt, .host = aws_byte_cursor_from_string(secure_tunnel->config->endpoint_host), .port = 443, - .proxy_options = &secure_tunnel->config->http_proxy_options, .handshake_request = secure_tunnel->handshake_request, .initial_window_size = MAX_WEBSOCKET_PAYLOAD, .manual_window_management = false, @@ -892,87 +889,78 @@ static uint64_t s_aws_high_res_clock_get_ticks_proxy(void) { return current_time; } -static int s_secure_tunneling_send_data( - struct aws_secure_tunnel *secure_tunnel, - const struct aws_byte_cursor *payload_data) { - if (secure_tunnel->config->stream_id == INVALID_STREAM_ID) { - AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Invalid Stream Id"); - return AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_STREAM; - } - struct aws_byte_cursor new_data = *payload_data; - while (new_data.len) { - size_t bytes_max = new_data.len; - size_t amount_to_send = bytes_max < AWS_IOT_ST_SPLIT_MESSAGE_SIZE ? bytes_max : AWS_IOT_ST_SPLIT_MESSAGE_SIZE; - - struct aws_byte_cursor send_cursor = aws_byte_cursor_advance(&new_data, amount_to_send); - AWS_FATAL_ASSERT(send_cursor.len > 0); - if (send_cursor.len) { - if (s_secure_tunneling_send(secure_tunnel, &send_cursor, NULL, AWS_SECURE_TUNNEL_MT_DATA) != - AWS_OP_SUCCESS) { - AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Failure writing data to out_buf"); - return AWS_OP_ERR; - } - } - } - return AWS_OP_SUCCESS; -} - -static int s_secure_tunneling_send_stream_start(struct aws_secure_tunnel *secure_tunnel) { - if (secure_tunnel->config->local_proxy_mode == AWS_SECURE_TUNNELING_DESTINATION_MODE) { - AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Start can only be sent from src mode"); - return AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INCORRECT_MODE; - } - secure_tunnel->config->stream_id += 1; - if (secure_tunnel->config->stream_id == 0) { - secure_tunnel->config->stream_id += 1; - } - return s_secure_tunneling_send(secure_tunnel, NULL, NULL, AWS_SECURE_TUNNEL_MT_STREAM_START); -} - -static int s_secure_tunneling_send_stream_start_v2( - struct aws_secure_tunnel *secure_tunnel, - const struct aws_byte_cursor *service_id_data) { - if (secure_tunnel->config->local_proxy_mode == AWS_SECURE_TUNNELING_DESTINATION_MODE) { - AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Start can only be sent from src mode"); - return AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INCORRECT_MODE; - } - - secure_tunnel->config->stream_id += 1; - if (secure_tunnel->config->stream_id == 0) { - secure_tunnel->config->stream_id += 1; - } - - /* STEVE TODO Secure Tunnel Stream Start needs to include Service Id */ - return s_secure_tunneling_send(secure_tunnel, NULL, service_id_data, AWS_SECURE_TUNNEL_MT_STREAM_START); -} - -static int s_secure_tunneling_send_stream_reset(struct aws_secure_tunnel *secure_tunnel) { - if (secure_tunnel->config->stream_id == INVALID_STREAM_ID) { - AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Invalid Stream Id"); - return AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_STREAM; - } - - int result = s_secure_tunneling_send(secure_tunnel, NULL, NULL, AWS_SECURE_TUNNEL_MT_STREAM_RESET); - s_reset_secure_tunnel(secure_tunnel); - return result; -} - -static void s_secure_tunneling_on_send_data_complete_callback( +// static int s_secure_tunneling_send_stream_start(struct aws_secure_tunnel *secure_tunnel) { +// if (secure_tunnel->config->local_proxy_mode == AWS_SECURE_TUNNELING_DESTINATION_MODE) { +// AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Start can only be sent from src mode"); +// return AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INCORRECT_MODE; +// } +// secure_tunnel->config->stream_id += 1; +// if (secure_tunnel->config->stream_id == 0) { +// secure_tunnel->config->stream_id += 1; +// } +// return s_secure_tunneling_send(secure_tunnel, NULL, NULL, AWS_SECURE_TUNNEL_MT_STREAM_START); +// } + +// static int s_secure_tunneling_send_stream_reset(struct aws_secure_tunnel *secure_tunnel) { +// if (secure_tunnel->config->stream_id == INVALID_STREAM_ID) { +// AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Invalid Stream Id"); +// return AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_STREAM; +// } + +// int result = s_secure_tunneling_send(secure_tunnel, NULL, NULL, AWS_SECURE_TUNNEL_MT_STREAM_RESET); +// s_reset_secure_tunnel(secure_tunnel); +// return result; +// } + +// static int s_secure_tunneling_send_data( +// struct aws_secure_tunnel *secure_tunnel, +// const struct aws_byte_cursor *payload_data) { +// if (secure_tunnel->config->stream_id == INVALID_STREAM_ID) { +// AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Invalid Stream Id"); +// return AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_STREAM; +// } +// struct aws_byte_cursor new_data = *payload_data; +// while (new_data.len) { +// size_t bytes_max = new_data.len; +// size_t amount_to_send = bytes_max < AWS_IOT_ST_SPLIT_MESSAGE_SIZE ? bytes_max : +// AWS_IOT_ST_SPLIT_MESSAGE_SIZE; + +// struct aws_byte_cursor send_cursor = aws_byte_cursor_advance(&new_data, amount_to_send); +// AWS_FATAL_ASSERT(send_cursor.len > 0); +// if (send_cursor.len) { +// if (s_secure_tunneling_send(secure_tunnel, &send_cursor, NULL, AWS_SECURE_TUNNEL_MT_DATA) != +// AWS_OP_SUCCESS) { +// AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Failure writing data to out_buf"); +// return AWS_OP_ERR; +// } +// } +// } +// return AWS_OP_SUCCESS; +// } + +static void s_secure_tunneling_websocket_on_send_data_complete_callback( struct aws_websocket *websocket, int error_code, void *user_data) { (void)websocket; struct data_tunnel_pair *pair = user_data; struct aws_secure_tunnel *secure_tunnel = (struct aws_secure_tunnel *)pair->secure_tunnel; - secure_tunnel->config->on_send_data_complete(error_code, pair->secure_tunnel->config->user_data); + if (secure_tunnel->config->on_send_data_complete) { + secure_tunnel->config->on_send_data_complete(error_code, pair->secure_tunnel->config->user_data); + } aws_byte_buf_clean_up(&pair->buf); aws_mem_release(secure_tunnel->allocator, pair); } -bool secure_tunneling_send_data_call(struct aws_websocket *websocket, struct aws_byte_buf *out_buf, void *user_data) { +bool secure_tunneling_websocket_stream_outgoing_payload( + struct aws_websocket *websocket, + struct aws_byte_buf *out_buf, + void *user_data) { (void)websocket; struct data_tunnel_pair *pair = user_data; size_t space_available = out_buf->capacity - out_buf->len; + + /* STEVE TODO need to check space available against service id as well */ if ((pair->length_prefix_written == false) && (space_available >= PAYLOAD_BYTE_LENGTH_PREFIX)) { if (aws_byte_buf_write_be16(out_buf, (int16_t)pair->buf.len) == false) { AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Failure writing buffer length prefix to out_buf"); @@ -981,18 +969,20 @@ bool secure_tunneling_send_data_call(struct aws_websocket *websocket, struct aws pair->length_prefix_written = true; space_available = out_buf->capacity - out_buf->len; } + if (pair->length_prefix_written == true) { size_t bytes_max = pair->cur.len; size_t amount_to_send = bytes_max < space_available ? bytes_max : space_available; struct aws_byte_cursor send_cursor = aws_byte_cursor_advance(&pair->cur, amount_to_send); if (send_cursor.len) { - if (aws_byte_buf_write_from_whole_cursor(out_buf, send_cursor) == false) { + if (!aws_byte_buf_write_from_whole_cursor(out_buf, send_cursor)) { AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Failure writing data to out_buf"); return false; } } } + return true; } @@ -1003,8 +993,8 @@ static void s_init_websocket_send_frame_options( AWS_ZERO_STRUCT(*frame_options); frame_options->payload_length = pair->buf.len + PAYLOAD_BYTE_LENGTH_PREFIX; frame_options->user_data = pair; - frame_options->stream_outgoing_payload = secure_tunneling_send_data_call; - frame_options->on_complete = s_secure_tunneling_on_send_data_complete_callback; + frame_options->stream_outgoing_payload = secure_tunneling_websocket_stream_outgoing_payload; + frame_options->on_complete = s_secure_tunneling_websocket_on_send_data_complete_callback; frame_options->opcode = AWS_WEBSOCKET_OPCODE_BINARY; frame_options->fin = true; } @@ -1053,6 +1043,24 @@ static int s_init_data_tunnel_pair( return AWS_OP_ERR; } +static int s_init_data_tunnel_pair_from_message( + struct aws_secure_tunnel *secure_tunnel, + struct data_tunnel_pair *pair, + struct aws_secure_tunnel_message_view *message_view) { + pair->secure_tunnel = secure_tunnel; + pair->length_prefix_written = false; + if(aws_iot_st_msg_serialize_from_view(&pair->buf, secure_tunnel->allocator, message_view){ + AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Failure serializing message"); + goto cleanup; + } + pair->cur = aws_byte_cursor_from_buf(&pair->buf); + return AWS_OP_SUCCESS; + cleanup: + aws_byte_buf_clean_up(&pair->buf); + aws_mem_release(secure_tunnel->allocator, (void *)pair); + return AWS_OP_ERR; +} + int secure_tunneling_init_send_frame( struct aws_websocket_send_frame_options *frame_options, struct aws_secure_tunnel *secure_tunnel, @@ -1070,29 +1078,32 @@ int secure_tunneling_init_send_frame( static int s_secure_tunneling_send( struct aws_secure_tunnel *secure_tunnel, - const struct aws_byte_cursor *payload_data, - const struct aws_byte_cursor *service_id_data, - enum aws_secure_tunnel_message_type type) { + const struct aws_secure_tunnel_message_view *message_view) { + (void)secure_tunnel; + (void)message_view; + // struct aws_secure_tunnel *secure_tunnel, + // const struct aws_byte_cursor *payload_data, + // const struct aws_byte_cursor *service_id_data, + // enum aws_secure_tunnel_message_type type) { + + // struct aws_websocket_send_frame_options frame_options; + // if (secure_tunneling_init_send_frame(&frame_options, secure_tunnel, payload_data, service_id_data, type) != + // AWS_OP_SUCCESS) { + // return AWS_OP_ERR; + // } + // return aws_websocket_send_frame(secure_tunnel->websocket, &frame_options); - struct aws_websocket_send_frame_options frame_options; - if (secure_tunneling_init_send_frame(&frame_options, secure_tunnel, payload_data, service_id_data, type) != - AWS_OP_SUCCESS) { - return AWS_OP_ERR; - } - return aws_websocket_send_frame(secure_tunnel->websocket, &frame_options); + return AWS_OP_SUCCESS; } static struct aws_secure_tunnel_vtable s_default_secure_tunnel_vtable = { .get_current_time_fn = s_aws_high_res_clock_get_ticks_proxy, - .http_proxy_new_socket_channel_fn = aws_http_proxy_new_socket_channel, - - .vtable_user_data = NULL, /* STEVE TODO remove these after changing the API to make these operations/tasks */ - .send_data = s_secure_tunneling_send_data, - .send_stream_start = s_secure_tunneling_send_stream_start, - .send_stream_start_v2 = s_secure_tunneling_send_stream_start_v2, - .send_stream_reset = s_secure_tunneling_send_stream_reset, + // .send_data = s_secure_tunneling_send_data, + // .send_stream_start = s_secure_tunneling_send_stream_start, + // .send_stream_start_v2 = s_secure_tunneling_send_stream_start_v2, + // .send_stream_reset = s_secure_tunneling_send_stream_reset, }; /********************************************************************************************************************* @@ -1225,15 +1236,6 @@ int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure next_operation = AWS_CONTAINER_OF(next_operation_node, struct aws_secure_tunnel_operation, node); - /* If a data message attempts to be sent on an unopen stream, discard it. */ - if (next_operation->operation_type == AWS_STOT_DATA) { - if ((*next_operation->vtable->aws_secure_tunnel_operation_set_stream_id_fn)( - next_operation, secure_tunnel)) { - int last_error_code = aws_last_error(); - s_complete_operation(secure_tunnel, next_operation, last_error_code, NULL); - } - } - secure_tunnel->current_operation = next_operation; break; } @@ -1244,41 +1246,63 @@ int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure break; } - /* write current operation to message, handle errors */ - /* STEVE TODO, encoding of message happens here */ - bool encoding_result = true; // This needs to be a result from the encoding of message - - /* - * if encoding finished: - * push to write completion - * clear current - * else (message full) - * break - */ - if (encoding_result) { - - } else { - // AWS_FATAL_ASSERT(encoding_result == AWS_SECURE_TUNNEL_ER_OUT_OF_ROOM); - break; - } + switch (current_operation->operation_type) { + case AWS_STOT_PING: + printf("\nOperation PING being executed\n"); + /* + * 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. + */ + if (secure_tunnel->websocket != NULL) { + struct aws_websocket_send_frame_options frame_options; + AWS_ZERO_STRUCT(frame_options); + frame_options.opcode = AWS_WEBSOCKET_OPCODE_PING; + frame_options.fin = true; + aws_websocket_send_frame(secure_tunnel->websocket, &frame_options); + } + s_complete_operation(secure_tunnel, current_operation, AWS_OP_SUCCESS, NULL); + secure_tunnel->current_operation = NULL; + break; + case AWS_STOT_DATA:; + printf("\nOperation DATA being executed\n"); + int error_code = AWS_OP_SUCCESS; - if (current_operation->operation_type == AWS_STOT_PING) { - /* - * 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. - */ - if (secure_tunnel->websocket != NULL) { - struct aws_websocket_send_frame_options frame_options; - AWS_ZERO_STRUCT(frame_options); - frame_options.opcode = AWS_WEBSOCKET_OPCODE_PING; - frame_options.fin = true; - aws_websocket_send_frame(secure_tunnel->websocket, &frame_options); - } + /* If a data message attempts to be sent on an unopen stream, discard it. */ + if ((*current_operation->vtable->aws_secure_tunnel_operation_set_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 message with error %d(%s)", + (void *)secure_tunnel, + error_code, + aws_error_debug_str(error_code)); + } else { - s_complete_operation(secure_tunnel, current_operation, AWS_OP_SUCCESS, NULL); - secure_tunnel->current_operation = NULL; + struct aws_secure_tunnel_message_view const *message_view = current_operation->message_view; + + /* Send the Data through the WebSocket */ + if (s_secure_tunneling_send(secure_tunnel, message_view)) { + error_code = aws_last_error(); + } + + aws_secure_tunnel_message_view_log(message_view, AWS_LL_DEBUG); + } + + s_complete_operation(secure_tunnel, current_operation, error_code, NULL); + secure_tunnel->current_operation = NULL; + + break; + case AWS_STOT_STREAM_RESET: + break; + case AWS_STOT_STREAM_START: + break; + case AWS_STOT_NONE: + break; } now = (*vtable->get_current_time_fn)(); @@ -1289,24 +1313,6 @@ int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure return aws_raise_error(operational_error_code); } - /* It's possible for there to be no data if we serviced operations that failed validation */ - // if (io_message->message_data.len == 0) { - // aws_mem_release(io_message->allocator, io_message); - // return AWS_OP_SUCCESS; - // } - - /* send io_message down channel in write direction, handle errors */ - // io_message->on_completion = s_aws_secure_tunnel_on_socket_write_completion; - // io_message->user_data = secure_tunnel; - // secure_tunnel->pending_write_completion = true; - - // if ((*vtable->aws_channel_slot_send_message_fn)( - // slot, io_message, AWS_CHANNEL_DIR_WRITE, vtable->vtable_user_data)) { - // secure_tunnel->pending_write_completion = false; - // aws_mem_release(io_message->allocator, io_message); - // return AWS_OP_ERR; - // } - return AWS_OP_SUCCESS; } @@ -1372,15 +1378,11 @@ static void s_secure_tunnel_submit_operation_task_fn(struct aws_task *task, void * If we're offline fail it immediately. */ struct aws_secure_tunnel *secure_tunnel = submit_operation_task->secure_tunnel; - struct aws_secure_tunnel_operation *operation = submit_operation_task->operation; if (secure_tunnel->current_state != AWS_STS_CONNECTED) { completion_error_code = AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_OPERATION_FAILED_DUE_TO_DISCONNECTION; goto error; } - /* Set the stream ID of message */ - aws_secure_tunnel_operation_set_stream_id(operation, secure_tunnel); - s_enqueue_operation_back(submit_operation_task->secure_tunnel, submit_operation_task->operation); goto done; @@ -1592,6 +1594,7 @@ static bool s_service_state_stopped(struct aws_secure_tunnel *secure_tunnel) { static void s_service_state_connecting(struct aws_secure_tunnel *secure_tunnel, uint64_t now) { (void)secure_tunnel; + (void)now; if (aws_secure_tunnel_service_operational_state(secure_tunnel)) { int error_code = aws_last_error(); @@ -1827,6 +1830,7 @@ int aws_secure_tunnel_stop(struct aws_secure_tunnel *secure_tunnel) { int aws_secure_tunnel_send_message( struct aws_secure_tunnel *secure_tunnel, const struct aws_secure_tunnel_message_view *message_options) { + printf("\naws_secure_tunnel_send_message called\n"); AWS_PRECONDITION(secure_tunnel != NULL); AWS_PRECONDITION(message_options != NULL); @@ -1842,7 +1846,6 @@ int aws_secure_tunnel_send_message( "id=%p: Submitting MESSAGE operation (%p)", (void *)secure_tunnel, (void *)message_op); - aws_secure_tunnel_message_view_log(message_op->base.message_view, AWS_LL_DEBUG); if (s_submit_operation(secure_tunnel, &message_op->base)) { goto error; @@ -1852,7 +1855,6 @@ int aws_secure_tunnel_send_message( error: aws_secure_tunnel_operation_release(&message_op->base); - return AWS_OP_ERR; } @@ -1864,12 +1866,6 @@ int aws_secure_tunnel_stream_start(struct aws_secure_tunnel *secure_tunnel) { return secure_tunnel->vtable->send_stream_start(secure_tunnel); } -int aws_secure_tunnel_stream_start_v2( - struct aws_secure_tunnel *secure_tunnel, - const struct aws_byte_cursor *service_id_data) { - return secure_tunnel->vtable->send_stream_start_v2(secure_tunnel, service_id_data); -} - /* Steve Todo a V2/V3 version is required to reset on a failed stream start attempt */ int aws_secure_tunnel_stream_reset(struct aws_secure_tunnel *secure_tunnel) { return secure_tunnel->vtable->send_stream_reset(secure_tunnel); diff --git a/source/secure_tunneling_operations.c b/source/secure_tunneling_operations.c index afe0db94..3e4aab22 100644 --- a/source/secure_tunneling_operations.c +++ b/source/secure_tunneling_operations.c @@ -84,11 +84,6 @@ static struct aws_secure_tunnel_operation_vtable s_empty_operation_vtable = { .aws_secure_tunnel_operation_get_stream_id_address_fn = NULL, }; -/********************************************************************************************************************* - * Connect - ********************************************************************************************************************/ -/* STEVE TODO Connect Operation Implementation */ - /********************************************************************************************************************* * Message ********************************************************************************************************************/ @@ -210,11 +205,11 @@ static int s_aws_secure_tunnel_operation_message_set_stream_id( struct aws_secure_tunnel_operation_message *message_op = operation->impl; int32_t stream_id = INVALID_STREAM_ID; - struct aws_secure_tunnel_message_storage *message_storage = &message_op->options_storage; + struct aws_secure_tunnel_message_view *message_view = &message_op->options_storage.storage_view; - if (message_storage->service_id.len > 0) { + if (message_view->service_id.len > 0) { struct aws_string *service_id = NULL; - service_id = aws_string_new_from_cursor(secure_tunnel->allocator, &message_storage->service_id); + service_id = aws_string_new_from_cursor(secure_tunnel->allocator, &message_view->service_id); if (secure_tunnel->config->service_id_1 != NULL && aws_string_compare(secure_tunnel->config->service_id_1, service_id) == 0) { @@ -229,12 +224,11 @@ static int s_aws_secure_tunnel_operation_message_set_stream_id( stream_id = secure_tunnel->config->service_id_3_stream_id; } else { /* service_id doesn't match any existing service id*/ - AWS_LOGF_ERROR( + AWS_LOGF_DEBUG( AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: aws_message_storage - invalid service_id:%s", - (void *)message_storage, + "id=%p: invalid service_id:%s attempted to be used with an outbound message", + (void *)message_view, aws_string_c_str(service_id)); - /* STEVE TODO should we throw something or just log the error here? */ stream_id = INVALID_STREAM_ID; } aws_string_destroy(service_id); @@ -294,6 +288,7 @@ struct aws_secure_tunnel_operation_message *aws_secure_tunnel_operation_message_ message_op->allocator = allocator; message_op->base.vtable = &s_message_operation_vtable; + message_op->base.operation_type = AWS_STOT_DATA; aws_ref_count_init(&message_op->base.ref_count, message_op, s_destroy_operation_message); message_op->base.impl = message_op; @@ -326,6 +321,8 @@ static void s_destroy_operation_pingreq(void *object) { } struct aws_secure_tunnel_operation_pingreq *aws_secure_tunnel_operation_pingreq_new(struct aws_allocator *allocator) { + AWS_PRECONDITION(allocator != NULL); + struct aws_secure_tunnel_operation_pingreq *pingreq_op = aws_mem_calloc(allocator, 1, sizeof(struct aws_secure_tunnel_operation_pingreq)); if (pingreq_op == NULL) { diff --git a/source/serializer.c b/source/serializer.c index 991a11a3..b996409a 100644 --- a/source/serializer.c +++ b/source/serializer.c @@ -79,12 +79,12 @@ static int s_iot_st_encode_varint( static int s_iot_st_encode_lengthdelim( const uint8_t field_number, const uint8_t wire_type, - struct aws_byte_buf *payload, + struct aws_byte_cursor *payload, struct aws_byte_buf *buffer) { const uint8_t field_and_wire_type = (field_number << AWS_IOT_ST_FIELD_NUMBER_SHIFT) + wire_type; aws_byte_buf_append_byte_dynamic_secure(buffer, field_and_wire_type); s_iot_st_encode_varint_uint32_t(buffer, (uint32_t)payload->len); - struct aws_byte_cursor temp = aws_byte_cursor_from_array(payload->buffer, payload->len); + struct aws_byte_cursor temp = aws_byte_cursor_from_array(payload->ptr, payload->len); return aws_byte_buf_append_dynamic_secure(buffer, &temp); } @@ -100,12 +100,12 @@ static int s_iot_st_encode_type(int32_t data, struct aws_byte_buf *buffer) { return s_iot_st_encode_varint(AWS_SECURE_TUNNEL_FN_TYPE, AWS_SECURE_TUNNEL_PBWT_VARINT, data, buffer); } -static int s_iot_st_encode_payload(struct aws_byte_buf *payload, struct aws_byte_buf *buffer) { +static int s_iot_st_encode_payload(struct aws_byte_cursor *payload, struct aws_byte_buf *buffer) { return s_iot_st_encode_lengthdelim( AWS_SECURE_TUNNEL_FN_PAYLOAD, AWS_SECURE_TUNNEL_PBWT_LENGTH_DELIMINTED, payload, buffer); } -static int s_iot_st_encode_service_id(struct aws_byte_buf *service_id, struct aws_byte_buf *buffer) { +static int s_iot_st_encode_service_id(struct aws_byte_cursor *service_id, struct aws_byte_buf *buffer) { return s_iot_st_encode_lengthdelim( AWS_SECURE_TUNNEL_FN_SERVICE_ID, AWS_SECURE_TUNNEL_PBWT_LENGTH_DELIMINTED, service_id, buffer); } @@ -132,7 +132,90 @@ static int s_iot_st_get_varint_size(size_t value, size_t *encode_size) { return AWS_OP_SUCCESS; } -static int s_iot_st_compute_message_length(const struct aws_iot_st_msg *message, size_t *message_length) { +// static int s_iot_st_compute_message_length(const struct aws_iot_st_msg *message, size_t *message_length) { +// fprintf(stdout, "\ns_iot_st_compute_message_length()\ntype: %d\n", message->type); +// size_t local_length = 0; + +// /* +// * 1 byte type key +// * 1 byte type varint +// */ +// local_length += 2; + +// if (message->stream_id != 0) { +// /* +// * 1 byte steram_id key +// * 1-4 byte stream_id varint +// */ +// size_t stream_id_length = 0; + +// if (s_iot_st_get_varint_size((uint32_t)message->stream_id, &stream_id_length)) { +// return AWS_OP_ERR; +// } + +// local_length += (1 + stream_id_length); +// fprintf(stdout, "adding stream_id:%d total:%zu\n", message->stream_id, local_length); +// } + +// if (message->ignorable != 0) { +// /* +// * 1 byte ignorable key +// * 1 byte ignorable varint +// */ +// local_length += 2; +// fprintf(stdout, "adding ignorable total:%zu\n", local_length); +// } + +// if (message->payload.len != 0) { +// /* +// * 1 byte key +// * 1-4 byte payload length varint +// * n bytes payload.len +// */ +// size_t payload_length = 0; +// if (s_iot_st_get_varint_size((uint32_t)message->payload.len, &payload_length)) { +// return AWS_OP_ERR; +// } +// local_length += (1 + message->payload.len + payload_length); +// fprintf(stdout, "adding message total:%zu\n", local_length); +// } + +// if (message->service_id.len != 0) { +// /* +// * 1 byte key +// * 1-4 byte payload length varint +// * n bytes service_id.len +// */ +// size_t service_id_length = 0; +// if (s_iot_st_get_varint_size((uint32_t)message->service_id.len, &service_id_length)) { +// return AWS_OP_ERR; +// } +// local_length += (1 + message->service_id.len + service_id_length); +// fprintf(stdout, "adding service_id total:%zu\n", local_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((uint32_t)message->connection_id, &connection_id_length)) { +// return AWS_OP_ERR; +// } + +// local_length += (1 + connection_id_length); +// fprintf(stdout, "adding connection_id total:%zu\n", local_length); +// } + +// *message_length = local_length; +// return AWS_OP_SUCCESS; +// } + +static int s_iot_st_compute_message_length( + const struct aws_secure_tunnel_message_view *message, + size_t *message_length) { fprintf(stdout, "\ns_iot_st_compute_message_length()\ntype: %d\n", message->type); size_t local_length = 0; @@ -177,7 +260,7 @@ static int s_iot_st_compute_message_length(const struct aws_iot_st_msg *message, return AWS_OP_ERR; } local_length += (1 + message->payload.len + payload_length); - fprintf(stdout, "adding message total:%zu\n", local_length); + fprintf(stdout, "adding payload total:%zu\n", local_length); } if (message->service_id.len != 0) { @@ -194,30 +277,70 @@ static int s_iot_st_compute_message_length(const struct aws_iot_st_msg *message, fprintf(stdout, "adding service_id total:%zu\n", local_length); } - if (message->connection_id != 0) { - /* - * 1 byte connection_id key - * 1-4 byte connection_id varint - */ - size_t connection_id_length = 0; + *message_length = local_length; + return AWS_OP_SUCCESS; +} - if (s_iot_st_get_varint_size((uint32_t)message->connection_id, &connection_id_length)) { - return AWS_OP_ERR; +int aws_iot_st_msg_serialize_from_view( + struct aws_byte_buf *buffer, + struct aws_allocator *allocator, + const struct aws_secure_tunnel_message_view *message_view) { + size_t message_total_length = 0; + if (s_iot_st_compute_message_length(message_view, &message_total_length)) { + return AWS_OP_ERR; + } + + if (aws_byte_buf_init(buffer, allocator, message_total_length) != AWS_OP_SUCCESS) { + return AWS_OP_ERR; + } + + if (message_view->type != AWS_SECURE_TUNNEL_MT_UNKNOWN) { + if (s_iot_st_encode_type(message_view->type, buffer)) { + goto cleanup; + } + fprintf(stdout, "message type encoded. buf length: %zu\n", buffer->len); + } + + if (message_view->stream_id != 0) { + if (s_iot_st_encode_stream_id(message_view->stream_id, buffer)) { + goto cleanup; + } + fprintf(stdout, "stream id encoded. buf length: %zu\n", buffer->len); + } + + if (message_view->ignorable != 0) { + if (s_iot_st_encode_ignorable(message_view->ignorable, buffer)) { + goto cleanup; } + fprintf(stdout, "ignorable encoded. buf length: %zu\n", buffer->len); + } - local_length += (1 + connection_id_length); - fprintf(stdout, "adding connection_id total:%zu\n", local_length); + if (message_view->payload.len != 0) { + if (s_iot_st_encode_payload(&message_view->payload, buffer)) { + goto cleanup; + } + fprintf(stdout, "payload encoded. buf length: %zu\n", buffer->len); } - *message_length = local_length; + if (message_view->service_id.len != 0) { + if (s_iot_st_encode_service_id(&message_view->service_id, buffer)) { + goto cleanup; + } + fprintf(stdout, "service id encoded. buf length: %zu\n", buffer->len); + } + + AWS_RETURN_ERROR_IF2(buffer->capacity < AWS_IOT_ST_MAX_MESSAGE_SIZE, AWS_ERROR_INVALID_BUFFER_SIZE); return AWS_OP_SUCCESS; + +cleanup: + aws_byte_buf_clean_up(buffer); + return AWS_OP_ERR; } int aws_iot_st_msg_serialize_from_struct( struct aws_byte_buf *buffer, struct aws_allocator *allocator, struct aws_iot_st_msg message) { - fprintf(stdout, "\naws_iot_st_msg_serialize_from_struct()\n"); size_t message_total_length = 0; if (s_iot_st_compute_message_length(&message, &message_total_length)) { diff --git a/tests/secure_tunneling_tests.c b/tests/secure_tunneling_tests.c index fbb0c5a3..89283099 100644 --- a/tests/secure_tunneling_tests.c +++ b/tests/secure_tunneling_tests.c @@ -333,7 +333,7 @@ static int s_test_sent_data( // aws_byte_buf_init( // &out_buf, test_context->secure_tunnel->options->allocator, (size_t)frame_options.payload_length)); - ASSERT_TRUE(secure_tunneling_send_data_call(NULL, &out_buf, frame_options.user_data)); + // ASSERT_TRUE(secure_tunneling_send_data_call(NULL, &out_buf, frame_options.user_data)); struct aws_byte_cursor out_buf_cur = aws_byte_cursor_from_buf(&out_buf); ASSERT_UINT_EQUALS(out_buf_cur.len - prefix_bytes, serialized_st_msg.len); From 16572d1d2ba07a21fe376399dba054c406b2dbed Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Thu, 26 Jan 2023 11:31:51 -0800 Subject: [PATCH 16/69] encoding of outbound messages --- .../iotdevice/private/iotdevice_internals.h | 6 +- .../private/secure_tunneling_operations.h | 1 - include/aws/iotdevice/private/serializer.h | 19 --- source/secure_tunneling.c | 116 ++++--------- source/secure_tunneling_operations.c | 2 - source/serializer.c | 161 +----------------- tests/secure_tunneling_tests.c | 142 +++++++-------- 7 files changed, 112 insertions(+), 335 deletions(-) diff --git a/include/aws/iotdevice/private/iotdevice_internals.h b/include/aws/iotdevice/private/iotdevice_internals.h index 37f0a583..effe0a29 100644 --- a/include/aws/iotdevice/private/iotdevice_internals.h +++ b/include/aws/iotdevice/private/iotdevice_internals.h @@ -18,11 +18,9 @@ AWS_EXTERN_C_BEGIN AWS_IOTDEVICE_API int secure_tunneling_init_send_frame( - struct aws_websocket_send_frame_options *frame_options, struct aws_secure_tunnel *secure_tunnel, - const struct aws_byte_cursor *payload_data, - const struct aws_byte_cursor *service_id_data, - enum aws_secure_tunnel_message_type type); + struct aws_websocket_send_frame_options *frame_options, + const struct aws_secure_tunnel_message_view *message_view); AWS_IOTDEVICE_API void init_websocket_client_connection_options( diff --git a/include/aws/iotdevice/private/secure_tunneling_operations.h b/include/aws/iotdevice/private/secure_tunneling_operations.h index b54b1627..71cd371d 100644 --- a/include/aws/iotdevice/private/secure_tunneling_operations.h +++ b/include/aws/iotdevice/private/secure_tunneling_operations.h @@ -20,7 +20,6 @@ struct aws_secure_tunnel_operation; enum aws_secure_tunnel_operation_type { AWS_STOT_NONE, - AWS_STOT_CONNECT, AWS_STOT_PING, AWS_STOT_DATA, AWS_STOT_STREAM_RESET, diff --git a/include/aws/iotdevice/private/serializer.h b/include/aws/iotdevice/private/serializer.h index 5e418a0a..c96ca02b 100644 --- a/include/aws/iotdevice/private/serializer.h +++ b/include/aws/iotdevice/private/serializer.h @@ -36,31 +36,12 @@ enum aws_secure_tunnel_protocol_buffer_wire_type { AWS_SECURE_TUNNEL_PBWT_32_BIT = 5, /* fixed32, sfixed32, float */ }; -/** - * A single IoT Secure Tunnel Message - * STEVE TODO remove this. replaced with aws_secure_tunnel_message_view - */ -struct aws_iot_st_msg { - enum aws_secure_tunnel_message_type type; - int32_t stream_id; - int ignorable; - struct aws_byte_buf payload; - struct aws_byte_buf service_id; - uint32_t connection_id; -}; - typedef void(aws_secure_tunnel_on_message_received_fn)( struct aws_secure_tunnel *secure_tunnel, struct aws_secure_tunnel_message_view *message_view); AWS_EXTERN_C_BEGIN -AWS_IOTDEVICE_API -int aws_iot_st_msg_serialize_from_struct( - struct aws_byte_buf *buffer, - struct aws_allocator *allocator, - struct aws_iot_st_msg message); - AWS_IOTDEVICE_API int aws_iot_st_msg_serialize_from_view( struct aws_byte_buf *buffer, diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index fc6aa6a4..22eca472 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -249,7 +249,6 @@ static void s_aws_secure_tunnel_connected_on_message_received( case AWS_SECURE_TUNNEL_MT_SESSION_RESET: s_aws_secure_tunnel_on_session_reset_received(secure_tunnel); break; - /* Steve todo implement processing of new message types */ case AWS_SECURE_TUNNEL_MT_SERVICE_IDS: s_aws_secure_tunnel_on_service_ids_received(secure_tunnel); break; @@ -670,6 +669,7 @@ static void s_change_current_state_to_connected(struct aws_secure_tunnel *secure AWS_FATAL_ASSERT(secure_tunnel->current_state == AWS_STS_CONNECTING); secure_tunnel->current_state = AWS_STS_CONNECTED; + secure_tunnel->pending_write_completion = false; secure_tunnel->reconnect_count = 0; /* @@ -950,6 +950,7 @@ static void s_secure_tunneling_websocket_on_send_data_complete_callback( } aws_byte_buf_clean_up(&pair->buf); aws_mem_release(secure_tunnel->allocator, pair); + secure_tunnel->pending_write_completion = false; } bool secure_tunneling_websocket_stream_outgoing_payload( @@ -960,7 +961,6 @@ bool secure_tunneling_websocket_stream_outgoing_payload( struct data_tunnel_pair *pair = user_data; size_t space_available = out_buf->capacity - out_buf->len; - /* STEVE TODO need to check space available against service id as well */ if ((pair->length_prefix_written == false) && (space_available >= PAYLOAD_BYTE_LENGTH_PREFIX)) { if (aws_byte_buf_write_be16(out_buf, (int16_t)pair->buf.len) == false) { AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Failure writing buffer length prefix to out_buf"); @@ -986,124 +986,72 @@ bool secure_tunneling_websocket_stream_outgoing_payload( return true; } -static void s_init_websocket_send_frame_options( - struct aws_websocket_send_frame_options *frame_options, - struct data_tunnel_pair *pair) { - - AWS_ZERO_STRUCT(*frame_options); - frame_options->payload_length = pair->buf.len + PAYLOAD_BYTE_LENGTH_PREFIX; - frame_options->user_data = pair; - frame_options->stream_outgoing_payload = secure_tunneling_websocket_stream_outgoing_payload; - frame_options->on_complete = s_secure_tunneling_websocket_on_send_data_complete_callback; - frame_options->opcode = AWS_WEBSOCKET_OPCODE_BINARY; - frame_options->fin = true; -} - -static int s_init_data_tunnel_pair( - struct data_tunnel_pair *pair, +static int s_init_data_tunnel_pair_from_message( struct aws_secure_tunnel *secure_tunnel, - const struct aws_byte_cursor *payload_data, - const struct aws_byte_cursor *service_id_data, - enum aws_secure_tunnel_message_type type) { - struct aws_iot_st_msg message; - message.stream_id = secure_tunnel->config->stream_id; - message.ignorable = 0; - message.type = type; - if (payload_data != NULL) { - message.payload.buffer = payload_data->ptr; - message.payload.len = payload_data->len; - } else { - message.payload.buffer = NULL; - message.payload.len = 0; - } - if (service_id_data != NULL) { - message.service_id.buffer = service_id_data->ptr; - message.service_id.len = service_id_data->len; - } else { - message.service_id.buffer = NULL; - message.service_id.len = 0; - } - message.connection_id = 0; + struct data_tunnel_pair *pair, + const struct aws_secure_tunnel_message_view *message_view) { pair->secure_tunnel = secure_tunnel; pair->length_prefix_written = false; - if (aws_iot_st_msg_serialize_from_struct(&pair->buf, secure_tunnel->allocator, message) != AWS_OP_SUCCESS) { + if (aws_iot_st_msg_serialize_from_view(&pair->buf, secure_tunnel->allocator, message_view)) { AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Failure serializing message"); goto cleanup; } - fprintf(stdout, "\npair->buf.len: %zu\n", pair->buf.len); + printf("\npair->buf.len %zu\n", pair->buf.len); 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 cleanup; } pair->cur = aws_byte_cursor_from_buf(&pair->buf); return AWS_OP_SUCCESS; + cleanup: aws_byte_buf_clean_up(&pair->buf); - aws_mem_release(pair->secure_tunnel->allocator, (void *)pair); + aws_mem_release(secure_tunnel->allocator, (void *)pair); return AWS_OP_ERR; } -static int s_init_data_tunnel_pair_from_message( - struct aws_secure_tunnel *secure_tunnel, +static void s_init_websocket_frame_options( struct data_tunnel_pair *pair, - struct aws_secure_tunnel_message_view *message_view) { - pair->secure_tunnel = secure_tunnel; - pair->length_prefix_written = false; - if(aws_iot_st_msg_serialize_from_view(&pair->buf, secure_tunnel->allocator, message_view){ - AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Failure serializing message"); - goto cleanup; - } - pair->cur = aws_byte_cursor_from_buf(&pair->buf); - return AWS_OP_SUCCESS; - cleanup: - aws_byte_buf_clean_up(&pair->buf); - aws_mem_release(secure_tunnel->allocator, (void *)pair); - return AWS_OP_ERR; + struct aws_websocket_send_frame_options *frame_options) { + AWS_ZERO_STRUCT(*frame_options); + frame_options->payload_length = pair->buf.len + PAYLOAD_BYTE_LENGTH_PREFIX; + frame_options->user_data = pair; + frame_options->stream_outgoing_payload = secure_tunneling_websocket_stream_outgoing_payload; + frame_options->on_complete = s_secure_tunneling_websocket_on_send_data_complete_callback; + frame_options->opcode = AWS_WEBSOCKET_OPCODE_BINARY; + frame_options->fin = true; } int secure_tunneling_init_send_frame( - struct aws_websocket_send_frame_options *frame_options, struct aws_secure_tunnel *secure_tunnel, - const struct aws_byte_cursor *payload_data, - const struct aws_byte_cursor *service_id_data, - enum aws_secure_tunnel_message_type type) { + struct aws_websocket_send_frame_options *frame_options, + const struct aws_secure_tunnel_message_view *message_view) { + struct data_tunnel_pair *pair = (struct data_tunnel_pair *)aws_mem_acquire(secure_tunnel->allocator, sizeof(struct data_tunnel_pair)); - if (s_init_data_tunnel_pair(pair, secure_tunnel, payload_data, service_id_data, type) != AWS_OP_SUCCESS) { + if (s_init_data_tunnel_pair_from_message(secure_tunnel, pair, message_view)) { return AWS_OP_ERR; } - s_init_websocket_send_frame_options(frame_options, pair); + + s_init_websocket_frame_options(pair, frame_options); return AWS_OP_SUCCESS; } static int s_secure_tunneling_send( struct aws_secure_tunnel *secure_tunnel, const struct aws_secure_tunnel_message_view *message_view) { - (void)secure_tunnel; - (void)message_view; - // struct aws_secure_tunnel *secure_tunnel, - // const struct aws_byte_cursor *payload_data, - // const struct aws_byte_cursor *service_id_data, - // enum aws_secure_tunnel_message_type type) { - - // struct aws_websocket_send_frame_options frame_options; - // if (secure_tunneling_init_send_frame(&frame_options, secure_tunnel, payload_data, service_id_data, type) != - // AWS_OP_SUCCESS) { - // return AWS_OP_ERR; - // } - // return aws_websocket_send_frame(secure_tunnel->websocket, &frame_options); + struct aws_websocket_send_frame_options frame_options; + if (secure_tunneling_init_send_frame(secure_tunnel, &frame_options, message_view)) { + return AWS_OP_ERR; + } - return AWS_OP_SUCCESS; + /* Prevent further operations that attempt to write to the WebSocket until current operation is completed */ + secure_tunnel->pending_write_completion = true; + return aws_websocket_send_frame(secure_tunnel->websocket, &frame_options); } static struct aws_secure_tunnel_vtable s_default_secure_tunnel_vtable = { .get_current_time_fn = s_aws_high_res_clock_get_ticks_proxy, - - /* STEVE TODO remove these after changing the API to make these operations/tasks */ - // .send_data = s_secure_tunneling_send_data, - // .send_stream_start = s_secure_tunneling_send_stream_start, - // .send_stream_start_v2 = s_secure_tunneling_send_stream_start_v2, - // .send_stream_reset = s_secure_tunneling_send_stream_reset, }; /********************************************************************************************************************* @@ -1169,7 +1117,7 @@ static uint64_t s_aws_secure_tunnel_compute_operational_state_service_time( struct aws_secure_tunnel *secure_tunnel, uint64_t now) { - /* If a message is in transit down the websocket, then wait for it to complete */ + /* If a message is in transit down the WebSocket, then wait for it to complete */ if (secure_tunnel->pending_write_completion) { return 0; } diff --git a/source/secure_tunneling_operations.c b/source/secure_tunneling_operations.c index 3e4aab22..45312f99 100644 --- a/source/secure_tunneling_operations.c +++ b/source/secure_tunneling_operations.c @@ -616,8 +616,6 @@ const char *aws_secure_tunnel_operation_type_to_c_string(enum aws_secure_tunnel_ switch (operation_type) { case AWS_STOT_NONE: return "NONE"; - case AWS_STOT_CONNECT: - return "CONNECT"; case AWS_STOT_PING: return "PING"; case AWS_STOT_DATA: diff --git a/source/serializer.c b/source/serializer.c index b996409a..abb3f628 100644 --- a/source/serializer.c +++ b/source/serializer.c @@ -79,7 +79,7 @@ static int s_iot_st_encode_varint( static int s_iot_st_encode_lengthdelim( const uint8_t field_number, const uint8_t wire_type, - struct aws_byte_cursor *payload, + const struct aws_byte_cursor *payload, struct aws_byte_buf *buffer) { const uint8_t field_and_wire_type = (field_number << AWS_IOT_ST_FIELD_NUMBER_SHIFT) + wire_type; aws_byte_buf_append_byte_dynamic_secure(buffer, field_and_wire_type); @@ -100,20 +100,16 @@ static int s_iot_st_encode_type(int32_t data, struct aws_byte_buf *buffer) { return s_iot_st_encode_varint(AWS_SECURE_TUNNEL_FN_TYPE, AWS_SECURE_TUNNEL_PBWT_VARINT, data, buffer); } -static int s_iot_st_encode_payload(struct aws_byte_cursor *payload, struct aws_byte_buf *buffer) { +static int s_iot_st_encode_payload(const struct aws_byte_cursor *payload, struct aws_byte_buf *buffer) { return s_iot_st_encode_lengthdelim( AWS_SECURE_TUNNEL_FN_PAYLOAD, AWS_SECURE_TUNNEL_PBWT_LENGTH_DELIMINTED, payload, buffer); } -static int s_iot_st_encode_service_id(struct aws_byte_cursor *service_id, struct aws_byte_buf *buffer) { +static int s_iot_st_encode_service_id(const struct aws_byte_cursor *service_id, struct aws_byte_buf *buffer) { return s_iot_st_encode_lengthdelim( AWS_SECURE_TUNNEL_FN_SERVICE_ID, AWS_SECURE_TUNNEL_PBWT_LENGTH_DELIMINTED, service_id, 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_get_varint_size(size_t value, size_t *encode_size) { if (value > AWS_IOT_ST_MAXIMUM_VARINT) { return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); @@ -132,87 +128,6 @@ static int s_iot_st_get_varint_size(size_t value, size_t *encode_size) { return AWS_OP_SUCCESS; } -// static int s_iot_st_compute_message_length(const struct aws_iot_st_msg *message, size_t *message_length) { -// fprintf(stdout, "\ns_iot_st_compute_message_length()\ntype: %d\n", message->type); -// size_t local_length = 0; - -// /* -// * 1 byte type key -// * 1 byte type varint -// */ -// local_length += 2; - -// if (message->stream_id != 0) { -// /* -// * 1 byte steram_id key -// * 1-4 byte stream_id varint -// */ -// size_t stream_id_length = 0; - -// if (s_iot_st_get_varint_size((uint32_t)message->stream_id, &stream_id_length)) { -// return AWS_OP_ERR; -// } - -// local_length += (1 + stream_id_length); -// fprintf(stdout, "adding stream_id:%d total:%zu\n", message->stream_id, local_length); -// } - -// if (message->ignorable != 0) { -// /* -// * 1 byte ignorable key -// * 1 byte ignorable varint -// */ -// local_length += 2; -// fprintf(stdout, "adding ignorable total:%zu\n", local_length); -// } - -// if (message->payload.len != 0) { -// /* -// * 1 byte key -// * 1-4 byte payload length varint -// * n bytes payload.len -// */ -// size_t payload_length = 0; -// if (s_iot_st_get_varint_size((uint32_t)message->payload.len, &payload_length)) { -// return AWS_OP_ERR; -// } -// local_length += (1 + message->payload.len + payload_length); -// fprintf(stdout, "adding message total:%zu\n", local_length); -// } - -// if (message->service_id.len != 0) { -// /* -// * 1 byte key -// * 1-4 byte payload length varint -// * n bytes service_id.len -// */ -// size_t service_id_length = 0; -// if (s_iot_st_get_varint_size((uint32_t)message->service_id.len, &service_id_length)) { -// return AWS_OP_ERR; -// } -// local_length += (1 + message->service_id.len + service_id_length); -// fprintf(stdout, "adding service_id total:%zu\n", local_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((uint32_t)message->connection_id, &connection_id_length)) { -// return AWS_OP_ERR; -// } - -// local_length += (1 + connection_id_length); -// fprintf(stdout, "adding connection_id total:%zu\n", local_length); -// } - -// *message_length = local_length; -// return AWS_OP_SUCCESS; -// } - static int s_iot_st_compute_message_length( const struct aws_secure_tunnel_message_view *message, size_t *message_length) { @@ -227,7 +142,7 @@ static int s_iot_st_compute_message_length( if (message->stream_id != 0) { /* - * 1 byte steram_id key + * 1 byte stream_id key * 1-4 byte stream_id varint */ size_t stream_id_length = 0; @@ -299,6 +214,9 @@ int aws_iot_st_msg_serialize_from_view( goto cleanup; } fprintf(stdout, "message type encoded. buf length: %zu\n", buffer->len); + } else { + AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Message missing type during encoding"); + goto cleanup; } if (message_view->stream_id != 0) { @@ -337,71 +255,6 @@ int aws_iot_st_msg_serialize_from_view( return AWS_OP_ERR; } -int aws_iot_st_msg_serialize_from_struct( - struct aws_byte_buf *buffer, - struct aws_allocator *allocator, - struct aws_iot_st_msg message) { - - size_t message_total_length = 0; - if (s_iot_st_compute_message_length(&message, &message_total_length)) { - return AWS_OP_ERR; - } - - if (aws_byte_buf_init(buffer, allocator, message_total_length) != AWS_OP_SUCCESS) { - return AWS_OP_ERR; - } - - if (message.type != AWS_SECURE_TUNNEL_MT_UNKNOWN) { - if (s_iot_st_encode_type(message.type, buffer)) { - goto cleanup; - } - } - - fprintf(stdout, "message type encoded. buf length: %zu\n", buffer->len); - - if (message.stream_id != 0) { - if (s_iot_st_encode_stream_id(message.stream_id, buffer)) { - goto cleanup; - } - } - fprintf(stdout, "stream id encoded. buf length: %zu\n", buffer->len); - - if (message.ignorable != 0) { - if (s_iot_st_encode_ignorable(message.ignorable, buffer)) { - goto cleanup; - } - } - fprintf(stdout, "ignorable encoded. buf length: %zu\n", buffer->len); - - if (message.payload.len != 0) { - if (s_iot_st_encode_payload(&message.payload, buffer)) { - goto cleanup; - } - } - fprintf(stdout, "payload encoded. buf length: %zu\n", buffer->len); - - if (message.service_id.len != 0) { - if (s_iot_st_encode_service_id(&message.service_id, buffer)) { - goto cleanup; - } - } - fprintf(stdout, "service id encoded. buf length: %zu\n", buffer->len); - - if (message.connection_id != 0) { - if (s_iot_st_encode_connection_id(message.connection_id, buffer)) { - goto cleanup; - } - } - fprintf(stdout, "connection id encoded. buf length: %zu\n", buffer->len); - - AWS_RETURN_ERROR_IF2(buffer->capacity < AWS_IOT_ST_MAX_MESSAGE_SIZE, AWS_ERROR_INVALID_BUFFER_SIZE); - return AWS_OP_SUCCESS; - -cleanup: - aws_byte_buf_clean_up(buffer); - return AWS_OP_ERR; -} - /***************************************************************************************************************** * DECODING *****************************************************************************************************************/ diff --git a/tests/secure_tunneling_tests.c b/tests/secure_tunneling_tests.c index 89283099..6f42dc91 100644 --- a/tests/secure_tunneling_tests.c +++ b/tests/secure_tunneling_tests.c @@ -129,11 +129,11 @@ int s_mock_aws_websocket_send_frame( // struct aws_byte_cursor cursor = aws_byte_cursor_from_buf(buf); /* Deserialize the wire format to obtain original payload. */ - struct aws_iot_st_msg message; + // struct aws_iot_st_msg message; // int rc = aws_iot_st_msg_deserialize_from_cursor(&message, &cursor, s_test_context.secure_tunnel->allocator); // ASSERT_INT_EQUALS(AWS_OP_SUCCESS, rc); - s_mock_aws_websocket_send_frame_payload_len += message.payload.len; - aws_byte_buf_clean_up(&message.payload); + // s_mock_aws_websocket_send_frame_payload_len += message.payload.len; + // aws_byte_buf_clean_up(&message.payload); /* Deallocate memory for the buffer holding the wire protocol data and the tunnel context. */ // aws_byte_buf_clean_up(buf); @@ -253,27 +253,27 @@ static int after(struct aws_allocator *allocator, int setup_result, void *ctx) { return AWS_OP_SUCCESS; } -static void s_send_secure_tunneling_frame_to_websocket( - const struct aws_iot_st_msg *st_msg, - struct aws_allocator *allocator, - struct aws_secure_tunnel *secure_tunnel) { +// static void s_send_secure_tunneling_frame_to_websocket( +// // const struct aws_iot_st_msg *st_msg, +// struct aws_allocator *allocator, +// struct aws_secure_tunnel *secure_tunnel) { - struct aws_byte_buf serialized_st_msg; - aws_iot_st_msg_serialize_from_struct(&serialized_st_msg, allocator, *st_msg); +// struct aws_byte_buf serialized_st_msg; +// // aws_iot_st_msg_serialize_from_struct(&serialized_st_msg, allocator, *st_msg); - /* Prepend 2 bytes length */ - struct aws_byte_buf websocket_frame; - aws_byte_buf_init(&websocket_frame, allocator, serialized_st_msg.len + 2); - aws_byte_buf_write_be16(&websocket_frame, (uint16_t)serialized_st_msg.len); - struct aws_byte_cursor c = aws_byte_cursor_from_buf(&serialized_st_msg); - aws_byte_buf_append(&websocket_frame, &c); - c = aws_byte_cursor_from_buf(&websocket_frame); +// /* Prepend 2 bytes length */ +// struct aws_byte_buf websocket_frame; +// aws_byte_buf_init(&websocket_frame, allocator, serialized_st_msg.len + 2); +// aws_byte_buf_write_be16(&websocket_frame, (uint16_t)serialized_st_msg.len); +// struct aws_byte_cursor c = aws_byte_cursor_from_buf(&serialized_st_msg); +// aws_byte_buf_append(&websocket_frame, &c); +// c = aws_byte_cursor_from_buf(&websocket_frame); - // on_websocket_incoming_frame_payload(NULL, NULL, c, secure_tunnel); +// // on_websocket_incoming_frame_payload(NULL, NULL, c, secure_tunnel); - aws_byte_buf_clean_up(&serialized_st_msg); - aws_byte_buf_clean_up(&websocket_frame); -} +// aws_byte_buf_clean_up(&serialized_st_msg); +// aws_byte_buf_clean_up(&websocket_frame); +// } static int s_test_sent_data( struct secure_tunneling_test_context *test_context, @@ -309,11 +309,11 @@ static int s_test_sent_data( * */ - struct aws_iot_st_msg message; - message.type = type; - message.stream_id = expected_stream_id; - message.ignorable = 0; - message.payload = aws_byte_buf_from_c_str(expected_payload); + // struct aws_iot_st_msg message; + // message.type = type; + // message.stream_id = expected_stream_id; + // message.ignorable = 0; + // message.payload = aws_byte_buf_from_c_str(expected_payload); struct aws_byte_buf serialized_st_msg; // aws_iot_st_msg_serialize_from_struct(&serialized_st_msg, test_context->secure_tunnel->options->allocator, // message); @@ -375,12 +375,12 @@ static int s_secure_tunneling_handle_stream_start_test(struct aws_allocator *all struct secure_tunneling_test_context *test_context = ctx; // test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_DESTINATION_MODE; - struct aws_iot_st_msg st_msg; - AWS_ZERO_STRUCT(st_msg); - st_msg.type = AWS_SECURE_TUNNEL_MT_STREAM_START; - st_msg.stream_id = STREAM_ID; + // struct aws_iot_st_msg st_msg; + // AWS_ZERO_STRUCT(st_msg); + // st_msg.type = AWS_SECURE_TUNNEL_MT_STREAM_START; + // st_msg.stream_id = STREAM_ID; s_on_stream_start_called = false; - s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); + // s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); ASSERT_TRUE(s_on_stream_start_called); // ASSERT_INT_EQUALS(STREAM_ID, test_context->secure_tunnel->config->stream_id); @@ -405,19 +405,19 @@ static int s_secure_tunneling_handle_data_receive_test(struct aws_allocator *all // test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_DESTINATION_MODE; /* Send StreamStart first */ - struct aws_iot_st_msg st_msg; - AWS_ZERO_STRUCT(st_msg); - st_msg.type = AWS_SECURE_TUNNEL_MT_STREAM_START; - st_msg.stream_id = STREAM_ID; - s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); + // struct aws_iot_st_msg st_msg; + // AWS_ZERO_STRUCT(st_msg); + // st_msg.type = AWS_SECURE_TUNNEL_MT_STREAM_START; + // st_msg.stream_id = STREAM_ID; + // s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); /* Send data */ - AWS_ZERO_STRUCT(st_msg); - st_msg.type = AWS_SECURE_TUNNEL_MT_DATA; - st_msg.stream_id = STREAM_ID; - st_msg.payload = aws_byte_buf_from_c_str(PAYLOAD); + // AWS_ZERO_STRUCT(st_msg); + // st_msg.type = AWS_SECURE_TUNNEL_MT_DATA; + // st_msg.stream_id = STREAM_ID; + // st_msg.payload = aws_byte_buf_from_c_str(PAYLOAD); s_on_data_receive_correct_payload = false; - s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); + // s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); ASSERT_TRUE(s_on_data_receive_correct_payload); // ASSERT_INT_EQUALS(STREAM_ID, test_context->secure_tunnel->config->stream_id); @@ -442,18 +442,18 @@ static int s_secure_tunneling_handle_stream_reset_test(struct aws_allocator *all // test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_DESTINATION_MODE; /* Send StreamStart first */ - struct aws_iot_st_msg st_msg; - AWS_ZERO_STRUCT(st_msg); - st_msg.type = AWS_SECURE_TUNNEL_MT_STREAM_START; - st_msg.stream_id = STREAM_ID; - s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); - - /* Send StreamReset */ - AWS_ZERO_STRUCT(st_msg); - st_msg.type = AWS_SECURE_TUNNEL_MT_STREAM_START; - st_msg.stream_id = STREAM_ID; - s_on_stream_reset_called = false; - s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); + // struct aws_iot_st_msg st_msg; + // AWS_ZERO_STRUCT(st_msg); + // st_msg.type = AWS_SECURE_TUNNEL_MT_STREAM_START; + // st_msg.stream_id = STREAM_ID; + // s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); + + // /* Send StreamReset */ + // AWS_ZERO_STRUCT(st_msg); + // st_msg.type = AWS_SECURE_TUNNEL_MT_STREAM_START; + // st_msg.stream_id = STREAM_ID; + // s_on_stream_reset_called = false; + // s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); ASSERT_TRUE(s_on_stream_reset_called); // ASSERT_INT_EQUALS(INVALID_STREAM_ID, test_context->secure_tunnel->config->stream_id); @@ -477,19 +477,19 @@ static int s_secure_tunneling_handle_session_reset_test(struct aws_allocator *al struct secure_tunneling_test_context *test_context = ctx; // test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_DESTINATION_MODE; - /* Send StreamStart first */ - struct aws_iot_st_msg st_msg; - AWS_ZERO_STRUCT(st_msg); - st_msg.type = AWS_SECURE_TUNNEL_MT_STREAM_START; - st_msg.stream_id = STREAM_ID; - s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); - - /* Send StreamReset */ - AWS_ZERO_STRUCT(st_msg); - st_msg.type = AWS_SECURE_TUNNEL_MT_SESSION_RESET; - st_msg.stream_id = STREAM_ID; - s_on_session_reset_called = false; - s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); + // /* Send StreamStart first */ + // struct aws_iot_st_msg st_msg; + // AWS_ZERO_STRUCT(st_msg); + // st_msg.type = AWS_SECURE_TUNNEL_MT_STREAM_START; + // st_msg.stream_id = STREAM_ID; + // s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); + + // /* Send StreamReset */ + // AWS_ZERO_STRUCT(st_msg); + // st_msg.type = AWS_SECURE_TUNNEL_MT_SESSION_RESET; + // st_msg.stream_id = STREAM_ID; + // s_on_session_reset_called = false; + // s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); ASSERT_TRUE(s_on_session_reset_called); // ASSERT_INT_EQUALS(INVALID_STREAM_ID, test_context->secure_tunnel->config->stream_id); @@ -513,12 +513,12 @@ static int s_secure_tunneling_handle_session_reset_no_stream_test(struct aws_all struct secure_tunneling_test_context *test_context = ctx; // test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_DESTINATION_MODE; - /* Send StreamReset without existing stream */ - struct aws_iot_st_msg st_msg; - AWS_ZERO_STRUCT(st_msg); - st_msg.type = AWS_SECURE_TUNNEL_MT_SESSION_RESET; - s_on_session_reset_called = false; - s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); + // /* Send StreamReset without existing stream */ + // struct aws_iot_st_msg st_msg; + // AWS_ZERO_STRUCT(st_msg); + // st_msg.type = AWS_SECURE_TUNNEL_MT_SESSION_RESET; + // s_on_session_reset_called = false; + // s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); ASSERT_FALSE(s_on_session_reset_called); // ASSERT_INT_EQUALS(INVALID_STREAM_ID, test_context->secure_tunnel->config->stream_id); From 7ee40aeb9a85adb370a06b079a4cbd7cfefcbe2b Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Thu, 26 Jan 2023 13:37:16 -0800 Subject: [PATCH 17/69] cleaning up --- .../iotdevice/private/iotdevice_internals.h | 35 ------- .../iotdevice/private/secure_tunneling_impl.h | 93 +++++-------------- source/secure_tunneling.c | 25 ++--- tests/aws_iot_secure_tunneling_client_test.c | 6 +- tests/secure_tunneling_tests.c | 5 +- 5 files changed, 39 insertions(+), 125 deletions(-) delete mode 100644 include/aws/iotdevice/private/iotdevice_internals.h diff --git a/include/aws/iotdevice/private/iotdevice_internals.h b/include/aws/iotdevice/private/iotdevice_internals.h deleted file mode 100644 index effe0a29..00000000 --- a/include/aws/iotdevice/private/iotdevice_internals.h +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ -#ifndef AWS_IOTDEVICE_IOTDEVICE_INTERNALS_H -#define AWS_IOTDEVICE_IOTDEVICE_INTERNALS_H - -#include - -struct aws_byte_buf; -struct aws_byte_cursor; -struct aws_secure_tunnel; -struct aws_websocket; -struct aws_websocket_client_connection_options; -struct aws_websocket_send_frame_options; - -AWS_EXTERN_C_BEGIN - -AWS_IOTDEVICE_API -int secure_tunneling_init_send_frame( - struct aws_secure_tunnel *secure_tunnel, - struct aws_websocket_send_frame_options *frame_options, - const struct aws_secure_tunnel_message_view *message_view); - -AWS_IOTDEVICE_API -void init_websocket_client_connection_options( - struct aws_secure_tunnel *secure_tunnel, - struct aws_websocket_client_connection_options *websocket_options); - -AWS_IOTDEVICE_API -bool secure_tunneling_send_data_call(struct aws_websocket *websocket, struct aws_byte_buf *out_buf, void *user_data); - -AWS_EXTERN_C_END - -#endif /* AWS_IOTDEVICE_IOTDEVICE_INTERNALS_H */ diff --git a/include/aws/iotdevice/private/secure_tunneling_impl.h b/include/aws/iotdevice/private/secure_tunneling_impl.h index 03867dbd..f5c58d40 100644 --- a/include/aws/iotdevice/private/secure_tunneling_impl.h +++ b/include/aws/iotdevice/private/secure_tunneling_impl.h @@ -37,14 +37,14 @@ enum aws_secure_tunnel_state { AWS_STS_STOPPED, /* - * The secure tunnel is attempting to connect to a remote endpoint, and is waiting for channel setup to complete. - * This state is not interruptible by any means other than channel setup completion. + * The secure tunnel is attempting to connect to a remote endpoint and establish a WebSocket upgrade. This state is + * not interruptible by any means other than WebSocket setup completion. * * Next States: * CONNECTED - if WebSocket handshake is successful and desired state is still CONNECTED - * WEBSOCKET_SHUTDOWN - if the websocket completes setup with no error but desired state is not CONNECTED - * PENDING_RECONNECT - if the websocket fails to complete setup and desired state is still CONNECTED - * STOPPED - if the websocket fails to complete setup and desired state is not CONNECTED + * WEBSOCKET_SHUTDOWN - if the WebSocket completes setup with no error but desired state is not CONNECTED + * PENDING_RECONNECT - if the WebSocket fails to complete setup and desired state is still CONNECTED + * STOPPED - if the WebSocket fails to complete setup and desired state is not CONNECTED */ AWS_STS_CONNECTING, @@ -53,28 +53,28 @@ enum aws_secure_tunnel_state { * * Next States: * WEBSOCKET_SHUTDOWN - desired state is no longer CONNECTED - * PENDING_RECONNECT - unexpected websocket shutdown completion and desired state still CONNECTED - * STOPPED - unexpected websocket shutdown completion and desired state no longer CONNECTED + * PENDING_RECONNECT - unexpected WebSocket shutdown completion and desired state still CONNECTED + * STOPPED - unexpected WebSocket shutdown completion and desired state no longer CONNECTED */ AWS_STS_CONNECTED, /* - * The secure tunnel is attempt to shut down a websocket connection cleanly by finishing the current operation and - * then transmitting a STREAM RESET message to all open streams. + * The secure tunnel is attempting to shut down a WebSocket connection cleanly by finishing the current operation + * and then transmitting a STREAM RESET message to all open streams. * * Next States: * WEBSOCKET_SHUTDOWN - on sucessful (or unsuccessful) disconnection - * PENDING_RECONNECT - unexpected websocket shutdown completion and desired state still CONNECTED - * STOPPED - unexpected websocket shutdown completion and desired state no longer CONNECTED + * PENDING_RECONNECT - unexpected WebSocket shutdown completion and desired state still CONNECTED + * STOPPED - unexpected WebSocket shutdown completion and desired state no longer CONNECTED */ AWS_STS_CLEAN_DISCONNECT, /* - * The secure tunnel is waiting for the websocket to completely shut down. This state is not interruptible. + * The secure tunnel is waiting for the WebSocket to completely shut down. This state is not interruptible. * * Next States: - * PENDING_RECONNECT - the websocket has shut down and desired state is still CONNECTED - * STOPPED - the websocket has shut down and desired state is not CONNECTED + * PENDING_RECONNECT - the WebSocket has shut down and desired state is still CONNECTED + * STOPPED - the WebSocket has shut down and desired state is not CONNECTED */ AWS_STS_WEBSOCKET_SHUTDOWN, @@ -119,37 +119,19 @@ struct data_tunnel_pair { bool length_prefix_written; }; -/* Secure Tunnel stream information */ -struct aws_secure_tunnel_stream { - struct aws_string *service_id; - int32_t stream_id; - /* for use with V3 */ - uint32_t connection_id; -}; - struct aws_secure_tunnel_vtable { /* aws_high_res_clock_get_ticks */ uint64_t (*get_current_time_fn)(void); - int (*send_data)(struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *data); - int (*send_data_v2)( - struct aws_secure_tunnel *secure_tunnel, - const struct aws_secure_tunnel_message_view *message_options); - int (*send_stream_start)(struct aws_secure_tunnel *secure_tunnel); - int (*send_stream_start_v2)(struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *service_id_data); - int (*send_stream_reset)(struct aws_secure_tunnel *secure_tunnel); + // int (*send_data)(struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *data); + // int (*send_data_v2)( + // struct aws_secure_tunnel *secure_tunnel, + // const struct aws_secure_tunnel_message_view *message_options); + // int (*send_stream_start)(struct aws_secure_tunnel *secure_tunnel); + // int (*send_stream_start_v2)(struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor + // *service_id_data); int (*send_stream_reset)(struct aws_secure_tunnel *secure_tunnel); }; -// struct aws_websocket_client_connection_options; -struct aws_websocket_send_frame_options; - -// struct aws_websocket_vtable { -// int (*client_connect)(const struct aws_websocket_client_connection_options *options); -// int (*send_frame)(struct aws_websocket *websocket, const struct aws_websocket_send_frame_options *options); -// void (*close)(struct aws_websocket *websocket, bool free_scarce_resources_immediately); -// void (*release)(struct aws_websocket *websocket); -// }; - struct aws_secure_tunnel { /* Static settings */ struct aws_allocator *allocator; @@ -199,20 +181,6 @@ struct aws_secure_tunnel { */ enum aws_secure_tunnel_state current_state; - // struct aws_websocket_vtable websocket_vtable; - - /* Used to check connection state */ - bool isConnected; - - /* - * Temporary state-related data. - * - * clean_disconnect_error_code - the CLEAN_DISCONNECT state takes time to complete and we want to be able - * to pass an error code from a prior event to the channel shutdown. This holds the "override" error code - * that we'd like to shutdown the channel with while CLEAN_DISCONNECT is processed. - */ - int clean_disconnect_error_code; - /* * handshake_request exists between the transform completion timepoint and the websocket setup callback. */ @@ -225,20 +193,11 @@ struct aws_secure_tunnel { /* Stores what has been received but not processed */ struct aws_byte_buf received_data; - /* Error code of last connection attempt */ - int connection_error_code; - /* * When should the secure tunnel next attempt to reconnect? Only used by PENDING_RECONNECT state. */ uint64_t next_reconnect_time_ns; - /* - * When should the secure tunnel reset current_reconnect_delay_interval_ms to the minimum value? Only relevant to - * the CONNECTED state. - */ - uint64_t next_reconnect_delay_reset_time_ns; - /* * How many consecutive reconnect failures have we experienced? */ @@ -248,7 +207,7 @@ struct aws_secure_tunnel { struct aws_secure_tunnel_operation *current_operation; /* - * Is there an io message in transit (to the socket) that has not invoked its write completion callback yet? + * Is there a WebSocket message in transit (to the socket) that has not invoked its write completion callback yet? * The secure tunnel implementation only allows one in-transit message at a time, and so if this is true, we don't * send additional ones/ */ @@ -256,14 +215,10 @@ struct aws_secure_tunnel { /* * When should the next PINGREQ be sent? + * The secure tunneling endpoint ELB drops idle connect after 1 minute. we need to send a ping periodically to keep + * the connection alive. */ uint64_t next_ping_time; - - /* Steve todo remove this and use next_ping_time above with tasks */ - /* The secure tunneling endpoint ELB drops idle connect after 1 minute. We need to send a ping periodically to keep - * the connection */ - - // struct ping_task_context *ping_task_context; }; #endif /* AWS_IOTDEVICE_SECURE_TUNNELING_IMPL_H */ diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 22eca472..87dd8839 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -648,7 +647,6 @@ static void s_change_current_state_to_connecting(struct aws_secure_tunnel *secur secure_tunnel->current_state == AWS_STS_STOPPED || secure_tunnel->current_state == AWS_STS_PENDING_RECONNECT); secure_tunnel->current_state = AWS_STS_CONNECTING; - secure_tunnel->clean_disconnect_error_code = AWS_ERROR_SUCCESS; int result = s_websocket_connect(secure_tunnel); @@ -1419,9 +1417,6 @@ static uint64_t s_compute_next_service_time_secure_tunnel_connected( next_service_time = s_min_non_0_64(operation_processing_time, next_service_time); - /* reset reconnect delay interval */ - next_service_time = s_min_non_0_64(secure_tunnel->next_reconnect_delay_reset_time_ns, next_service_time); - return next_service_time; } @@ -1806,15 +1801,15 @@ int aws_secure_tunnel_send_message( return AWS_OP_ERR; } -int aws_secure_tunnel_send_data(struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *data) { - return secure_tunnel->vtable->send_data(secure_tunnel, data); -} +// int aws_secure_tunnel_send_data(struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *data) { +// return secure_tunnel->vtable->send_data(secure_tunnel, data); +// } -int aws_secure_tunnel_stream_start(struct aws_secure_tunnel *secure_tunnel) { - return secure_tunnel->vtable->send_stream_start(secure_tunnel); -} +// int aws_secure_tunnel_stream_start(struct aws_secure_tunnel *secure_tunnel) { +// return secure_tunnel->vtable->send_stream_start(secure_tunnel); +// } -/* Steve Todo a V2/V3 version is required to reset on a failed stream start attempt */ -int aws_secure_tunnel_stream_reset(struct aws_secure_tunnel *secure_tunnel) { - return secure_tunnel->vtable->send_stream_reset(secure_tunnel); -} +// /* Steve Todo a V2/V3 version is required to reset on a failed stream start attempt */ +// int aws_secure_tunnel_stream_reset(struct aws_secure_tunnel *secure_tunnel) { +// return secure_tunnel->vtable->send_stream_reset(secure_tunnel); +// } diff --git a/tests/aws_iot_secure_tunneling_client_test.c b/tests/aws_iot_secure_tunneling_client_test.c index 9f98809e..32723301 100644 --- a/tests/aws_iot_secure_tunneling_client_test.c +++ b/tests/aws_iot_secure_tunneling_client_test.c @@ -165,14 +165,14 @@ int main(int argc, char **argv) { aws_mutex_unlock(&mutex); if (local_proxy_mode == AWS_SECURE_TUNNELING_SOURCE_MODE) { - AWS_RETURN_ERROR_IF2(aws_secure_tunnel_stream_start(secure_tunnel) == AWS_OP_SUCCESS, AWS_OP_ERR); + // AWS_RETURN_ERROR_IF2(aws_secure_tunnel_stream_start(secure_tunnel) == AWS_OP_SUCCESS, AWS_OP_ERR); int cLen = 500000; char *payload = malloc(cLen + 1); memset(payload, 'a', cLen); payload[cLen] = 0; - struct aws_byte_cursor cur = aws_byte_cursor_from_c_str(payload); - AWS_RETURN_ERROR_IF2(aws_secure_tunnel_send_data(secure_tunnel, &cur) == AWS_OP_SUCCESS, AWS_OP_ERR); + // struct aws_byte_cursor cur = aws_byte_cursor_from_c_str(payload); + // AWS_RETURN_ERROR_IF2(aws_secure_tunnel_send_data(secure_tunnel, &cur) == AWS_OP_SUCCESS, AWS_OP_ERR); // AWS_RETURN_ERROR_IF2(aws_secure_tunnel_stream_reset(secure_tunnel) == AWS_OP_SUCCESS, AWS_OP_ERR); ASSERT_SUCCESS(aws_condition_variable_wait(&condition_variable, &mutex)); diff --git a/tests/secure_tunneling_tests.c b/tests/secure_tunneling_tests.c index 6f42dc91..62e7abdd 100644 --- a/tests/secure_tunneling_tests.c +++ b/tests/secure_tunneling_tests.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -691,7 +690,7 @@ static int s_secure_tunneling_handle_send_data_public(struct aws_allocator *allo /* Start a stream. */ s_mock_aws_websocket_send_frame_call_count = 0U; s_mock_aws_websocket_send_frame_payload_len = 0U; - rc = aws_secure_tunnel_stream_start(test_context->secure_tunnel); + // rc = aws_secure_tunnel_stream_start(test_context->secure_tunnel); ASSERT_INT_EQUALS(AWS_OP_SUCCESS, rc); ASSERT_UINT_EQUALS(1U, s_mock_aws_websocket_send_frame_call_count); ASSERT_UINT_EQUALS(0U, s_mock_aws_websocket_send_frame_payload_len); @@ -706,7 +705,7 @@ static int s_secure_tunneling_handle_send_data_public(struct aws_allocator *allo /* Call public api to send data over secure tunnel. */ s_mock_aws_websocket_send_frame_call_count = 0U; s_mock_aws_websocket_send_frame_payload_len = 0U; - rc = aws_secure_tunnel_send_data(test_context->secure_tunnel, &cur); + // rc = aws_secure_tunnel_send_data(test_context->secure_tunnel, &cur); ASSERT_INT_EQUALS(AWS_OP_SUCCESS, rc); int expected_call_count = (int)buf_sizes[i] / AWS_IOT_ST_SPLIT_MESSAGE_SIZE + 1; ASSERT_UINT_EQUALS(expected_call_count, s_mock_aws_websocket_send_frame_call_count); From ca98b92907d984ed3f317916e1d45ee85813684e Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Fri, 27 Jan 2023 11:02:38 -0800 Subject: [PATCH 18/69] clean up --- .../iotdevice/private/secure_tunneling_impl.h | 26 -- .../private/secure_tunneling_operations.h | 39 +- include/aws/iotdevice/secure_tunneling.h | 28 +- source/secure_tunneling.c | 378 ++++++++++-------- source/secure_tunneling_operations.c | 109 +++-- source/serializer.c | 10 - tests/aws_iot_secure_tunneling_client_test.c | 46 +-- tests/secure_tunneling_tests.c | 2 +- 8 files changed, 328 insertions(+), 310 deletions(-) diff --git a/include/aws/iotdevice/private/secure_tunneling_impl.h b/include/aws/iotdevice/private/secure_tunneling_impl.h index f5c58d40..8cbdfc23 100644 --- a/include/aws/iotdevice/private/secure_tunneling_impl.h +++ b/include/aws/iotdevice/private/secure_tunneling_impl.h @@ -94,24 +94,6 @@ enum aws_secure_tunnel_state { AWS_STS_TERMINATED }; -/** - * Signature of the continuation function to be called after user-code transforms a websocket handshake request - */ -typedef void(aws_secure_tunnel_transform_websocket_handshake_complete_fn)( - struct aws_http_message *request, - int error_code, - void *complete_ctx); - -/** - * Signature of the websocket handshake request transformation function. After transformation, the completion - * function must be invoked to send the request. - */ -typedef void(aws_secure_tunnel_transform_websocket_handshake_fn)( - struct aws_http_message *request, - void *user_data, - aws_secure_tunnel_transform_websocket_handshake_complete_fn *complete_fn, - void *complete_ctx); - struct data_tunnel_pair { struct aws_byte_buf buf; struct aws_byte_cursor cur; @@ -122,14 +104,6 @@ struct data_tunnel_pair { struct aws_secure_tunnel_vtable { /* aws_high_res_clock_get_ticks */ uint64_t (*get_current_time_fn)(void); - - // int (*send_data)(struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *data); - // int (*send_data_v2)( - // struct aws_secure_tunnel *secure_tunnel, - // const struct aws_secure_tunnel_message_view *message_options); - // int (*send_stream_start)(struct aws_secure_tunnel *secure_tunnel); - // int (*send_stream_start_v2)(struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor - // *service_id_data); int (*send_stream_reset)(struct aws_secure_tunnel *secure_tunnel); }; struct aws_secure_tunnel { diff --git a/include/aws/iotdevice/private/secure_tunneling_operations.h b/include/aws/iotdevice/private/secure_tunneling_operations.h index 71cd371d..0983d0cf 100644 --- a/include/aws/iotdevice/private/secure_tunneling_operations.h +++ b/include/aws/iotdevice/private/secure_tunneling_operations.h @@ -50,9 +50,10 @@ struct aws_secure_tunnel_operation_vtable { struct aws_secure_tunnel_operation *operation, struct aws_secure_tunnel *secure_tunnel); - /* Get the stream id from an address */ - int32_t *(*aws_secure_tunnel_operation_get_stream_id_address_fn)( - const struct aws_secure_tunnel_operation *operation); + /* Set the stream id of outgoing st_msg 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); }; /** @@ -115,19 +116,17 @@ struct aws_secure_tunnel_options_storage { /* Callbacks */ aws_secure_tunnel_message_received_fn *on_message_received; - - void *user_data; - - /* STEVE TODO we can depricate/remove these. Client only supports destination mode */ - enum aws_secure_tunneling_local_proxy_mode local_proxy_mode; 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_data_receive_fn *on_data_receive; 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; + + aws_secure_tunneling_on_send_data_complete_fn *on_send_data_complete; aws_secure_tunneling_on_termination_complete_fn *on_termination_complete; + + void *user_data; + enum aws_secure_tunneling_local_proxy_mode local_proxy_mode; }; AWS_EXTERN_C_BEGIN @@ -169,7 +168,8 @@ AWS_IOTDEVICE_API int aws_secure_tunnel_message_storage_init( struct aws_secure_tunnel_message_storage *message_storage, struct aws_allocator *allocator, - const struct aws_secure_tunnel_message_view *message_options); + const struct aws_secure_tunnel_message_view *message_options, + enum aws_secure_tunnel_operation_type type); AWS_IOTDEVICE_API void aws_secure_tunnel_message_storage_clean_up(struct aws_secure_tunnel_message_storage *message_storage); @@ -178,7 +178,8 @@ AWS_IOTDEVICE_API struct aws_secure_tunnel_operation_message *aws_secure_tunnel_operation_message_new( struct aws_allocator *allocator, const struct aws_secure_tunnel *secure_tunnel, - const struct aws_secure_tunnel_message_view *message_options); + const struct aws_secure_tunnel_message_view *message_options, + enum aws_secure_tunnel_operation_type type); /* Ping */ @@ -193,11 +194,6 @@ struct aws_secure_tunnel_operation_pingreq *aws_secure_tunnel_operation_pingreq_ AWS_IOTDEVICE_API int aws_secure_tunnel_options_validate(const struct aws_secure_tunnel_options *options); -AWS_IOTDEVICE_API -void aws_secure_tunnel_options_storage_log( - const struct aws_secure_tunnel_options_storage *options_storage, - enum aws_log_level level); - /** * Destroy options storage, and release any references held. */ @@ -220,15 +216,6 @@ void aws_secure_tunnel_options_storage_log( AWS_IOTDEVICE_API const char *aws_secure_tunnel_operation_type_to_c_string(enum aws_secure_tunnel_operation_type operation_type); -/* Stream */ - -/* STEVE TODO add stream to API */ -// AWS_IOTDEVICE_API -// struct aws_secure_tunnel_operation_data *aws_secure_tunnel_operation_stream_new( -// struct aws_allocator *allocator, -// const struct aws_secure_tunnel *secure_tunnel, -// const struct aws_secure_tunnel_message_stream_view *stream_options); - AWS_EXTERN_C_END #endif /* AWS_IOTDEVICE_SECURE_TUNNELING_OPERATION_H */ diff --git a/include/aws/iotdevice/secure_tunneling.h b/include/aws/iotdevice/secure_tunneling.h index b7e7c9ee..24851d0d 100644 --- a/include/aws/iotdevice/secure_tunneling.h +++ b/include/aws/iotdevice/secure_tunneling.h @@ -102,10 +102,9 @@ typedef void( aws_secure_tunnel_message_received_fn)(const struct aws_secure_tunnel_message_view *message, void *user_data); /* STEVE TODO Old callbacks can probably be removed */ -typedef void(aws_secure_tunneling_on_connection_complete_fn)(void *user_data); -typedef void(aws_secure_tunneling_on_connection_shutdown_fn)(void *user_data); +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_send_data_complete_fn)(int error_code, void *user_data); -typedef void(aws_secure_tunneling_on_data_receive_fn)(const struct aws_byte_buf *data, void *user_data); typedef void(aws_secure_tunneling_on_stream_start_fn)( const struct aws_secure_tunnel_message_view *message, int error_code, @@ -168,7 +167,6 @@ 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_data_receive_fn *on_data_receive; 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; @@ -256,31 +254,13 @@ int aws_secure_tunnel_send_message( const struct aws_secure_tunnel_message_view *message_options); //*********************************************************************************************************************** -/* STEVE TODO REMOVE OR HIDE ALL BELOW */ +/* THESE API SHOULD ONLY BE USED FROM SOURCE MODE */ //*********************************************************************************************************************** - -/* TODO STEVE depricate/replace new aws_secure_tunnel_send_message_new */ -AWS_IOTDEVICE_API -int aws_secure_tunnel_send_data(struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *data); - -/* TODO STEVE depricate/remove. Destination device does not send a stream start. Keep only for internal testing */ -AWS_IOTDEVICE_API -int aws_secure_tunnel_stream_start(struct aws_secure_tunnel *secure_tunnel); - -/* TODO STEVE see above. Remove or leave solely for testing purposes */ AWS_IOTDEVICE_API -int aws_secure_tunnel_stream_start_new( +int aws_secure_tunnel_stream_start( struct aws_secure_tunnel *secure_tunnel, const struct aws_secure_tunnel_message_view *message_options); -/* Making this exposed public to verify testing in the sdk layer */ -AWS_IOTDEVICE_API -bool on_websocket_incoming_frame_payload( - struct aws_websocket *websocket, - const struct aws_websocket_incoming_frame *frame, - struct aws_byte_cursor data, - void *user_data); - AWS_EXTERN_C_END #endif /* AWS_IOTDEVICE_SECURE_TUNNELING_H */ diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 87dd8839..0beed31b 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -19,7 +19,6 @@ #include #define MAX_WEBSOCKET_PAYLOAD 131076 -#define AWS_SECURE_TUNNEL_IO_MESSAGE_DEFAULT_LENGTH 4096 #define INVALID_STREAM_ID 0 #define PAYLOAD_BYTE_LENGTH_PREFIX 2 #define MIN_RECONNECT_DELAY_MS 1000 @@ -118,7 +117,7 @@ static void s_on_secure_tunnel_zero_ref_count(void *user_data) { } /***************************************************************************************************************** - * RECEIVED MESSAGE HANDLING + * RECEIVE MESSAGE HANDLING *****************************************************************************************************************/ /* @@ -131,13 +130,12 @@ static void s_reset_secure_tunnel(struct aws_secure_tunnel *secure_tunnel) { secure_tunnel->config->service_id_1_stream_id = INVALID_STREAM_ID; secure_tunnel->config->service_id_2_stream_id = INVALID_STREAM_ID; secure_tunnel->config->service_id_3_stream_id = INVALID_STREAM_ID; - // Steve TODO we may not want to drop data because we don't know which stream the data is for. secure_tunnel->received_data.len = 0; /* Drop any incomplete secure tunnel frame */ } static int s_aws_secure_tunnel_set_stream_id( struct aws_secure_tunnel *secure_tunnel, - struct aws_byte_cursor *service_id, + 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) { @@ -191,19 +189,25 @@ 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); - secure_tunnel->config->on_stream_start(message_view, result, secure_tunnel->config->user_data); + if (secure_tunnel->config->on_stream_start) { + secure_tunnel->config->on_stream_start(message_view, result, secure_tunnel->config->user_data); + } } 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 = s_aws_secure_tunnel_set_stream_id(secure_tunnel, &message_view->service_id, INVALID_STREAM_ID); - secure_tunnel->config->on_stream_reset(message_view, result, secure_tunnel->config->user_data); + if (secure_tunnel->config->on_stream_reset) { + secure_tunnel->config->on_stream_reset(message_view, result, secure_tunnel->config->user_data); + } } static void s_aws_secure_tunnel_on_session_reset_received(struct aws_secure_tunnel *secure_tunnel) { s_reset_secure_tunnel(secure_tunnel); - secure_tunnel->config->on_session_reset(secure_tunnel->config->user_data); + if (secure_tunnel->config->on_session_reset) { + secure_tunnel->config->on_session_reset(secure_tunnel->config->user_data); + } } static void s_aws_secure_tunnel_on_service_ids_received(struct aws_secure_tunnel *secure_tunnel) { @@ -233,11 +237,11 @@ static void s_aws_secure_tunnel_on_service_ids_received(struct aws_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) { - printf("\n\ns_aws_secure_tunnel_connected_on_message_received() called \n\n"); - printf("\nMessage Type: %s", aws_secure_tunnel_message_type_to_c_string(message_view->type)); switch (message_view->type) { case AWS_SECURE_TUNNEL_MT_DATA: - secure_tunnel->config->on_message_received(message_view, secure_tunnel); + if (secure_tunnel->config->on_message_received) { + secure_tunnel->config->on_message_received(message_view, secure_tunnel->config->user_data); + } break; case AWS_SECURE_TUNNEL_MT_STREAM_START: s_aws_secure_tunnel_on_stream_start_received(secure_tunnel, message_view); @@ -294,6 +298,121 @@ static void s_process_received_data(struct aws_secure_tunnel *secure_tunnel) { } } +/***************************************************************************************************************** + * SEND MESSAGE HANDLING + *****************************************************************************************************************/ + +static void s_secure_tunneling_websocket_on_send_data_complete_callback( + struct aws_websocket *websocket, + int error_code, + void *user_data) { + (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); + } + aws_byte_buf_clean_up(&pair->buf); + aws_mem_release(secure_tunnel->allocator, pair); + secure_tunnel->pending_write_completion = false; +} + +bool secure_tunneling_websocket_stream_outgoing_payload( + struct aws_websocket *websocket, + struct aws_byte_buf *out_buf, + void *user_data) { + (void)websocket; + struct data_tunnel_pair *pair = user_data; + size_t space_available = out_buf->capacity - out_buf->len; + + if ((pair->length_prefix_written == false) && (space_available >= PAYLOAD_BYTE_LENGTH_PREFIX)) { + if (aws_byte_buf_write_be16(out_buf, (int16_t)pair->buf.len) == false) { + AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Failure writing buffer length prefix to out_buf"); + return false; + } + pair->length_prefix_written = true; + space_available = out_buf->capacity - out_buf->len; + } + + if (pair->length_prefix_written == true) { + size_t bytes_max = pair->cur.len; + size_t amount_to_send = bytes_max < space_available ? bytes_max : space_available; + + struct aws_byte_cursor send_cursor = aws_byte_cursor_advance(&pair->cur, amount_to_send); + if (send_cursor.len) { + if (!aws_byte_buf_write_from_whole_cursor(out_buf, send_cursor)) { + AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Failure writing data to out_buf"); + return false; + } + } + } + + return true; +} + +static int s_init_data_tunnel_pair_from_message( + struct aws_secure_tunnel *secure_tunnel, + struct data_tunnel_pair *pair, + const struct aws_secure_tunnel_message_view *message_view) { + pair->secure_tunnel = secure_tunnel; + pair->length_prefix_written = false; + if (aws_iot_st_msg_serialize_from_view(&pair->buf, secure_tunnel->allocator, message_view)) { + AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Failure serializing message"); + goto cleanup; + } + 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 cleanup; + } + pair->cur = aws_byte_cursor_from_buf(&pair->buf); + return AWS_OP_SUCCESS; + +cleanup: + aws_byte_buf_clean_up(&pair->buf); + aws_mem_release(secure_tunnel->allocator, (void *)pair); + return AWS_OP_ERR; +} + +static void s_init_websocket_frame_options( + struct data_tunnel_pair *pair, + struct aws_websocket_send_frame_options *frame_options) { + AWS_ZERO_STRUCT(*frame_options); + frame_options->payload_length = pair->buf.len + PAYLOAD_BYTE_LENGTH_PREFIX; + frame_options->user_data = pair; + frame_options->stream_outgoing_payload = secure_tunneling_websocket_stream_outgoing_payload; + frame_options->on_complete = s_secure_tunneling_websocket_on_send_data_complete_callback; + frame_options->opcode = AWS_WEBSOCKET_OPCODE_BINARY; + frame_options->fin = true; +} + +int secure_tunneling_init_send_frame( + struct aws_secure_tunnel *secure_tunnel, + struct aws_websocket_send_frame_options *frame_options, + const struct aws_secure_tunnel_message_view *message_view) { + + struct data_tunnel_pair *pair = + (struct data_tunnel_pair *)aws_mem_acquire(secure_tunnel->allocator, sizeof(struct data_tunnel_pair)); + if (s_init_data_tunnel_pair_from_message(secure_tunnel, pair, message_view)) { + return AWS_OP_ERR; + } + + s_init_websocket_frame_options(pair, frame_options); + return AWS_OP_SUCCESS; +} + +static int s_secure_tunneling_send( + struct aws_secure_tunnel *secure_tunnel, + const struct aws_secure_tunnel_message_view *message_view) { + struct aws_websocket_send_frame_options frame_options; + if (secure_tunneling_init_send_frame(secure_tunnel, &frame_options, message_view)) { + return AWS_OP_ERR; + } + + /* Prevent further operations that attempt to write to the WebSocket until current operation is completed */ + secure_tunnel->pending_write_completion = true; + return aws_websocket_send_frame(secure_tunnel->websocket, &frame_options); +} + /***************************************************************************************************************** * Websocket *****************************************************************************************************************/ @@ -366,7 +485,7 @@ static void s_secure_tunnel_shutdown(struct aws_client_bootstrap *bootstrap, int } /* Normal call to shutdown the websocket */ -static void s_secure_tunnel_shutdown_websocket(struct aws_secure_tunnel *secure_tunnel) { +static void s_secure_tunnel_shutdown_websocket(struct aws_secure_tunnel *secure_tunnel, int error_code) { if (secure_tunnel->current_state != AWS_STS_CONNECTED && secure_tunnel->current_state != AWS_STS_CLEAN_DISCONNECT) { AWS_LOGF_ERROR( AWS_LS_IOTDEVICE_SECURE_TUNNELING, @@ -377,6 +496,10 @@ static void s_secure_tunnel_shutdown_websocket(struct aws_secure_tunnel *secure_ return; } + if (secure_tunnel->config->on_connection_shutdown) { + secure_tunnel->config->on_connection_shutdown(error_code, secure_tunnel->config->user_data); + } + s_change_current_state(secure_tunnel, AWS_STS_WEBSOCKET_SHUTDOWN); } @@ -430,6 +553,10 @@ static void s_on_websocket_setup(const struct aws_websocket_on_connection_setup_ secure_tunnel->websocket = setup->websocket; + if (secure_tunnel->config->on_connection_complete) { + secure_tunnel->config->on_connection_complete(setup->error_code, secure_tunnel->config->user_data); + } + /* Failed/Successful websocket creation and associated errors logged by "websocket-setup" */ s_secure_tunnel_setup(secure_tunnel->config->bootstrap, setup->error_code, secure_tunnel); @@ -936,118 +1063,6 @@ static uint64_t s_aws_high_res_clock_get_ticks_proxy(void) { // return AWS_OP_SUCCESS; // } -static void s_secure_tunneling_websocket_on_send_data_complete_callback( - struct aws_websocket *websocket, - int error_code, - void *user_data) { - (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); - } - aws_byte_buf_clean_up(&pair->buf); - aws_mem_release(secure_tunnel->allocator, pair); - secure_tunnel->pending_write_completion = false; -} - -bool secure_tunneling_websocket_stream_outgoing_payload( - struct aws_websocket *websocket, - struct aws_byte_buf *out_buf, - void *user_data) { - (void)websocket; - struct data_tunnel_pair *pair = user_data; - size_t space_available = out_buf->capacity - out_buf->len; - - if ((pair->length_prefix_written == false) && (space_available >= PAYLOAD_BYTE_LENGTH_PREFIX)) { - if (aws_byte_buf_write_be16(out_buf, (int16_t)pair->buf.len) == false) { - AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Failure writing buffer length prefix to out_buf"); - return false; - } - pair->length_prefix_written = true; - space_available = out_buf->capacity - out_buf->len; - } - - if (pair->length_prefix_written == true) { - size_t bytes_max = pair->cur.len; - size_t amount_to_send = bytes_max < space_available ? bytes_max : space_available; - - struct aws_byte_cursor send_cursor = aws_byte_cursor_advance(&pair->cur, amount_to_send); - if (send_cursor.len) { - if (!aws_byte_buf_write_from_whole_cursor(out_buf, send_cursor)) { - AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Failure writing data to out_buf"); - return false; - } - } - } - - return true; -} - -static int s_init_data_tunnel_pair_from_message( - struct aws_secure_tunnel *secure_tunnel, - struct data_tunnel_pair *pair, - const struct aws_secure_tunnel_message_view *message_view) { - pair->secure_tunnel = secure_tunnel; - pair->length_prefix_written = false; - if (aws_iot_st_msg_serialize_from_view(&pair->buf, secure_tunnel->allocator, message_view)) { - AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Failure serializing message"); - goto cleanup; - } - printf("\npair->buf.len %zu\n", pair->buf.len); - 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 cleanup; - } - pair->cur = aws_byte_cursor_from_buf(&pair->buf); - return AWS_OP_SUCCESS; - -cleanup: - aws_byte_buf_clean_up(&pair->buf); - aws_mem_release(secure_tunnel->allocator, (void *)pair); - return AWS_OP_ERR; -} - -static void s_init_websocket_frame_options( - struct data_tunnel_pair *pair, - struct aws_websocket_send_frame_options *frame_options) { - AWS_ZERO_STRUCT(*frame_options); - frame_options->payload_length = pair->buf.len + PAYLOAD_BYTE_LENGTH_PREFIX; - frame_options->user_data = pair; - frame_options->stream_outgoing_payload = secure_tunneling_websocket_stream_outgoing_payload; - frame_options->on_complete = s_secure_tunneling_websocket_on_send_data_complete_callback; - frame_options->opcode = AWS_WEBSOCKET_OPCODE_BINARY; - frame_options->fin = true; -} - -int secure_tunneling_init_send_frame( - struct aws_secure_tunnel *secure_tunnel, - struct aws_websocket_send_frame_options *frame_options, - const struct aws_secure_tunnel_message_view *message_view) { - - struct data_tunnel_pair *pair = - (struct data_tunnel_pair *)aws_mem_acquire(secure_tunnel->allocator, sizeof(struct data_tunnel_pair)); - if (s_init_data_tunnel_pair_from_message(secure_tunnel, pair, message_view)) { - return AWS_OP_ERR; - } - - s_init_websocket_frame_options(pair, frame_options); - return AWS_OP_SUCCESS; -} - -static int s_secure_tunneling_send( - struct aws_secure_tunnel *secure_tunnel, - const struct aws_secure_tunnel_message_view *message_view) { - struct aws_websocket_send_frame_options frame_options; - if (secure_tunneling_init_send_frame(secure_tunnel, &frame_options, message_view)) { - return AWS_OP_ERR; - } - - /* Prevent further operations that attempt to write to the WebSocket until current operation is completed */ - secure_tunnel->pending_write_completion = true; - return aws_websocket_send_frame(secure_tunnel->websocket, &frame_options); -} - static struct aws_secure_tunnel_vtable s_default_secure_tunnel_vtable = { .get_current_time_fn = s_aws_high_res_clock_get_ticks_proxy, }; @@ -1191,30 +1206,24 @@ int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure if (current_operation == NULL) { break; } + int error_code = AWS_OP_SUCCESS; switch (current_operation->operation_type) { - case AWS_STOT_PING: - printf("\nOperation PING being executed\n"); + case AWS_STOT_PING:; /* * 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. */ - if (secure_tunnel->websocket != NULL) { - struct aws_websocket_send_frame_options frame_options; - AWS_ZERO_STRUCT(frame_options); - frame_options.opcode = AWS_WEBSOCKET_OPCODE_PING; - frame_options.fin = true; - aws_websocket_send_frame(secure_tunnel->websocket, &frame_options); - } - s_complete_operation(secure_tunnel, current_operation, AWS_OP_SUCCESS, NULL); - secure_tunnel->current_operation = NULL; - break; - case AWS_STOT_DATA:; - printf("\nOperation DATA being executed\n"); - int error_code = AWS_OP_SUCCESS; + struct aws_websocket_send_frame_options frame_options; + AWS_ZERO_STRUCT(frame_options); + frame_options.opcode = AWS_WEBSOCKET_OPCODE_PING; + frame_options.fin = true; + aws_websocket_send_frame(secure_tunnel->websocket, &frame_options); + break; + case AWS_STOT_DATA: /* If a data message attempts to be sent on an unopen stream, discard it. */ if ((*current_operation->vtable->aws_secure_tunnel_operation_set_stream_id_fn)( current_operation, secure_tunnel)) { @@ -1223,34 +1232,48 @@ int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure AWS_LOGF_DEBUG( AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: failed to send message with error %d(%s)", + "id=%p: failed to send DATA message with error %d(%s)", (void *)secure_tunnel, error_code, aws_error_debug_str(error_code)); } else { - - struct aws_secure_tunnel_message_view const *message_view = current_operation->message_view; - - /* Send the Data through the WebSocket */ - if (s_secure_tunneling_send(secure_tunnel, 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_secure_tunnel_message_view_log(message_view, AWS_LL_DEBUG); + aws_secure_tunnel_message_view_log(current_operation->message_view, AWS_LL_DEBUG); } - s_complete_operation(secure_tunnel, current_operation, error_code, NULL); - secure_tunnel->current_operation = NULL; - break; + case AWS_STOT_STREAM_RESET: break; case AWS_STOT_STREAM_START: + if ((*current_operation->vtable->aws_secure_tunnel_operation_set_next_stream_id_fn)( + current_operation, secure_tunnel)) { + error_code = aws_last_error(); + AWS_LOGF_DEBUG( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: failed to send STREAM START message with error %d(%s)", + (void *)secure_tunnel, + error_code, + aws_error_debug_str(error_code)); + } else { + /* 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); + } break; + case AWS_STOT_NONE: break; } + s_complete_operation(secure_tunnel, current_operation, AWS_OP_SUCCESS, NULL); + secure_tunnel->current_operation = NULL; + now = (*vtable->get_current_time_fn)(); should_service = s_aws_secure_tunnel_should_service_operational_state(secure_tunnel, now); } while (should_service); @@ -1547,7 +1570,7 @@ static void s_service_state_connecting(struct aws_secure_tunnel *secure_tunnel, (void *)secure_tunnel, error_code, aws_error_debug_str(error_code)); - s_secure_tunnel_shutdown_websocket(secure_tunnel); + s_secure_tunnel_shutdown_websocket(secure_tunnel, error_code); return; } } @@ -1559,7 +1582,7 @@ static void s_service_state_connected(struct aws_secure_tunnel *secure_tunnel, u AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: channel shutdown due to user Stop request", (void *)secure_tunnel); - s_secure_tunnel_shutdown_websocket(secure_tunnel); + s_secure_tunnel_shutdown_websocket(secure_tunnel, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_USER_REQUESTED_STOP); return; } @@ -1572,7 +1595,7 @@ static void s_service_state_connected(struct aws_secure_tunnel *secure_tunnel, u (void *)secure_tunnel, error_code, aws_error_debug_str(error_code)); - s_secure_tunnel_shutdown_websocket(secure_tunnel); + s_secure_tunnel_shutdown_websocket(secure_tunnel, error_code); return; } } @@ -1585,7 +1608,7 @@ static void s_service_state_connected(struct aws_secure_tunnel *secure_tunnel, u (void *)secure_tunnel, error_code, aws_error_debug_str(error_code)); - s_secure_tunnel_shutdown_websocket(secure_tunnel); + s_secure_tunnel_shutdown_websocket(secure_tunnel, error_code); return; } } @@ -1600,7 +1623,7 @@ static void s_service_state_clean_disconnect(struct aws_secure_tunnel *secure_tu (void *)secure_tunnel, error_code, aws_error_debug_str(error_code)); - s_secure_tunnel_shutdown_websocket(secure_tunnel); + s_secure_tunnel_shutdown_websocket(secure_tunnel, error_code); return; } } @@ -1734,7 +1757,6 @@ struct aws_secure_tunnel *aws_secure_tunnel_new(const struct aws_secure_tunnel_o secure_tunnel->handshake_request = NULL; secure_tunnel->websocket = NULL; - /* TODO: Release this buffer when there is no data to hold */ aws_byte_buf_init(&secure_tunnel->received_data, options->allocator, MAX_WEBSOCKET_PAYLOAD); aws_secure_tunnel_options_storage_log(secure_tunnel->config, AWS_LL_DEBUG); @@ -1773,12 +1795,11 @@ int aws_secure_tunnel_stop(struct aws_secure_tunnel *secure_tunnel) { int aws_secure_tunnel_send_message( struct aws_secure_tunnel *secure_tunnel, const struct aws_secure_tunnel_message_view *message_options) { - printf("\naws_secure_tunnel_send_message called\n"); 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); + struct aws_secure_tunnel_operation_message *message_op = aws_secure_tunnel_operation_message_new( + secure_tunnel->allocator, secure_tunnel, message_options, AWS_STOT_DATA); if (message_op == NULL) { return AWS_OP_ERR; @@ -1801,13 +1822,42 @@ int aws_secure_tunnel_send_message( return AWS_OP_ERR; } -// int aws_secure_tunnel_send_data(struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *data) { -// return secure_tunnel->vtable->send_data(secure_tunnel, data); -// } +int aws_secure_tunnel_stream_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); -// int aws_secure_tunnel_stream_start(struct aws_secure_tunnel *secure_tunnel) { -// return secure_tunnel->vtable->send_stream_start(secure_tunnel); -// } + 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"); + 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_STREAM_START); + + if (message_op == NULL) { + return AWS_OP_ERR; + } + + AWS_LOGF_DEBUG( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Submitting MESSAGE operation (%p)", + (void *)secure_tunnel, + (void *)message_op); + + if (s_submit_operation(secure_tunnel, &message_op->base)) { + goto error; + } + + s_aws_secure_tunnel_set_stream_id(secure_tunnel, &message_options->service_id, message_options->stream_id); + + return AWS_OP_SUCCESS; + +error: + aws_secure_tunnel_operation_release(&message_op->base); + return AWS_OP_ERR; +} // /* Steve Todo a V2/V3 version is required to reset on a failed stream start attempt */ // int aws_secure_tunnel_stream_reset(struct aws_secure_tunnel *secure_tunnel) { diff --git a/source/secure_tunneling_operations.c b/source/secure_tunneling_operations.c index 45312f99..b53f895f 100644 --- a/source/secure_tunneling_operations.c +++ b/source/secure_tunneling_operations.c @@ -57,31 +57,10 @@ void aws_secure_tunnel_operation_set_stream_id( } } -int32_t aws_secure_tunnel_operation_get_stream_id(const struct aws_secure_tunnel_operation *operation) { - AWS_FATAL_ASSERT(operation->vtable != NULL); - if (operation->vtable->aws_secure_tunnel_operation_get_stream_id_address_fn != NULL) { - int32_t *stream_id_ptr = (*operation->vtable->aws_secure_tunnel_operation_get_stream_id_address_fn)(operation); - if (stream_id_ptr != NULL) { - return *stream_id_ptr; - } - } - - return 0; -} - -int32_t *aws_secure_tunnel_operation_get_stream_id_address(const struct aws_secure_tunnel_operation *operation) { - AWS_FATAL_ASSERT(operation->vtable != NULL); - if (operation->vtable->aws_secure_tunnel_operation_get_stream_id_address_fn != NULL) { - return (*operation->vtable->aws_secure_tunnel_operation_get_stream_id_address_fn)(operation); - } - - return NULL; -} - static struct aws_secure_tunnel_operation_vtable s_empty_operation_vtable = { .aws_secure_tunnel_operation_completion_fn = NULL, .aws_secure_tunnel_operation_set_stream_id_fn = NULL, - .aws_secure_tunnel_operation_get_stream_id_address_fn = NULL, + .aws_secure_tunnel_operation_set_next_stream_id_fn = NULL, }; /********************************************************************************************************************* @@ -94,10 +73,10 @@ int aws_secure_tunnel_message_view_validate(const struct aws_secure_tunnel_messa return aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_OPTIONS_VALIDATION); } - if (message_view->stream_id != 0) { + 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 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); } @@ -166,7 +145,8 @@ static size_t s_aws_secure_tunnel_message_compute_storage_size( int aws_secure_tunnel_message_storage_init( struct aws_secure_tunnel_message_storage *message_storage, struct aws_allocator *allocator, - const struct aws_secure_tunnel_message_view *message_options) { + const struct aws_secure_tunnel_message_view *message_options, + enum aws_secure_tunnel_operation_type type) { AWS_ZERO_STRUCT(*message_storage); size_t storage_capacity = s_aws_secure_tunnel_message_compute_storage_size(message_options); @@ -180,6 +160,21 @@ int aws_secure_tunnel_message_storage_init( storage_view->ignorable = message_options->ignorable; storage_view->stream_id = message_options->stream_id; + switch (type) { + case AWS_STOT_DATA: + storage_view->type = AWS_SECURE_TUNNEL_MT_DATA; + break; + case AWS_STOT_STREAM_START: + storage_view->type = AWS_SECURE_TUNNEL_MT_STREAM_START; + break; + case AWS_STOT_STREAM_RESET: + storage_view->type = AWS_SECURE_TUNNEL_MT_STREAM_RESET; + break; + default: + storage_view->type = AWS_SECURE_TUNNEL_MT_UNKNOWN; + break; + } + storage_view->service_id = message_options->service_id; if (aws_byte_buf_append_and_update(&message_storage->storage, &storage_view->service_id)) { return AWS_OP_ERR; @@ -244,16 +239,59 @@ static int s_aws_secure_tunnel_operation_message_set_stream_id( return AWS_OP_SUCCESS; } -static int32_t *s_aws_secure_tunnel_operation_message_get_stream_id_address_fn( - const struct aws_secure_tunnel_operation *operation) { +static int s_aws_secure_tunnel_operation_message_set_next_stream_id( + struct aws_secure_tunnel_operation *operation, + struct aws_secure_tunnel *secure_tunnel) { + struct aws_secure_tunnel_operation_message *message_op = operation->impl; - return &message_op->options_storage.storage_view.stream_id; + int32_t stream_id = INVALID_STREAM_ID; + + struct aws_secure_tunnel_message_view *message_view = &message_op->options_storage.storage_view; + + if (message_view->service_id.len > 0) { + struct aws_string *service_id = NULL; + service_id = aws_string_new_from_cursor(secure_tunnel->allocator, &message_view->service_id); + + if (secure_tunnel->config->service_id_1 != NULL && + aws_string_compare(secure_tunnel->config->service_id_1, service_id) == 0) { + stream_id = secure_tunnel->config->service_id_1_stream_id + 1; + secure_tunnel->config->service_id_1_stream_id = stream_id; + } else if ( + secure_tunnel->config->service_id_2 != NULL && + aws_string_compare(secure_tunnel->config->service_id_2, service_id) == 0) { + stream_id = secure_tunnel->config->service_id_2_stream_id + 1; + secure_tunnel->config->service_id_2_stream_id = stream_id; + } else if ( + secure_tunnel->config->service_id_3 != NULL && + aws_string_compare(secure_tunnel->config->service_id_3, service_id) == 0) { + stream_id = secure_tunnel->config->service_id_3_stream_id + 1; + secure_tunnel->config->service_id_3_stream_id = stream_id; + } else { + /* service_id doesn't match any existing service id*/ + AWS_LOGF_DEBUG( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: invalid service_id:%s attempted to be used with an outbound message", + (void *)message_view, + aws_string_c_str(service_id)); + stream_id = INVALID_STREAM_ID; + } + aws_string_destroy(service_id); + } else { + stream_id = secure_tunnel->config->stream_id + 1; + secure_tunnel->config->stream_id = stream_id; + } + + if (stream_id == INVALID_STREAM_ID) { + return aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_STREAM); + } + + message_op->options_storage.storage_view.stream_id = stream_id; + return AWS_OP_SUCCESS; } static struct aws_secure_tunnel_operation_vtable s_message_operation_vtable = { .aws_secure_tunnel_operation_set_stream_id_fn = s_aws_secure_tunnel_operation_message_set_stream_id, - .aws_secure_tunnel_operation_get_stream_id_address_fn = - s_aws_secure_tunnel_operation_message_get_stream_id_address_fn, + .aws_secure_tunnel_operation_set_next_stream_id_fn = s_aws_secure_tunnel_operation_message_set_next_stream_id, }; static void s_destroy_operation_message(void *object) { @@ -271,7 +309,8 @@ static void s_destroy_operation_message(void *object) { struct aws_secure_tunnel_operation_message *aws_secure_tunnel_operation_message_new( struct aws_allocator *allocator, const struct aws_secure_tunnel *secure_tunnel, - const struct aws_secure_tunnel_message_view *message_options) { + const struct aws_secure_tunnel_message_view *message_options, + enum aws_secure_tunnel_operation_type type) { (void)secure_tunnel; AWS_PRECONDITION(allocator != NULL); AWS_PRECONDITION(message_options != NULL); @@ -288,11 +327,11 @@ struct aws_secure_tunnel_operation_message *aws_secure_tunnel_operation_message_ message_op->allocator = allocator; message_op->base.vtable = &s_message_operation_vtable; - message_op->base.operation_type = AWS_STOT_DATA; + message_op->base.operation_type = type; aws_ref_count_init(&message_op->base.ref_count, message_op, s_destroy_operation_message); message_op->base.impl = message_op; - if (aws_secure_tunnel_message_storage_init(&message_op->options_storage, allocator, message_options)) { + if (aws_secure_tunnel_message_storage_init(&message_op->options_storage, allocator, message_options, type)) { goto error; } @@ -575,7 +614,6 @@ struct aws_secure_tunnel_options_storage *aws_secure_tunnel_options_storage_new( storage->client_token = aws_string_new_from_buf(allocator, &uuid_buf); } - /* STEVE TODO can be removed except for testing */ storage->local_proxy_mode = options->local_proxy_mode; /* acquire reference to everything that's ref-counted */ @@ -594,12 +632,11 @@ struct aws_secure_tunnel_options_storage *aws_secure_tunnel_options_storage_new( storage->on_message_received = options->on_message_received; storage->user_data = options->user_data; - /* STEVE TODO these can probably be deprecated/removed as client only supports destination mode */ + /* These can be deprecated/removed as secure tunnel from the iot sdk perspective only supports destination mode */ 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_data_receive = options->on_data_receive; storage->on_stream_start = options->on_stream_start; storage->on_stream_reset = options->on_stream_reset; storage->on_session_reset = options->on_session_reset; diff --git a/source/serializer.c b/source/serializer.c index abb3f628..25db741e 100644 --- a/source/serializer.c +++ b/source/serializer.c @@ -131,7 +131,6 @@ static int s_iot_st_get_varint_size(size_t value, size_t *encode_size) { static int s_iot_st_compute_message_length( const struct aws_secure_tunnel_message_view *message, size_t *message_length) { - fprintf(stdout, "\ns_iot_st_compute_message_length()\ntype: %d\n", message->type); size_t local_length = 0; /* @@ -152,7 +151,6 @@ static int s_iot_st_compute_message_length( } local_length += (1 + stream_id_length); - fprintf(stdout, "adding stream_id:%d total:%zu\n", message->stream_id, local_length); } if (message->ignorable != 0) { @@ -161,7 +159,6 @@ static int s_iot_st_compute_message_length( * 1 byte ignorable varint */ local_length += 2; - fprintf(stdout, "adding ignorable total:%zu\n", local_length); } if (message->payload.len != 0) { @@ -175,7 +172,6 @@ static int s_iot_st_compute_message_length( return AWS_OP_ERR; } local_length += (1 + message->payload.len + payload_length); - fprintf(stdout, "adding payload total:%zu\n", local_length); } if (message->service_id.len != 0) { @@ -189,7 +185,6 @@ static int s_iot_st_compute_message_length( return AWS_OP_ERR; } local_length += (1 + message->service_id.len + service_id_length); - fprintf(stdout, "adding service_id total:%zu\n", local_length); } *message_length = local_length; @@ -213,7 +208,6 @@ int aws_iot_st_msg_serialize_from_view( if (s_iot_st_encode_type(message_view->type, buffer)) { goto cleanup; } - fprintf(stdout, "message type encoded. buf length: %zu\n", buffer->len); } else { AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Message missing type during encoding"); goto cleanup; @@ -223,28 +217,24 @@ int aws_iot_st_msg_serialize_from_view( if (s_iot_st_encode_stream_id(message_view->stream_id, buffer)) { goto cleanup; } - fprintf(stdout, "stream id encoded. buf length: %zu\n", buffer->len); } if (message_view->ignorable != 0) { if (s_iot_st_encode_ignorable(message_view->ignorable, buffer)) { goto cleanup; } - fprintf(stdout, "ignorable encoded. buf length: %zu\n", buffer->len); } if (message_view->payload.len != 0) { if (s_iot_st_encode_payload(&message_view->payload, buffer)) { goto cleanup; } - fprintf(stdout, "payload encoded. buf length: %zu\n", buffer->len); } if (message_view->service_id.len != 0) { if (s_iot_st_encode_service_id(&message_view->service_id, buffer)) { goto cleanup; } - fprintf(stdout, "service id encoded. buf length: %zu\n", buffer->len); } AWS_RETURN_ERROR_IF2(buffer->capacity < AWS_IOT_ST_MAX_MESSAGE_SIZE, AWS_ERROR_INVALID_BUFFER_SIZE); diff --git a/tests/aws_iot_secure_tunneling_client_test.c b/tests/aws_iot_secure_tunneling_client_test.c index 32723301..690e64d6 100644 --- a/tests/aws_iot_secure_tunneling_client_test.c +++ b/tests/aws_iot_secure_tunneling_client_test.c @@ -25,31 +25,31 @@ static void s_on_send_data_complete(int error_code, void *user_data) { on_send_data_complete_error_code = error_code; } -static void s_on_connection_complete(void *user_data) { - UNUSED(user_data); - aws_mutex_lock(&mutex); - aws_condition_variable_notify_one(&condition_variable); - aws_mutex_unlock(&mutex); -} +// static void s_on_connection_complete(void *user_data) { +// UNUSED(user_data); +// aws_mutex_lock(&mutex); +// aws_condition_variable_notify_one(&condition_variable); +// aws_mutex_unlock(&mutex); +// } -static void s_on_connection_shutdown(void *user_data) { - UNUSED(user_data); -} +// static void s_on_connection_shutdown(void *user_data) { +// UNUSED(user_data); +// } -static void s_on_data_receive(const struct aws_byte_buf *data, void *user_data) { - AWS_LOGF_INFO(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Client received data:"); +// static void s_on_data_receive(const struct aws_byte_buf *data, void *user_data) { +// AWS_LOGF_INFO(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Client received data:"); - struct aws_allocator *allocator = (struct aws_allocator *)user_data; +// struct aws_allocator *allocator = (struct aws_allocator *)user_data; - struct aws_byte_cursor data_cursor = aws_byte_cursor_from_buf(data); - struct aws_byte_buf data_to_print; - aws_byte_buf_init(&data_to_print, allocator, data->len + 1); /* +1 for null terminator */ - aws_byte_buf_append(&data_to_print, &data_cursor); - aws_byte_buf_append_null_terminator(&data_to_print); - AWS_LOGF_INFO(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "%s", (char *)data_to_print.buffer); +// struct aws_byte_cursor data_cursor = aws_byte_cursor_from_buf(data); +// struct aws_byte_buf data_to_print; +// aws_byte_buf_init(&data_to_print, allocator, data->len + 1); /* +1 for null terminator */ +// aws_byte_buf_append(&data_to_print, &data_cursor); +// aws_byte_buf_append_null_terminator(&data_to_print); +// AWS_LOGF_INFO(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "%s", (char *)data_to_print.buffer); - aws_byte_buf_clean_up(&data_to_print); -} +// aws_byte_buf_clean_up(&data_to_print); +// } // static void s_on_stream_start(void *user_data) { // UNUSED(user_data); @@ -93,10 +93,10 @@ static void s_init_secure_tunneling_connection_config( config->endpoint_host = aws_byte_cursor_from_c_str(endpoint); config->root_ca = root_ca; - config->on_connection_complete = s_on_connection_complete; - config->on_connection_shutdown = s_on_connection_shutdown; + // config->on_connection_complete = s_on_connection_complete; + // config->on_connection_shutdown = s_on_connection_shutdown; config->on_send_data_complete = s_on_send_data_complete; - config->on_data_receive = s_on_data_receive; + // config->on_data_receive = s_on_data_receive; // config->on_stream_start = s_on_stream_start; // config->on_stream_reset = s_on_stream_reset; config->on_session_reset = s_on_session_reset; diff --git a/tests/secure_tunneling_tests.c b/tests/secure_tunneling_tests.c index 62e7abdd..982621be 100644 --- a/tests/secure_tunneling_tests.c +++ b/tests/secure_tunneling_tests.c @@ -700,7 +700,7 @@ static int s_secure_tunneling_handle_send_data_public(struct aws_allocator *allo rc = s_byte_buf_init_rand(&buf, allocator, buf_sizes[i]); ASSERT_INT_EQUALS(AWS_OP_SUCCESS, rc); - struct aws_byte_cursor cur = aws_byte_cursor_from_buf(&buf); + // struct aws_byte_cursor cur = aws_byte_cursor_from_buf(&buf); /* Call public api to send data over secure tunnel. */ s_mock_aws_websocket_send_frame_call_count = 0U; From 968c684210701b6ee30051028277d61891514210 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Fri, 27 Jan 2023 11:28:58 -0800 Subject: [PATCH 19/69] more clean up --- include/aws/iotdevice/secure_tunneling.h | 5 ----- source/secure_tunneling.c | 5 ----- source/secure_tunneling_operations.c | 7 +++++-- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/include/aws/iotdevice/secure_tunneling.h b/include/aws/iotdevice/secure_tunneling.h index 24851d0d..61b01f5d 100644 --- a/include/aws/iotdevice/secure_tunneling.h +++ b/include/aws/iotdevice/secure_tunneling.h @@ -16,7 +16,6 @@ struct aws_websocket; struct aws_websocket_incoming_frame; struct aws_http_proxy_options; -/* STEVE TODO remove or move to private. We only support Destination Mode */ enum aws_secure_tunneling_local_proxy_mode { AWS_SECURE_TUNNELING_SOURCE_MODE, AWS_SECURE_TUNNELING_DESTINATION_MODE }; /** @@ -101,7 +100,6 @@ struct aws_secure_tunnel_message_view { typedef void( aws_secure_tunnel_message_received_fn)(const struct aws_secure_tunnel_message_view *message, void *user_data); -/* STEVE TODO Old callbacks can probably be removed */ 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_send_data_complete_fn)(int error_code, void *user_data); @@ -161,7 +159,6 @@ struct aws_secure_tunnel_options { void *user_data; - /* Steve TODO we only support destination mode so this can be removed outside of testing */ enum aws_secure_tunneling_local_proxy_mode local_proxy_mode; aws_secure_tunneling_on_connection_complete_fn *on_connection_complete; @@ -219,7 +216,6 @@ struct aws_secure_tunnel *aws_secure_tunnel_acquire(struct aws_secure_tunnel *se AWS_IOTDEVICE_API void aws_secure_tunnel_release(struct aws_secure_tunnel *secure_tunnel); -/* TODO STEVE NEW replace aws_secure_tunnel_connect and put it in a state where it wants to be connected */ /** * Asynchronous notify to the secure tunnel that you want it to attempt to connect. * The secure tunnel will attempt to stay connected. @@ -230,7 +226,6 @@ void aws_secure_tunnel_release(struct aws_secure_tunnel *secure_tunnel); AWS_IOTDEVICE_API int aws_secure_tunnel_start(struct aws_secure_tunnel *secure_tunnel); -/* TODO STEVE NEW replace aws_secure_tunnel_close and put it in a state where it wants to be disconnected */ /** * Asynchronous notify to the secure tunnel that you want it to transition to the stopped state. When the * secure tunnel reaches the stopped state, all session state is erased. diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 0beed31b..e0e3f031 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -1858,8 +1858,3 @@ int aws_secure_tunnel_stream_start( aws_secure_tunnel_operation_release(&message_op->base); return AWS_OP_ERR; } - -// /* Steve Todo a V2/V3 version is required to reset on a failed stream start attempt */ -// int aws_secure_tunnel_stream_reset(struct aws_secure_tunnel *secure_tunnel) { -// return secure_tunnel->vtable->send_stream_reset(secure_tunnel); -// } diff --git a/source/secure_tunneling_operations.c b/source/secure_tunneling_operations.c index b53f895f..d7a90b8c 100644 --- a/source/secure_tunneling_operations.c +++ b/source/secure_tunneling_operations.c @@ -192,7 +192,7 @@ void aws_secure_tunnel_message_storage_clean_up(struct aws_secure_tunnel_message aws_byte_buf_clean_up(&message_storage->storage); } -/* Sets the stream id on outbound messages*/ +/* 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_set_stream_id( struct aws_secure_tunnel_operation *operation, struct aws_secure_tunnel *secure_tunnel) { @@ -239,6 +239,10 @@ static int s_aws_secure_tunnel_operation_message_set_stream_id( return AWS_OP_SUCCESS; } +/* + * Check the outbound stream start service id (or lack of one for V1) and set the secure tunnel and stream start + * message's stream id to the next value. + */ static int s_aws_secure_tunnel_operation_message_set_next_stream_id( struct aws_secure_tunnel_operation *operation, struct aws_secure_tunnel *secure_tunnel) { @@ -632,7 +636,6 @@ struct aws_secure_tunnel_options_storage *aws_secure_tunnel_options_storage_new( storage->on_message_received = options->on_message_received; storage->user_data = options->user_data; - /* These can be deprecated/removed as secure tunnel from the iot sdk perspective only supports destination mode */ storage->local_proxy_mode = options->local_proxy_mode; storage->on_connection_complete = options->on_connection_complete; storage->on_connection_shutdown = options->on_connection_shutdown; From d75b11a2f9722d1c992fbe7230853784b6902920 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Fri, 27 Jan 2023 11:43:43 -0800 Subject: [PATCH 20/69] clean up --- source/secure_tunneling.c | 49 --------------------------------------- 1 file changed, 49 deletions(-) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index e0e3f031..af98c403 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -1014,55 +1014,6 @@ static uint64_t s_aws_high_res_clock_get_ticks_proxy(void) { return current_time; } -// static int s_secure_tunneling_send_stream_start(struct aws_secure_tunnel *secure_tunnel) { -// if (secure_tunnel->config->local_proxy_mode == AWS_SECURE_TUNNELING_DESTINATION_MODE) { -// AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Start can only be sent from src mode"); -// return AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INCORRECT_MODE; -// } -// secure_tunnel->config->stream_id += 1; -// if (secure_tunnel->config->stream_id == 0) { -// secure_tunnel->config->stream_id += 1; -// } -// return s_secure_tunneling_send(secure_tunnel, NULL, NULL, AWS_SECURE_TUNNEL_MT_STREAM_START); -// } - -// static int s_secure_tunneling_send_stream_reset(struct aws_secure_tunnel *secure_tunnel) { -// if (secure_tunnel->config->stream_id == INVALID_STREAM_ID) { -// AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Invalid Stream Id"); -// return AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_STREAM; -// } - -// int result = s_secure_tunneling_send(secure_tunnel, NULL, NULL, AWS_SECURE_TUNNEL_MT_STREAM_RESET); -// s_reset_secure_tunnel(secure_tunnel); -// return result; -// } - -// static int s_secure_tunneling_send_data( -// struct aws_secure_tunnel *secure_tunnel, -// const struct aws_byte_cursor *payload_data) { -// if (secure_tunnel->config->stream_id == INVALID_STREAM_ID) { -// AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Invalid Stream Id"); -// return AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_STREAM; -// } -// struct aws_byte_cursor new_data = *payload_data; -// while (new_data.len) { -// size_t bytes_max = new_data.len; -// size_t amount_to_send = bytes_max < AWS_IOT_ST_SPLIT_MESSAGE_SIZE ? bytes_max : -// AWS_IOT_ST_SPLIT_MESSAGE_SIZE; - -// struct aws_byte_cursor send_cursor = aws_byte_cursor_advance(&new_data, amount_to_send); -// AWS_FATAL_ASSERT(send_cursor.len > 0); -// if (send_cursor.len) { -// if (s_secure_tunneling_send(secure_tunnel, &send_cursor, NULL, AWS_SECURE_TUNNEL_MT_DATA) != -// AWS_OP_SUCCESS) { -// AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Failure writing data to out_buf"); -// return AWS_OP_ERR; -// } -// } -// } -// return AWS_OP_SUCCESS; -// } - static struct aws_secure_tunnel_vtable s_default_secure_tunnel_vtable = { .get_current_time_fn = s_aws_high_res_clock_get_ticks_proxy, }; From 94082acb62a4cbd27f5bbc99beeb1354767d4fc2 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 30 Jan 2023 10:44:33 -0800 Subject: [PATCH 21/69] stream reset added --- .../private/secure_tunneling_operations.h | 2 +- include/aws/iotdevice/secure_tunneling.h | 5 + source/secure_tunneling.c | 104 +++++++++++++++++- 3 files changed, 106 insertions(+), 5 deletions(-) diff --git a/include/aws/iotdevice/private/secure_tunneling_operations.h b/include/aws/iotdevice/private/secure_tunneling_operations.h index 0983d0cf..e0131eb2 100644 --- a/include/aws/iotdevice/private/secure_tunneling_operations.h +++ b/include/aws/iotdevice/private/secure_tunneling_operations.h @@ -65,7 +65,7 @@ struct aws_secure_tunnel_operation { struct aws_linked_list_node node; enum aws_secure_tunnel_operation_type operation_type; - const void *message_view; + const struct aws_secure_tunnel_message_view *message_view; /* Size of the secure tunnel message this operation represents */ size_t message_size; diff --git a/include/aws/iotdevice/secure_tunneling.h b/include/aws/iotdevice/secure_tunneling.h index 61b01f5d..ca977387 100644 --- a/include/aws/iotdevice/secure_tunneling.h +++ b/include/aws/iotdevice/secure_tunneling.h @@ -248,6 +248,11 @@ int aws_secure_tunnel_send_message( struct aws_secure_tunnel *secure_tunnel, const struct aws_secure_tunnel_message_view *message_options); +AWS_IOTDEVICE_API +int aws_secure_tunnel_stream_reset( + struct aws_secure_tunnel *secure_tunnel, + const struct aws_secure_tunnel_message_view *message_options); + //*********************************************************************************************************************** /* THESE API SHOULD ONLY BE USED FROM SOURCE MODE */ //*********************************************************************************************************************** diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index af98c403..6ca6ed85 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -133,6 +133,45 @@ static void s_reset_secure_tunnel(struct aws_secure_tunnel *secure_tunnel) { secure_tunnel->received_data.len = 0; /* Drop any incomplete secure tunnel frame */ } +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) { + return (secure_tunnel->config->stream_id == stream_id); + } + + struct aws_string *service_id_to_set = aws_string_new_from_cursor(secure_tunnel->allocator, service_id); + if (service_id_to_set == NULL) { + return false; + } + int32_t current_stream_id = INVALID_STREAM_ID; + + if (secure_tunnel->config->service_id_1 != NULL && + aws_string_compare(secure_tunnel->config->service_id_1, service_id_to_set) == 0) { + current_stream_id = secure_tunnel->config->service_id_1_stream_id; + } else if ( + secure_tunnel->config->service_id_2 != NULL && + aws_string_compare(secure_tunnel->config->service_id_2, service_id_to_set) == 0) { + current_stream_id = secure_tunnel->config->service_id_2_stream_id; + } else if ( + secure_tunnel->config->service_id_3 != NULL && + aws_string_compare(secure_tunnel->config->service_id_3, service_id_to_set) == 0) { + current_stream_id = secure_tunnel->config->service_id_3_stream_id; + } else { + 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)); + aws_string_destroy(service_id_to_set); + return false; + } + + return (stream_id == current_stream_id); +} + static int s_aws_secure_tunnel_set_stream_id( struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *service_id, @@ -197,7 +236,10 @@ 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 = s_aws_secure_tunnel_set_stream_id(secure_tunnel, &message_view->service_id, INVALID_STREAM_ID); + 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 (secure_tunnel->config->on_stream_reset) { secure_tunnel->config->on_stream_reset(message_view, result, secure_tunnel->config->user_data); } @@ -1197,8 +1239,6 @@ int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure break; - case AWS_STOT_STREAM_RESET: - break; case AWS_STOT_STREAM_START: if ((*current_operation->vtable->aws_secure_tunnel_operation_set_next_stream_id_fn)( current_operation, secure_tunnel)) { @@ -1218,6 +1258,30 @@ int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure } break; + case AWS_STOT_STREAM_RESET: + + if ((*current_operation->vtable->aws_secure_tunnel_operation_set_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)); + } 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, ¤t_operation->message_view->service_id, INVALID_STREAM_ID); + } + aws_secure_tunnel_message_view_log(current_operation->message_view, AWS_LL_DEBUG); + } + + break; + case AWS_STOT_NONE: break; } @@ -1793,7 +1857,39 @@ int aws_secure_tunnel_stream_start( AWS_LOGF_DEBUG( AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: Submitting MESSAGE operation (%p)", + "id=%p: Submitting STREAM START operation (%p)", + (void *)secure_tunnel, + (void *)message_op); + + if (s_submit_operation(secure_tunnel, &message_op->base)) { + goto error; + } + + s_aws_secure_tunnel_set_stream_id(secure_tunnel, &message_options->service_id, message_options->stream_id); + + return AWS_OP_SUCCESS; + +error: + aws_secure_tunnel_operation_release(&message_op->base); + return AWS_OP_ERR; +} + +int aws_secure_tunnel_stream_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_STREAM_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); From 8a452c85e3df13beba58da6ba53e111136caa803 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 30 Jan 2023 15:56:16 -0800 Subject: [PATCH 22/69] logging of error added --- source/secure_tunneling.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 6ca6ed85..b8bce7cb 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -1233,6 +1233,12 @@ int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure /* 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); } From c89082057db11f51046bc737dc1ccc9b784f669e Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 30 Jan 2023 16:09:12 -0800 Subject: [PATCH 23/69] handling of the on_connection_complete callback --- source/secure_tunneling.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index b8bce7cb..d25c10d6 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -274,6 +274,11 @@ static void s_aws_secure_tunnel_on_service_ids_received(struct aws_secure_tunnel (void *)secure_tunnel, AWS_BYTE_CURSOR_PRI(aws_byte_cursor_from_string(secure_tunnel->config->service_id_3))); } + + /* A connection can only be used once available service ids are established with the secure tunnel. */ + if (secure_tunnel->config->on_connection_complete) { + secure_tunnel->config->on_connection_complete(AWS_ERROR_SUCCESS, secure_tunnel->config->user_data); + } } static void s_aws_secure_tunnel_connected_on_message_received( @@ -595,7 +600,8 @@ static void s_on_websocket_setup(const struct aws_websocket_on_connection_setup_ secure_tunnel->websocket = setup->websocket; - if (secure_tunnel->config->on_connection_complete) { + /* Report a failed WebSocket Upgrade attempt */ + if (setup->error_code && secure_tunnel->config->on_connection_complete) { secure_tunnel->config->on_connection_complete(setup->error_code, secure_tunnel->config->user_data); } From ee05c2159bb46f200a57880d39bc338cb6aa60c8 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 30 Jan 2023 16:21:11 -0800 Subject: [PATCH 24/69] typo --- source/secure_tunneling_operations.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/secure_tunneling_operations.c b/source/secure_tunneling_operations.c index d7a90b8c..1a13fa98 100644 --- a/source/secure_tunneling_operations.c +++ b/source/secure_tunneling_operations.c @@ -113,7 +113,7 @@ void aws_secure_tunnel_message_view_log( log_handle, level, AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: aws_secure_tunnel_message_view service_id set to" PRInSTR, + "id=%p: aws_secure_tunnel_message_view service_id set to " PRInSTR, (void *)message_view, AWS_BYTE_CURSOR_PRI(message_view->service_id)); } else { From 1b6a666e53a488314f008aa88b2cab5acd0942ae Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Tue, 7 Feb 2023 14:21:35 -0800 Subject: [PATCH 25/69] code review changes WIP --- .../iotdevice/private/secure_tunneling_impl.h | 43 ++++++++++++++ .../private/secure_tunneling_operations.h | 46 +-------------- include/aws/iotdevice/private/serializer.h | 3 +- include/aws/iotdevice/secure_tunneling.h | 21 ++++--- source/secure_tunneling.c | 6 +- source/secure_tunneling_operations.c | 57 +++++++++++-------- source/serializer.c | 32 +++++++---- 7 files changed, 114 insertions(+), 94 deletions(-) diff --git a/include/aws/iotdevice/private/secure_tunneling_impl.h b/include/aws/iotdevice/private/secure_tunneling_impl.h index 8cbdfc23..09a4b2fb 100644 --- a/include/aws/iotdevice/private/secure_tunneling_impl.h +++ b/include/aws/iotdevice/private/secure_tunneling_impl.h @@ -101,6 +101,49 @@ struct data_tunnel_pair { bool length_prefix_written; }; +/* + * Secure tunnel configuration + */ +struct aws_secure_tunnel_options_storage { + + // struct aws_secure_tunnel_options options; + struct aws_allocator *allocator; + + /* backup */ + + struct aws_client_bootstrap *bootstrap; + struct aws_socket_options socket_options; + struct aws_http_proxy_options http_proxy_options; + struct aws_http_proxy_config *http_proxy_config; + struct aws_string *access_token; + struct aws_string *client_token; + + struct aws_string *endpoint_host; + + /* Stream related info */ + int32_t stream_id; + struct aws_string *service_id_1; + int32_t service_id_1_stream_id; + struct aws_string *service_id_2; + int32_t service_id_2_stream_id; + struct aws_string *service_id_3; + int32_t service_id_3_stream_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_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; + + aws_secure_tunneling_on_send_data_complete_fn *on_send_data_complete; + aws_secure_tunneling_on_termination_complete_fn *on_termination_complete; + + void *user_data; + enum aws_secure_tunneling_local_proxy_mode local_proxy_mode; +}; + struct aws_secure_tunnel_vtable { /* aws_high_res_clock_get_ticks */ uint64_t (*get_current_time_fn)(void); diff --git a/include/aws/iotdevice/private/secure_tunneling_operations.h b/include/aws/iotdevice/private/secure_tunneling_operations.h index e0131eb2..4ab0c7dd 100644 --- a/include/aws/iotdevice/private/secure_tunneling_operations.h +++ b/include/aws/iotdevice/private/secure_tunneling_operations.h @@ -21,7 +21,7 @@ struct aws_secure_tunnel_operation; enum aws_secure_tunnel_operation_type { AWS_STOT_NONE, AWS_STOT_PING, - AWS_STOT_DATA, + AWS_STOT_MESSAGE, AWS_STOT_STREAM_RESET, AWS_STOT_STREAM_START }; @@ -85,50 +85,6 @@ struct aws_secure_tunnel_operation_pingreq { struct aws_allocator *allocator; }; -/* - * Secure tunnel configuration - */ -struct aws_secure_tunnel_options_storage { - - // struct aws_secure_tunnel_options options; - struct aws_allocator *allocator; - struct aws_secure_tunnel *secure_tunnel; - - /* backup */ - - struct aws_client_bootstrap *bootstrap; - struct aws_socket_options socket_options; - struct aws_http_proxy_options http_proxy_options; - struct aws_http_proxy_config *http_proxy_config; - struct aws_string *access_token; - struct aws_string *client_token; - - struct aws_string *endpoint_host; - - /* Stream related info */ - int32_t stream_id; - struct aws_string *service_id_1; - int32_t service_id_1_stream_id; - struct aws_string *service_id_2; - int32_t service_id_2_stream_id; - struct aws_string *service_id_3; - int32_t service_id_3_stream_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_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; - - aws_secure_tunneling_on_send_data_complete_fn *on_send_data_complete; - aws_secure_tunneling_on_termination_complete_fn *on_termination_complete; - - void *user_data; - enum aws_secure_tunneling_local_proxy_mode local_proxy_mode; -}; - AWS_EXTERN_C_BEGIN /* Operation Base */ diff --git a/include/aws/iotdevice/private/serializer.h b/include/aws/iotdevice/private/serializer.h index c96ca02b..2b0673ad 100644 --- a/include/aws/iotdevice/private/serializer.h +++ b/include/aws/iotdevice/private/serializer.h @@ -14,8 +14,7 @@ #define AWS_IOT_ST_FIELD_NUMBER_SHIFT 3 #define AWS_IOT_ST_MAXIMUM_VARINT 268435455 -#define AWS_IOT_ST_MAX_MESSAGE_SIZE 64 * 1024 -#define AWS_IOT_ST_MAX_PAYLOAD_SIZE 64512 +#define AWS_IOT_ST_MAX_MESSAGE_SIZE (64 * 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 ca977387..39f4491d 100644 --- a/include/aws/iotdevice/secure_tunneling.h +++ b/include/aws/iotdevice/secure_tunneling.h @@ -6,9 +6,10 @@ #ifndef AWS_IOTDEVICE_SECURE_TUNNELING_H #define AWS_IOTDEVICE_SECURE_TUNNELING_H -#include #include +#include + #define AWS_IOT_ST_SPLIT_MESSAGE_SIZE 15000 struct aws_secure_tunnel; @@ -16,7 +17,10 @@ struct aws_websocket; struct aws_websocket_incoming_frame; struct aws_http_proxy_options; -enum aws_secure_tunneling_local_proxy_mode { AWS_SECURE_TUNNELING_SOURCE_MODE, AWS_SECURE_TUNNELING_DESTINATION_MODE }; +enum aws_secure_tunneling_local_proxy_mode { + AWS_SECURE_TUNNELING_SOURCE_MODE, + AWS_SECURE_TUNNELING_DESTINATION_MODE, +}; /** * Type of IoT Secure Tunnel message. @@ -88,8 +92,8 @@ struct aws_secure_tunnel_message_view { /** * Secure tunnel multiplexing identifier */ - struct aws_byte_cursor service_id; - struct aws_byte_cursor payload; + struct aws_byte_cursor *service_id; + struct aws_byte_cursor *payload; }; /* Callbacks */ @@ -248,16 +252,19 @@ int aws_secure_tunnel_send_message( struct aws_secure_tunnel *secure_tunnel, const struct aws_secure_tunnel_message_view *message_options); +//*********************************************************************************************************************** +/* THIS API SHOULD ONLY BE USED FROM SOURCE MODE */ +//*********************************************************************************************************************** AWS_IOTDEVICE_API -int aws_secure_tunnel_stream_reset( +int aws_secure_tunnel_stream_start( struct aws_secure_tunnel *secure_tunnel, const struct aws_secure_tunnel_message_view *message_options); //*********************************************************************************************************************** -/* THESE API SHOULD ONLY BE USED FROM SOURCE MODE */ +/* THIS API SHOULD NOT BE USED BY THE CUSTOMER AND SHOULD BE DEPRECATED */ //*********************************************************************************************************************** AWS_IOTDEVICE_API -int aws_secure_tunnel_stream_start( +int aws_secure_tunnel_stream_reset( struct aws_secure_tunnel *secure_tunnel, const struct aws_secure_tunnel_message_view *message_options); diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index d25c10d6..df059de5 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -641,7 +641,6 @@ void s_websocket_transform_complete_task_fn(struct aws_task *task, void *arg, en .host = aws_byte_cursor_from_string(secure_tunnel->config->endpoint_host), .port = 443, .handshake_request = secure_tunnel->handshake_request, - .initial_window_size = MAX_WEBSOCKET_PAYLOAD, .manual_window_management = false, .user_data = secure_tunnel, .requested_event_loop = secure_tunnel->loop, @@ -1222,7 +1221,7 @@ int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure aws_websocket_send_frame(secure_tunnel->websocket, &frame_options); break; - case AWS_STOT_DATA: + 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_set_stream_id_fn)( current_operation, secure_tunnel)) { @@ -1740,7 +1739,6 @@ struct aws_secure_tunnel *aws_secure_tunnel_new(const struct aws_secure_tunnel_o if (secure_tunnel->config == NULL) { goto error; } - secure_tunnel->config->secure_tunnel = secure_tunnel; /* 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); @@ -1826,7 +1824,7 @@ int aws_secure_tunnel_send_message( 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_DATA); + secure_tunnel->allocator, secure_tunnel, message_options, AWS_STOT_MESSAGE); if (message_op == NULL) { return AWS_OP_ERR; diff --git a/source/secure_tunneling_operations.c b/source/secure_tunneling_operations.c index 1a13fa98..8c752d41 100644 --- a/source/secure_tunneling_operations.c +++ b/source/secure_tunneling_operations.c @@ -13,6 +13,7 @@ #include #include +/* STEVE TODO explain max payload size value */ #define MAX_PAYLOAD_SIZE 64512 #define INVALID_STREAM_ID 0 /********************************************************************************************************************* @@ -81,7 +82,7 @@ int aws_secure_tunnel_message_view_validate(const struct aws_secure_tunnel_messa return aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_OPTIONS_VALIDATION); } - if (message_view->payload.len > MAX_PAYLOAD_SIZE) { + if (message_view->payload != NULL && message_view->payload->len > MAX_PAYLOAD_SIZE) { AWS_LOGF_ERROR( AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: aws_secure_tunnel_message_view - payload too long", @@ -108,14 +109,14 @@ void aws_secure_tunnel_message_view_log( (void *)message_view, (int)message_view->stream_id); - if (message_view->service_id.len > 0) { + 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)); + AWS_BYTE_CURSOR_PRI(*message_view->service_id)); } else { AWS_LOGUF( log_handle, @@ -125,19 +126,21 @@ void aws_secure_tunnel_message_view_log( (void *)message_view); } - AWS_LOGUF( - log_handle, - level, - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: aws_secure_tunnel_message_view payload set containing %zu bytes", - (void *)message_view, - message_view->payload.len); + if (message_view->payload != NULL) { + AWS_LOGUF( + log_handle, + level, + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: aws_secure_tunnel_message_view payload set containing %zu bytes", + (void *)message_view, + message_view->payload->len); + } } static size_t s_aws_secure_tunnel_message_compute_storage_size( const struct aws_secure_tunnel_message_view *message_view) { - size_t storage_size = message_view->payload.len; - storage_size += message_view->service_id.len; + size_t storage_size = message_view->payload == NULL ? 0 : message_view->payload->len; + storage_size += message_view->service_id == NULL ? 0 : message_view->service_id->len; return storage_size; } @@ -161,7 +164,7 @@ int aws_secure_tunnel_message_storage_init( storage_view->stream_id = message_options->stream_id; switch (type) { - case AWS_STOT_DATA: + case AWS_STOT_MESSAGE: storage_view->type = AWS_SECURE_TUNNEL_MT_DATA; break; case AWS_STOT_STREAM_START: @@ -175,14 +178,20 @@ int aws_secure_tunnel_message_storage_init( break; } - storage_view->service_id = message_options->service_id; - if (aws_byte_buf_append_and_update(&message_storage->storage, &storage_view->service_id)) { - return AWS_OP_ERR; + if (message_options->service_id != NULL) { + message_storage->service_id = *message_options->service_id; + if (aws_byte_buf_append_and_update(&message_storage->storage, &message_storage->service_id)) { + return AWS_OP_ERR; + } + storage_view->service_id = &message_storage->service_id; } - storage_view->payload = message_options->payload; - if (aws_byte_buf_append_and_update(&message_storage->storage, &storage_view->payload)) { - return AWS_OP_ERR; + if (message_options->payload != NULL) { + message_storage->payload = *message_options->payload; + if (aws_byte_buf_append_and_update(&message_storage->storage, &message_storage->payload)) { + return AWS_OP_ERR; + } + storage_view->payload = &message_storage->payload; } return AWS_OP_SUCCESS; @@ -202,9 +211,9 @@ static int s_aws_secure_tunnel_operation_message_set_stream_id( struct aws_secure_tunnel_message_view *message_view = &message_op->options_storage.storage_view; - if (message_view->service_id.len > 0) { + if (message_view->service_id != NULL) { struct aws_string *service_id = NULL; - service_id = aws_string_new_from_cursor(secure_tunnel->allocator, &message_view->service_id); + service_id = aws_string_new_from_cursor(secure_tunnel->allocator, message_view->service_id); if (secure_tunnel->config->service_id_1 != NULL && aws_string_compare(secure_tunnel->config->service_id_1, service_id) == 0) { @@ -252,9 +261,9 @@ 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.len > 0) { + if (message_view->service_id != NULL) { struct aws_string *service_id = NULL; - service_id = aws_string_new_from_cursor(secure_tunnel->allocator, &message_view->service_id); + service_id = aws_string_new_from_cursor(secure_tunnel->allocator, message_view->service_id); if (secure_tunnel->config->service_id_1 != NULL && aws_string_compare(secure_tunnel->config->service_id_1, service_id) == 0) { @@ -658,7 +667,7 @@ const char *aws_secure_tunnel_operation_type_to_c_string(enum aws_secure_tunnel_ return "NONE"; case AWS_STOT_PING: return "PING"; - case AWS_STOT_DATA: + case AWS_STOT_MESSAGE: return "DATA"; case AWS_STOT_STREAM_RESET: return "STREAM RESET"; diff --git a/source/serializer.c b/source/serializer.c index 25db741e..77c839c4 100644 --- a/source/serializer.c +++ b/source/serializer.c @@ -161,30 +161,30 @@ static int s_iot_st_compute_message_length( local_length += 2; } - if (message->payload.len != 0) { + if (message->payload != NULL && message->payload->len != 0) { /* * 1 byte key * 1-4 byte payload length varint * n bytes payload.len */ size_t payload_length = 0; - if (s_iot_st_get_varint_size((uint32_t)message->payload.len, &payload_length)) { + if (s_iot_st_get_varint_size((uint32_t)message->payload->len, &payload_length)) { return AWS_OP_ERR; } - local_length += (1 + message->payload.len + payload_length); + local_length += (1 + message->payload->len + payload_length); } - if (message->service_id.len != 0) { + if (message->service_id != NULL && message->service_id->len != 0) { /* * 1 byte key * 1-4 byte payload length varint * n bytes service_id.len */ size_t service_id_length = 0; - if (s_iot_st_get_varint_size((uint32_t)message->service_id.len, &service_id_length)) { + if (s_iot_st_get_varint_size((uint32_t)message->service_id->len, &service_id_length)) { return AWS_OP_ERR; } - local_length += (1 + message->service_id.len + service_id_length); + local_length += (1 + message->service_id->len + service_id_length); } *message_length = local_length; @@ -225,14 +225,14 @@ int aws_iot_st_msg_serialize_from_view( } } - if (message_view->payload.len != 0) { - if (s_iot_st_encode_payload(&message_view->payload, buffer)) { + if (message_view->payload != NULL) { + if (s_iot_st_encode_payload(message_view->payload, buffer)) { goto cleanup; } } - if (message_view->service_id.len != 0) { - if (s_iot_st_encode_service_id(&message_view->service_id, buffer)) { + if (message_view->service_id != NULL) { + if (s_iot_st_encode_service_id(message_view->service_id, buffer)) { goto cleanup; } } @@ -285,11 +285,15 @@ int aws_secure_tunnel_deserialize_message_from_cursor( uint8_t wire_type; uint8_t field_number; struct aws_byte_buf payload_buf; + struct aws_byte_cursor payload_cur; struct aws_byte_buf service_id_buf; + struct aws_byte_cursor service_id_cur; struct aws_byte_buf available_service_id_buf; AWS_ZERO_STRUCT(payload_buf); + AWS_ZERO_STRUCT(payload_cur); AWS_ZERO_STRUCT(service_id_buf); + AWS_ZERO_STRUCT(service_id_cur); int service_ids_set = 0; while ((aws_byte_cursor_is_valid(cursor)) && (cursor->len > 0)) { @@ -337,7 +341,9 @@ int aws_secure_tunnel_deserialize_message_from_cursor( goto error; } aws_byte_cursor_advance(cursor, length); - message->payload = aws_byte_cursor_from_buf(&payload_buf); + + payload_cur = aws_byte_cursor_from_buf(&payload_buf); + message->payload = &payload_cur; break; case AWS_SECURE_TUNNEL_FN_SERVICE_ID: @@ -346,7 +352,9 @@ int aws_secure_tunnel_deserialize_message_from_cursor( goto error; } aws_byte_cursor_advance(cursor, length); - message->service_id = aws_byte_cursor_from_buf(&service_id_buf); + + service_id_cur = aws_byte_cursor_from_buf(&service_id_buf); + message->service_id = &service_id_cur; break; case AWS_SECURE_TUNNEL_FN_AVAILABLE_SERVICE_IDS: From f3a399381841a334cdcb9191e19cb1926b292194 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Tue, 7 Feb 2023 14:26:08 -0800 Subject: [PATCH 26/69] wip --- source/secure_tunneling.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index df059de5..d898827c 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -177,7 +177,7 @@ static int s_aws_secure_tunnel_set_stream_id( 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) { + if (service_id == NULL || service_id->len == 0) { secure_tunnel->config->stream_id = stream_id; AWS_LOGF_INFO( AWS_LS_IOTDEVICE_SECURE_TUNNELING, @@ -227,7 +227,7 @@ 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); + int result = s_aws_secure_tunnel_set_stream_id(secure_tunnel, message_view->service_id, message_view->stream_id); if (secure_tunnel->config->on_stream_start) { secure_tunnel->config->on_stream_start(message_view, result, secure_tunnel->config->user_data); } @@ -237,8 +237,8 @@ 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_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 (secure_tunnel->config->on_stream_reset) { secure_tunnel->config->on_stream_reset(message_view, result, secure_tunnel->config->user_data); @@ -1286,7 +1286,7 @@ int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure error_code = aws_last_error(); } else { s_aws_secure_tunnel_set_stream_id( - secure_tunnel, ¤t_operation->message_view->service_id, INVALID_STREAM_ID); + secure_tunnel, current_operation->message_view->service_id, INVALID_STREAM_ID); } aws_secure_tunnel_message_view_log(current_operation->message_view, AWS_LL_DEBUG); } @@ -1875,7 +1875,7 @@ int aws_secure_tunnel_stream_start( goto error; } - s_aws_secure_tunnel_set_stream_id(secure_tunnel, &message_options->service_id, message_options->stream_id); + s_aws_secure_tunnel_set_stream_id(secure_tunnel, message_options->service_id, message_options->stream_id); return AWS_OP_SUCCESS; @@ -1907,7 +1907,7 @@ int aws_secure_tunnel_stream_reset( goto error; } - s_aws_secure_tunnel_set_stream_id(secure_tunnel, &message_options->service_id, message_options->stream_id); + s_aws_secure_tunnel_set_stream_id(secure_tunnel, message_options->service_id, message_options->stream_id); return AWS_OP_SUCCESS; From e24924b58ca37f17cbf809080d0b8301b7cdd4ff Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Tue, 7 Feb 2023 16:26:58 -0800 Subject: [PATCH 27/69] pr wip --- .../iotdevice/private/secure_tunneling_impl.h | 2 -- .../private/secure_tunneling_operations.h | 1 + include/aws/iotdevice/secure_tunneling.h | 8 ++++---- source/secure_tunneling.c | 18 ++++++++++-------- source/secure_tunneling_operations.c | 12 +++++++----- tests/aws_iot_secure_tunneling_client_test.c | 4 ++-- tests/secure_tunneling_tests.c | 15 ++++++++------- 7 files changed, 32 insertions(+), 28 deletions(-) diff --git a/include/aws/iotdevice/private/secure_tunneling_impl.h b/include/aws/iotdevice/private/secure_tunneling_impl.h index 09a4b2fb..60ac120e 100644 --- a/include/aws/iotdevice/private/secure_tunneling_impl.h +++ b/include/aws/iotdevice/private/secure_tunneling_impl.h @@ -105,8 +105,6 @@ struct data_tunnel_pair { * Secure tunnel configuration */ struct aws_secure_tunnel_options_storage { - - // struct aws_secure_tunnel_options options; struct aws_allocator *allocator; /* backup */ diff --git a/include/aws/iotdevice/private/secure_tunneling_operations.h b/include/aws/iotdevice/private/secure_tunneling_operations.h index 4ab0c7dd..8deba8a3 100644 --- a/include/aws/iotdevice/private/secure_tunneling_operations.h +++ b/include/aws/iotdevice/private/secure_tunneling_operations.h @@ -162,6 +162,7 @@ void aws_secure_tunnel_options_storage_destroy(struct aws_secure_tunnel_options_ */ AWS_IOTDEVICE_API 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 diff --git a/include/aws/iotdevice/secure_tunneling.h b/include/aws/iotdevice/secure_tunneling.h index 39f4491d..152463c5 100644 --- a/include/aws/iotdevice/secure_tunneling.h +++ b/include/aws/iotdevice/secure_tunneling.h @@ -124,8 +124,6 @@ typedef void(aws_secure_tunneling_on_termination_complete_fn)(void *user_data); * Contains connection properties for the creation of a Secure Tunnel */ struct aws_secure_tunnel_options { - struct aws_allocator *allocator; - /** * Host to establish Secure Tunnel connection to */ @@ -175,7 +173,7 @@ struct aws_secure_tunnel_options { }; /** - * Signature of callback to invoke when a DISCONNECT is fully written to the socket (or fails to be) + * 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); @@ -199,7 +197,9 @@ AWS_EXTERN_C_BEGIN * @return a new secure tunnel or NULL */ AWS_IOTDEVICE_API -struct aws_secure_tunnel *aws_secure_tunnel_new(const struct aws_secure_tunnel_options *options); +struct aws_secure_tunnel *aws_secure_tunnel_new( + struct aws_allocator *allocator, + const struct aws_secure_tunnel_options *options); /** * Acquires a reference to a secure tunnel diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index d898827c..f23be49d 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -1715,18 +1715,20 @@ static void s_secure_tunnel_service_task_fn(struct aws_task *task, void *arg, en * API Calls ********************************************************************************************************************/ -struct aws_secure_tunnel *aws_secure_tunnel_new(const struct aws_secure_tunnel_options *options) { +struct aws_secure_tunnel *aws_secure_tunnel_new( + struct aws_allocator *allocator, + const struct aws_secure_tunnel_options *options) { AWS_FATAL_ASSERT(options != NULL); - AWS_FATAL_ASSERT(options->allocator != NULL); + AWS_FATAL_ASSERT(allocator != NULL); - struct aws_secure_tunnel *secure_tunnel = aws_mem_calloc(options->allocator, 1, sizeof(struct aws_secure_tunnel)); + struct aws_secure_tunnel *secure_tunnel = aws_mem_calloc(allocator, 1, sizeof(struct aws_secure_tunnel)); if (secure_tunnel == NULL) { return NULL; } aws_task_init(&secure_tunnel->service_task, s_secure_tunnel_service_task_fn, secure_tunnel, "SecureTunnelService"); - secure_tunnel->allocator = options->allocator; + secure_tunnel->allocator = allocator; secure_tunnel->vtable = &s_default_secure_tunnel_vtable; aws_ref_count_init(&secure_tunnel->ref_count, secure_tunnel, s_on_secure_tunnel_zero_ref_count); @@ -1735,7 +1737,7 @@ struct aws_secure_tunnel *aws_secure_tunnel_new(const struct aws_secure_tunnel_o secure_tunnel->current_operation = NULL; /* store options */ - secure_tunnel->config = aws_secure_tunnel_options_storage_new(options); + secure_tunnel->config = aws_secure_tunnel_options_storage_new(allocator, options); if (secure_tunnel->config == NULL) { goto error; } @@ -1760,7 +1762,7 @@ struct aws_secure_tunnel *aws_secure_tunnel_new(const struct aws_secure_tunnel_o } } - secure_tunnel->tls_ctx = aws_tls_client_ctx_new(options->allocator, &tls_ctx_opt); + secure_tunnel->tls_ctx = aws_tls_client_ctx_new(allocator, &tls_ctx_opt); if (secure_tunnel->tls_ctx == NULL) { goto error; } @@ -1768,7 +1770,7 @@ struct aws_secure_tunnel *aws_secure_tunnel_new(const struct aws_secure_tunnel_o /* 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, options->allocator, (struct aws_byte_cursor *)&options->endpoint_host)) { + &secure_tunnel->tls_con_opt, allocator, (struct aws_byte_cursor *)&options->endpoint_host)) { goto error; } @@ -1782,7 +1784,7 @@ struct aws_secure_tunnel *aws_secure_tunnel_new(const struct aws_secure_tunnel_o secure_tunnel->handshake_request = NULL; secure_tunnel->websocket = NULL; - aws_byte_buf_init(&secure_tunnel->received_data, options->allocator, MAX_WEBSOCKET_PAYLOAD); + aws_byte_buf_init(&secure_tunnel->received_data, allocator, MAX_WEBSOCKET_PAYLOAD); aws_secure_tunnel_options_storage_log(secure_tunnel->config, AWS_LL_DEBUG); diff --git a/source/secure_tunneling_operations.c b/source/secure_tunneling_operations.c index 8c752d41..9ac901b5 100644 --- a/source/secure_tunneling_operations.c +++ b/source/secure_tunneling_operations.c @@ -398,19 +398,23 @@ struct aws_secure_tunnel_operation_pingreq *aws_secure_tunnel_operation_pingreq_ * Validation of options on creation of a new secure tunnel */ int aws_secure_tunnel_options_validate(const struct aws_secure_tunnel_options *options) { - AWS_ASSERT(options && options->allocator); + AWS_ASSERT(options); + if (options->bootstrap == NULL) { AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "bootstrap cannot be NULL"); return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); } + if (options->socket_options == NULL) { AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "socket options cannot be NULL"); return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); } + if (options->access_token.len == 0) { AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "access token is required"); return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); } + if (options->endpoint_host.len == 0) { AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "endpoint host is required"); return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); @@ -571,20 +575,18 @@ void aws_secure_tunnel_options_storage_destroy(struct aws_secure_tunnel_options_ * Copy and store secure tunnel options */ struct aws_secure_tunnel_options_storage *aws_secure_tunnel_options_storage_new( + struct aws_allocator *allocator, const struct aws_secure_tunnel_options *options) { + AWS_PRECONDITION(allocator != NULL); AWS_PRECONDITION(options != NULL); - AWS_PRECONDITION(options->allocator != NULL); if (aws_secure_tunnel_options_validate(options)) { return NULL; } - struct aws_allocator *allocator = options->allocator; - struct aws_secure_tunnel_options_storage *storage = aws_mem_calloc(allocator, 1, sizeof(struct aws_secure_tunnel_options_storage)); - storage->allocator = options->allocator; storage->socket_options = *options->socket_options; storage->endpoint_host = aws_string_new_from_cursor(allocator, &options->endpoint_host); if (storage->endpoint_host == NULL) { diff --git a/tests/aws_iot_secure_tunneling_client_test.c b/tests/aws_iot_secure_tunneling_client_test.c index 97401807..797f7eed 100644 --- a/tests/aws_iot_secure_tunneling_client_test.c +++ b/tests/aws_iot_secure_tunneling_client_test.c @@ -84,7 +84,7 @@ static void s_init_secure_tunneling_connection_config( struct aws_secure_tunnel_options *config) { AWS_ZERO_STRUCT(*config); - config->allocator = allocator; + // config->allocator = allocator; config->bootstrap = bootstrap; config->socket_options = socket_options; @@ -155,7 +155,7 @@ int main(int argc, char **argv) { allocator, bootstrap, &socket_options, access_token, local_proxy_mode, endpoint, root_ca, &config); /* Create a secure tunnel object and connect */ - struct aws_secure_tunnel *secure_tunnel = aws_secure_tunnel_new(&config); + struct aws_secure_tunnel *secure_tunnel = aws_secure_tunnel_new(allocator, &config); aws_secure_tunnel_start(secure_tunnel); /* wait here until the connection is done */ diff --git a/tests/secure_tunneling_tests.c b/tests/secure_tunneling_tests.c index b98d73dc..41041bed 100644 --- a/tests/secure_tunneling_tests.c +++ b/tests/secure_tunneling_tests.c @@ -86,7 +86,7 @@ static void s_init_secure_tunneling_connection_config( struct aws_secure_tunnel_options *options) { AWS_ZERO_STRUCT(*options); - options->allocator = allocator; + // options->allocator = allocator; options->bootstrap = bootstrap; options->socket_options = socket_options; @@ -154,10 +154,10 @@ void s_mock_aws_websocket_release(struct aws_websocket *websocket) { /* s_secure_tunnel_new_mock returns a secure_tunnel that mocks the aws websocket public api. */ static struct aws_secure_tunnel *s_secure_tunnel_new_mock(const struct aws_secure_tunnel_options *options) { - struct aws_secure_tunnel *secure_tunnel = aws_secure_tunnel_new(options); - if (!secure_tunnel) { - return secure_tunnel; - } + // struct aws_secure_tunnel *secure_tunnel = aws_secure_tunnel_new(options); + // if (!secure_tunnel) { + // return secure_tunnel; + // } // secure_tunnel->websocket_vtable.client_connect = s_mock_aws_websocket_client_connect; // secure_tunnel->websocket_vtable.send_frame = s_mock_aws_websocket_send_frame; // secure_tunnel->websocket_vtable.close = s_mock_aws_websocket_close; @@ -173,9 +173,10 @@ static struct aws_secure_tunnel *s_secure_tunnel_new_mock(const struct aws_secur * as soon as the tunnel is created. Since the aws_websocket struct is opaque * to this module, we use the placeholder value 1 to set the member non-null. */ - secure_tunnel->websocket = (void *)1; + // secure_tunnel->websocket = (void *)1; - return secure_tunnel; + // return secure_tunnel; + return 0; } static int before(struct aws_allocator *allocator, void *ctx) { From 8d42b5fab8bffdcae37c61bc15f1733361b82a6c Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Tue, 7 Feb 2023 16:42:01 -0800 Subject: [PATCH 28/69] remove typedef --- include/aws/iotdevice/secure_tunneling.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/aws/iotdevice/secure_tunneling.h b/include/aws/iotdevice/secure_tunneling.h index 152463c5..88968335 100644 --- a/include/aws/iotdevice/secure_tunneling.h +++ b/include/aws/iotdevice/secure_tunneling.h @@ -185,9 +185,6 @@ struct aws_secure_tunnel_disconnect_completion_options { void *completion_user_data; }; -/* deprecated: "_config" is renamed "_options" for consistency with similar code in the aws-c libraries */ -#define aws_secure_tunneling_connection_config aws_secure_tunnel_options - AWS_EXTERN_C_BEGIN /** From cedc949c15c456de0b27f8486006a846806bfde2 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 8 Feb 2023 10:22:12 -0800 Subject: [PATCH 29/69] pr wip --- include/aws/iotdevice/iotdevice.h | 1 + include/aws/iotdevice/private/serializer.h | 15 ++-- source/iotdevice.c | 3 + source/serializer.c | 96 +++++++++++++--------- 4 files changed, 72 insertions(+), 43 deletions(-) diff --git a/include/aws/iotdevice/iotdevice.h b/include/aws/iotdevice/iotdevice.h index d0e6b4bb..1f18507c 100644 --- a/include/aws/iotdevice/iotdevice.h +++ b/include/aws/iotdevice/iotdevice.h @@ -35,6 +35,7 @@ enum aws_iotdevice_error { AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_UNEXPECTED_HANGUP, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_USER_REQUESTED_STOP, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_TERMINATED, + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DECODE_FAILURE, AWS_ERROR_END_IOTDEVICE_RANGE = AWS_ERROR_ENUM_END_RANGE(AWS_C_IOTDEVICE_PACKAGE_ID), }; diff --git a/include/aws/iotdevice/private/serializer.h b/include/aws/iotdevice/private/serializer.h index 2b0673ad..4f95d5e6 100644 --- a/include/aws/iotdevice/private/serializer.h +++ b/include/aws/iotdevice/private/serializer.h @@ -14,6 +14,9 @@ #define AWS_IOT_ST_FIELD_NUMBER_SHIFT 3 #define AWS_IOT_ST_MAXIMUM_VARINT 268435455 +#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) enum aws_secure_tunnel_field_number { @@ -27,12 +30,12 @@ enum aws_secure_tunnel_field_number { }; enum aws_secure_tunnel_protocol_buffer_wire_type { - AWS_SECURE_TUNNEL_PBWT_VARINT = 0, /* int32, int64, uint32, uint64, sint32, sint64, bool, enum */ - AWS_SECURE_TUNNEL_PBWT_64_BIT = 1, /* fixed64, sfixed64, double */ - AWS_SECURE_TUNNEL_PBWT_LENGTH_DELIMINTED = 2, /* string, bytes, embedded messages, packed repeated fields */ - AWS_SECURE_TUNNEL_PBWT_START_GROUP = 3, /* groups (deprecated) */ - AWS_SECURE_TUNNEL_PBWT_END_GROUP = 4, /* groups (deprecated) */ - AWS_SECURE_TUNNEL_PBWT_32_BIT = 5, /* fixed32, sfixed32, float */ + AWS_SECURE_TUNNEL_PBWT_VARINT = 0, /* int32, int64, uint32, uint64, sint32, sint64, bool, enum */ + AWS_SECURE_TUNNEL_PBWT_64_BIT = 1, /* fixed64, sfixed64, double */ + AWS_SECURE_TUNNEL_PBWT_LENGTH_DELIMITED = 2, /* string, bytes, embedded messages, packed repeated fields */ + AWS_SECURE_TUNNEL_PBWT_START_GROUP = 3, /* groups (deprecated) */ + AWS_SECURE_TUNNEL_PBWT_END_GROUP = 4, /* groups (deprecated) */ + AWS_SECURE_TUNNEL_PBWT_32_BIT = 5, /* fixed32, sfixed32, float */ }; typedef void(aws_secure_tunnel_on_message_received_fn)( diff --git a/source/iotdevice.c b/source/iotdevice.c index f444c235..28e5475d 100644 --- a/source/iotdevice.c +++ b/source/iotdevice.c @@ -81,6 +81,9 @@ static struct aws_error_info s_errors[] = { AWS_DEFINE_ERROR_INFO_IOTDEVICE( AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_TERMINATED, "Secure Tunnel terminated by user request."), + AWS_DEFINE_ERROR_INFO_IOTDEVICE( + AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DECODE_FAILURE, + "Error occured while decoding an incoming message." ), }; /* clang-format on */ #undef AWS_DEFINE_ERROR_INFO_IOTDEVICE diff --git a/source/serializer.c b/source/serializer.c index 77c839c4..495f4b1e 100644 --- a/source/serializer.c +++ b/source/serializer.c @@ -76,7 +76,7 @@ static int s_iot_st_encode_varint( return s_iot_st_encode_varint_pos(buffer, value); } -static int s_iot_st_encode_lengthdelim( +static int s_iot_st_encode_byte_range( const uint8_t field_number, const uint8_t wire_type, const struct aws_byte_cursor *payload, @@ -101,13 +101,13 @@ static int s_iot_st_encode_type(int32_t data, struct aws_byte_buf *buffer) { } static int s_iot_st_encode_payload(const struct aws_byte_cursor *payload, struct aws_byte_buf *buffer) { - return s_iot_st_encode_lengthdelim( - AWS_SECURE_TUNNEL_FN_PAYLOAD, AWS_SECURE_TUNNEL_PBWT_LENGTH_DELIMINTED, payload, buffer); + return s_iot_st_encode_byte_range( + AWS_SECURE_TUNNEL_FN_PAYLOAD, AWS_SECURE_TUNNEL_PBWT_LENGTH_DELIMITED, payload, buffer); } static int s_iot_st_encode_service_id(const struct aws_byte_cursor *service_id, struct aws_byte_buf *buffer) { - return s_iot_st_encode_lengthdelim( - AWS_SECURE_TUNNEL_FN_SERVICE_ID, AWS_SECURE_TUNNEL_PBWT_LENGTH_DELIMINTED, service_id, buffer); + return s_iot_st_encode_byte_range( + AWS_SECURE_TUNNEL_FN_SERVICE_ID, AWS_SECURE_TUNNEL_PBWT_LENGTH_DELIMITED, service_id, buffer); } static int s_iot_st_get_varint_size(size_t value, size_t *encode_size) { @@ -115,11 +115,11 @@ static int s_iot_st_get_varint_size(size_t value, size_t *encode_size) { return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); } - if (value < 128) { + if (value < AWS_IOT_ST_MAXIMUM_1_BYTE_VARINT_VALUE) { *encode_size = 1; - } else if (value < 16384) { + } else if (value < AWS_IOT_ST_MAXIMUM_2_BYTE_VARINT_VALUE) { *encode_size = 2; - } else if (value < 2097152) { + } else if (value < AWS_IOT_ST_MAXIMUM_3_BYTE_VARINT_VALUE) { *encode_size = 3; } else { *encode_size = 4; @@ -237,7 +237,11 @@ int aws_iot_st_msg_serialize_from_view( } } - AWS_RETURN_ERROR_IF2(buffer->capacity < AWS_IOT_ST_MAX_MESSAGE_SIZE, AWS_ERROR_INVALID_BUFFER_SIZE); + if (buffer->capacity < AWS_IOT_ST_MAX_MESSAGE_SIZE) { + aws_raise_error(AWS_ERROR_INVALID_BUFFER_SIZE); + goto cleanup; + } + return AWS_OP_SUCCESS; cleanup: @@ -259,20 +263,51 @@ static int s_iot_st_decode_varint_uint32_t(struct aws_byte_cursor *cursor, uint3 // Zero out the first bit // 0x7F == b01111111 *result += ((castPtrValue & 0x7F) << bits); - AWS_RETURN_ERROR_IF2(aws_byte_cursor_advance(cursor, 1).ptr != NULL, AWS_OP_ERR); + AWS_RETURN_ERROR_IF2( + aws_byte_cursor_advance(cursor, 1).ptr != NULL, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DECODE_FAILURE); bits += 7; } castPtrValue = *cursor->ptr; - AWS_RETURN_ERROR_IF2(aws_byte_cursor_advance(cursor, 1).ptr != NULL, AWS_OP_ERR); + AWS_RETURN_ERROR_IF2( + aws_byte_cursor_advance(cursor, 1).ptr != NULL, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DECODE_FAILURE); // Zero out the first bit // 0x7F == b01111111 *result += ((castPtrValue & 0x7F) << bits); return AWS_OP_SUCCESS; } -static int s_aws_st_decode_lengthdelim(struct aws_byte_cursor *cursor, struct aws_byte_buf *buffer, int length) { +static int s_aws_st_decode_byte_range(struct aws_byte_cursor *cursor, struct aws_byte_buf *buffer, int length) { struct aws_byte_cursor temp = aws_byte_cursor_from_array(cursor->ptr, length); - AWS_RETURN_ERROR_IF2(aws_byte_buf_append_dynamic_secure(buffer, &temp) == 0, AWS_OP_ERR); + AWS_RETURN_ERROR_IF2( + aws_byte_buf_append_dynamic_secure(buffer, &temp) == 0, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DECODE_FAILURE); + return AWS_OP_SUCCESS; +} + +int aws_secure_tunnel_deserialize_varint_from_cursor_to_message( + struct aws_byte_cursor *cursor, + uint8_t field_number, + struct aws_secure_tunnel_message_view *message) { + uint32_t result = 0; + + if (s_iot_st_decode_varint_uint32_t(cursor, &result)) { + return AWS_OP_ERR; + } + + switch (field_number) { + case AWS_SECURE_TUNNEL_FN_TYPE: + message->type = result; + break; + case AWS_SECURE_TUNNEL_FN_STREAM_ID: + message->stream_id = result; + break; + case AWS_SECURE_TUNNEL_FN_IGNORABLE: + message->ignorable = result; + break; + default: + /* Unexpected field_number */ + break; + } + return AWS_OP_SUCCESS; } @@ -307,37 +342,22 @@ int aws_secure_tunnel_deserialize_message_from_cursor( message->ignorable = false; switch (wire_type) { - case AWS_SECURE_TUNNEL_PBWT_VARINT: { - uint32_t res = 0; - if (s_iot_st_decode_varint_uint32_t(cursor, &res)) { - return AWS_OP_ERR; - } - switch (field_number) { - case AWS_SECURE_TUNNEL_FN_TYPE: - message->type = res; - break; - case AWS_SECURE_TUNNEL_FN_STREAM_ID: - message->stream_id = res; - break; - case AWS_SECURE_TUNNEL_FN_IGNORABLE: - message->ignorable = res; - break; - default: - /* Unexpected field_number */ - break; + case AWS_SECURE_TUNNEL_PBWT_VARINT: + if (aws_secure_tunnel_deserialize_varint_from_cursor_to_message(cursor, field_number, message)) { + goto error; } - } break; + break; - case AWS_SECURE_TUNNEL_PBWT_LENGTH_DELIMINTED: { + case AWS_SECURE_TUNNEL_PBWT_LENGTH_DELIMITED: { uint32_t length = 0; if (s_iot_st_decode_varint_uint32_t(cursor, &length)) { goto error; } - switch (field_number) { case AWS_SECURE_TUNNEL_FN_PAYLOAD: + if (aws_byte_buf_init(&payload_buf, secure_tunnel->allocator, length) || - s_aws_st_decode_lengthdelim(cursor, &payload_buf, length)) { + s_aws_st_decode_byte_range(cursor, &payload_buf, length)) { goto error; } aws_byte_cursor_advance(cursor, length); @@ -347,8 +367,9 @@ int aws_secure_tunnel_deserialize_message_from_cursor( break; case AWS_SECURE_TUNNEL_FN_SERVICE_ID: + if (aws_byte_buf_init(&service_id_buf, secure_tunnel->allocator, length) || - s_aws_st_decode_lengthdelim(cursor, &service_id_buf, length)) { + s_aws_st_decode_byte_range(cursor, &service_id_buf, length)) { goto error; } aws_byte_cursor_advance(cursor, length); @@ -360,7 +381,7 @@ int aws_secure_tunnel_deserialize_message_from_cursor( case AWS_SECURE_TUNNEL_FN_AVAILABLE_SERVICE_IDS: AWS_ZERO_STRUCT(available_service_id_buf); if (aws_byte_buf_init(&available_service_id_buf, secure_tunnel->allocator, length) || - s_aws_st_decode_lengthdelim(cursor, &available_service_id_buf, length)) { + s_aws_st_decode_byte_range(cursor, &available_service_id_buf, length)) { goto error; } @@ -410,6 +431,7 @@ int aws_secure_tunnel_deserialize_message_from_cursor( } on_message_received(secure_tunnel, message); + aws_byte_buf_clean_up(&payload_buf); aws_byte_buf_clean_up(&service_id_buf); /* If any service ids were set, clear the ones that haven't been set by this message. */ From 824c96a75c18cd2457ca2677f50b724bef39489e Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 8 Feb 2023 11:11:50 -0800 Subject: [PATCH 30/69] serializer changes test --- include/aws/iotdevice/private/serializer.h | 1 - include/aws/iotdevice/secure_tunneling.h | 3 ++ source/secure_tunneling.c | 4 +- source/serializer.c | 46 ++++++++++++---------- 4 files changed, 30 insertions(+), 24 deletions(-) diff --git a/include/aws/iotdevice/private/serializer.h b/include/aws/iotdevice/private/serializer.h index 4f95d5e6..306f800c 100644 --- a/include/aws/iotdevice/private/serializer.h +++ b/include/aws/iotdevice/private/serializer.h @@ -53,7 +53,6 @@ int aws_iot_st_msg_serialize_from_view( AWS_IOTDEVICE_API int aws_secure_tunnel_deserialize_message_from_cursor( struct aws_secure_tunnel *secure_tunnel, - struct aws_secure_tunnel_message_view *message, struct aws_byte_cursor *cursor, aws_secure_tunnel_on_message_received_fn *on_message_received); diff --git a/include/aws/iotdevice/secure_tunneling.h b/include/aws/iotdevice/secure_tunneling.h index 88968335..db5438af 100644 --- a/include/aws/iotdevice/secure_tunneling.h +++ b/include/aws/iotdevice/secure_tunneling.h @@ -93,6 +93,9 @@ struct aws_secure_tunnel_message_view { * Secure tunnel multiplexing identifier */ struct aws_byte_cursor *service_id; + struct aws_byte_cursor *service_id_2; + struct aws_byte_cursor *service_id_3; + struct aws_byte_cursor *payload; }; diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index f23be49d..ddc1fc36 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -332,10 +332,8 @@ static void s_process_received_data(struct aws_secure_tunnel *secure_tunnel) { aws_byte_cursor_advance(&cursor, data_length); tmp_cursor = cursor; - struct aws_secure_tunnel_message_view message_view; - AWS_ZERO_STRUCT(message_view); aws_secure_tunnel_deserialize_message_from_cursor( - secure_tunnel, &message_view, &st_frame, &s_aws_secure_tunnel_connected_on_message_received); + secure_tunnel, &st_frame, &s_aws_secure_tunnel_connected_on_message_received); } if (cursor.ptr != received_data->buffer) { diff --git a/source/serializer.c b/source/serializer.c index 495f4b1e..741a7184 100644 --- a/source/serializer.c +++ b/source/serializer.c @@ -313,23 +313,25 @@ int aws_secure_tunnel_deserialize_varint_from_cursor_to_message( int aws_secure_tunnel_deserialize_message_from_cursor( struct aws_secure_tunnel *secure_tunnel, - struct aws_secure_tunnel_message_view *message, struct aws_byte_cursor *cursor, aws_secure_tunnel_on_message_received_fn *on_message_received) { 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; + int service_ids_set = 0; + struct aws_secure_tunnel_message_view message_view; + AWS_ZERO_STRUCT(message_view); + + /* STEVE TODO REMOVE THESE BUFS */ struct aws_byte_buf payload_buf; struct aws_byte_cursor payload_cur; struct aws_byte_buf service_id_buf; struct aws_byte_cursor service_id_cur; struct aws_byte_buf available_service_id_buf; - AWS_ZERO_STRUCT(payload_buf); AWS_ZERO_STRUCT(payload_cur); AWS_ZERO_STRUCT(service_id_buf); AWS_ZERO_STRUCT(service_id_cur); - int service_ids_set = 0; while ((aws_byte_cursor_is_valid(cursor)) && (cursor->len > 0)) { // wire_type is only the first 3 bits, Zeroing out the first 5 @@ -339,11 +341,11 @@ int aws_secure_tunnel_deserialize_message_from_cursor( aws_byte_cursor_advance(cursor, 1); /* ignorable defaults to false unless set to true in the incoming message*/ - message->ignorable = false; + message_view.ignorable = false; switch (wire_type) { case AWS_SECURE_TUNNEL_PBWT_VARINT: - if (aws_secure_tunnel_deserialize_varint_from_cursor_to_message(cursor, field_number, message)) { + if (aws_secure_tunnel_deserialize_varint_from_cursor_to_message(cursor, field_number, &message_view)) { goto error; } break; @@ -356,26 +358,30 @@ int aws_secure_tunnel_deserialize_message_from_cursor( switch (field_number) { case AWS_SECURE_TUNNEL_FN_PAYLOAD: - if (aws_byte_buf_init(&payload_buf, secure_tunnel->allocator, length) || - s_aws_st_decode_byte_range(cursor, &payload_buf, length)) { - goto error; - } - aws_byte_cursor_advance(cursor, length); + payload_cur = aws_byte_cursor_advance(cursor, length); + message_view.payload = &payload_cur; + // if (aws_byte_buf_init(&payload_buf, secure_tunnel->allocator, length) || + // s_aws_st_decode_byte_range(cursor, &payload_buf, length)) { + // goto error; + // } + // aws_byte_cursor_advance(cursor, length); - payload_cur = aws_byte_cursor_from_buf(&payload_buf); - message->payload = &payload_cur; + // payload_cur = aws_byte_cursor_from_buf(&payload_buf); + // message_view.payload = &payload_cur; break; case AWS_SECURE_TUNNEL_FN_SERVICE_ID: - if (aws_byte_buf_init(&service_id_buf, secure_tunnel->allocator, length) || - s_aws_st_decode_byte_range(cursor, &service_id_buf, length)) { - goto error; - } - aws_byte_cursor_advance(cursor, length); + service_id_cur = aws_byte_cursor_advance(cursor, length); + message_view.service_id = &service_id_cur; + // if (aws_byte_buf_init(&service_id_buf, secure_tunnel->allocator, length) || + // s_aws_st_decode_byte_range(cursor, &service_id_buf, length)) { + // goto error; + // } + // aws_byte_cursor_advance(cursor, length); - service_id_cur = aws_byte_cursor_from_buf(&service_id_buf); - message->service_id = &service_id_cur; + // service_id_cur = aws_byte_cursor_from_buf(&service_id_buf); + // message_view.service_id = &service_id_cur; break; case AWS_SECURE_TUNNEL_FN_AVAILABLE_SERVICE_IDS: @@ -430,7 +436,7 @@ int aws_secure_tunnel_deserialize_message_from_cursor( } } - on_message_received(secure_tunnel, message); + on_message_received(secure_tunnel, &message_view); aws_byte_buf_clean_up(&payload_buf); aws_byte_buf_clean_up(&service_id_buf); From bfcff0ec2b8e755a2c4a440212919e00aca5e823 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 8 Feb 2023 11:30:07 -0800 Subject: [PATCH 31/69] serializer simplification --- source/secure_tunneling.c | 30 ++++++++++-- source/serializer.c | 96 ++++++++++----------------------------- 2 files changed, 48 insertions(+), 78 deletions(-) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index ddc1fc36..db69a511 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -252,22 +252,42 @@ static void s_aws_secure_tunnel_on_session_reset_received(struct aws_secure_tunn } } -static void s_aws_secure_tunnel_on_service_ids_received(struct aws_secure_tunnel *secure_tunnel) { - if (secure_tunnel->config->service_id_1) { +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 (message_view->service_id != NULL) { + if (secure_tunnel->config->service_id_1) { + aws_string_destroy(secure_tunnel->config->service_id_1); + } + secure_tunnel->config->service_id_1 = + aws_string_new_from_cursor(secure_tunnel->allocator, message_view->service_id); AWS_LOGF_INFO( AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: secure tunnel service id 1 set to: " PRInSTR, (void *)secure_tunnel, AWS_BYTE_CURSOR_PRI(aws_byte_cursor_from_string(secure_tunnel->config->service_id_1))); } - if (secure_tunnel->config->service_id_2) { + + if (message_view->service_id_2 != NULL) { + if (secure_tunnel->config->service_id_2) { + aws_string_destroy(secure_tunnel->config->service_id_2); + } + secure_tunnel->config->service_id_2 = + aws_string_new_from_cursor(secure_tunnel->allocator, message_view->service_id_2); AWS_LOGF_INFO( AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: secure tunnel service id 2 set to: " PRInSTR, (void *)secure_tunnel, AWS_BYTE_CURSOR_PRI(aws_byte_cursor_from_string(secure_tunnel->config->service_id_2))); } - if (secure_tunnel->config->service_id_3) { + + if (message_view->service_id_3 != NULL) { + if (secure_tunnel->config->service_id_3) { + aws_string_destroy(secure_tunnel->config->service_id_3); + } + secure_tunnel->config->service_id_3 = + aws_string_new_from_cursor(secure_tunnel->allocator, message_view->service_id_3); AWS_LOGF_INFO( AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: secure tunnel service id 3 set to: " PRInSTR, @@ -300,7 +320,7 @@ static void s_aws_secure_tunnel_connected_on_message_received( 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); + s_aws_secure_tunnel_on_service_ids_received(secure_tunnel, message_view); break; case AWS_SECURE_TUNNEL_MT_CONNECTION_START: case AWS_SECURE_TUNNEL_MT_CONNECTION_RESET: diff --git a/source/serializer.c b/source/serializer.c index 741a7184..a4f0ca4a 100644 --- a/source/serializer.c +++ b/source/serializer.c @@ -276,12 +276,12 @@ static int s_iot_st_decode_varint_uint32_t(struct aws_byte_cursor *cursor, uint3 return AWS_OP_SUCCESS; } -static int s_aws_st_decode_byte_range(struct aws_byte_cursor *cursor, struct aws_byte_buf *buffer, int length) { - struct aws_byte_cursor temp = aws_byte_cursor_from_array(cursor->ptr, length); - AWS_RETURN_ERROR_IF2( - aws_byte_buf_append_dynamic_secure(buffer, &temp) == 0, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DECODE_FAILURE); - return AWS_OP_SUCCESS; -} +// static int s_aws_st_decode_byte_range(struct aws_byte_cursor *cursor, struct aws_byte_buf *buffer, int length) { +// struct aws_byte_cursor temp = aws_byte_cursor_from_array(cursor->ptr, length); +// AWS_RETURN_ERROR_IF2( +// aws_byte_buf_append_dynamic_secure(buffer, &temp) == 0, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DECODE_FAILURE); +// return AWS_OP_SUCCESS; +// } int aws_secure_tunnel_deserialize_varint_from_cursor_to_message( struct aws_byte_cursor *cursor, @@ -322,16 +322,14 @@ int aws_secure_tunnel_deserialize_message_from_cursor( struct aws_secure_tunnel_message_view message_view; AWS_ZERO_STRUCT(message_view); - /* STEVE TODO REMOVE THESE BUFS */ - struct aws_byte_buf payload_buf; struct aws_byte_cursor payload_cur; - struct aws_byte_buf service_id_buf; - struct aws_byte_cursor service_id_cur; - struct aws_byte_buf available_service_id_buf; - AWS_ZERO_STRUCT(payload_buf); + struct aws_byte_cursor service_id_1_cur; + struct aws_byte_cursor service_id_2_cur; + struct aws_byte_cursor service_id_3_cur; AWS_ZERO_STRUCT(payload_cur); - AWS_ZERO_STRUCT(service_id_buf); - AWS_ZERO_STRUCT(service_id_cur); + AWS_ZERO_STRUCT(service_id_1_cur); + AWS_ZERO_STRUCT(service_id_2_cur); + AWS_ZERO_STRUCT(service_id_3_cur); while ((aws_byte_cursor_is_valid(cursor)) && (cursor->len > 0)) { // wire_type is only the first 3 bits, Zeroing out the first 5 @@ -351,76 +349,41 @@ int aws_secure_tunnel_deserialize_message_from_cursor( break; case AWS_SECURE_TUNNEL_PBWT_LENGTH_DELIMITED: { + uint32_t length = 0; if (s_iot_st_decode_varint_uint32_t(cursor, &length)) { goto error; } + switch (field_number) { case AWS_SECURE_TUNNEL_FN_PAYLOAD: - payload_cur = aws_byte_cursor_advance(cursor, length); message_view.payload = &payload_cur; - // if (aws_byte_buf_init(&payload_buf, secure_tunnel->allocator, length) || - // s_aws_st_decode_byte_range(cursor, &payload_buf, length)) { - // goto error; - // } - // aws_byte_cursor_advance(cursor, length); - - // payload_cur = aws_byte_cursor_from_buf(&payload_buf); - // message_view.payload = &payload_cur; break; case AWS_SECURE_TUNNEL_FN_SERVICE_ID: - - service_id_cur = aws_byte_cursor_advance(cursor, length); - message_view.service_id = &service_id_cur; - // if (aws_byte_buf_init(&service_id_buf, secure_tunnel->allocator, length) || - // s_aws_st_decode_byte_range(cursor, &service_id_buf, length)) { - // goto error; - // } - // aws_byte_cursor_advance(cursor, length); - - // service_id_cur = aws_byte_cursor_from_buf(&service_id_buf); - // message_view.service_id = &service_id_cur; + service_id_1_cur = aws_byte_cursor_advance(cursor, length); + message_view.service_id = &service_id_1_cur; break; case AWS_SECURE_TUNNEL_FN_AVAILABLE_SERVICE_IDS: - AWS_ZERO_STRUCT(available_service_id_buf); - if (aws_byte_buf_init(&available_service_id_buf, secure_tunnel->allocator, length) || - s_aws_st_decode_byte_range(cursor, &available_service_id_buf, length)) { - goto error; - } - - aws_byte_cursor_advance(cursor, length); switch (service_ids_set) { case 0: - if (secure_tunnel->config->service_id_1) { - aws_string_destroy(secure_tunnel->config->service_id_1); - } - secure_tunnel->config->service_id_1 = - aws_string_new_from_buf(secure_tunnel->allocator, &available_service_id_buf); + service_id_1_cur = aws_byte_cursor_advance(cursor, length); + message_view.service_id = &service_id_1_cur; break; case 1: - if (secure_tunnel->config->service_id_2) { - aws_string_destroy(secure_tunnel->config->service_id_2); - } - secure_tunnel->config->service_id_2 = - aws_string_new_from_buf(secure_tunnel->allocator, &available_service_id_buf); + service_id_2_cur = aws_byte_cursor_advance(cursor, length); + message_view.service_id_2 = &service_id_2_cur; break; case 2: - if (secure_tunnel->config->service_id_3) { - aws_string_destroy(secure_tunnel->config->service_id_3); - } - secure_tunnel->config->service_id_3 = - aws_string_new_from_buf(secure_tunnel->allocator, &available_service_id_buf); + service_id_3_cur = aws_byte_cursor_advance(cursor, length); + message_view.service_id_3 = &service_id_3_cur; break; default: - aws_byte_buf_clean_up(&available_service_id_buf); goto error; break; } - - aws_byte_buf_clean_up(&available_service_id_buf); service_ids_set++; break; } @@ -438,23 +401,10 @@ int aws_secure_tunnel_deserialize_message_from_cursor( on_message_received(secure_tunnel, &message_view); - aws_byte_buf_clean_up(&payload_buf); - aws_byte_buf_clean_up(&service_id_buf); - /* If any service ids were set, clear the ones that haven't been set by this message. */ - if (service_ids_set) { - switch (service_ids_set) { - case 1: - aws_string_destroy(secure_tunnel->config->service_id_2); - case 2: - aws_string_destroy(secure_tunnel->config->service_id_3); - } - } return AWS_OP_SUCCESS; error: - aws_byte_buf_clean_up(&payload_buf); - aws_byte_buf_clean_up(&service_id_buf); - return AWS_OP_ERR; + 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) { From 08c55e905b57bed9812370edcd99766cbf144c75 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 8 Feb 2023 11:59:46 -0800 Subject: [PATCH 32/69] formatting --- source/secure_tunneling_operations.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/source/secure_tunneling_operations.c b/source/secure_tunneling_operations.c index 9ac901b5..6f9def80 100644 --- a/source/secure_tunneling_operations.c +++ b/source/secure_tunneling_operations.c @@ -101,20 +101,12 @@ void aws_secure_tunnel_message_view_log( return; } - 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->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, + "id=%p: aws_secure_tunnel_message_view service_id set to '" PRInSTR "'", (void *)message_view, AWS_BYTE_CURSOR_PRI(*message_view->service_id)); } else { @@ -126,6 +118,14 @@ void aws_secure_tunnel_message_view_log( (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); + if (message_view->payload != NULL) { AWS_LOGUF( log_handle, From cb5eb9f9a5008515f3db2c621e6075c02d1150fa Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 8 Feb 2023 13:05:43 -0800 Subject: [PATCH 33/69] capacity check fix --- source/serializer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/serializer.c b/source/serializer.c index a4f0ca4a..8f30adde 100644 --- a/source/serializer.c +++ b/source/serializer.c @@ -237,7 +237,7 @@ int aws_iot_st_msg_serialize_from_view( } } - if (buffer->capacity < AWS_IOT_ST_MAX_MESSAGE_SIZE) { + if (buffer->capacity > AWS_IOT_ST_MAX_MESSAGE_SIZE) { aws_raise_error(AWS_ERROR_INVALID_BUFFER_SIZE); goto cleanup; } From 55e7ebbbd3e9df1d91b7d614e5423e90a20a9658 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 8 Feb 2023 14:27:37 -0800 Subject: [PATCH 34/69] pr changes --- .../private/secure_tunneling_operations.h | 6 ++--- source/secure_tunneling.c | 27 +++++++++++-------- source/secure_tunneling_operations.c | 14 +++++----- source/serializer.c | 12 +++------ 4 files changed, 28 insertions(+), 31 deletions(-) diff --git a/include/aws/iotdevice/private/secure_tunneling_operations.h b/include/aws/iotdevice/private/secure_tunneling_operations.h index 8deba8a3..ffea59a1 100644 --- a/include/aws/iotdevice/private/secure_tunneling_operations.h +++ b/include/aws/iotdevice/private/secure_tunneling_operations.h @@ -45,8 +45,8 @@ struct aws_secure_tunnel_operation_vtable { int error_code, const void *completion_view); - /* Set the stream id of outgoing st_msg */ - int (*aws_secure_tunnel_operation_set_stream_id_fn)( + /* Set the stream id of outgoing st_msg based on current service id */ + int (*aws_secure_tunnel_operation_assign_stream_id_fn)( struct aws_secure_tunnel_operation *operation, struct aws_secure_tunnel *secure_tunnel); @@ -100,7 +100,7 @@ AWS_IOTDEVICE_API void aws_secure_tunnel_operation_complete( int error_code, const void *associated_view); -AWS_IOTDEVICE_API void aws_secure_tunnel_operation_set_stream_id( +AWS_IOTDEVICE_API void aws_secure_tunnel_operation_assign_stream_id( struct aws_secure_tunnel_operation *operation, struct aws_secure_tunnel *secure_tunnel); diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index db69a511..924c8b6c 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -256,10 +256,21 @@ static void s_aws_secure_tunnel_on_service_ids_received( struct aws_secure_tunnel *secure_tunnel, struct aws_secure_tunnel_message_view *message_view) { + /* Clean up existing service ids first */ + if (secure_tunnel->config->service_id_1) { + aws_string_destroy(secure_tunnel->config->service_id_1); + secure_tunnel->config->service_id_1 = NULL; + } + if (secure_tunnel->config->service_id_2) { + aws_string_destroy(secure_tunnel->config->service_id_2); + secure_tunnel->config->service_id_2 = NULL; + } + if (secure_tunnel->config->service_id_3) { + aws_string_destroy(secure_tunnel->config->service_id_3); + secure_tunnel->config->service_id_3 = NULL; + } + if (message_view->service_id != NULL) { - if (secure_tunnel->config->service_id_1) { - aws_string_destroy(secure_tunnel->config->service_id_1); - } secure_tunnel->config->service_id_1 = aws_string_new_from_cursor(secure_tunnel->allocator, message_view->service_id); AWS_LOGF_INFO( @@ -270,9 +281,6 @@ static void s_aws_secure_tunnel_on_service_ids_received( } if (message_view->service_id_2 != NULL) { - if (secure_tunnel->config->service_id_2) { - aws_string_destroy(secure_tunnel->config->service_id_2); - } secure_tunnel->config->service_id_2 = aws_string_new_from_cursor(secure_tunnel->allocator, message_view->service_id_2); AWS_LOGF_INFO( @@ -283,9 +291,6 @@ static void s_aws_secure_tunnel_on_service_ids_received( } if (message_view->service_id_3 != NULL) { - if (secure_tunnel->config->service_id_3) { - aws_string_destroy(secure_tunnel->config->service_id_3); - } secure_tunnel->config->service_id_3 = aws_string_new_from_cursor(secure_tunnel->allocator, message_view->service_id_3); AWS_LOGF_INFO( @@ -1241,7 +1246,7 @@ int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure break; case AWS_STOT_MESSAGE: /* If a data message attempts to be sent on an unopen stream, discard it. */ - if ((*current_operation->vtable->aws_secure_tunnel_operation_set_stream_id_fn)( + if ((*current_operation->vtable->aws_secure_tunnel_operation_assign_stream_id_fn)( current_operation, secure_tunnel)) { error_code = aws_last_error(); @@ -1289,7 +1294,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_set_stream_id_fn)( + if ((*current_operation->vtable->aws_secure_tunnel_operation_assign_stream_id_fn)( current_operation, secure_tunnel)) { error_code = aws_last_error(); AWS_LOGF_DEBUG( diff --git a/source/secure_tunneling_operations.c b/source/secure_tunneling_operations.c index 6f9def80..2b0af181 100644 --- a/source/secure_tunneling_operations.c +++ b/source/secure_tunneling_operations.c @@ -13,8 +13,6 @@ #include #include -/* STEVE TODO explain max payload size value */ -#define MAX_PAYLOAD_SIZE 64512 #define INVALID_STREAM_ID 0 /********************************************************************************************************************* * Operation base @@ -49,18 +47,18 @@ void aws_secure_tunnel_operation_complete( } } -void aws_secure_tunnel_operation_set_stream_id( +void aws_secure_tunnel_operation_assign_stream_id( struct aws_secure_tunnel_operation *operation, struct aws_secure_tunnel *secure_tunnel) { AWS_FATAL_ASSERT(operation->vtable != NULL); - if (operation->vtable->aws_secure_tunnel_operation_set_stream_id_fn != NULL) { - (*operation->vtable->aws_secure_tunnel_operation_set_stream_id_fn)(operation, secure_tunnel); + if (operation->vtable->aws_secure_tunnel_operation_assign_stream_id_fn != NULL) { + (*operation->vtable->aws_secure_tunnel_operation_assign_stream_id_fn)(operation, secure_tunnel); } } static struct aws_secure_tunnel_operation_vtable s_empty_operation_vtable = { .aws_secure_tunnel_operation_completion_fn = NULL, - .aws_secure_tunnel_operation_set_stream_id_fn = NULL, + .aws_secure_tunnel_operation_assign_stream_id_fn = NULL, .aws_secure_tunnel_operation_set_next_stream_id_fn = NULL, }; @@ -82,7 +80,7 @@ int aws_secure_tunnel_message_view_validate(const struct aws_secure_tunnel_messa return aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_OPTIONS_VALIDATION); } - if (message_view->payload != NULL && message_view->payload->len > MAX_PAYLOAD_SIZE) { + if (message_view->payload != NULL && message_view->payload->len > AWS_IOT_ST_MAX_MESSAGE_SIZE) { AWS_LOGF_ERROR( AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: aws_secure_tunnel_message_view - payload too long", @@ -303,7 +301,7 @@ static int s_aws_secure_tunnel_operation_message_set_next_stream_id( } static struct aws_secure_tunnel_operation_vtable s_message_operation_vtable = { - .aws_secure_tunnel_operation_set_stream_id_fn = s_aws_secure_tunnel_operation_message_set_stream_id, + .aws_secure_tunnel_operation_assign_stream_id_fn = s_aws_secure_tunnel_operation_message_set_stream_id, .aws_secure_tunnel_operation_set_next_stream_id_fn = s_aws_secure_tunnel_operation_message_set_next_stream_id, }; diff --git a/source/serializer.c b/source/serializer.c index 8f30adde..dcada35c 100644 --- a/source/serializer.c +++ b/source/serializer.c @@ -276,13 +276,6 @@ static int s_iot_st_decode_varint_uint32_t(struct aws_byte_cursor *cursor, uint3 return AWS_OP_SUCCESS; } -// static int s_aws_st_decode_byte_range(struct aws_byte_cursor *cursor, struct aws_byte_buf *buffer, int length) { -// struct aws_byte_cursor temp = aws_byte_cursor_from_array(cursor->ptr, length); -// AWS_RETURN_ERROR_IF2( -// aws_byte_buf_append_dynamic_secure(buffer, &temp) == 0, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DECODE_FAILURE); -// return AWS_OP_SUCCESS; -// } - int aws_secure_tunnel_deserialize_varint_from_cursor_to_message( struct aws_byte_cursor *cursor, uint8_t field_number, @@ -318,15 +311,16 @@ int aws_secure_tunnel_deserialize_message_from_cursor( 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; - int service_ids_set = 0; struct aws_secure_tunnel_message_view message_view; AWS_ZERO_STRUCT(message_view); struct aws_byte_cursor payload_cur; + AWS_ZERO_STRUCT(payload_cur); + + int service_ids_set = 0; struct aws_byte_cursor service_id_1_cur; struct aws_byte_cursor service_id_2_cur; struct aws_byte_cursor service_id_3_cur; - AWS_ZERO_STRUCT(payload_cur); AWS_ZERO_STRUCT(service_id_1_cur); AWS_ZERO_STRUCT(service_id_2_cur); AWS_ZERO_STRUCT(service_id_3_cur); From 9332167834bee76385d4677a3af65dd308f766e8 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Thu, 9 Feb 2023 15:10:50 -0800 Subject: [PATCH 35/69] logging --- source/secure_tunneling.c | 2 -- source/secure_tunneling_operations.c | 8 ++++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 924c8b6c..0ef5707f 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -1932,8 +1932,6 @@ int aws_secure_tunnel_stream_reset( goto error; } - s_aws_secure_tunnel_set_stream_id(secure_tunnel, message_options->service_id, message_options->stream_id); - return AWS_OP_SUCCESS; error: diff --git a/source/secure_tunneling_operations.c b/source/secure_tunneling_operations.c index 2b0af181..eada22f3 100644 --- a/source/secure_tunneling_operations.c +++ b/source/secure_tunneling_operations.c @@ -297,6 +297,14 @@ 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; } From 4c42d98469272f0b3eeffcef8cf3f8bbce4b52bd Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Thu, 9 Feb 2023 15:23:19 -0800 Subject: [PATCH 36/69] added logging --- source/secure_tunneling.c | 25 +++++++++++++++++-------- source/secure_tunneling_operations.c | 4 ++-- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 0ef5707f..d92040c6 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -1251,12 +1251,23 @@ int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure error_code = aws_last_error(); - AWS_LOGF_DEBUG( - 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 (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)); + } } else { /* Send the Data message through the WebSocket */ if (s_secure_tunneling_send(secure_tunnel, current_operation->message_view)) { @@ -1900,8 +1911,6 @@ int aws_secure_tunnel_stream_start( goto error; } - s_aws_secure_tunnel_set_stream_id(secure_tunnel, message_options->service_id, message_options->stream_id); - return AWS_OP_SUCCESS; error: diff --git a/source/secure_tunneling_operations.c b/source/secure_tunneling_operations.c index eada22f3..69f1eb77 100644 --- a/source/secure_tunneling_operations.c +++ b/source/secure_tunneling_operations.c @@ -200,7 +200,7 @@ void aws_secure_tunnel_message_storage_clean_up(struct aws_secure_tunnel_message } /* 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_set_stream_id( +static int s_aws_secure_tunnel_operation_message_assign_stream_id( struct aws_secure_tunnel_operation *operation, struct aws_secure_tunnel *secure_tunnel) { @@ -309,7 +309,7 @@ static int s_aws_secure_tunnel_operation_message_set_next_stream_id( } 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_set_stream_id, + .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, }; From e68753543f6f18131da8b0538e2595ffba013b1e Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Thu, 9 Feb 2023 16:33:29 -0800 Subject: [PATCH 37/69] add error logging --- source/secure_tunneling.c | 12 ++++++++++-- source/serializer.c | 7 +++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index d92040c6..472cf7e4 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -357,8 +357,16 @@ static void s_process_received_data(struct aws_secure_tunnel *secure_tunnel) { aws_byte_cursor_advance(&cursor, data_length); tmp_cursor = cursor; - aws_secure_tunnel_deserialize_message_from_cursor( - secure_tunnel, &st_frame, &s_aws_secure_tunnel_connected_on_message_received); + 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)); + } } if (cursor.ptr != received_data->buffer) { diff --git a/source/serializer.c b/source/serializer.c index dcada35c..241742d8 100644 --- a/source/serializer.c +++ b/source/serializer.c @@ -308,6 +308,13 @@ int aws_secure_tunnel_deserialize_message_from_cursor( struct aws_secure_tunnel *secure_tunnel, struct aws_byte_cursor *cursor, aws_secure_tunnel_on_message_received_fn *on_message_received) { + + AWS_LOGF_DEBUG( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: deserializing message from cursor of size %zu.", + (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; From abdd57fe2a7b29da413704ccbb4eff1c16bf106d Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Thu, 9 Feb 2023 16:40:09 -0800 Subject: [PATCH 38/69] add more logging --- source/serializer.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source/serializer.c b/source/serializer.c index 241742d8..63cccaea 100644 --- a/source/serializer.c +++ b/source/serializer.c @@ -200,6 +200,12 @@ int aws_iot_st_msg_serialize_from_view( return AWS_OP_ERR; } + AWS_LOGF_DEBUG( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: serializing message from view of size %zu.", + (void *)secure_tunnel, + message_total_length); + if (aws_byte_buf_init(buffer, allocator, message_total_length) != AWS_OP_SUCCESS) { return AWS_OP_ERR; } From f548e39ab53b9fa3489334009b363b0cf79a9a41 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Thu, 9 Feb 2023 16:41:25 -0800 Subject: [PATCH 39/69] fix log --- source/serializer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/serializer.c b/source/serializer.c index 63cccaea..08385b34 100644 --- a/source/serializer.c +++ b/source/serializer.c @@ -203,7 +203,7 @@ int aws_iot_st_msg_serialize_from_view( AWS_LOGF_DEBUG( AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: serializing message from view of size %zu.", - (void *)secure_tunnel, + (void *)message_view, message_total_length); if (aws_byte_buf_init(buffer, allocator, message_total_length) != AWS_OP_SUCCESS) { From 0c706393eb01fc33dbbc400d6147a2a977de83d5 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Thu, 9 Feb 2023 17:04:01 -0800 Subject: [PATCH 40/69] expand logging --- source/secure_tunneling.c | 1 + source/secure_tunneling_operations.c | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 472cf7e4..584a73da 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -309,6 +309,7 @@ static void s_aws_secure_tunnel_on_service_ids_received( 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: if (secure_tunnel->config->on_message_received) { diff --git a/source/secure_tunneling_operations.c b/source/secure_tunneling_operations.c index 69f1eb77..5c4d4460 100644 --- a/source/secure_tunneling_operations.c +++ b/source/secure_tunneling_operations.c @@ -99,6 +99,14 @@ void aws_secure_tunnel_message_view_log( return; } + AWS_LOGUF( + log_handle, + level, + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: aws_secure_tunnel_message_view type '%s'", + (void *)message_view, + aws_secure_tunnel_message_type_to_c_string(message_view->type)); + if (message_view->service_id != NULL) { AWS_LOGUF( log_handle, From 611dc840e96dd2b66f6b5f7259d0c755a8803bc3 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Fri, 10 Feb 2023 10:25:43 -0800 Subject: [PATCH 41/69] add connection_view for connection complete settings --- include/aws/iotdevice/secure_tunneling.h | 14 +++++++++++++- source/secure_tunneling.c | 11 +++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/include/aws/iotdevice/secure_tunneling.h b/include/aws/iotdevice/secure_tunneling.h index db5438af..0c4cf0bb 100644 --- a/include/aws/iotdevice/secure_tunneling.h +++ b/include/aws/iotdevice/secure_tunneling.h @@ -99,6 +99,15 @@ struct aws_secure_tunnel_message_view { struct aws_byte_cursor *payload; }; +/** + * Read-only snapshot of a Secure Tunnel Connection Completion Data + */ +struct aws_secure_tunnel_connection_view { + struct aws_byte_cursor *service_id_1; + struct aws_byte_cursor *service_id_2; + struct aws_byte_cursor *service_id_3; +}; + /* Callbacks */ /** @@ -107,7 +116,10 @@ struct aws_secure_tunnel_message_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)(int error_code, 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_send_data_complete_fn)(int error_code, void *user_data); typedef void(aws_secure_tunneling_on_stream_start_fn)( diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 584a73da..d459b805 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -300,9 +300,16 @@ static void s_aws_secure_tunnel_on_service_ids_received( AWS_BYTE_CURSOR_PRI(aws_byte_cursor_from_string(secure_tunnel->config->service_id_3))); } + struct aws_secure_tunnel_connection_view connection_view; + AWS_ZERO_STRUCT(connection_view); + connection_view.service_id_1 = message_view->service_id; + connection_view.service_id_2 = message_view->service_id_2; + connection_view.service_id_3 = message_view->service_id_3; + /* A connection can only be used once available service ids are established with the secure tunnel. */ if (secure_tunnel->config->on_connection_complete) { - secure_tunnel->config->on_connection_complete(AWS_ERROR_SUCCESS, secure_tunnel->config->user_data); + secure_tunnel->config->on_connection_complete( + &connection_view, AWS_ERROR_SUCCESS, secure_tunnel->config->user_data); } } @@ -634,7 +641,7 @@ static void s_on_websocket_setup(const struct aws_websocket_on_connection_setup_ /* Report a failed WebSocket Upgrade attempt */ if (setup->error_code && secure_tunnel->config->on_connection_complete) { - secure_tunnel->config->on_connection_complete(setup->error_code, secure_tunnel->config->user_data); + secure_tunnel->config->on_connection_complete(NULL, setup->error_code, secure_tunnel->config->user_data); } /* Failed/Successful websocket creation and associated errors logged by "websocket-setup" */ From 0cb7f759af8e4b0b87ce2a2726888a7d16d17253 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Fri, 10 Feb 2023 12:14:37 -0800 Subject: [PATCH 42/69] handle zero length service id edge case --- source/secure_tunneling_operations.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/secure_tunneling_operations.c b/source/secure_tunneling_operations.c index 5c4d4460..b5bc4347 100644 --- a/source/secure_tunneling_operations.c +++ b/source/secure_tunneling_operations.c @@ -267,7 +267,7 @@ 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) { + if (message_view->service_id != NULL && message_view->service_id->len > 0) { struct aws_string *service_id = NULL; service_id = aws_string_new_from_cursor(secure_tunnel->allocator, message_view->service_id); From 959c68247f1f19f3ccaea8b825da2cf8bcdb8e1f Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 13 Feb 2023 10:19:57 -0800 Subject: [PATCH 43/69] report a failed connection attempt --- source/secure_tunneling.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index d459b805..26e1362d 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -610,6 +610,12 @@ static void s_secure_tunnel_setup(struct aws_client_bootstrap *bootstrap, int er struct aws_secure_tunnel *secure_tunnel = user_data; if (error_code != AWS_OP_SUCCESS) { + if (secure_tunnel->config->on_connection_complete) { + if (secure_tunnel->config->on_connection_complete) { + secure_tunnel->config->on_connection_complete( + NULL, AWS_ERROR_SUCCESS, secure_tunnel->config->user_data); + } + } s_on_websocket_shutdown(secure_tunnel->websocket, error_code, secure_tunnel); return; } From 442300cb41f5e9f7323bfd59d8882cf4630bff79 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 13 Feb 2023 10:21:40 -0800 Subject: [PATCH 44/69] actually pass along the error code --- source/secure_tunneling.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 26e1362d..31e2e3a5 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -612,8 +612,7 @@ static void s_secure_tunnel_setup(struct aws_client_bootstrap *bootstrap, int er if (error_code != AWS_OP_SUCCESS) { if (secure_tunnel->config->on_connection_complete) { if (secure_tunnel->config->on_connection_complete) { - secure_tunnel->config->on_connection_complete( - NULL, AWS_ERROR_SUCCESS, secure_tunnel->config->user_data); + secure_tunnel->config->on_connection_complete(NULL, error_code, secure_tunnel->config->user_data); } } s_on_websocket_shutdown(secure_tunnel->websocket, error_code, secure_tunnel); From 539da71622ea6b5133e2b8a8ebc201428c353893 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Tue, 14 Feb 2023 10:29:21 -0800 Subject: [PATCH 45/69] add an on_stopped callback --- include/aws/iotdevice/private/secure_tunneling_impl.h | 1 + include/aws/iotdevice/secure_tunneling.h | 2 ++ source/secure_tunneling.c | 4 ++++ source/secure_tunneling_operations.c | 1 + 4 files changed, 8 insertions(+) diff --git a/include/aws/iotdevice/private/secure_tunneling_impl.h b/include/aws/iotdevice/private/secure_tunneling_impl.h index 60ac120e..775f7284 100644 --- a/include/aws/iotdevice/private/secure_tunneling_impl.h +++ b/include/aws/iotdevice/private/secure_tunneling_impl.h @@ -134,6 +134,7 @@ struct aws_secure_tunnel_options_storage { 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; + aws_secure_tunneling_on_stopped_fn *on_stopped; aws_secure_tunneling_on_send_data_complete_fn *on_send_data_complete; aws_secure_tunneling_on_termination_complete_fn *on_termination_complete; diff --git a/include/aws/iotdevice/secure_tunneling.h b/include/aws/iotdevice/secure_tunneling.h index 0c4cf0bb..ea51c2ad 100644 --- a/include/aws/iotdevice/secure_tunneling.h +++ b/include/aws/iotdevice/secure_tunneling.h @@ -131,6 +131,7 @@ typedef void(aws_secure_tunneling_on_stream_reset_fn)( 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); /** @@ -184,6 +185,7 @@ struct aws_secure_tunnel_options { 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; + aws_secure_tunneling_on_stopped_fn *on_stopped; aws_secure_tunneling_on_termination_complete_fn *on_termination_complete; }; diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 31e2e3a5..13c4c950 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -858,6 +858,10 @@ static void s_change_current_state_to_stopped(struct aws_secure_tunnel *secure_t /* 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); + + if (secure_tunnel->config->on_stopped) { + secure_tunnel->config->on_stopped(secure_tunnel->config->user_data); + } } static void s_change_current_state_to_connecting(struct aws_secure_tunnel *secure_tunnel) { diff --git a/source/secure_tunneling_operations.c b/source/secure_tunneling_operations.c index b5bc4347..06c1ee4b 100644 --- a/source/secure_tunneling_operations.c +++ b/source/secure_tunneling_operations.c @@ -668,6 +668,7 @@ struct aws_secure_tunnel_options_storage *aws_secure_tunnel_options_storage_new( storage->on_stream_start = options->on_stream_start; storage->on_stream_reset = options->on_stream_reset; storage->on_session_reset = options->on_session_reset; + storage->on_stopped = options->on_stopped; storage->on_termination_complete = options->on_termination_complete; return storage; From 176e38bc10b4c07293c66f8e8deea93fd316a3f0 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Tue, 14 Feb 2023 15:12:07 -0800 Subject: [PATCH 46/69] give options_storage its allocator to use during cleanup --- source/secure_tunneling_operations.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/secure_tunneling_operations.c b/source/secure_tunneling_operations.c index 06c1ee4b..9feb4f5c 100644 --- a/source/secure_tunneling_operations.c +++ b/source/secure_tunneling_operations.c @@ -601,6 +601,8 @@ struct aws_secure_tunnel_options_storage *aws_secure_tunnel_options_storage_new( struct aws_secure_tunnel_options_storage *storage = aws_mem_calloc(allocator, 1, sizeof(struct aws_secure_tunnel_options_storage)); + storage->allocator = allocator; + storage->socket_options = *options->socket_options; storage->endpoint_host = aws_string_new_from_cursor(allocator, &options->endpoint_host); if (storage->endpoint_host == NULL) { From 05d26046b8c26e8853eded9e19b36abb24a33182 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 15 Feb 2023 10:33:11 -0800 Subject: [PATCH 47/69] added /bin files --- bin/securetunnel/CMakeLists.txt | 29 ++ bin/securetunnel/main.c | 512 ++++++++++++++++++++++++++++++++ 2 files changed, 541 insertions(+) create mode 100644 bin/securetunnel/CMakeLists.txt create mode 100644 bin/securetunnel/main.c diff --git a/bin/securetunnel/CMakeLists.txt b/bin/securetunnel/CMakeLists.txt new file mode 100644 index 00000000..63b9845b --- /dev/null +++ b/bin/securetunnel/CMakeLists.txt @@ -0,0 +1,29 @@ +project(securetunnel C) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_INSTALL_PREFIX}/lib/cmake") + +file(GLOB SECURETUNNEL_SRC + "*.c" + ) + +set(SECURETUNNEL_PROJECT_NAME securetunnel) +add_executable(${SECURETUNNEL_PROJECT_NAME} ${SECURETUNNEL_SRC}) +aws_set_common_properties(${SECURETUNNEL_PROJECT_NAME}) + + +target_include_directories(${SECURETUNNEL_PROJECT_NAME} PUBLIC + $ + $) + +target_link_libraries(${SECURETUNNEL_PROJECT_NAME} aws-c-iot) + +if (BUILD_SHARED_LIBS AND NOT WIN32) + message(INFO " securetunnel will be built with shared libs, but you may need to set LD_LIBRARY_PATH=${CMAKE_INSTALL_PREFIX}/lib to run the application") +endif() + +install(TARGETS ${SECURETUNNEL_PROJECT_NAME} + EXPORT ${SECURETUNNEL_PROJECT_NAME}-targets + COMPONENT Runtime + RUNTIME + DESTINATION bin + COMPONENT Runtime) diff --git a/bin/securetunnel/main.c b/bin/securetunnel/main.c new file mode 100644 index 00000000..e7f3aa8d --- /dev/null +++ b/bin/securetunnel/main.c @@ -0,0 +1,512 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#define SLEEP_TIME_NS 1000000000 +#define MAX_WEBSOCKET_PAYLOAD 131076 + +struct app_ctx { + struct aws_allocator *allocator; + struct aws_secure_tunnel *secure_tunnel; + struct aws_mutex lock; + struct aws_condition_variable signal; + struct aws_uri uri; + uint16_t port; + const char *cacert; + const char *access_token; + const char *access_token_file; + const char *client_token; + const char *client_token_file; + int connect_timeout; + enum aws_secure_tunneling_local_proxy_mode local_proxy_mode; + + struct aws_tls_connection_options tls_connection_options; + + const char *log_filename; + enum aws_log_level log_level; +}; + +static void s_usage(int exit_code) { + + fprintf(stderr, "usage: securetunnel [options] endpoint\n"); + fprintf(stderr, " endpoint: url to connect to\n"); + fprintf(stderr, " access-token: token for secure tunnel\n"); + fprintf(stderr, " access-token-file: File containing token for secure tunnel\n"); + fprintf(stderr, "\n Options:\n\n"); + fprintf(stderr, " client-token: token for secure tunnel\n"); + fprintf(stderr, " client-token-file: File containing token for secure tunnel\n"); + fprintf(stderr, " --cacert FILE: path to a CA certficate file.\n"); + fprintf(stderr, " --connect-timeout INT: time in milliseconds to wait for a connection.\n"); + fprintf(stderr, " -s, --source: use secure tunnel client in source mode.\n"); + fprintf(stderr, " -d, --destination: use secure tunnel client in destination mode.\n"); + fprintf(stderr, " -l, --log FILE: dumps logs to FILE instead of stderr.\n"); + fprintf(stderr, " -v, --verbose: ERROR|INFO|DEBUG|TRACE: log level to configure. Default is none.\n"); + fprintf(stderr, " -w, --websockets: use mqtt-over-websockets rather than direct mqtt\n"); + fprintf(stderr, " -h, --help\n"); + fprintf(stderr, " Display this message and quit.\n"); + exit(exit_code); +} + +static struct aws_cli_option s_long_options[] = { + {"cacert", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 'a'}, + {"access-token", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 't'}, + {"access-token-file", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 'T'}, + {"client-token", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 'c'}, + {"client-token-file", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 'C'}, + {"source", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 's'}, + {"destination", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 'd'}, + {"connect-timeout", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 'f'}, + {"log", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 'l'}, + {"verbose", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 'v'}, + {"help", AWS_CLI_OPTIONS_NO_ARGUMENT, NULL, 'h'}, + {"endpoint", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 'E'}, + /* Per getopt(3) the last element of the array has to be filled with all zeros */ + {NULL, AWS_CLI_OPTIONS_NO_ARGUMENT, NULL, 0}, +}; + +static void s_parse_options(int argc, char **argv, struct app_ctx *ctx) { + bool uri_found = false; + + while (true) { + int option_index = 0; + int c = aws_cli_getopt_long(argc, argv, "a:t:T:c:C:s:d:f:l:v:h:", s_long_options, &option_index); + if (c == -1) { + break; + } + + switch (c) { + case 0: + /* getopt_long() returns 0 if an option.flag is non-null */ + break; + case 'a': + ctx->cacert = aws_cli_optarg; + break; + case 't': + ctx->access_token = aws_cli_optarg; + break; + case 'T': + ctx->access_token_file = aws_cli_optarg; + break; + case 'c': + ctx->client_token = aws_cli_optarg; + break; + case 'C': + ctx->client_token_file = aws_cli_optarg; + break; + case 's': + ctx->local_proxy_mode = AWS_SECURE_TUNNELING_SOURCE_MODE; + break; + case 'd': + ctx->local_proxy_mode = AWS_SECURE_TUNNELING_DESTINATION_MODE; + break; + case 'f': + ctx->connect_timeout = atoi(aws_cli_optarg); + break; + case 'l': + ctx->log_filename = aws_cli_optarg; + break; + case 'v': + if (!strcmp(aws_cli_optarg, "TRACE")) { + ctx->log_level = AWS_LL_TRACE; + } else if (!strcmp(aws_cli_optarg, "INFO")) { + ctx->log_level = AWS_LL_INFO; + } else if (!strcmp(aws_cli_optarg, "DEBUG")) { + ctx->log_level = AWS_LL_DEBUG; + } else if (!strcmp(aws_cli_optarg, "ERROR")) { + ctx->log_level = AWS_LL_ERROR; + } else if (!strcmp(aws_cli_optarg, "WARN")) { + ctx->log_level = AWS_LL_WARN; + } else { + fprintf(stderr, "unsupported log level %s.\n", aws_cli_optarg); + s_usage(1); + } + break; + case 'h': + s_usage(0); + break; + case 0x02: { + struct aws_byte_cursor uri_cursor = aws_byte_cursor_from_c_str(aws_cli_positional_arg); + if (aws_uri_init_parse(&ctx->uri, ctx->allocator, &uri_cursor)) { + fprintf( + stderr, + "Failed to parse uri %s with error %s\n", + (char *)uri_cursor.ptr, + aws_error_debug_str(aws_last_error())); + s_usage(1); + } + uri_found = true; + break; + } + + default: + fprintf(stderr, "Unknown option\n"); + s_usage(1); + } + } + + if (!uri_found) { + fprintf(stderr, "A URI for the request must be supplied.\n"); + s_usage(1); + } +} + +static void s_on_message_received(const struct aws_secure_tunnel_message_view *message, void *user_data) { + (void)user_data; + if (message->service_id != NULL) { + if (message->payload != NULL) { + printf( + "\nMessage received on service id: '" PRInSTR "' with payload: '" PRInSTR "'\n", + AWS_BYTE_CURSOR_PRI(*message->service_id), + AWS_BYTE_CURSOR_PRI(*message->payload)); + } else { + printf( + "\nMessage received on service id: '" PRInSTR "' with no payload\n", + AWS_BYTE_CURSOR_PRI(*message->service_id)); + } + } else if (message->payload != NULL) { + printf("\nMessage received with payload: '" PRInSTR "'\n", AWS_BYTE_CURSOR_PRI(*message->payload)); + } +} + +static void s_on_connection_complete( + const struct aws_secure_tunnel_connection_view *connection_view, + int error_code, + void *user_data) { + (void)connection_view; + printf( + "\nSecure Tunnel Client received s_on_connection_complete callback with error_code:%d (%s)\n", + error_code, + aws_error_name(error_code)); + struct aws_secure_tunnel *secure_tunnel = user_data; + + if (secure_tunnel->config->local_proxy_mode == AWS_SECURE_TUNNELING_DESTINATION_MODE) { + printf("\nConnected in Destination Mode\n"); + } else { + printf("\nConnected in Source Mode\nSending Stream Start\n"); + struct aws_byte_cursor service_id_cur = aws_byte_cursor_from_c_str("ssh"); + struct aws_secure_tunnel_message_view message_data = {.service_id = &service_id_cur}; + // struct aws_secure_tunnel_message_view message_data = {}; + + printf("\nSending Stream Start Message\n"); + aws_secure_tunnel_stream_start(secure_tunnel, &message_data); + } +} + +static void s_on_connection_shutdown(int error_code, void *user_data) { + (void)user_data; + printf( + "\nSecure Tunnel Client received s_on_connection_shutdown callback with error_code:%d (%s)\n", + error_code, + aws_error_name(error_code)); +} + +static void s_on_stream_start( + const struct aws_secure_tunnel_message_view *message_view, + int error_code, + void *user_data) { + (void)user_data; + (void)error_code; + if (message_view->service_id != NULL) { + printf( + "\nSecure Tunnel Client received s_on_stream_start callback with service id:" PRInSTR " stream id:%d", + AWS_BYTE_CURSOR_PRI(*message_view->service_id), + message_view->stream_id); + } + struct app_ctx *app_ctx_user = user_data; + struct aws_secure_tunnel *secure_tunnel = app_ctx_user->secure_tunnel; + if (secure_tunnel->config->local_proxy_mode == AWS_SECURE_TUNNELING_DESTINATION_MODE) { + printf("\nStream Start recieved in Destination Mode\n"); + + struct aws_byte_cursor payload_cur = aws_byte_cursor_from_c_str("TEST PAYLOAD"); + struct aws_secure_tunnel_message_view message_data = { + .payload = &payload_cur, + .service_id = message_view->service_id, + }; + + printf("\nSending Data Message\n"); + aws_secure_tunnel_send_message(secure_tunnel, &message_data); + } +} + +static void s_on_stream_reset( + const struct aws_secure_tunnel_message_view *message_view, + int error_code, + void *user_data) { + (void)user_data; + (void)error_code; + if (message_view->service_id != NULL) { + printf( + "\nSecure Tunnel Client received s_on_stream_reset callback with service id:" PRInSTR " stream id:%d", + AWS_BYTE_CURSOR_PRI(*message_view->service_id), + message_view->stream_id); + } +} + +static void s_on_send_data_complete(int error_code, void *user_data) { + (void)user_data; + printf( + "\nSecure Tunnel Client received s_on_send_data_complete callback with error_code:%d (%s)\n", + error_code, + aws_error_name(error_code)); +} + +int main(int argc, char **argv) { + + /***************************************************************************************************************** + * Initialize + *****************************************************************************************************************/ + struct aws_allocator *allocator = aws_mem_tracer_new(aws_default_allocator(), NULL, AWS_MEMTRACE_STACKS, 15); + + aws_io_library_init(allocator); + aws_http_library_init(allocator); + aws_iotdevice_library_init(allocator); + + struct app_ctx app_ctx; + AWS_ZERO_STRUCT(app_ctx); + app_ctx.allocator = allocator; + app_ctx.signal = (struct aws_condition_variable)AWS_CONDITION_VARIABLE_INIT; + app_ctx.connect_timeout = 3000; + aws_mutex_init(&app_ctx.lock); + app_ctx.port = 1883; /* STEVE TODO NOT NECESSARY */ + + s_parse_options(argc, argv, &app_ctx); + if (app_ctx.uri.port) { + app_ctx.port = app_ctx.uri.port; + } + + struct aws_logger logger; + AWS_ZERO_STRUCT(logger); + + struct aws_logger_standard_options options = { + .level = app_ctx.log_level, + }; + + if (app_ctx.log_level) { + if (app_ctx.log_filename) { + if (remove(app_ctx.log_filename)) { + fprintf(stderr, "\nDeleted existing log\n"); + } else { + fprintf(stderr, "\nFailed to delete existing log\n"); + } + options.filename = app_ctx.log_filename; + } else { + options.file = stderr; + } + + if (aws_logger_init_standard(&logger, allocator, &options)) { + fprintf(stderr, "Failed to initialize logger with error %s\n", aws_error_debug_str(aws_last_error())); + exit(1); + } + + aws_logger_set(&logger); + } + + struct aws_event_loop_group *el_group = aws_event_loop_group_new_default(allocator, 2, NULL); + + struct aws_host_resolver_default_options resolver_options = { + .el_group = el_group, + .max_entries = 8, + }; + + struct aws_host_resolver *resolver = aws_host_resolver_new_default(allocator, &resolver_options); + + struct aws_client_bootstrap_options bootstrap_options = { + .event_loop_group = el_group, + .host_resolver = resolver, + }; + + struct aws_client_bootstrap *bootstrap = aws_client_bootstrap_new(allocator, &bootstrap_options); + + struct aws_socket_options socket_options = { + .type = AWS_SOCKET_STREAM, + .connect_timeout_ms = (uint32_t)app_ctx.connect_timeout, + .keep_alive_timeout_sec = 0, + .keepalive = false, + .keep_alive_interval_sec = 0, + }; + + /***************************************************************************************************************** + * Create Secure Tunnel + *****************************************************************************************************************/ + + /* ACCESS TOKEN */ + struct aws_byte_cursor access_token; + AWS_ZERO_STRUCT(access_token); + + struct aws_byte_buf access_token_tmp; + AWS_ZERO_STRUCT(access_token_tmp); + if (app_ctx.access_token_file) { + if (aws_byte_buf_init_from_file(&access_token_tmp, allocator, app_ctx.access_token_file)) { + goto error; + } + access_token = aws_byte_cursor_from_buf(&access_token_tmp); + } + if (access_token.ptr == NULL) { + access_token = aws_byte_cursor_from_array(app_ctx.access_token, strlen(app_ctx.access_token)); + } + + /* CLIENT TOKEN */ + struct aws_byte_cursor client_token; + AWS_ZERO_STRUCT(client_token); + struct aws_byte_buf client_token_tmp; + AWS_ZERO_STRUCT(client_token_tmp); + if (app_ctx.client_token_file) { + if (aws_byte_buf_init_from_file(&client_token_tmp, allocator, app_ctx.client_token_file)) { + goto error; + } + client_token = aws_byte_cursor_from_buf(&client_token_tmp); + } else if (app_ctx.client_token != NULL) { + client_token = aws_byte_cursor_from_array(app_ctx.client_token, strlen(app_ctx.client_token)); + } + + /* SECURE TUNNEL OPTIONS */ + struct aws_secure_tunnel_options secure_tunnel_options = { + .endpoint_host = app_ctx.uri.host_name, + .bootstrap = bootstrap, + .socket_options = &socket_options, + .access_token = access_token, + .client_token = client_token, + .on_message_received = &s_on_message_received, + .on_connection_complete = &s_on_connection_complete, + .on_connection_shutdown = &s_on_connection_shutdown, + .on_stream_start = &s_on_stream_start, + .on_stream_reset = &s_on_stream_reset, + .on_send_data_complete = &s_on_send_data_complete, + .local_proxy_mode = app_ctx.local_proxy_mode, + .user_data = &app_ctx, + }; + + printf("\nCreating Secure Tunnel\n"); + struct aws_secure_tunnel *secure_tunnel = aws_secure_tunnel_new(allocator, &secure_tunnel_options); + app_ctx.secure_tunnel = secure_tunnel; + + printf("\nStarting Secure Tunnel\n"); + aws_secure_tunnel_start(secure_tunnel); + + uint64_t start_1_sleep_time_sec = 30; + bool is_keep_running = true; + + do { + + printf("\nRunning secure tunnel for %llu seconds\n", start_1_sleep_time_sec); + aws_thread_current_sleep(SLEEP_TIME_NS * start_1_sleep_time_sec); + + } while (is_keep_running); + + uint16_t payload_size = (rand() % MAX_WEBSOCKET_PAYLOAD) + 1; + uint8_t payload_data[MAX_WEBSOCKET_PAYLOAD]; + + struct aws_byte_cursor payload_cur = { + .ptr = payload_data, + .len = payload_size, + }; + + struct aws_byte_cursor service_id_cur = aws_byte_cursor_from_c_str("ssh"); + + struct aws_secure_tunnel_message_view message_data = { + .stream_id = 0, + .payload = &payload_cur, + .service_id = &service_id_cur, + }; + + printf("\nSending Data Message\n"); + aws_secure_tunnel_send_message(secure_tunnel, &message_data); + + printf("\nRunning secure tunnel for %llu seconds\n", start_1_sleep_time_sec); + aws_thread_current_sleep(SLEEP_TIME_NS * start_1_sleep_time_sec); + + printf("\nStopping Secure Tunnel\n"); + aws_secure_tunnel_stop(secure_tunnel); + + uint64_t stop_1_sleep_time_sec = 30; + printf("\nSleeping after STOP for %llu seconds\n", stop_1_sleep_time_sec); + aws_thread_current_sleep(SLEEP_TIME_NS * stop_1_sleep_time_sec); + + printf("\nStarting Secure Tunnel Again\n"); + aws_secure_tunnel_start(secure_tunnel); + + uint64_t start_2_sleep_time_sec = 120; + printf("\nRunning secure tunnel for %llu seconds\n", start_2_sleep_time_sec); + aws_thread_current_sleep(SLEEP_TIME_NS * start_2_sleep_time_sec); + + printf("\nStopping Secure Tunnel again\n"); + aws_secure_tunnel_stop(secure_tunnel); + + /* CLEAN UP */ + aws_secure_tunnel_release(secure_tunnel); + aws_client_bootstrap_release(bootstrap); + aws_host_resolver_release(resolver); + aws_event_loop_group_release(el_group); + aws_byte_buf_clean_up(&client_token_tmp); + aws_byte_buf_clean_up(&access_token_tmp); + + aws_thread_join_all_managed(); + + const size_t outstanding_bytes = aws_mem_tracer_bytes(allocator); + printf("\n\nSummary:\n\n"); + printf(" Outstanding bytes: %zu\n\n", outstanding_bytes); + + if (app_ctx.log_level) { + aws_logger_set(NULL); + aws_logger_clean_up(&logger); + } + + aws_uri_clean_up(&app_ctx.uri); + + aws_http_library_clean_up(); + aws_io_library_clean_up(); + aws_iotdevice_library_clean_up(); + + const size_t leaked_bytes = aws_mem_tracer_bytes(allocator); + if (leaked_bytes) { + struct aws_logger memory_logger; + AWS_ZERO_STRUCT(memory_logger); + + aws_logger_init_noalloc(&memory_logger, aws_default_allocator(), &options); + aws_logger_set(&memory_logger); + + aws_mqtt_library_init(aws_default_allocator()); + + printf("Writing memory leaks to log.\n"); + aws_mem_tracer_dump(allocator); + + aws_logger_set(NULL); + aws_logger_clean_up(&memory_logger); + + aws_mqtt_library_clean_up(); + } else { + printf("Finished, with no memory leaks\n"); + } + + aws_mem_tracer_destroy(allocator); + + return 0; + +error: + return 1; +} From 82b3179ef13a4481a61d977f288c4301b82d687a Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 15 Feb 2023 16:20:15 -0800 Subject: [PATCH 48/69] code review fixes wip --- .../iotdevice/private/secure_tunneling_impl.h | 1 + .../private/secure_tunneling_operations.h | 11 +++ source/secure_tunneling.c | 93 +++++++------------ source/secure_tunneling_operations.c | 46 +++++++++ 4 files changed, 89 insertions(+), 62 deletions(-) diff --git a/include/aws/iotdevice/private/secure_tunneling_impl.h b/include/aws/iotdevice/private/secure_tunneling_impl.h index 775f7284..d5645517 100644 --- a/include/aws/iotdevice/private/secure_tunneling_impl.h +++ b/include/aws/iotdevice/private/secure_tunneling_impl.h @@ -95,6 +95,7 @@ enum aws_secure_tunnel_state { }; struct data_tunnel_pair { + struct aws_allocator *allocator; struct aws_byte_buf buf; struct aws_byte_cursor cur; const struct aws_secure_tunnel *secure_tunnel; diff --git a/include/aws/iotdevice/private/secure_tunneling_operations.h b/include/aws/iotdevice/private/secure_tunneling_operations.h index ffea59a1..114f6cbc 100644 --- a/include/aws/iotdevice/private/secure_tunneling_operations.h +++ b/include/aws/iotdevice/private/secure_tunneling_operations.h @@ -173,6 +173,17 @@ void aws_secure_tunnel_options_storage_log( AWS_IOTDEVICE_API const char *aws_secure_tunnel_operation_type_to_c_string(enum aws_secure_tunnel_operation_type operation_type); +/* Data Tunnel Pair */ + +AWS_IOTDEVICE_API +void aws_secure_tunnel_data_tunnel_pair_destroy(struct data_tunnel_pair *pair); + +AWS_IOTDEVICE_API +struct data_tunnel_pair *aws_secure_tunnel_data_tunnel_pair_new( + struct aws_allocator *allocator, + const struct aws_secure_tunnel *secure_tunnel, + const struct aws_secure_tunnel_message_view *message_view); + AWS_EXTERN_C_END #endif /* AWS_IOTDEVICE_SECURE_TUNNELING_OPERATION_H */ diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 13c4c950..f1dcae79 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -398,8 +398,7 @@ static void s_secure_tunneling_websocket_on_send_data_complete_callback( if (secure_tunnel->config->on_send_data_complete) { secure_tunnel->config->on_send_data_complete(error_code, pair->secure_tunnel->config->user_data); } - aws_byte_buf_clean_up(&pair->buf); - aws_mem_release(secure_tunnel->allocator, pair); + aws_secure_tunnel_data_tunnel_pair_destroy(pair); secure_tunnel->pending_write_completion = false; } @@ -436,29 +435,6 @@ bool secure_tunneling_websocket_stream_outgoing_payload( return true; } -static int s_init_data_tunnel_pair_from_message( - struct aws_secure_tunnel *secure_tunnel, - struct data_tunnel_pair *pair, - const struct aws_secure_tunnel_message_view *message_view) { - pair->secure_tunnel = secure_tunnel; - pair->length_prefix_written = false; - if (aws_iot_st_msg_serialize_from_view(&pair->buf, secure_tunnel->allocator, message_view)) { - AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Failure serializing message"); - goto cleanup; - } - 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 cleanup; - } - pair->cur = aws_byte_cursor_from_buf(&pair->buf); - return AWS_OP_SUCCESS; - -cleanup: - aws_byte_buf_clean_up(&pair->buf); - aws_mem_release(secure_tunnel->allocator, (void *)pair); - return AWS_OP_ERR; -} - static void s_init_websocket_frame_options( struct data_tunnel_pair *pair, struct aws_websocket_send_frame_options *frame_options) { @@ -477,8 +453,9 @@ int secure_tunneling_init_send_frame( const struct aws_secure_tunnel_message_view *message_view) { struct data_tunnel_pair *pair = - (struct data_tunnel_pair *)aws_mem_acquire(secure_tunnel->allocator, sizeof(struct data_tunnel_pair)); - if (s_init_data_tunnel_pair_from_message(secure_tunnel, pair, message_view)) { + aws_secure_tunnel_data_tunnel_pair_new(secure_tunnel->allocator, secure_tunnel, message_view); + + if (!pair) { return AWS_OP_ERR; } @@ -540,10 +517,15 @@ static bool s_on_websocket_incoming_frame_complete( void *user_data) { (void)websocket; (void)frame; - (void)error_code; - (void)user_data; - /* TODO: Check error_code */ + if (error_code) { + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Error on s_on_websocket_incoming_frame_complete() with error %d(%s).", + (void *)user_data, + error_code, + aws_error_debug_str(error_code)); + } return true; } @@ -562,7 +544,7 @@ static void s_secure_tunnel_shutdown(struct aws_client_bootstrap *bootstrap, int secure_tunnel->current_operation = NULL; } - if (aws_linked_list_empty(&secure_tunnel->queued_operations)) { + if (!aws_linked_list_empty(&secure_tunnel->queued_operations)) { s_complete_operation_list( secure_tunnel, &secure_tunnel->queued_operations, @@ -572,6 +554,7 @@ static void s_secure_tunnel_shutdown(struct aws_client_bootstrap *bootstrap, int /* Normal call to shutdown the websocket */ static void s_secure_tunnel_shutdown_websocket(struct aws_secure_tunnel *secure_tunnel, int error_code) { + (void)error_code; if (secure_tunnel->current_state != AWS_STS_CONNECTED && secure_tunnel->current_state != AWS_STS_CLEAN_DISCONNECT) { AWS_LOGF_ERROR( AWS_LS_IOTDEVICE_SECURE_TUNNELING, @@ -582,10 +565,6 @@ static void s_secure_tunnel_shutdown_websocket(struct aws_secure_tunnel *secure_ return; } - if (secure_tunnel->config->on_connection_shutdown) { - secure_tunnel->config->on_connection_shutdown(error_code, secure_tunnel->config->user_data); - } - s_change_current_state(secure_tunnel, AWS_STS_WEBSOCKET_SHUTDOWN); } @@ -594,8 +573,11 @@ static void s_on_websocket_shutdown(struct aws_websocket *websocket, int error_c struct aws_secure_tunnel *secure_tunnel = user_data; s_secure_tunnel_shutdown(secure_tunnel->config->bootstrap, error_code, secure_tunnel); - if (websocket) { - aws_websocket_release(websocket); + aws_websocket_release(websocket); + websocket = NULL; + + if (secure_tunnel->config->on_connection_shutdown) { + secure_tunnel->config->on_connection_shutdown(error_code, secure_tunnel->config->user_data); } if (secure_tunnel->desired_state == AWS_STS_CONNECTED) { @@ -924,9 +906,7 @@ 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) { uint64_t delay_ms = MIN_RECONNECT_DELAY_MS; - for (int i = 0; i < (int)secure_tunnel->reconnect_count; ++i) { - delay_ms = delay_ms * 2; - } + delay_ms = delay_ms << (int)secure_tunnel->reconnect_count; delay_ms = aws_min_u64(delay_ms, MAX_RECONNECT_DELAY_MS); uint64_t now = (*secure_tunnel->vtable->get_current_time_fn)(); @@ -1161,7 +1141,8 @@ static bool s_aws_secure_tunnel_has_pending_operational_work(const struct aws_se 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 */ + /* 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: @@ -1237,14 +1218,13 @@ int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure */ struct aws_secure_tunnel_operation *next_operation = NULL; - while (!aws_linked_list_empty(&secure_tunnel->queued_operations)) { + if (!aws_linked_list_empty(&secure_tunnel->queued_operations)) { struct aws_linked_list_node *next_operation_node = aws_linked_list_pop_front(&secure_tunnel->queued_operations); next_operation = AWS_CONTAINER_OF(next_operation_node, struct aws_secure_tunnel_operation, node); secure_tunnel->current_operation = next_operation; - break; } } @@ -1257,10 +1237,10 @@ int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure switch (current_operation->operation_type) { case AWS_STOT_PING:; /* - * 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. + * 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. */ struct aws_websocket_send_frame_options frame_options; AWS_ZERO_STRUCT(frame_options); @@ -1589,7 +1569,8 @@ static void s_reevaluate_service_task(struct aws_secure_tunnel *secure_tunnel) { /* * This catches both the case when there's an existing service schedule and we either want to not - * perform it (next_service_time == 0) or need to run service at a different time than the current scheduled time. + * perform it (next_service_time == 0) or need to run service at a different time than the current scheduled + * time. */ if (next_service_time != secure_tunnel->next_service_task_run_time && secure_tunnel->next_service_task_run_time > 0) { @@ -1646,18 +1627,6 @@ static bool s_service_state_stopped(struct aws_secure_tunnel *secure_tunnel) { static void s_service_state_connecting(struct aws_secure_tunnel *secure_tunnel, uint64_t now) { (void)secure_tunnel; (void)now; - - if (aws_secure_tunnel_service_operational_state(secure_tunnel)) { - int error_code = aws_last_error(); - AWS_LOGF_ERROR( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: failed to service websocket connect to channel with error %d(%s)", - (void *)secure_tunnel, - error_code, - aws_error_debug_str(error_code)); - s_secure_tunnel_shutdown_websocket(secure_tunnel, error_code); - return; - } } static void s_service_state_connected(struct aws_secure_tunnel *secure_tunnel, uint64_t now) { @@ -1758,8 +1727,8 @@ static void s_secure_tunnel_service_task_fn(struct aws_task *task, void *arg, en } /* - * We can only enter the terminated state from stopped. If we do so, the secure tunnel memory is now freed and we - * will crash if we access anything anymore. + * We can only enter the terminated state from stopped. If we do so, the secure tunnel memory is now freed and + * we will crash if we access anything anymore. */ if (terminated) { return; diff --git a/source/secure_tunneling_operations.c b/source/secure_tunneling_operations.c index 9feb4f5c..93baa63f 100644 --- a/source/secure_tunneling_operations.c +++ b/source/secure_tunneling_operations.c @@ -680,6 +680,52 @@ struct aws_secure_tunnel_options_storage *aws_secure_tunnel_options_storage_new( return NULL; } +/********************************************************************************************************************* + * Data Tunnel Pair + ********************************************************************************************************************/ + +/* + * Clean up data tunnel pair + */ +void aws_secure_tunnel_data_tunnel_pair_destroy(struct data_tunnel_pair *pair) { + aws_byte_buf_clean_up(&pair->buf); + aws_mem_release(pair->allocator, (void *)pair); +} + +/* + * Create a new data tunnel pair + */ +struct data_tunnel_pair *aws_secure_tunnel_data_tunnel_pair_new( + struct aws_allocator *allocator, + const struct aws_secure_tunnel *secure_tunnel, + const struct aws_secure_tunnel_message_view *message_view) { + AWS_PRECONDITION(allocator != NULL); + AWS_PRECONDITION(secure_tunnel != NULL); + AWS_PRECONDITION(message_view != NULL); + + struct data_tunnel_pair *pair = aws_mem_calloc(allocator, 1, sizeof(struct data_tunnel_pair)); + pair->allocator = allocator; + pair->secure_tunnel = secure_tunnel; + 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"); + 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); + + return pair; + +error: + + aws_secure_tunnel_data_tunnel_pair_destroy(pair); + return NULL; +} + const char *aws_secure_tunnel_operation_type_to_c_string(enum aws_secure_tunnel_operation_type operation_type) { switch (operation_type) { case AWS_STOT_NONE: From 71a082feb0f43c1fc2a9247d28325815cb4743fc Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 15 Feb 2023 16:51:06 -0800 Subject: [PATCH 49/69] use aws_byte_buf_write_to_capacity --- source/secure_tunneling.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index f1dcae79..6e972a08 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -420,16 +420,10 @@ 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); + size_t bytes_max = pair->cur.len; size_t amount_to_send = bytes_max < space_available ? bytes_max : space_available; - - struct aws_byte_cursor send_cursor = aws_byte_cursor_advance(&pair->cur, amount_to_send); - if (send_cursor.len) { - if (!aws_byte_buf_write_from_whole_cursor(out_buf, send_cursor)) { - AWS_LOGF_ERROR(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Failure writing data to out_buf"); - return false; - } - } } return true; From 96c1ad35f3aeadafba469840fe809ca3a899ebdc Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 15 Feb 2023 16:52:36 -0800 Subject: [PATCH 50/69] removed unused variable --- source/secure_tunneling.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 6e972a08..d2a19118 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -421,9 +421,6 @@ 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); - - size_t bytes_max = pair->cur.len; - size_t amount_to_send = bytes_max < space_available ? bytes_max : space_available; } return true; From 04358b5b4fea9c9f04f9c3655e38fd86678f09a8 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Thu, 16 Feb 2023 12:14:17 -0800 Subject: [PATCH 51/69] convert service ids to hash table --- .../iotdevice/private/secure_tunneling_impl.h | 15 +- source/secure_tunneling.c | 239 ++++++++++++------ source/secure_tunneling_operations.c | 180 ++++++------- 3 files changed, 256 insertions(+), 178 deletions(-) diff --git a/include/aws/iotdevice/private/secure_tunneling_impl.h b/include/aws/iotdevice/private/secure_tunneling_impl.h index d5645517..bc7f4b3f 100644 --- a/include/aws/iotdevice/private/secure_tunneling_impl.h +++ b/include/aws/iotdevice/private/secure_tunneling_impl.h @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -121,12 +122,14 @@ struct aws_secure_tunnel_options_storage { /* Stream related info */ int32_t stream_id; - struct aws_string *service_id_1; - int32_t service_id_1_stream_id; - struct aws_string *service_id_2; - int32_t service_id_2_stream_id; - struct aws_string *service_id_3; - int32_t service_id_3_stream_id; + // struct aws_string *service_id_1; + // int32_t service_id_1_stream_id; + // struct aws_string *service_id_2; + // int32_t service_id_2_stream_id; + // struct aws_string *service_id_3; + // int32_t service_id_3_stream_id; + + struct aws_hash_table service_ids; /* Callbacks */ aws_secure_tunnel_message_received_fn *on_message_received; diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index d2a19118..edb8bc97 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -80,6 +80,12 @@ 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; + p_element->value = INVALID_STREAM_ID; + return AWS_COMMON_HASH_TABLE_ITER_CONTINUE; +} + /********************************************************************************************************************* * Secure Tunnel Clean Up ********************************************************************************************************************/ @@ -127,9 +133,10 @@ 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->stream_id = INVALID_STREAM_ID; - secure_tunnel->config->service_id_1_stream_id = INVALID_STREAM_ID; - secure_tunnel->config->service_id_2_stream_id = INVALID_STREAM_ID; - secure_tunnel->config->service_id_3_stream_id = INVALID_STREAM_ID; + aws_hash_table_foreach(&secure_tunnel->config->service_ids, s_reset_service_id, NULL); + // secure_tunnel->config->service_id_1_stream_id = INVALID_STREAM_ID; + // secure_tunnel->config->service_id_2_stream_id = INVALID_STREAM_ID; + // secure_tunnel->config->service_id_3_stream_id = INVALID_STREAM_ID; secure_tunnel->received_data.len = 0; /* Drop any incomplete secure tunnel frame */ } @@ -142,34 +149,48 @@ static bool s_aws_secure_tunnel_stream_id_check_match( return (secure_tunnel->config->stream_id == stream_id); } - struct aws_string *service_id_to_set = aws_string_new_from_cursor(secure_tunnel->allocator, service_id); - if (service_id_to_set == NULL) { - return false; - } - int32_t current_stream_id = INVALID_STREAM_ID; - - if (secure_tunnel->config->service_id_1 != NULL && - aws_string_compare(secure_tunnel->config->service_id_1, service_id_to_set) == 0) { - current_stream_id = secure_tunnel->config->service_id_1_stream_id; - } else if ( - secure_tunnel->config->service_id_2 != NULL && - aws_string_compare(secure_tunnel->config->service_id_2, service_id_to_set) == 0) { - current_stream_id = secure_tunnel->config->service_id_2_stream_id; - } else if ( - secure_tunnel->config->service_id_3 != NULL && - aws_string_compare(secure_tunnel->config->service_id_3, service_id_to_set) == 0) { - current_stream_id = secure_tunnel->config->service_id_3_stream_id; - } else { + 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)); - aws_string_destroy(service_id_to_set); return false; } - return (stream_id == current_stream_id); + int32_t *stored_id = elem->value; + return (stream_id == *stored_id); + + // struct aws_string *service_id_to_set = aws_string_new_from_cursor(secure_tunnel->allocator, service_id); + // if (service_id_to_set == NULL) { + // return false; + // } + // int32_t current_stream_id = INVALID_STREAM_ID; + + // if (secure_tunnel->config->service_id_1 != NULL && + // aws_string_compare(secure_tunnel->config->service_id_1, service_id_to_set) == 0) { + // current_stream_id = secure_tunnel->config->service_id_1_stream_id; + // } else if ( + // secure_tunnel->config->service_id_2 != NULL && + // aws_string_compare(secure_tunnel->config->service_id_2, service_id_to_set) == 0) { + // current_stream_id = secure_tunnel->config->service_id_2_stream_id; + // } else if ( + // secure_tunnel->config->service_id_3 != NULL && + // aws_string_compare(secure_tunnel->config->service_id_3, service_id_to_set) == 0) { + // current_stream_id = secure_tunnel->config->service_id_3_stream_id; + // } else { + // 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)); + // aws_string_destroy(service_id_to_set); + // return false; + // } + + // return (stream_id == current_stream_id); } static int s_aws_secure_tunnel_set_stream_id( @@ -187,32 +208,18 @@ static int s_aws_secure_tunnel_set_stream_id( return AWS_OP_SUCCESS; } - struct aws_string *service_id_to_set = aws_string_new_from_cursor(secure_tunnel->allocator, service_id); - if (service_id_to_set == NULL) { - return AWS_OP_ERR; - } - - if (secure_tunnel->config->service_id_1 != NULL && - aws_string_compare(secure_tunnel->config->service_id_1, service_id_to_set) == 0) { - secure_tunnel->config->service_id_1_stream_id = stream_id; - } else if ( - secure_tunnel->config->service_id_2 != NULL && - aws_string_compare(secure_tunnel->config->service_id_2, service_id_to_set) == 0) { - secure_tunnel->config->service_id_2_stream_id = stream_id; - } else if ( - secure_tunnel->config->service_id_3 != NULL && - aws_string_compare(secure_tunnel->config->service_id_3, service_id_to_set) == 0) { - secure_tunnel->config->service_id_3_stream_id = stream_id; - } else { + 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 request for unsupported service_id: " PRInSTR, (void *)secure_tunnel, AWS_BYTE_CURSOR_PRI(*service_id)); - aws_string_destroy(service_id_to_set); - return AWS_OP_ERR; + return false; } + aws_hash_table_put(&secure_tunnel->config->service_ids, service_id, &stream_id, NULL); AWS_LOGF_INFO( AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: Secure tunnel service_id '" PRInSTR "' stream_id set to %d", @@ -220,8 +227,43 @@ static int s_aws_secure_tunnel_set_stream_id( AWS_BYTE_CURSOR_PRI(*service_id), stream_id); - aws_string_destroy(service_id_to_set); return AWS_OP_SUCCESS; + + // struct aws_string *service_id_to_set = aws_string_new_from_cursor(secure_tunnel->allocator, service_id); + // if (service_id_to_set == NULL) { + // return AWS_OP_ERR; + // } + + // if (secure_tunnel->config->service_id_1 != NULL && + // aws_string_compare(secure_tunnel->config->service_id_1, service_id_to_set) == 0) { + // secure_tunnel->config->service_id_1_stream_id = stream_id; + // } else if ( + // secure_tunnel->config->service_id_2 != NULL && + // aws_string_compare(secure_tunnel->config->service_id_2, service_id_to_set) == 0) { + // secure_tunnel->config->service_id_2_stream_id = stream_id; + // } else if ( + // secure_tunnel->config->service_id_3 != NULL && + // aws_string_compare(secure_tunnel->config->service_id_3, service_id_to_set) == 0) { + // secure_tunnel->config->service_id_3_stream_id = stream_id; + // } else { + // AWS_LOGF_WARN( + // AWS_LS_IOTDEVICE_SECURE_TUNNELING, + // "id=%p: Secure tunnel request for unsupported service_id: " PRInSTR, + // (void *)secure_tunnel, + // AWS_BYTE_CURSOR_PRI(*service_id)); + // aws_string_destroy(service_id_to_set); + // return AWS_OP_ERR; + // } + + // 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(*service_id), + // stream_id); + + // aws_string_destroy(service_id_to_set); + // return AWS_OP_SUCCESS; } static void s_aws_secure_tunnel_on_stream_start_received( @@ -256,49 +298,78 @@ static void s_aws_secure_tunnel_on_service_ids_received( struct aws_secure_tunnel *secure_tunnel, struct aws_secure_tunnel_message_view *message_view) { - /* Clean up existing service ids first */ - if (secure_tunnel->config->service_id_1) { - aws_string_destroy(secure_tunnel->config->service_id_1); - secure_tunnel->config->service_id_1 = NULL; - } - if (secure_tunnel->config->service_id_2) { - aws_string_destroy(secure_tunnel->config->service_id_2); - secure_tunnel->config->service_id_2 = NULL; - } - if (secure_tunnel->config->service_id_3) { - aws_string_destroy(secure_tunnel->config->service_id_3); - secure_tunnel->config->service_id_3 = NULL; - } + aws_hash_table_clear(&secure_tunnel->config->service_ids); if (message_view->service_id != NULL) { - secure_tunnel->config->service_id_1 = - aws_string_new_from_cursor(secure_tunnel->allocator, message_view->service_id); + aws_hash_table_put(&secure_tunnel->config->service_ids, message_view->service_id, INVALID_STREAM_ID, NULL); AWS_LOGF_INFO( AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: secure tunnel service id 1 set to: " PRInSTR, (void *)secure_tunnel, - AWS_BYTE_CURSOR_PRI(aws_byte_cursor_from_string(secure_tunnel->config->service_id_1))); - } - - if (message_view->service_id_2 != NULL) { - secure_tunnel->config->service_id_2 = - aws_string_new_from_cursor(secure_tunnel->allocator, message_view->service_id_2); - AWS_LOGF_INFO( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: secure tunnel service id 2 set to: " PRInSTR, - (void *)secure_tunnel, - AWS_BYTE_CURSOR_PRI(aws_byte_cursor_from_string(secure_tunnel->config->service_id_2))); + AWS_BYTE_CURSOR_PRI(*message_view->service_id)); + if (message_view->service_id_2 != NULL) { + aws_hash_table_put( + &secure_tunnel->config->service_ids, message_view->service_id_2, INVALID_STREAM_ID, NULL); + AWS_LOGF_INFO( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: secure tunnel service id 2 set to: " PRInSTR, + (void *)secure_tunnel, + AWS_BYTE_CURSOR_PRI(*message_view->service_id_2)); + if (message_view->service_id_3 != NULL) { + aws_hash_table_put( + &secure_tunnel->config->service_ids, message_view->service_id_3, INVALID_STREAM_ID, NULL); + AWS_LOGF_INFO( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: secure tunnel service id 3 set to: " PRInSTR, + (void *)secure_tunnel, + AWS_BYTE_CURSOR_PRI(*message_view->service_id_3)); + } + } } - if (message_view->service_id_3 != NULL) { - secure_tunnel->config->service_id_3 = - aws_string_new_from_cursor(secure_tunnel->allocator, message_view->service_id_3); - AWS_LOGF_INFO( - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: secure tunnel service id 3 set to: " PRInSTR, - (void *)secure_tunnel, - AWS_BYTE_CURSOR_PRI(aws_byte_cursor_from_string(secure_tunnel->config->service_id_3))); - } + /* Clean up existing service ids first */ + // if (secure_tunnel->config->service_id_1) { + // aws_string_destroy(secure_tunnel->config->service_id_1); + // secure_tunnel->config->service_id_1 = NULL; + // } + // if (secure_tunnel->config->service_id_2) { + // aws_string_destroy(secure_tunnel->config->service_id_2); + // secure_tunnel->config->service_id_2 = NULL; + // } + // if (secure_tunnel->config->service_id_3) { + // aws_string_destroy(secure_tunnel->config->service_id_3); + // secure_tunnel->config->service_id_3 = NULL; + // } + + // if (message_view->service_id != NULL) { + // secure_tunnel->config->service_id_1 = + // aws_string_new_from_cursor(secure_tunnel->allocator, message_view->service_id); + // AWS_LOGF_INFO( + // AWS_LS_IOTDEVICE_SECURE_TUNNELING, + // "id=%p: secure tunnel service id 1 set to: " PRInSTR, + // (void *)secure_tunnel, + // AWS_BYTE_CURSOR_PRI(aws_byte_cursor_from_string(secure_tunnel->config->service_id_1))); + // } + + // if (message_view->service_id_2 != NULL) { + // secure_tunnel->config->service_id_2 = + // aws_string_new_from_cursor(secure_tunnel->allocator, message_view->service_id_2); + // AWS_LOGF_INFO( + // AWS_LS_IOTDEVICE_SECURE_TUNNELING, + // "id=%p: secure tunnel service id 2 set to: " PRInSTR, + // (void *)secure_tunnel, + // AWS_BYTE_CURSOR_PRI(aws_byte_cursor_from_string(secure_tunnel->config->service_id_2))); + // } + + // if (message_view->service_id_3 != NULL) { + // secure_tunnel->config->service_id_3 = + // aws_string_new_from_cursor(secure_tunnel->allocator, message_view->service_id_3); + // AWS_LOGF_INFO( + // AWS_LS_IOTDEVICE_SECURE_TUNNELING, + // "id=%p: secure tunnel service id 3 set to: " PRInSTR, + // (void *)secure_tunnel, + // AWS_BYTE_CURSOR_PRI(aws_byte_cursor_from_string(secure_tunnel->config->service_id_3))); + // } struct aws_secure_tunnel_connection_view connection_view; AWS_ZERO_STRUCT(connection_view); @@ -402,7 +473,7 @@ static void s_secure_tunneling_websocket_on_send_data_complete_callback( secure_tunnel->pending_write_completion = false; } -bool secure_tunneling_websocket_stream_outgoing_payload( +static bool secure_tunneling_websocket_stream_outgoing_payload( struct aws_websocket *websocket, struct aws_byte_buf *out_buf, void *user_data) { @@ -1797,9 +1868,13 @@ struct aws_secure_tunnel *aws_secure_tunnel_new( /* Connection reset */ secure_tunnel->config->stream_id = INVALID_STREAM_ID; - secure_tunnel->config->service_id_1_stream_id = INVALID_STREAM_ID; - secure_tunnel->config->service_id_2_stream_id = INVALID_STREAM_ID; - secure_tunnel->config->service_id_3_stream_id = INVALID_STREAM_ID; + + aws_hash_table_foreach(&secure_tunnel->config->service_ids, s_reset_service_id, NULL); + + // secure_tunnel->config->service_id_1_stream_id = INVALID_STREAM_ID; + // secure_tunnel->config->service_id_2_stream_id = INVALID_STREAM_ID; + // secure_tunnel->config->service_id_3_stream_id = INVALID_STREAM_ID; + secure_tunnel->handshake_request = NULL; secure_tunnel->websocket = NULL; diff --git a/source/secure_tunneling_operations.c b/source/secure_tunneling_operations.c index 93baa63f..54758cc9 100644 --- a/source/secure_tunneling_operations.c +++ b/source/secure_tunneling_operations.c @@ -14,6 +14,7 @@ #include #define INVALID_STREAM_ID 0 + /********************************************************************************************************************* * Operation base ********************************************************************************************************************/ @@ -218,30 +219,45 @@ 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) { - struct aws_string *service_id = NULL; - service_id = aws_string_new_from_cursor(secure_tunnel->allocator, message_view->service_id); - - if (secure_tunnel->config->service_id_1 != NULL && - aws_string_compare(secure_tunnel->config->service_id_1, service_id) == 0) { - stream_id = secure_tunnel->config->service_id_1_stream_id; - } else if ( - secure_tunnel->config->service_id_2 != NULL && - aws_string_compare(secure_tunnel->config->service_id_2, service_id) == 0) { - stream_id = secure_tunnel->config->service_id_2_stream_id; - } else if ( - secure_tunnel->config->service_id_3 != NULL && - aws_string_compare(secure_tunnel->config->service_id_3, service_id) == 0) { - stream_id = secure_tunnel->config->service_id_3_stream_id; - } else { - /* service_id doesn't match any existing service id*/ - AWS_LOGF_DEBUG( + 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:%s attempted to be used with an outbound message", + "id=%p: invalid service_id:'" PRInSTR "' attempted to be used with an outbound message", (void *)message_view, - aws_string_c_str(service_id)); + AWS_BYTE_CURSOR_PRI(*message_view->service_id)); stream_id = INVALID_STREAM_ID; + } else { + + int32_t *stored_id = elem->value; + stream_id = *stored_id; } - aws_string_destroy(service_id); + + // struct aws_string *service_id = NULL; + // service_id = aws_string_new_from_cursor(secure_tunnel->allocator, message_view->service_id); + + // if (secure_tunnel->config->service_id_1 != NULL && + // aws_string_compare(secure_tunnel->config->service_id_1, service_id) == 0) { + // stream_id = secure_tunnel->config->service_id_1_stream_id; + // } else if ( + // secure_tunnel->config->service_id_2 != NULL && + // aws_string_compare(secure_tunnel->config->service_id_2, service_id) == 0) { + // stream_id = secure_tunnel->config->service_id_2_stream_id; + // } else if ( + // secure_tunnel->config->service_id_3 != NULL && + // aws_string_compare(secure_tunnel->config->service_id_3, service_id) == 0) { + // stream_id = secure_tunnel->config->service_id_3_stream_id; + // } else { + // /* service_id doesn't match any existing service id*/ + // AWS_LOGF_DEBUG( + // AWS_LS_IOTDEVICE_SECURE_TUNNELING, + // "id=%p: invalid service_id:%s attempted to be used with an outbound message", + // (void *)message_view, + // aws_string_c_str(service_id)); + // stream_id = INVALID_STREAM_ID; + // } + // aws_string_destroy(service_id); } else { stream_id = secure_tunnel->config->stream_id; } @@ -268,33 +284,49 @@ 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) { - struct aws_string *service_id = NULL; - service_id = aws_string_new_from_cursor(secure_tunnel->allocator, message_view->service_id); - - if (secure_tunnel->config->service_id_1 != NULL && - aws_string_compare(secure_tunnel->config->service_id_1, service_id) == 0) { - stream_id = secure_tunnel->config->service_id_1_stream_id + 1; - secure_tunnel->config->service_id_1_stream_id = stream_id; - } else if ( - secure_tunnel->config->service_id_2 != NULL && - aws_string_compare(secure_tunnel->config->service_id_2, service_id) == 0) { - stream_id = secure_tunnel->config->service_id_2_stream_id + 1; - secure_tunnel->config->service_id_2_stream_id = stream_id; - } else if ( - secure_tunnel->config->service_id_3 != NULL && - aws_string_compare(secure_tunnel->config->service_id_3, service_id) == 0) { - stream_id = secure_tunnel->config->service_id_3_stream_id + 1; - secure_tunnel->config->service_id_3_stream_id = stream_id; - } else { - /* service_id doesn't match any existing service id*/ - AWS_LOGF_DEBUG( + 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:%s attempted to be used with an outbound message", + "id=%p: invalid service_id:'" PRInSTR "' attempted to be used with an outbound message", (void *)message_view, - aws_string_c_str(service_id)); + AWS_BYTE_CURSOR_PRI(*message_view->service_id)); stream_id = INVALID_STREAM_ID; + } else { + + int32_t *stored_id = elem->value; + stream_id = *stored_id + 1; + aws_hash_table_put(&secure_tunnel->config->service_ids, message_view->service_id, &stream_id, NULL); } - aws_string_destroy(service_id); + + // struct aws_string *service_id = NULL; + // service_id = aws_string_new_from_cursor(secure_tunnel->allocator, message_view->service_id); + + // if (secure_tunnel->config->service_id_1 != NULL && + // aws_string_compare(secure_tunnel->config->service_id_1, service_id) == 0) { + // stream_id = secure_tunnel->config->service_id_1_stream_id + 1; + // secure_tunnel->config->service_id_1_stream_id = stream_id; + // } else if ( + // secure_tunnel->config->service_id_2 != NULL && + // aws_string_compare(secure_tunnel->config->service_id_2, service_id) == 0) { + // stream_id = secure_tunnel->config->service_id_2_stream_id + 1; + // secure_tunnel->config->service_id_2_stream_id = stream_id; + // } else if ( + // secure_tunnel->config->service_id_3 != NULL && + // aws_string_compare(secure_tunnel->config->service_id_3, service_id) == 0) { + // stream_id = secure_tunnel->config->service_id_3_stream_id + 1; + // secure_tunnel->config->service_id_3_stream_id = stream_id; + // } else { + // /* service_id doesn't match any existing service id*/ + // AWS_LOGF_DEBUG( + // AWS_LS_IOTDEVICE_SECURE_TUNNELING, + // "id=%p: invalid service_id:%s attempted to be used with an outbound message", + // (void *)message_view, + // aws_string_c_str(service_id)); + // stream_id = INVALID_STREAM_ID; + // } + // aws_string_destroy(service_id); } else { stream_id = secure_tunnel->config->stream_id + 1; secure_tunnel->config->stream_id = stream_id; @@ -520,50 +552,6 @@ void aws_secure_tunnel_options_storage_log( (void *)options_storage->http_proxy_options.proxy_strategy); } } - - bool is_service_id_used = false; - - if (options_storage->service_id_1 != NULL) { - is_service_id_used = true; - AWS_LOGUF( - log_handle, - level, - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: aws_secure_tunnel_options_storage service id 1:%s", - (void *)options_storage, - aws_string_c_str(options_storage->service_id_1)); - } - - if (options_storage->service_id_2 != NULL) { - is_service_id_used = true; - AWS_LOGUF( - log_handle, - level, - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: aws_secure_tunnel_options_storage service id 2:%s", - (void *)options_storage, - aws_string_c_str(options_storage->service_id_2)); - } - - if (options_storage->service_id_3 != NULL) { - is_service_id_used = true; - AWS_LOGUF( - log_handle, - level, - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: aws_secure_tunnel_options_storage service id 3:%s", - (void *)options_storage, - aws_string_c_str(options_storage->service_id_3)); - } - - if (!is_service_id_used) { - AWS_LOGUF( - log_handle, - level, - AWS_LS_IOTDEVICE_SECURE_TUNNELING, - "id=%p: aws_secure_tunnel_options_storage no service id set", - (void *)options_storage); - } } /* @@ -579,9 +567,10 @@ void aws_secure_tunnel_options_storage_destroy(struct aws_secure_tunnel_options_ aws_string_destroy(storage->endpoint_host); aws_string_destroy(storage->access_token); aws_string_destroy(storage->client_token); - aws_string_destroy(storage->service_id_1); - aws_string_destroy(storage->service_id_2); - aws_string_destroy(storage->service_id_3); + // aws_string_destroy(storage->service_id_1); + // aws_string_destroy(storage->service_id_2); + // aws_string_destroy(storage->service_id_3); + aws_hash_table_clean_up(&storage->service_ids); aws_mem_release(storage->allocator, storage); } @@ -660,6 +649,17 @@ 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, + NULL)) { + goto error; + } + storage->on_message_received = options->on_message_received; storage->user_data = options->user_data; From a731303b69729d9a4a644bd523143111db1f4fdf Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Thu, 16 Feb 2023 14:12:07 -0800 Subject: [PATCH 52/69] fix hash table for service ids --- bin/securetunnel/main.c | 3 +- .../private/secure_tunneling_operations.h | 13 +++++ source/secure_tunneling.c | 25 +++++++--- source/secure_tunneling_operations.c | 49 ++++++++++++++++--- 4 files changed, 74 insertions(+), 16 deletions(-) diff --git a/bin/securetunnel/main.c b/bin/securetunnel/main.c index e7f3aa8d..41ee88f3 100644 --- a/bin/securetunnel/main.c +++ b/bin/securetunnel/main.c @@ -202,7 +202,8 @@ static void s_on_connection_complete( "\nSecure Tunnel Client received s_on_connection_complete callback with error_code:%d (%s)\n", error_code, aws_error_name(error_code)); - struct aws_secure_tunnel *secure_tunnel = user_data; + struct app_ctx *ctx = user_data; + struct aws_secure_tunnel *secure_tunnel = ctx->secure_tunnel; if (secure_tunnel->config->local_proxy_mode == AWS_SECURE_TUNNELING_DESTINATION_MODE) { printf("\nConnected in Destination Mode\n"); diff --git a/include/aws/iotdevice/private/secure_tunneling_operations.h b/include/aws/iotdevice/private/secure_tunneling_operations.h index 114f6cbc..6196351d 100644 --- a/include/aws/iotdevice/private/secure_tunneling_operations.h +++ b/include/aws/iotdevice/private/secure_tunneling_operations.h @@ -26,6 +26,13 @@ enum aws_secure_tunnel_operation_type { AWS_STOT_STREAM_START }; +struct aws_service_id_element { + struct aws_allocator *allocator; + struct aws_byte_cursor service_id_cur; + struct aws_string *service_id_string; + int32_t stream_id; +}; + struct aws_secure_tunnel_message_storage { struct aws_allocator *allocator; struct aws_secure_tunnel_message_view storage_view; @@ -184,6 +191,12 @@ struct data_tunnel_pair *aws_secure_tunnel_data_tunnel_pair_new( const struct aws_secure_tunnel *secure_tunnel, const struct aws_secure_tunnel_message_view *message_view); +AWS_IOTDEVICE_API +struct aws_service_id_element *aws_service_id_element_new( + struct aws_allocator *allocator, + const struct aws_byte_cursor *service_id, + int32_t stream_id); + AWS_EXTERN_C_END #endif /* AWS_IOTDEVICE_SECURE_TUNNELING_OPERATION_H */ diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index edb8bc97..d7aa8d10 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -82,7 +82,8 @@ static const char *s_get_proxy_mode_string(enum aws_secure_tunneling_local_proxy static int s_reset_service_id(void *context, struct aws_hash_element *p_element) { (void)context; - p_element->value = INVALID_STREAM_ID; + struct aws_service_id_element *service_id_elem = p_element->value; + service_id_elem->stream_id = INVALID_STREAM_ID; return AWS_COMMON_HASH_TABLE_ITER_CONTINUE; } @@ -160,8 +161,8 @@ static bool s_aws_secure_tunnel_stream_id_check_match( return false; } - int32_t *stored_id = elem->value; - return (stream_id == *stored_id); + struct aws_service_id_element *service_id_elem = elem->value; + return (stream_id == service_id_elem->stream_id); // struct aws_string *service_id_to_set = aws_string_new_from_cursor(secure_tunnel->allocator, service_id); // if (service_id_to_set == NULL) { @@ -219,7 +220,10 @@ static int s_aws_secure_tunnel_set_stream_id( return false; } - aws_hash_table_put(&secure_tunnel->config->service_ids, service_id, &stream_id, NULL); + struct aws_service_id_element *replacement_elem = + aws_service_id_element_new(secure_tunnel->allocator, 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", @@ -301,23 +305,30 @@ static void s_aws_secure_tunnel_on_service_ids_received( aws_hash_table_clear(&secure_tunnel->config->service_ids); if (message_view->service_id != NULL) { - aws_hash_table_put(&secure_tunnel->config->service_ids, message_view->service_id, INVALID_STREAM_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); AWS_LOGF_INFO( AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: secure tunnel service id 1 set to: " PRInSTR, (void *)secure_tunnel, AWS_BYTE_CURSOR_PRI(*message_view->service_id)); if (message_view->service_id_2 != NULL) { + 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, message_view->service_id_2, INVALID_STREAM_ID, NULL); + &secure_tunnel->config->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, (void *)secure_tunnel, AWS_BYTE_CURSOR_PRI(*message_view->service_id_2)); if (message_view->service_id_3 != NULL) { + 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, message_view->service_id_3, INVALID_STREAM_ID, NULL); + &secure_tunnel->config->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, diff --git a/source/secure_tunneling_operations.c b/source/secure_tunneling_operations.c index 54758cc9..42c5b2f7 100644 --- a/source/secure_tunneling_operations.c +++ b/source/secure_tunneling_operations.c @@ -15,6 +15,36 @@ #define INVALID_STREAM_ID 0 +/* for the hash table, to destroy elements */ +static void s_destroy_service_id(void *data) { + struct aws_service_id_element *elem = data; + aws_string_destroy(elem->service_id_string); + aws_mem_release(elem->allocator, elem); +} + +struct aws_service_id_element *aws_service_id_element_new( + struct aws_allocator *allocator, + const struct aws_byte_cursor *service_id, + int32_t stream_id) { + AWS_PRECONDITION(allocator != NULL); + AWS_PRECONDITION(service_id != NULL); + + struct aws_service_id_element *elem = aws_mem_calloc(allocator, 1, sizeof(struct aws_service_id_element)); + elem->allocator = allocator; + elem->service_id_string = aws_string_new_from_cursor(allocator, service_id); + if (elem->service_id_string == NULL) { + goto error; + } + elem->service_id_cur = aws_byte_cursor_from_string(elem->service_id_string); + elem->stream_id = stream_id; + + return elem; + +error: + s_destroy_service_id(elem); + return NULL; +} + /********************************************************************************************************************* * Operation base ********************************************************************************************************************/ @@ -229,9 +259,8 @@ 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 { - - int32_t *stored_id = elem->value; - stream_id = *stored_id; + struct aws_service_id_element *service_id_elem = elem->value; + stream_id = service_id_elem->stream_id; } // struct aws_string *service_id = NULL; @@ -289,15 +318,19 @@ static int s_aws_secure_tunnel_operation_message_set_next_stream_id( 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 used to set next stream id on an outbound message", (void *)message_view, AWS_BYTE_CURSOR_PRI(*message_view->service_id)); stream_id = INVALID_STREAM_ID; } else { + struct aws_service_id_element *service_id_elem = elem->value; + stream_id = service_id_elem->stream_id + 1; - int32_t *stored_id = elem->value; - stream_id = *stored_id + 1; - aws_hash_table_put(&secure_tunnel->config->service_ids, message_view->service_id, &stream_id, NULL); + struct aws_service_id_element *replacement_elem = + 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); } // struct aws_string *service_id = NULL; @@ -656,7 +689,7 @@ struct aws_secure_tunnel_options_storage *aws_secure_tunnel_options_storage_new( aws_hash_byte_cursor_ptr, (aws_hash_callback_eq_fn *)aws_byte_cursor_eq, NULL, - NULL)) { + s_destroy_service_id)) { goto error; } From 4cb2261a6cdd6d6490b9334a2229deb19a0b3fcf Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Thu, 16 Feb 2023 14:15:49 -0800 Subject: [PATCH 53/69] cleanup old service id --- .../iotdevice/private/secure_tunneling_impl.h | 6 - source/secure_tunneling.c | 118 +----------------- source/secure_tunneling_operations.c | 56 --------- 3 files changed, 1 insertion(+), 179 deletions(-) diff --git a/include/aws/iotdevice/private/secure_tunneling_impl.h b/include/aws/iotdevice/private/secure_tunneling_impl.h index bc7f4b3f..7fa292aa 100644 --- a/include/aws/iotdevice/private/secure_tunneling_impl.h +++ b/include/aws/iotdevice/private/secure_tunneling_impl.h @@ -122,12 +122,6 @@ struct aws_secure_tunnel_options_storage { /* Stream related info */ int32_t stream_id; - // struct aws_string *service_id_1; - // int32_t service_id_1_stream_id; - // struct aws_string *service_id_2; - // int32_t service_id_2_stream_id; - // struct aws_string *service_id_3; - // int32_t service_id_3_stream_id; struct aws_hash_table service_ids; diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index d7aa8d10..9f27c9db 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -135,9 +135,6 @@ static void s_reset_secure_tunnel(struct aws_secure_tunnel *secure_tunnel) { secure_tunnel->config->stream_id = INVALID_STREAM_ID; aws_hash_table_foreach(&secure_tunnel->config->service_ids, s_reset_service_id, NULL); - // secure_tunnel->config->service_id_1_stream_id = INVALID_STREAM_ID; - // secure_tunnel->config->service_id_2_stream_id = INVALID_STREAM_ID; - // secure_tunnel->config->service_id_3_stream_id = INVALID_STREAM_ID; secure_tunnel->received_data.len = 0; /* Drop any incomplete secure tunnel frame */ } @@ -163,35 +160,6 @@ static bool s_aws_secure_tunnel_stream_id_check_match( struct aws_service_id_element *service_id_elem = elem->value; return (stream_id == service_id_elem->stream_id); - - // struct aws_string *service_id_to_set = aws_string_new_from_cursor(secure_tunnel->allocator, service_id); - // if (service_id_to_set == NULL) { - // return false; - // } - // int32_t current_stream_id = INVALID_STREAM_ID; - - // if (secure_tunnel->config->service_id_1 != NULL && - // aws_string_compare(secure_tunnel->config->service_id_1, service_id_to_set) == 0) { - // current_stream_id = secure_tunnel->config->service_id_1_stream_id; - // } else if ( - // secure_tunnel->config->service_id_2 != NULL && - // aws_string_compare(secure_tunnel->config->service_id_2, service_id_to_set) == 0) { - // current_stream_id = secure_tunnel->config->service_id_2_stream_id; - // } else if ( - // secure_tunnel->config->service_id_3 != NULL && - // aws_string_compare(secure_tunnel->config->service_id_3, service_id_to_set) == 0) { - // current_stream_id = secure_tunnel->config->service_id_3_stream_id; - // } else { - // 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)); - // aws_string_destroy(service_id_to_set); - // return false; - // } - - // return (stream_id == current_stream_id); } static int s_aws_secure_tunnel_set_stream_id( @@ -232,42 +200,6 @@ static int s_aws_secure_tunnel_set_stream_id( stream_id); return AWS_OP_SUCCESS; - - // struct aws_string *service_id_to_set = aws_string_new_from_cursor(secure_tunnel->allocator, service_id); - // if (service_id_to_set == NULL) { - // return AWS_OP_ERR; - // } - - // if (secure_tunnel->config->service_id_1 != NULL && - // aws_string_compare(secure_tunnel->config->service_id_1, service_id_to_set) == 0) { - // secure_tunnel->config->service_id_1_stream_id = stream_id; - // } else if ( - // secure_tunnel->config->service_id_2 != NULL && - // aws_string_compare(secure_tunnel->config->service_id_2, service_id_to_set) == 0) { - // secure_tunnel->config->service_id_2_stream_id = stream_id; - // } else if ( - // secure_tunnel->config->service_id_3 != NULL && - // aws_string_compare(secure_tunnel->config->service_id_3, service_id_to_set) == 0) { - // secure_tunnel->config->service_id_3_stream_id = stream_id; - // } else { - // AWS_LOGF_WARN( - // AWS_LS_IOTDEVICE_SECURE_TUNNELING, - // "id=%p: Secure tunnel request for unsupported service_id: " PRInSTR, - // (void *)secure_tunnel, - // AWS_BYTE_CURSOR_PRI(*service_id)); - // aws_string_destroy(service_id_to_set); - // return AWS_OP_ERR; - // } - - // 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(*service_id), - // stream_id); - - // aws_string_destroy(service_id_to_set); - // return AWS_OP_SUCCESS; } static void s_aws_secure_tunnel_on_stream_start_received( @@ -338,50 +270,6 @@ static void s_aws_secure_tunnel_on_service_ids_received( } } - /* Clean up existing service ids first */ - // if (secure_tunnel->config->service_id_1) { - // aws_string_destroy(secure_tunnel->config->service_id_1); - // secure_tunnel->config->service_id_1 = NULL; - // } - // if (secure_tunnel->config->service_id_2) { - // aws_string_destroy(secure_tunnel->config->service_id_2); - // secure_tunnel->config->service_id_2 = NULL; - // } - // if (secure_tunnel->config->service_id_3) { - // aws_string_destroy(secure_tunnel->config->service_id_3); - // secure_tunnel->config->service_id_3 = NULL; - // } - - // if (message_view->service_id != NULL) { - // secure_tunnel->config->service_id_1 = - // aws_string_new_from_cursor(secure_tunnel->allocator, message_view->service_id); - // AWS_LOGF_INFO( - // AWS_LS_IOTDEVICE_SECURE_TUNNELING, - // "id=%p: secure tunnel service id 1 set to: " PRInSTR, - // (void *)secure_tunnel, - // AWS_BYTE_CURSOR_PRI(aws_byte_cursor_from_string(secure_tunnel->config->service_id_1))); - // } - - // if (message_view->service_id_2 != NULL) { - // secure_tunnel->config->service_id_2 = - // aws_string_new_from_cursor(secure_tunnel->allocator, message_view->service_id_2); - // AWS_LOGF_INFO( - // AWS_LS_IOTDEVICE_SECURE_TUNNELING, - // "id=%p: secure tunnel service id 2 set to: " PRInSTR, - // (void *)secure_tunnel, - // AWS_BYTE_CURSOR_PRI(aws_byte_cursor_from_string(secure_tunnel->config->service_id_2))); - // } - - // if (message_view->service_id_3 != NULL) { - // secure_tunnel->config->service_id_3 = - // aws_string_new_from_cursor(secure_tunnel->allocator, message_view->service_id_3); - // AWS_LOGF_INFO( - // AWS_LS_IOTDEVICE_SECURE_TUNNELING, - // "id=%p: secure tunnel service id 3 set to: " PRInSTR, - // (void *)secure_tunnel, - // AWS_BYTE_CURSOR_PRI(aws_byte_cursor_from_string(secure_tunnel->config->service_id_3))); - // } - struct aws_secure_tunnel_connection_view connection_view; AWS_ZERO_STRUCT(connection_view); connection_view.service_id_1 = message_view->service_id; @@ -821,7 +709,7 @@ static struct aws_http_message *s_new_handshake_request(const struct aws_secure_ goto error; } - // /* Secure Tunnel specific headers */ + /* Secure Tunnel specific headers */ struct aws_http_header header_protocol = { .name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL(WEBSOCKET_HEADER_NAME_PROTOCOL), .value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL(WEBSOCKET_HEADER_PROTOCOL_VALUE), @@ -1882,10 +1770,6 @@ struct aws_secure_tunnel *aws_secure_tunnel_new( aws_hash_table_foreach(&secure_tunnel->config->service_ids, s_reset_service_id, NULL); - // secure_tunnel->config->service_id_1_stream_id = INVALID_STREAM_ID; - // secure_tunnel->config->service_id_2_stream_id = INVALID_STREAM_ID; - // secure_tunnel->config->service_id_3_stream_id = INVALID_STREAM_ID; - secure_tunnel->handshake_request = NULL; secure_tunnel->websocket = NULL; diff --git a/source/secure_tunneling_operations.c b/source/secure_tunneling_operations.c index 42c5b2f7..3850d2ad 100644 --- a/source/secure_tunneling_operations.c +++ b/source/secure_tunneling_operations.c @@ -262,31 +262,6 @@ static int s_aws_secure_tunnel_operation_message_assign_stream_id( struct aws_service_id_element *service_id_elem = elem->value; stream_id = service_id_elem->stream_id; } - - // struct aws_string *service_id = NULL; - // service_id = aws_string_new_from_cursor(secure_tunnel->allocator, message_view->service_id); - - // if (secure_tunnel->config->service_id_1 != NULL && - // aws_string_compare(secure_tunnel->config->service_id_1, service_id) == 0) { - // stream_id = secure_tunnel->config->service_id_1_stream_id; - // } else if ( - // secure_tunnel->config->service_id_2 != NULL && - // aws_string_compare(secure_tunnel->config->service_id_2, service_id) == 0) { - // stream_id = secure_tunnel->config->service_id_2_stream_id; - // } else if ( - // secure_tunnel->config->service_id_3 != NULL && - // aws_string_compare(secure_tunnel->config->service_id_3, service_id) == 0) { - // stream_id = secure_tunnel->config->service_id_3_stream_id; - // } else { - // /* service_id doesn't match any existing service id*/ - // AWS_LOGF_DEBUG( - // AWS_LS_IOTDEVICE_SECURE_TUNNELING, - // "id=%p: invalid service_id:%s attempted to be used with an outbound message", - // (void *)message_view, - // aws_string_c_str(service_id)); - // stream_id = INVALID_STREAM_ID; - // } - // aws_string_destroy(service_id); } else { stream_id = secure_tunnel->config->stream_id; } @@ -332,34 +307,6 @@ static int s_aws_secure_tunnel_operation_message_set_next_stream_id( aws_hash_table_put( &secure_tunnel->config->service_ids, &replacement_elem->service_id_cur, replacement_elem, NULL); } - - // struct aws_string *service_id = NULL; - // service_id = aws_string_new_from_cursor(secure_tunnel->allocator, message_view->service_id); - - // if (secure_tunnel->config->service_id_1 != NULL && - // aws_string_compare(secure_tunnel->config->service_id_1, service_id) == 0) { - // stream_id = secure_tunnel->config->service_id_1_stream_id + 1; - // secure_tunnel->config->service_id_1_stream_id = stream_id; - // } else if ( - // secure_tunnel->config->service_id_2 != NULL && - // aws_string_compare(secure_tunnel->config->service_id_2, service_id) == 0) { - // stream_id = secure_tunnel->config->service_id_2_stream_id + 1; - // secure_tunnel->config->service_id_2_stream_id = stream_id; - // } else if ( - // secure_tunnel->config->service_id_3 != NULL && - // aws_string_compare(secure_tunnel->config->service_id_3, service_id) == 0) { - // stream_id = secure_tunnel->config->service_id_3_stream_id + 1; - // secure_tunnel->config->service_id_3_stream_id = stream_id; - // } else { - // /* service_id doesn't match any existing service id*/ - // AWS_LOGF_DEBUG( - // AWS_LS_IOTDEVICE_SECURE_TUNNELING, - // "id=%p: invalid service_id:%s attempted to be used with an outbound message", - // (void *)message_view, - // aws_string_c_str(service_id)); - // stream_id = INVALID_STREAM_ID; - // } - // aws_string_destroy(service_id); } else { stream_id = secure_tunnel->config->stream_id + 1; secure_tunnel->config->stream_id = stream_id; @@ -600,9 +547,6 @@ void aws_secure_tunnel_options_storage_destroy(struct aws_secure_tunnel_options_ aws_string_destroy(storage->endpoint_host); aws_string_destroy(storage->access_token); aws_string_destroy(storage->client_token); - // aws_string_destroy(storage->service_id_1); - // aws_string_destroy(storage->service_id_2); - // aws_string_destroy(storage->service_id_3); aws_hash_table_clean_up(&storage->service_ids); aws_mem_release(storage->allocator, storage); } From 5e6c3774e4ee6a4ef5134806bef856f9a2c2aacd Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Thu, 16 Feb 2023 14:45:47 -0800 Subject: [PATCH 54/69] more cr fixes/changes --- include/aws/iotdevice/secure_tunneling.h | 2 +- source/secure_tunneling.c | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/aws/iotdevice/secure_tunneling.h b/include/aws/iotdevice/secure_tunneling.h index ea51c2ad..40fc6061 100644 --- a/include/aws/iotdevice/secure_tunneling.h +++ b/include/aws/iotdevice/secure_tunneling.h @@ -232,7 +232,7 @@ struct aws_secure_tunnel *aws_secure_tunnel_acquire(struct aws_secure_tunnel *se * @return NULL */ AWS_IOTDEVICE_API -void aws_secure_tunnel_release(struct aws_secure_tunnel *secure_tunnel); +struct aws_secure_tunnel *aws_secure_tunnel_release(struct aws_secure_tunnel *secure_tunnel); /** * Asynchronous notify to the secure tunnel that you want it to attempt to connect. diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 9f27c9db..983ac856 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -1792,10 +1792,12 @@ struct aws_secure_tunnel *aws_secure_tunnel_acquire(struct aws_secure_tunnel *se return secure_tunnel; } -void aws_secure_tunnel_release(struct aws_secure_tunnel *secure_tunnel) { +struct aws_secure_tunnel *aws_secure_tunnel_release(struct aws_secure_tunnel *secure_tunnel) { if (secure_tunnel != NULL) { aws_ref_count_release(&secure_tunnel->ref_count); } + + return NULL; } int aws_secure_tunnel_start(struct aws_secure_tunnel *secure_tunnel) { From b28da64ea354dab0e8543ed0e0a62f7c501b2a3d Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Fri, 17 Feb 2023 15:52:59 -0800 Subject: [PATCH 55/69] add termination callback specific user data --- include/aws/iotdevice/private/secure_tunneling_impl.h | 1 + include/aws/iotdevice/secure_tunneling.h | 5 +++++ source/secure_tunneling.c | 2 +- source/secure_tunneling_operations.c | 1 + 4 files changed, 8 insertions(+), 1 deletion(-) diff --git a/include/aws/iotdevice/private/secure_tunneling_impl.h b/include/aws/iotdevice/private/secure_tunneling_impl.h index 7fa292aa..433c72dd 100644 --- a/include/aws/iotdevice/private/secure_tunneling_impl.h +++ b/include/aws/iotdevice/private/secure_tunneling_impl.h @@ -136,6 +136,7 @@ struct aws_secure_tunnel_options_storage { 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; void *user_data; enum aws_secure_tunneling_local_proxy_mode local_proxy_mode; diff --git a/include/aws/iotdevice/secure_tunneling.h b/include/aws/iotdevice/secure_tunneling.h index 40fc6061..a3a9f165 100644 --- a/include/aws/iotdevice/secure_tunneling.h +++ b/include/aws/iotdevice/secure_tunneling.h @@ -186,7 +186,12 @@ struct aws_secure_tunnel_options { aws_secure_tunneling_on_stream_reset_fn *on_stream_reset; aws_secure_tunneling_on_session_reset_fn *on_session_reset; aws_secure_tunneling_on_stopped_fn *on_stopped; + + /** + * Callback for when the secure tunnel has completely destroyed itself. + */ aws_secure_tunneling_on_termination_complete_fn *on_termination_complete; + void *secure_tunnel_on_termination_user_data; }; /** diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 983ac856..de0dee3a 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -100,7 +100,7 @@ static void s_secure_tunnel_final_destroy(struct aws_secure_tunnel *secure_tunne void *termination_complete_user_data = NULL; if (secure_tunnel->config != NULL) { on_termination_complete = secure_tunnel->config->on_termination_complete; - termination_complete_user_data = secure_tunnel->config->user_data; + termination_complete_user_data = secure_tunnel->config->secure_tunnel_on_termination_user_data; } aws_secure_tunnel_operational_state_clean_up(secure_tunnel); diff --git a/source/secure_tunneling_operations.c b/source/secure_tunneling_operations.c index 3850d2ad..9931805d 100644 --- a/source/secure_tunneling_operations.c +++ b/source/secure_tunneling_operations.c @@ -649,6 +649,7 @@ struct aws_secure_tunnel_options_storage *aws_secure_tunnel_options_storage_new( storage->on_session_reset = options->on_session_reset; storage->on_stopped = options->on_stopped; storage->on_termination_complete = options->on_termination_complete; + storage->secure_tunnel_on_termination_user_data = options->secure_tunnel_on_termination_user_data; return storage; From 0b6027c02dc8fc1dc156ceee72eb11172a01bcd7 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 20 Feb 2023 11:45:29 -0800 Subject: [PATCH 56/69] vtable expanded for testing --- .../iotdevice/private/secure_tunneling_impl.h | 22 + source/secure_tunneling.c | 17 +- tests/CMakeLists.txt | 24 +- tests/secure_tunnel_tests.c | 527 ++++++++++++++++++ 4 files changed, 577 insertions(+), 13 deletions(-) create mode 100644 tests/secure_tunnel_tests.c diff --git a/include/aws/iotdevice/private/secure_tunneling_impl.h b/include/aws/iotdevice/private/secure_tunneling_impl.h index 433c72dd..4a0526ae 100644 --- a/include/aws/iotdevice/private/secure_tunneling_impl.h +++ b/include/aws/iotdevice/private/secure_tunneling_impl.h @@ -145,6 +145,11 @@ struct aws_secure_tunnel_options_storage { struct aws_secure_tunnel_vtable { /* aws_high_res_clock_get_ticks */ uint64_t (*get_current_time_fn)(void); + + /* For test verification */ + int (*aws_websocket_client_connect_fn)(const struct aws_websocket_client_connection_options *options); + + void *vtable_user_data; }; struct aws_secure_tunnel { @@ -236,4 +241,21 @@ struct aws_secure_tunnel { uint64_t next_ping_time; }; +AWS_EXTERN_C_BEGIN + +/* + * Override the vtable used by the secure tunnel; useful for mocking certain scenarios. + */ +AWS_IOTDEVICE_API void aws_secure_tunnel_set_vtable( + struct aws_secure_tunnel *secure_tunnel, + const struct aws_secure_tunnel_vtable *vtable); + +/* + * Gets the default vtable used by the secure tunnel. In order to mock something, we start with the default and then + * mutate it selectively to achieve the scenario we're interested in. + */ +AWS_IOTDEVICE_API const struct aws_secure_tunnel_vtable *aws_secure_tunnel_get_default_vtable(void); + +AWS_EXTERN_C_END + #endif /* AWS_IOTDEVICE_SECURE_TUNNELING_IMPL_H */ diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index de0dee3a..8f4d1f24 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -629,7 +629,7 @@ void s_websocket_transform_complete_task_fn(struct aws_task *task, void *arg, en .port = 443, .handshake_request = secure_tunnel->handshake_request, .manual_window_management = false, - .user_data = secure_tunnel, + .user_data = secure_tunnel->config->user_data, .requested_event_loop = secure_tunnel->loop, .on_connection_setup = s_on_websocket_setup, @@ -643,7 +643,7 @@ void s_websocket_transform_complete_task_fn(struct aws_task *task, void *arg, en websocket_options.proxy_options = &secure_tunnel->config->http_proxy_options; } - if (aws_websocket_client_connect(&websocket_options)) { + if (secure_tunnel->vtable->aws_websocket_client_connect_fn(&websocket_options)) { AWS_LOGF_ERROR( AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: Failed to initiate websocket connection.", @@ -1052,8 +1052,21 @@ static uint64_t s_aws_high_res_clock_get_ticks_proxy(void) { static struct aws_secure_tunnel_vtable s_default_secure_tunnel_vtable = { .get_current_time_fn = s_aws_high_res_clock_get_ticks_proxy, + .aws_websocket_client_connect_fn = aws_websocket_client_connect, + + .vtable_user_data = NULL, }; +void aws_secure_tunnel_set_vtable( + struct aws_secure_tunnel *secure_tunnel, + const struct aws_secure_tunnel_vtable *vtable) { + secure_tunnel->vtable = vtable; +} + +const struct aws_secure_tunnel_vtable *aws_secure_tunnel_get_default_vtable(void) { + return &s_default_secure_tunnel_vtable; +} + /********************************************************************************************************************* * Operations ********************************************************************************************************************/ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5d1053c3..44646b54 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -3,7 +3,7 @@ include(AwsTestHarness) enable_testing() file(GLOB TEST_HDRS "mqtt_mock_structs.h") -set(TEST_SRC iotdevice_tests.c metrics_tests.c secure_tunneling_tests.c) +set(TEST_SRC iotdevice_tests.c metrics_tests.c secure_tunneling_tests.c secure_tunnel_tests.c) file(GLOB TESTS ${TEST_HDRS} ${TEST_SRC}) add_test_case(library_init) @@ -19,16 +19,18 @@ if (UNIX AND NOT APPLE) add_test_case(devicedefender_publish_failure_callback_invoked) endif() -add_net_test_case(secure_tunneling_handle_stream_start_test) -add_net_test_case(secure_tunneling_handle_data_receive_test) -add_net_test_case(secure_tunneling_handle_stream_reset_test) -add_net_test_case(secure_tunneling_handle_session_reset_test) -add_net_test_case(secure_tunneling_handle_session_reset_no_stream_test) -add_net_test_case(secure_tunneling_init_websocket_options_test) -add_net_test_case(secure_tunneling_handle_send_data) -add_net_test_case(secure_tunneling_handle_send_data_stream_start) -add_net_test_case(secure_tunneling_handle_send_data_stream_reset) -add_net_test_case(secure_tunneling_handle_send_data_public) +add_test_case(secure_tunneling_serializer_data_message_test) + +# add_net_test_case(secure_tunneling_handle_stream_start_test) +# add_net_test_case(secure_tunneling_handle_data_receive_test) +# add_net_test_case(secure_tunneling_handle_stream_reset_test) +# add_net_test_case(secure_tunneling_handle_session_reset_test) +# add_net_test_case(secure_tunneling_handle_session_reset_no_stream_test) +# add_net_test_case(secure_tunneling_init_websocket_options_test) +# add_net_test_case(secure_tunneling_handle_send_data) +# add_net_test_case(secure_tunneling_handle_send_data_stream_start) +# add_net_test_case(secure_tunneling_handle_send_data_stream_reset) +# add_net_test_case(secure_tunneling_handle_send_data_public) generate_test_driver(${PROJECT_NAME}-tests) diff --git a/tests/secure_tunnel_tests.c b/tests/secure_tunnel_tests.c new file mode 100644 index 00000000..31477f49 --- /dev/null +++ b/tests/secure_tunnel_tests.c @@ -0,0 +1,527 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +AWS_STATIC_STRING_FROM_LITERAL(s_client_token, "IAmAClientToken"); +AWS_STATIC_STRING_FROM_LITERAL(s_access_token, "IAmAnAccessToken"); +AWS_STATIC_STRING_FROM_LITERAL(s_endpoint_host, "IAmAnEndpointHost"); + +#ifdef _WIN32 +# define LOCAL_SOCK_TEST_PATTERN "\\\\.\\pipe\\testsock%llu" +#else +# define LOCAL_SOCK_TEST_PATTERN "testsock%llu.sock" +#endif + +struct aws_secure_tunnel_server_mock_connection_context { + struct aws_allocator *allocator; + + struct aws_channel *channel; + struct aws_channel_handler handler; + struct aws_channel_slot *slot; + + // Add a function table for serializer.c decoding/encoding + + struct aws_secure_tunnel_mock_test_fixture *test_fixture; + + struct aws_task service_task; +}; + +typedef int(aws_secure_tunnel_on_mock_server_message_received_fn)( + void *message_view, + struct aws_secure_tunnel_server_mock_connection_context *connection, + void *message_received_user_data); + +typedef void(aws_secure_tunnel_mock_server_service_fn)( + struct aws_secure_tunnel_server_mock_connection_context *mock_server, + void *user_data); + +struct aws_secure_tunnel_mock_server_vtable { + aws_secure_tunnel_on_mock_server_message_received_fn *packet_handler_fn; + aws_secure_tunnel_mock_server_service_fn *service_task_fn; +}; + +struct aws_secure_tunnel_mock_test_fixture_options { + struct aws_secure_tunnel_options *secure_tunnel_options; + const struct aws_secure_tunnel_mock_server_vtable *server_function_table; + + void *mock_server_user_data; +}; + +struct aws_secure_tunnel_mock_test_fixture { + struct aws_allocator *allocator; + + struct aws_event_loop_group *secure_tunnel_elg; + struct aws_event_loop_group *server_elg; + struct aws_host_resolver *host_resolver; + struct aws_client_bootstrap *secure_tunnel_bootstrap; + struct aws_server_bootstrap *server_bootstrap; + struct aws_socket_endpoint endpoint; + struct aws_socket_options socket_options; + struct aws_socket *listener; + struct aws_channel *server_channel; + + const struct aws_secure_tunnel_mock_server_vtable *server_function_table; + void *mock_server_user_data; + + struct aws_secure_tunnel *secure_tunnel; + struct aws_secure_tunnel_vtable secure_tunnel_vtable; + + struct aws_mutex lock; + struct aws_condition_variable signal; + bool listener_destroyed; + bool secure_tunnel_terminated; + bool secure_tunnel_connected_succesfully; + bool secure_tunnel_connection_failed; +}; + +static void s_on_test_secure_tunnel_termination(void *user_data) { + struct aws_secure_tunnel_mock_test_fixture *test_fixture = 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); +} + +static void s_on_test_secure_tunnel_connection_complete( + const struct aws_secure_tunnel_connection_view *connection_view, + int error_code, + void *user_data) { + struct aws_secure_tunnel_mock_test_fixture *test_fixture = user_data; + + aws_mutex_lock(&test_fixture->lock); + if (error_code == 0) { + test_fixture->secure_tunnel_connected_succesfully = true; + } else { + test_fixture->secure_tunnel_connection_failed = true; + } + aws_mutex_unlock(&test_fixture->lock); + aws_condition_variable_notify_all(&test_fixture->signal); +} + +struct secure_tunnel_test_options { + struct aws_secure_tunnel_options secure_tunnel_options; + struct aws_secure_tunnel_mock_server_vtable server_function_table; +}; + +static void s_secure_tunnel_test_init_default_options(struct secure_tunnel_test_options *test_options) { + struct aws_secure_tunnel_options local_secure_tunnel_options = { + .endpoint_host = aws_byte_cursor_from_string(s_endpoint_host), + .access_token = aws_byte_cursor_from_string(s_access_token), + .local_proxy_mode = AWS_SECURE_TUNNELING_DESTINATION_MODE, + }; + test_options->secure_tunnel_options = local_secure_tunnel_options; +} + +static int s_process_read_message( + struct aws_channel_handler *handler, + struct aws_channel_slot *slot, + struct aws_io_message *message) { + + struct aws_secure_tunnel_server_mock_connection_context *server_connection = handler->impl; + + if (message->message_type != AWS_IO_MESSAGE_APPLICATION_DATA) { + return AWS_OP_ERR; + } + + // STEVE TODO incomming data needs to be processed + (void)server_connection; + // struct aws_byte_cursor message_cursor = aws_byte_cursor_from_buf(&message->message_data); + + // int result = aws_mqtt5_decoder_on_data_received(&server_connection->decoder, message_cursor); + // if (result != AWS_OP_SUCCESS) { + // aws_channel_shutdown(server_connection->channel, aws_last_error()); + // goto done; + // } + + aws_channel_slot_increment_read_window(slot, message->message_data.len); + + // done: + + aws_mem_release(message->allocator, message); + + return AWS_OP_SUCCESS; +} + +static int s_shutdown( + struct aws_channel_handler *handler, + struct aws_channel_slot *slot, + enum aws_channel_direction dir, + int error_code, + bool free_scarce_resources_immediately) { + + (void)handler; + + return aws_channel_slot_on_handler_shutdown_complete(slot, dir, error_code, free_scarce_resources_immediately); +} + +static size_t s_initial_window_size(struct aws_channel_handler *handler) { + (void)handler; + + return SIZE_MAX; +} + +static void s_destroy(struct aws_channel_handler *handler) { + struct aws_secure_tunnel_server_mock_connection_context *server_connection = handler->impl; + + aws_event_loop_cancel_task( + aws_channel_get_event_loop(server_connection->channel), &server_connection->service_task); + + // aws_mqtt5_decoder_clean_up(&server_connection->decoder); + // aws_mqtt5_encoder_clean_up(&server_connection->encoder); + // aws_mqtt5_inbound_topic_alias_resolver_clean_up(&server_connection->inbound_alias_resolver); + + aws_mem_release(server_connection->allocator, server_connection); +} + +static size_t s_message_overhead(struct aws_channel_handler *handler) { + (void)handler; + + return 0; +} + +static bool s_has_secure_tunnel_terminated(void *arg) { + struct aws_secure_tunnel_mock_test_fixture *test_fixture = arg; + return test_fixture->secure_tunnel_terminated; +} + +static void s_wait_for_secure_tunnel_terminated(struct aws_secure_tunnel_mock_test_fixture *test_context) { + aws_mutex_lock(&test_context->lock); + aws_condition_variable_wait_pred( + &test_context->signal, &test_context->lock, s_has_secure_tunnel_terminated, test_context); + aws_mutex_unlock(&test_context->lock); +} + +static bool s_has_secure_tunnel_connected_succesfully(void *arg) { + struct aws_secure_tunnel_mock_test_fixture *test_fixture = arg; + return test_fixture->secure_tunnel_connected_succesfully; +} + +static void s_wait_for_connected_successfully(struct aws_secure_tunnel_mock_test_fixture *test_context) { + aws_mutex_lock(&test_context->lock); + aws_condition_variable_wait_pred( + &test_context->signal, &test_context->lock, s_has_secure_tunnel_connected_succesfully, test_context); + aws_mutex_unlock(&test_context->lock); +} + +static struct aws_channel_handler_vtable s_secure_tunnel_mock_server_channel_handler_vtable = { + .process_read_message = &s_process_read_message, + .process_write_message = NULL, + .increment_read_window = NULL, + .shutdown = &s_shutdown, + .initial_window_size = &s_initial_window_size, + .message_overhead = &s_message_overhead, + .destroy = &s_destroy, +}; + +static void s_mock_server_service_task_fn(struct aws_task *task, void *arg, enum aws_task_status status) { + if (status != AWS_TASK_STATUS_RUN_READY) { + return; + } + + struct aws_secure_tunnel_server_mock_connection_context *server_connection = arg; + + aws_secure_tunnel_mock_server_service_fn *service_fn = + server_connection->test_fixture->server_function_table->service_task_fn; + if (service_fn != NULL) { + (*service_fn)(server_connection, server_connection->test_fixture->mock_server_user_data); + } + + uint64_t now = 0; + aws_high_res_clock_get_ticks(&now); + uint64_t next_service_time = now + aws_timestamp_convert(1, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL); + + aws_event_loop_schedule_task_future( + aws_channel_get_event_loop(server_connection->channel), task, next_service_time); +} + +static void s_on_incoming_channel_setup_fn( + struct aws_server_bootstrap *bootstrap, + int error_code, + struct aws_channel *channel, + void *user_data) { + (void)bootstrap; + struct aws_secure_tunnel_mock_test_fixture *test_fixture = user_data; + + if (!error_code) { + struct aws_channel_slot *test_handler_slot = aws_channel_slot_new(channel); + aws_channel_slot_insert_end(channel, test_handler_slot); + + struct aws_secure_tunnel_server_mock_connection_context *server_connection = + aws_mem_calloc(test_fixture->allocator, 1, sizeof(struct aws_secure_tunnel_server_mock_connection_context)); + server_connection->allocator = test_fixture->allocator; + server_connection->channel = channel; + server_connection->test_fixture = test_fixture; + server_connection->slot = test_handler_slot; + server_connection->handler.alloc = server_connection->allocator; + server_connection->handler.vtable = &s_secure_tunnel_mock_server_channel_handler_vtable; + server_connection->handler.impl = server_connection; + + aws_task_init( + &server_connection->service_task, + s_mock_server_service_task_fn, + server_connection, + "mock_server_service_task_fn"); + aws_event_loop_schedule_task_now(aws_channel_get_event_loop(channel), &server_connection->service_task); + + aws_channel_slot_set_handler(server_connection->slot, &server_connection->handler); + + // STEVE TODO add serializer.c function calls to encode/decode messages + + // aws_mqtt5_encode_init_testing_function_table(&server_connection->encoding_table); + + // struct aws_mqtt5_encoder_options encoder_options = { + // .client = NULL, + // .encoders = &server_connection->encoding_table, + // }; + + // aws_mqtt5_encoder_init(&server_connection->encoder, server_connection->allocator, &encoder_options); + + // aws_mqtt5_decode_init_testing_function_table(&server_connection->decoding_table); + + // struct aws_mqtt5_decoder_options decoder_options = { + // .callback_user_data = server_connection, + // .on_packet_received = s_aws_mqtt5_mock_test_fixture_on_packet_received_fn, + // .decoder_table = &server_connection->decoding_table, + // }; + + // aws_mqtt5_decoder_init(&server_connection->decoder, server_connection->allocator, &decoder_options); + // aws_mqtt5_inbound_topic_alias_resolver_init( + // &server_connection->inbound_alias_resolver, server_connection->allocator); + // aws_mqtt5_inbound_topic_alias_resolver_reset( + // &server_connection->inbound_alias_resolver, test_fixture->maximum_inbound_topic_aliases); + // aws_mqtt5_decoder_set_inbound_topic_alias_resolver( + // &server_connection->decoder, &server_connection->inbound_alias_resolver); + + aws_mutex_lock(&test_fixture->lock); + test_fixture->server_channel = channel; + aws_mutex_unlock(&test_fixture->lock); + + /* + * Just like the tls tests in aws-c-io, it's possible for the server channel setup to execute after the client + * channel setup has already posted data to the socket. In this case, the read notification gets lost because + * the server hasn't subscribed to it yet and then we hang and time out. So do the same thing we do for + * tls server channel setup and force a read of the socket after we're fully initialized. + */ + aws_channel_trigger_read(channel); + } +} + +static void s_on_incoming_channel_shutdown_fn( + struct aws_server_bootstrap *bootstrap, + int error_code, + struct aws_channel *channel, + void *user_data) { + (void)bootstrap; + (void)error_code; + (void)channel; + (void)user_data; +} + +static void s_on_listener_destroy(struct aws_server_bootstrap *bootstrap, void *user_data) { + (void)bootstrap; + struct aws_secure_tunnel_mock_test_fixture *test_fixture = user_data; + aws_mutex_lock(&test_fixture->lock); + test_fixture->listener_destroyed = true; + aws_mutex_unlock(&test_fixture->lock); + aws_condition_variable_notify_one(&test_fixture->signal); +} + +static bool s_is_listener_destroyed(void *arg) { + struct aws_secure_tunnel_mock_test_fixture *test_fixture = arg; + return test_fixture->listener_destroyed; +} + +static void s_wait_on_listener_cleanup(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_is_listener_destroyed, test_fixture); + aws_mutex_unlock(&test_fixture->lock); +} + +int aws_websocket_client_connect_mock_fn(const struct aws_websocket_client_connection_options *options) { + struct aws_secure_tunnel_mock_test_fixture *test_fixture = options->user_data; + struct aws_secure_tunnel *secure_tunnel = test_fixture->secure_tunnel; + (void)secure_tunnel; + return AWS_OP_SUCCESS; +} + +int aws_secure_tunnel_mock_test_fixture_init( + struct aws_secure_tunnel_mock_test_fixture *test_fixture, + struct aws_allocator *allocator, + struct aws_secure_tunnel_mock_test_fixture_options *options) { + + AWS_ZERO_STRUCT(*test_fixture); + test_fixture->allocator = allocator; + + aws_mutex_init(&test_fixture->lock); + aws_condition_variable_init(&test_fixture->signal); + + test_fixture->server_function_table = options->server_function_table; + test_fixture->mock_server_user_data = options->mock_server_user_data; + + struct aws_socket_options socket_options = { + .connect_timeout_ms = 1000, + .domain = AWS_SOCKET_LOCAL, + }; + + test_fixture->socket_options = socket_options; + test_fixture->server_elg = aws_event_loop_group_new_default(allocator, 1, NULL); + test_fixture->server_bootstrap = aws_server_bootstrap_new(allocator, test_fixture->server_elg); + + test_fixture->secure_tunnel_elg = aws_event_loop_group_new_default(allocator, 4, NULL); + 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); + + struct aws_client_bootstrap_options bootstrap_options = { + .event_loop_group = test_fixture->secure_tunnel_elg, + .user_data = test_fixture, + .host_resolver = test_fixture->host_resolver, + }; + + test_fixture->secure_tunnel_bootstrap = aws_client_bootstrap_new(allocator, &bootstrap_options); + + uint64_t timestamp = 0; + ASSERT_SUCCESS(aws_sys_clock_get_ticks(×tamp)); + + snprintf( + test_fixture->endpoint.address, + sizeof(test_fixture->endpoint.address), + LOCAL_SOCK_TEST_PATTERN, + (long long unsigned)timestamp); + + struct aws_server_socket_channel_bootstrap_options server_bootstrap_options = { + .bootstrap = test_fixture->server_bootstrap, + .host_name = test_fixture->endpoint.address, + .port = test_fixture->endpoint.port, + .socket_options = &test_fixture->socket_options, + .incoming_callback = s_on_incoming_channel_setup_fn, + .shutdown_callback = s_on_incoming_channel_shutdown_fn, + .destroy_callback = s_on_listener_destroy, + .user_data = test_fixture, + }; + test_fixture->listener = aws_server_bootstrap_new_socket_listener(&server_bootstrap_options); + + options->secure_tunnel_options->endpoint_host = aws_byte_cursor_from_c_str(test_fixture->endpoint.address); + options->secure_tunnel_options->bootstrap = test_fixture->secure_tunnel_bootstrap; + options->secure_tunnel_options->socket_options = &test_fixture->socket_options; + options->secure_tunnel_options->access_token = aws_byte_cursor_from_string(s_access_token); + options->secure_tunnel_options->user_data = test_fixture; + + // Steve TODO set secure tunnel callbacks + options->secure_tunnel_options->on_connection_complete = s_on_test_secure_tunnel_connection_complete; + // options->secure_tunnel_options->on_connection_shutdown + // options->secure_tunnel_options->on_message_received + // options->secure_tunnel_options->on_send_data_complete + // options->secure_tunnel_options->on_session_reset + // options->secure_tunnel_options->on_stopped + // options->secure_tunnel_options->on_stream_reset + // options->secure_tunnel_options->on_stream_start + 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); + + 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_mock_fn; + test_fixture->secure_tunnel_vtable.vtable_user_data = test_fixture; + + aws_secure_tunnel_set_vtable(test_fixture->secure_tunnel, &test_fixture->secure_tunnel_vtable); + + return AWS_OP_SUCCESS; +} + +void aws_secure_tunnel_mock_test_fixture_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); + aws_server_bootstrap_destroy_socket_listener(test_fixture->server_bootstrap, test_fixture->listener); + + s_wait_on_listener_cleanup(test_fixture); + + aws_server_bootstrap_release(test_fixture->server_bootstrap); + aws_event_loop_group_release(test_fixture->server_elg); + aws_event_loop_group_release(test_fixture->secure_tunnel_elg); + + // aws_thread_join_all_managed(); + + // for (size_t i = 0; i < aws_array_list_length(&test_fixture->server_received_packets); ++i) { + // struct aws_mqtt5_mock_server_packet_record *packet = NULL; + // aws_array_list_get_at_ptr(&test_fixture->server_received_packets, (void **)&packet, i); + + // s_destroy_packet_storage(packet->packet_storage, packet->storage_allocator, packet->packet_type); + // } + + // aws_array_list_clean_up(&test_fixture->server_received_packets); + + // for (size_t i = 0; i < aws_array_list_length(&test_fixture->lifecycle_events); ++i) { + // struct aws_mqtt5_lifecycle_event_record *event = NULL; + // aws_array_list_get_at(&test_fixture->lifecycle_events, &event, i); + + // s_destroy_lifecycle_event_storage(event); + // } + + // aws_array_list_clean_up(&test_fixture->lifecycle_events); + // aws_array_list_clean_up(&test_fixture->client_states); + // aws_array_list_clean_up(&test_fixture->client_statistics); + + aws_mutex_clean_up(&test_fixture->lock); + aws_condition_variable_clean_up(&test_fixture->signal); +} + +/********************************************************************************************************************* + * TESTS + ********************************************************************************************************************/ + +static int s_secure_tunneling_serializer_data_message_test_fn(struct aws_allocator *allocator, void *ctx) { + return AWS_OP_SUCCESS; + // aws_iotdevice_library_init(allocator); + 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, + .server_function_table = &test_options.server_function_table, + }; + + struct aws_secure_tunnel_mock_test_fixture test_context; + ASSERT_SUCCESS(aws_secure_tunnel_mock_test_fixture_init(&test_context, allocator, &test_fixture_options)); + + struct aws_secure_tunnel *secure_tunnel = test_context.secure_tunnel; + + ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); + s_wait_for_connected_successfully(&test_context); + + aws_secure_tunnel_mock_test_fixture_clean_up(&test_context); + // 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) From c7accbae53a1e8f73bfde14c2768997cfba381a6 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 20 Feb 2023 13:49:46 -0800 Subject: [PATCH 57/69] expand vtable for testing --- .../iotdevice/private/secure_tunneling_impl.h | 13 ++- include/aws/iotdevice/secure_tunneling.h | 2 - source/secure_tunneling.c | 9 +- tests/secure_tunnel_tests.c | 93 +++++++++++++------ 4 files changed, 84 insertions(+), 33 deletions(-) diff --git a/include/aws/iotdevice/private/secure_tunneling_impl.h b/include/aws/iotdevice/private/secure_tunneling_impl.h index 4a0526ae..4cbfd8fb 100644 --- a/include/aws/iotdevice/private/secure_tunneling_impl.h +++ b/include/aws/iotdevice/private/secure_tunneling_impl.h @@ -149,6 +149,17 @@ struct aws_secure_tunnel_vtable { /* For test verification */ int (*aws_websocket_client_connect_fn)(const struct aws_websocket_client_connection_options *options); + /* For test verification */ + int (*aws_websocket_send_frame_fn)( + struct aws_websocket *websocket, + const struct aws_websocket_send_frame_options *options); + + /* For test verification */ + void (*aws_websocket_release_fn)(struct aws_websocket *websocket); + + /* For test verification */ + void (*aws_websocket_close_fn)(struct aws_websocket *websocket, bool free_scarce_resources_immediately); + void *vtable_user_data; }; @@ -157,7 +168,7 @@ struct aws_secure_tunnel { struct aws_allocator *allocator; struct aws_ref_count ref_count; - struct aws_secure_tunnel_vtable *vtable; + const struct aws_secure_tunnel_vtable *vtable; /* * Secure tunnel configuration diff --git a/include/aws/iotdevice/secure_tunneling.h b/include/aws/iotdevice/secure_tunneling.h index a3a9f165..d6e8f932 100644 --- a/include/aws/iotdevice/secure_tunneling.h +++ b/include/aws/iotdevice/secure_tunneling.h @@ -13,8 +13,6 @@ #define AWS_IOT_ST_SPLIT_MESSAGE_SIZE 15000 struct aws_secure_tunnel; -struct aws_websocket; -struct aws_websocket_incoming_frame; struct aws_http_proxy_options; enum aws_secure_tunneling_local_proxy_mode { diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 8f4d1f24..34bb0a75 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -434,7 +434,7 @@ static int s_secure_tunneling_send( /* Prevent further operations that attempt to write to the WebSocket until current operation is completed */ secure_tunnel->pending_write_completion = true; - return aws_websocket_send_frame(secure_tunnel->websocket, &frame_options); + return secure_tunnel->vtable->aws_websocket_send_frame_fn(secure_tunnel->websocket, &frame_options); } /***************************************************************************************************************** @@ -629,7 +629,7 @@ void s_websocket_transform_complete_task_fn(struct aws_task *task, void *arg, en .port = 443, .handshake_request = secure_tunnel->handshake_request, .manual_window_management = false, - .user_data = secure_tunnel->config->user_data, + .user_data = secure_tunnel, .requested_event_loop = secure_tunnel->loop, .on_connection_setup = s_on_websocket_setup, @@ -1053,6 +1053,9 @@ static uint64_t s_aws_high_res_clock_get_ticks_proxy(void) { static struct aws_secure_tunnel_vtable s_default_secure_tunnel_vtable = { .get_current_time_fn = s_aws_high_res_clock_get_ticks_proxy, .aws_websocket_client_connect_fn = aws_websocket_client_connect, + .aws_websocket_send_frame_fn = aws_websocket_send_frame, + .aws_websocket_release_fn = aws_websocket_release, + .aws_websocket_close_fn = aws_websocket_close, .vtable_user_data = NULL, }; @@ -1220,7 +1223,7 @@ int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure AWS_ZERO_STRUCT(frame_options); frame_options.opcode = AWS_WEBSOCKET_OPCODE_PING; frame_options.fin = true; - aws_websocket_send_frame(secure_tunnel->websocket, &frame_options); + secure_tunnel->vtable->aws_websocket_send_frame_fn(secure_tunnel->websocket, &frame_options); break; case AWS_STOT_MESSAGE: diff --git a/tests/secure_tunnel_tests.c b/tests/secure_tunnel_tests.c index 31477f49..2c003b7c 100644 --- a/tests/secure_tunnel_tests.c +++ b/tests/secure_tunnel_tests.c @@ -20,7 +20,6 @@ #include #include -AWS_STATIC_STRING_FROM_LITERAL(s_client_token, "IAmAClientToken"); AWS_STATIC_STRING_FROM_LITERAL(s_access_token, "IAmAnAccessToken"); AWS_STATIC_STRING_FROM_LITERAL(s_endpoint_host, "IAmAnEndpointHost"); @@ -54,13 +53,20 @@ typedef void(aws_secure_tunnel_mock_server_service_fn)( void *user_data); struct aws_secure_tunnel_mock_server_vtable { + // STEVE TODO Not needed? aws_secure_tunnel_on_mock_server_message_received_fn *packet_handler_fn; aws_secure_tunnel_mock_server_service_fn *service_task_fn; + + aws_websocket_on_connection_setup_fn *on_connection_setup_fn; + aws_websocket_on_connection_shutdown_fn *on_connection_shutdown_fn; + aws_websocket_on_incoming_frame_begin_fn *on_incoming_frame_begin_fn; + aws_websocket_on_incoming_frame_payload_fn *on_incoming_frame_payload_fn; + aws_websocket_on_incoming_frame_complete_fn *on_incoming_frame_complete_fn; }; struct aws_secure_tunnel_mock_test_fixture_options { struct aws_secure_tunnel_options *secure_tunnel_options; - const struct aws_secure_tunnel_mock_server_vtable *server_function_table; + struct aws_secure_tunnel_mock_server_vtable *server_function_table; void *mock_server_user_data; }; @@ -78,7 +84,7 @@ struct aws_secure_tunnel_mock_test_fixture { struct aws_socket *listener; struct aws_channel *server_channel; - const struct aws_secure_tunnel_mock_server_vtable *server_function_table; + struct aws_secure_tunnel_mock_server_vtable *server_function_table; void *mock_server_user_data; struct aws_secure_tunnel *secure_tunnel; @@ -356,13 +362,64 @@ static void s_wait_on_listener_cleanup(struct aws_secure_tunnel_mock_test_fixtur aws_mutex_unlock(&test_fixture->lock); } +/***************************************************************************************************************** + * WEBSOCKET MOCK FUNCTIONS + *****************************************************************************************************************/ + int aws_websocket_client_connect_mock_fn(const struct aws_websocket_client_connection_options *options) { - struct aws_secure_tunnel_mock_test_fixture *test_fixture = options->user_data; - struct aws_secure_tunnel *secure_tunnel = test_fixture->secure_tunnel; - (void)secure_tunnel; + struct aws_secure_tunnel *secure_tunnel = options->user_data; + struct aws_secure_tunnel_mock_test_fixture *test_fixture = secure_tunnel->config->user_data; + + if (!options->handshake_request) { + AWS_LOGF_ERROR( + AWS_LS_HTTP_WEBSOCKET_SETUP, + "id=static: Invalid connection options, missing required request for websocket client handshake."); + return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + } + + const struct aws_http_headers *request_headers = aws_http_message_get_headers(options->handshake_request); + struct aws_byte_cursor access_token_cur; + if (aws_http_headers_get(request_headers, aws_byte_cursor_from_c_str("access-token"), &access_token_cur)) { + AWS_LOGF_ERROR( + AWS_LS_HTTP_WEBSOCKET_SETUP, + "id=static: Websocket handshake request is missing required 'access-token' header"); + return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + } + + test_fixture->server_function_table->on_connection_setup_fn = options->on_connection_setup; + test_fixture->server_function_table->on_connection_shutdown_fn = options->on_connection_shutdown; + test_fixture->server_function_table->on_incoming_frame_begin_fn = options->on_incoming_frame_begin; + test_fixture->server_function_table->on_incoming_frame_payload_fn = options->on_incoming_frame_payload; + test_fixture->server_function_table->on_incoming_frame_complete_fn = options->on_incoming_frame_complete; + + // struct aws_websocket websocket = + // { + + // } + + struct aws_websocket_on_connection_setup_data websocket_setup = {.error_code = AWS_ERROR_SUCCESS}; + + (test_fixture->server_function_table->on_connection_setup_fn)(&websocket_setup, secure_tunnel); + return AWS_OP_SUCCESS; } +int aws_websocket_send_frame_mock_fn( + struct aws_websocket *websocket, + const struct aws_websocket_send_frame_options *options) { + (void)websocket; + (void)options; + return AWS_OP_SUCCESS; +} + +void aws_websocket_release_mock_fn(struct aws_websocket *websocket) {} + +void aws_websocket_close_mock_fn(struct aws_websocket *websocket, bool free_scarce_resources_immediately) {} + +/***************************************************************************************************************** + * TEST FIXTURE + *****************************************************************************************************************/ + int aws_secure_tunnel_mock_test_fixture_init( struct aws_secure_tunnel_mock_test_fixture *test_fixture, struct aws_allocator *allocator, @@ -444,6 +501,9 @@ int aws_secure_tunnel_mock_test_fixture_init( 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_mock_fn; + test_fixture->secure_tunnel_vtable.aws_websocket_send_frame_fn = aws_websocket_send_frame_mock_fn; + test_fixture->secure_tunnel_vtable.aws_websocket_release_fn = aws_websocket_release_mock_fn; + test_fixture->secure_tunnel_vtable.aws_websocket_close_fn = aws_websocket_close_mock_fn; test_fixture->secure_tunnel_vtable.vtable_user_data = test_fixture; aws_secure_tunnel_set_vtable(test_fixture->secure_tunnel, &test_fixture->secure_tunnel_vtable); @@ -466,26 +526,6 @@ void aws_secure_tunnel_mock_test_fixture_clean_up(struct aws_secure_tunnel_mock_ // aws_thread_join_all_managed(); - // for (size_t i = 0; i < aws_array_list_length(&test_fixture->server_received_packets); ++i) { - // struct aws_mqtt5_mock_server_packet_record *packet = NULL; - // aws_array_list_get_at_ptr(&test_fixture->server_received_packets, (void **)&packet, i); - - // s_destroy_packet_storage(packet->packet_storage, packet->storage_allocator, packet->packet_type); - // } - - // aws_array_list_clean_up(&test_fixture->server_received_packets); - - // for (size_t i = 0; i < aws_array_list_length(&test_fixture->lifecycle_events); ++i) { - // struct aws_mqtt5_lifecycle_event_record *event = NULL; - // aws_array_list_get_at(&test_fixture->lifecycle_events, &event, i); - - // s_destroy_lifecycle_event_storage(event); - // } - - // aws_array_list_clean_up(&test_fixture->lifecycle_events); - // aws_array_list_clean_up(&test_fixture->client_states); - // aws_array_list_clean_up(&test_fixture->client_statistics); - aws_mutex_clean_up(&test_fixture->lock); aws_condition_variable_clean_up(&test_fixture->signal); } @@ -495,7 +535,6 @@ void aws_secure_tunnel_mock_test_fixture_clean_up(struct aws_secure_tunnel_mock_ ********************************************************************************************************************/ static int s_secure_tunneling_serializer_data_message_test_fn(struct aws_allocator *allocator, void *ctx) { - return AWS_OP_SUCCESS; // aws_iotdevice_library_init(allocator); aws_http_library_init(allocator); aws_iotdevice_library_init(allocator); From 35fe644ea257b987a4d7126b0956a34a8743f812 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 20 Feb 2023 14:04:23 -0800 Subject: [PATCH 58/69] simplify websocket creation --- source/secure_tunneling.c | 42 +++++++++++++------------------------ tests/secure_tunnel_tests.c | 10 +++------ 2 files changed, 17 insertions(+), 35 deletions(-) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 34bb0a75..c7f3a6ce 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -548,17 +548,23 @@ static void s_on_websocket_shutdown(struct aws_websocket *websocket, int error_c } } -static void s_secure_tunnel_setup(struct aws_client_bootstrap *bootstrap, int error_code, void *user_data) { - (void)bootstrap; +/* Called on successful or failed websocket setup attempt */ +static void s_on_websocket_setup(const struct aws_websocket_on_connection_setup_data *setup, void *user_data) { struct aws_secure_tunnel *secure_tunnel = user_data; + secure_tunnel->handshake_request = aws_http_message_release(secure_tunnel->handshake_request); + + /* Setup callback contract is: if error_code is non-zero then websocket is NULL. */ + AWS_FATAL_ASSERT((setup->error_code != 0) == (setup->websocket == NULL)); - if (error_code != AWS_OP_SUCCESS) { + secure_tunnel->websocket = setup->websocket; + + if (setup->error_code != AWS_OP_SUCCESS) { + /* Report a failed WebSocket Upgrade attempt */ if (secure_tunnel->config->on_connection_complete) { - if (secure_tunnel->config->on_connection_complete) { - secure_tunnel->config->on_connection_complete(NULL, error_code, secure_tunnel->config->user_data); - } + secure_tunnel->config->on_connection_complete(NULL, setup->error_code, secure_tunnel->config->user_data); } - s_on_websocket_shutdown(secure_tunnel->websocket, error_code, secure_tunnel); + /* Failed/Successful websocket creation and associated errors logged by "websocket-setup" */ + s_on_websocket_shutdown(secure_tunnel->websocket, setup->error_code, secure_tunnel); return; } @@ -574,27 +580,7 @@ static void s_secure_tunnel_setup(struct aws_client_bootstrap *bootstrap, int er return; error: - s_on_websocket_shutdown(secure_tunnel->websocket, error_code, secure_tunnel); -} - -/* Called on successful or failed websocket setup attempt */ -static void s_on_websocket_setup(const struct aws_websocket_on_connection_setup_data *setup, void *user_data) { - struct aws_secure_tunnel *secure_tunnel = user_data; - secure_tunnel->handshake_request = aws_http_message_release(secure_tunnel->handshake_request); - - /* Setup callback contract is: if error_code is non-zero then websocket is NULL. */ - AWS_FATAL_ASSERT((setup->error_code != 0) == (setup->websocket == NULL)); - - secure_tunnel->websocket = setup->websocket; - - /* Report a failed WebSocket Upgrade attempt */ - if (setup->error_code && secure_tunnel->config->on_connection_complete) { - secure_tunnel->config->on_connection_complete(NULL, setup->error_code, secure_tunnel->config->user_data); - } - - /* Failed/Successful websocket creation and associated errors logged by "websocket-setup" */ - - s_secure_tunnel_setup(secure_tunnel->config->bootstrap, setup->error_code, secure_tunnel); + s_on_websocket_shutdown(secure_tunnel->websocket, setup->error_code, secure_tunnel); } struct aws_secure_tunnel_websocket_transform_complete_task { diff --git a/tests/secure_tunnel_tests.c b/tests/secure_tunnel_tests.c index 2c003b7c..c3b4b24d 100644 --- a/tests/secure_tunnel_tests.c +++ b/tests/secure_tunnel_tests.c @@ -392,14 +392,10 @@ int aws_websocket_client_connect_mock_fn(const struct aws_websocket_client_conne test_fixture->server_function_table->on_incoming_frame_payload_fn = options->on_incoming_frame_payload; test_fixture->server_function_table->on_incoming_frame_complete_fn = options->on_incoming_frame_complete; - // struct aws_websocket websocket = - // { + // struct aws_websocket_on_connection_setup_data websocket_setup = {.error_code = AWS_ERROR_SUCCESS, + // .websocket = test_fixture}; - // } - - struct aws_websocket_on_connection_setup_data websocket_setup = {.error_code = AWS_ERROR_SUCCESS}; - - (test_fixture->server_function_table->on_connection_setup_fn)(&websocket_setup, secure_tunnel); + // (test_fixture->server_function_table->on_connection_setup_fn)(&websocket_setup, secure_tunnel); return AWS_OP_SUCCESS; } From 2d9ee53eb5aa5249f65347346beb9eaf7ab6377e Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 20 Feb 2023 14:33:15 -0800 Subject: [PATCH 59/69] add encoding for service ids on outbound message --- source/serializer.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/source/serializer.c b/source/serializer.c index 08385b34..f9bee089 100644 --- a/source/serializer.c +++ b/source/serializer.c @@ -110,6 +110,11 @@ static int s_iot_st_encode_service_id(const struct aws_byte_cursor *service_id, AWS_SECURE_TUNNEL_FN_SERVICE_ID, AWS_SECURE_TUNNEL_PBWT_LENGTH_DELIMITED, service_id, buffer); } +static int s_iot_st_encode_service_ids(const struct aws_byte_cursor *service_id, struct aws_byte_buf *buffer) { + return s_iot_st_encode_byte_range( + AWS_SECURE_TUNNEL_FN_AVAILABLE_SERVICE_IDS, AWS_SECURE_TUNNEL_PBWT_LENGTH_DELIMITED, service_id, buffer); +} + static int s_iot_st_get_varint_size(size_t value, size_t *encode_size) { if (value > AWS_IOT_ST_MAXIMUM_VARINT) { return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); @@ -237,7 +242,23 @@ int aws_iot_st_msg_serialize_from_view( } } - if (message_view->service_id != NULL) { + if (message_view->type == AWS_SECURE_TUNNEL_MT_SERVICE_IDS) { + if (message_view->service_id != 0) { + if (s_iot_st_encode_service_ids(message_view->service_id, buffer)) { + goto cleanup; + } + } + if (message_view->service_id_2 != 0) { + if (s_iot_st_encode_service_ids(message_view->service_id_2, buffer)) { + goto cleanup; + } + } + if (message_view->service_id_3 != 0) { + if (s_iot_st_encode_service_ids(message_view->service_id_3, buffer)) { + goto cleanup; + } + } + } else if (message_view->service_id != NULL) { if (s_iot_st_encode_service_id(message_view->service_id, buffer)) { goto cleanup; } From fa2b9de2ab71111d89e6969f28d1c4bdd46d9f6d Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 20 Feb 2023 15:50:42 -0800 Subject: [PATCH 60/69] fix for length on multiple service ids --- source/serializer.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/source/serializer.c b/source/serializer.c index f9bee089..2c3a99a2 100644 --- a/source/serializer.c +++ b/source/serializer.c @@ -192,6 +192,32 @@ static int s_iot_st_compute_message_length( local_length += (1 + message->service_id->len + service_id_length); } + if (message->service_id_2 != NULL && message->service_id_2->len != 0) { + /* + * 1 byte key + * 1-4 byte payload length varint + * n bytes service_id.len + */ + size_t service_id_length_2 = 0; + if (s_iot_st_get_varint_size((uint32_t)message->service_id_2->len, &service_id_length_2)) { + return AWS_OP_ERR; + } + local_length += (1 + message->service_id_2->len + service_id_length_2); + } + + if (message->service_id_3 != NULL && message->service_id_3->len != 0) { + /* + * 1 byte key + * 1-4 byte payload length varint + * n bytes service_id.len + */ + size_t service_id_length_3 = 0; + if (s_iot_st_get_varint_size((uint32_t)message->service_id_3->len, &service_id_length_3)) { + return AWS_OP_ERR; + } + local_length += (1 + message->service_id_3->len + service_id_length_3); + } + *message_length = local_length; return AWS_OP_SUCCESS; } From 8fbe49ace96a3793088ae35c68b0f0796f0352d0 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Tue, 21 Feb 2023 11:21:03 -0800 Subject: [PATCH 61/69] cr changes --- source/secure_tunneling.c | 4 +- tests/CMakeLists.txt | 16 +- tests/secure_tunnel_tests.c | 315 ++++++++---------------------------- 3 files changed, 76 insertions(+), 259 deletions(-) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index c7f3a6ce..8430e278 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -534,7 +534,7 @@ static void s_on_websocket_shutdown(struct aws_websocket *websocket, int error_c struct aws_secure_tunnel *secure_tunnel = user_data; s_secure_tunnel_shutdown(secure_tunnel->config->bootstrap, error_code, secure_tunnel); - aws_websocket_release(websocket); + secure_tunnel->vtable->aws_websocket_release_fn(websocket); websocket = NULL; if (secure_tunnel->config->on_connection_shutdown) { @@ -842,7 +842,7 @@ static void s_change_current_state_to_websocket_shutdown(struct aws_secure_tunne current_state == AWS_STS_CLEAN_DISCONNECT); if (secure_tunnel->websocket) { - aws_websocket_close(secure_tunnel->websocket, false); + secure_tunnel->vtable->aws_websocket_close_fn(secure_tunnel->websocket, false); } else { s_on_websocket_shutdown(secure_tunnel->websocket, AWS_ERROR_UNKNOWN, secure_tunnel); } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 44646b54..e0649267 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -19,7 +19,7 @@ if (UNIX AND NOT APPLE) add_test_case(devicedefender_publish_failure_callback_invoked) endif() -add_test_case(secure_tunneling_serializer_data_message_test) +# add_test_case(secure_tunneling_serializer_data_message_test) # add_net_test_case(secure_tunneling_handle_stream_start_test) # add_net_test_case(secure_tunneling_handle_data_receive_test) @@ -44,13 +44,13 @@ target_compile_definitions(${TEST_DD_CLIENT_BINARY_NAME} PRIVATE AWS_UNSTABLE_TE target_include_directories(${TEST_DD_CLIENT_BINARY_NAME} PRIVATE ${CMAKE_CURRENT_LIST_DIR}) # Secure Tunneling test client -set(TEST_ST_CLIENT_BINARY_NAME ${PROJECT_NAME}-secure_tunneling-client) -add_executable(${TEST_ST_CLIENT_BINARY_NAME} "aws_iot_secure_tunneling_client_test.c") -target_link_libraries(${TEST_ST_CLIENT_BINARY_NAME} PRIVATE ${PROJECT_NAME}) -aws_set_common_properties(${TEST_ST_CLIENT_BINARY_NAME} NO_WEXTRA NO_PEDANTIC) -aws_add_sanitizers(${TEST_ST_CLIENT_BINARY_NAME} ${${PROJECT_NAME}_SANITIZERS}) -target_compile_definitions(${TEST_ST_CLIENT_BINARY_NAME} PRIVATE AWS_UNSTABLE_TESTING_API=1) -target_include_directories(${TEST_ST_CLIENT_BINARY_NAME} PRIVATE ${CMAKE_CURRENT_LIST_DIR}) +# set(TEST_ST_CLIENT_BINARY_NAME ${PROJECT_NAME}-secure_tunneling-client) +# add_executable(${TEST_ST_CLIENT_BINARY_NAME} "aws_iot_secure_tunneling_client_test.c") +# target_link_libraries(${TEST_ST_CLIENT_BINARY_NAME} PRIVATE ${PROJECT_NAME}) +# aws_set_common_properties(${TEST_ST_CLIENT_BINARY_NAME} NO_WEXTRA NO_PEDANTIC) +# aws_add_sanitizers(${TEST_ST_CLIENT_BINARY_NAME} ${${PROJECT_NAME}_SANITIZERS}) +# target_compile_definitions(${TEST_ST_CLIENT_BINARY_NAME} PRIVATE AWS_UNSTABLE_TESTING_API=1) +# target_include_directories(${TEST_ST_CLIENT_BINARY_NAME} PRIVATE ${CMAKE_CURRENT_LIST_DIR}) if ($ENV{PROTOBUF_TEST}) add_subdirectory(tests_protobuf) diff --git a/tests/secure_tunnel_tests.c b/tests/secure_tunnel_tests.c index c3b4b24d..2ec3afb6 100644 --- a/tests/secure_tunnel_tests.c +++ b/tests/secure_tunnel_tests.c @@ -20,8 +20,12 @@ #include #include +#define PAYLOAD_BYTE_LENGTH_PREFIX 2 AWS_STATIC_STRING_FROM_LITERAL(s_access_token, "IAmAnAccessToken"); AWS_STATIC_STRING_FROM_LITERAL(s_endpoint_host, "IAmAnEndpointHost"); +AWS_STATIC_STRING_FROM_LITERAL(s_service_id_1, "ServiceId1"); +AWS_STATIC_STRING_FROM_LITERAL(s_service_id_2, "ServiceId2"); +AWS_STATIC_STRING_FROM_LITERAL(s_service_id_3, "ServiceId3"); #ifdef _WIN32 # define LOCAL_SOCK_TEST_PATTERN "\\\\.\\pipe\\testsock%llu" @@ -36,8 +40,6 @@ struct aws_secure_tunnel_server_mock_connection_context { struct aws_channel_handler handler; struct aws_channel_slot *slot; - // Add a function table for serializer.c decoding/encoding - struct aws_secure_tunnel_mock_test_fixture *test_fixture; struct aws_task service_task; @@ -52,11 +54,7 @@ typedef void(aws_secure_tunnel_mock_server_service_fn)( struct aws_secure_tunnel_server_mock_connection_context *mock_server, void *user_data); -struct aws_secure_tunnel_mock_server_vtable { - // STEVE TODO Not needed? - aws_secure_tunnel_on_mock_server_message_received_fn *packet_handler_fn; - aws_secure_tunnel_mock_server_service_fn *service_task_fn; - +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; aws_websocket_on_incoming_frame_begin_fn *on_incoming_frame_begin_fn; @@ -66,7 +64,7 @@ struct aws_secure_tunnel_mock_server_vtable { struct aws_secure_tunnel_mock_test_fixture_options { struct aws_secure_tunnel_options *secure_tunnel_options; - struct aws_secure_tunnel_mock_server_vtable *server_function_table; + struct aws_secure_tunnel_mock_websocket_vtable *websocket_function_table; void *mock_server_user_data; }; @@ -75,16 +73,12 @@ struct aws_secure_tunnel_mock_test_fixture { struct aws_allocator *allocator; struct aws_event_loop_group *secure_tunnel_elg; - struct aws_event_loop_group *server_elg; struct aws_host_resolver *host_resolver; struct aws_client_bootstrap *secure_tunnel_bootstrap; - struct aws_server_bootstrap *server_bootstrap; struct aws_socket_endpoint endpoint; struct aws_socket_options socket_options; - struct aws_socket *listener; - struct aws_channel *server_channel; - struct aws_secure_tunnel_mock_server_vtable *server_function_table; + struct aws_secure_tunnel_mock_websocket_vtable *websocket_function_table; void *mock_server_user_data; struct aws_secure_tunnel *secure_tunnel; @@ -125,7 +119,7 @@ static void s_on_test_secure_tunnel_connection_complete( struct secure_tunnel_test_options { struct aws_secure_tunnel_options secure_tunnel_options; - struct aws_secure_tunnel_mock_server_vtable server_function_table; + struct aws_secure_tunnel_mock_websocket_vtable websocket_function_table; }; static void s_secure_tunnel_test_init_default_options(struct secure_tunnel_test_options *test_options) { @@ -137,73 +131,6 @@ static void s_secure_tunnel_test_init_default_options(struct secure_tunnel_test_ test_options->secure_tunnel_options = local_secure_tunnel_options; } -static int s_process_read_message( - struct aws_channel_handler *handler, - struct aws_channel_slot *slot, - struct aws_io_message *message) { - - struct aws_secure_tunnel_server_mock_connection_context *server_connection = handler->impl; - - if (message->message_type != AWS_IO_MESSAGE_APPLICATION_DATA) { - return AWS_OP_ERR; - } - - // STEVE TODO incomming data needs to be processed - (void)server_connection; - // struct aws_byte_cursor message_cursor = aws_byte_cursor_from_buf(&message->message_data); - - // int result = aws_mqtt5_decoder_on_data_received(&server_connection->decoder, message_cursor); - // if (result != AWS_OP_SUCCESS) { - // aws_channel_shutdown(server_connection->channel, aws_last_error()); - // goto done; - // } - - aws_channel_slot_increment_read_window(slot, message->message_data.len); - - // done: - - aws_mem_release(message->allocator, message); - - return AWS_OP_SUCCESS; -} - -static int s_shutdown( - struct aws_channel_handler *handler, - struct aws_channel_slot *slot, - enum aws_channel_direction dir, - int error_code, - bool free_scarce_resources_immediately) { - - (void)handler; - - return aws_channel_slot_on_handler_shutdown_complete(slot, dir, error_code, free_scarce_resources_immediately); -} - -static size_t s_initial_window_size(struct aws_channel_handler *handler) { - (void)handler; - - return SIZE_MAX; -} - -static void s_destroy(struct aws_channel_handler *handler) { - struct aws_secure_tunnel_server_mock_connection_context *server_connection = handler->impl; - - aws_event_loop_cancel_task( - aws_channel_get_event_loop(server_connection->channel), &server_connection->service_task); - - // aws_mqtt5_decoder_clean_up(&server_connection->decoder); - // aws_mqtt5_encoder_clean_up(&server_connection->encoder); - // aws_mqtt5_inbound_topic_alias_resolver_clean_up(&server_connection->inbound_alias_resolver); - - aws_mem_release(server_connection->allocator, server_connection); -} - -static size_t s_message_overhead(struct aws_channel_handler *handler) { - (void)handler; - - return 0; -} - static bool s_has_secure_tunnel_terminated(void *arg) { struct aws_secure_tunnel_mock_test_fixture *test_fixture = arg; return test_fixture->secure_tunnel_terminated; @@ -228,144 +155,31 @@ static void s_wait_for_connected_successfully(struct aws_secure_tunnel_mock_test aws_mutex_unlock(&test_context->lock); } -static struct aws_channel_handler_vtable s_secure_tunnel_mock_server_channel_handler_vtable = { - .process_read_message = &s_process_read_message, - .process_write_message = NULL, - .increment_read_window = NULL, - .shutdown = &s_shutdown, - .initial_window_size = &s_initial_window_size, - .message_overhead = &s_message_overhead, - .destroy = &s_destroy, -}; - -static void s_mock_server_service_task_fn(struct aws_task *task, void *arg, enum aws_task_status status) { - if (status != AWS_TASK_STATUS_RUN_READY) { - return; - } - - struct aws_secure_tunnel_server_mock_connection_context *server_connection = arg; - - aws_secure_tunnel_mock_server_service_fn *service_fn = - server_connection->test_fixture->server_function_table->service_task_fn; - if (service_fn != NULL) { - (*service_fn)(server_connection, server_connection->test_fixture->mock_server_user_data); - } - - uint64_t now = 0; - aws_high_res_clock_get_ticks(&now); - uint64_t next_service_time = now + aws_timestamp_convert(1, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL); - - aws_event_loop_schedule_task_future( - aws_channel_get_event_loop(server_connection->channel), task, next_service_time); -} - -static void s_on_incoming_channel_setup_fn( - struct aws_server_bootstrap *bootstrap, - int error_code, - struct aws_channel *channel, - void *user_data) { - (void)bootstrap; - struct aws_secure_tunnel_mock_test_fixture *test_fixture = user_data; - - if (!error_code) { - struct aws_channel_slot *test_handler_slot = aws_channel_slot_new(channel); - aws_channel_slot_insert_end(channel, test_handler_slot); - - struct aws_secure_tunnel_server_mock_connection_context *server_connection = - aws_mem_calloc(test_fixture->allocator, 1, sizeof(struct aws_secure_tunnel_server_mock_connection_context)); - server_connection->allocator = test_fixture->allocator; - server_connection->channel = channel; - server_connection->test_fixture = test_fixture; - server_connection->slot = test_handler_slot; - server_connection->handler.alloc = server_connection->allocator; - server_connection->handler.vtable = &s_secure_tunnel_mock_server_channel_handler_vtable; - server_connection->handler.impl = server_connection; - - aws_task_init( - &server_connection->service_task, - s_mock_server_service_task_fn, - server_connection, - "mock_server_service_task_fn"); - aws_event_loop_schedule_task_now(aws_channel_get_event_loop(channel), &server_connection->service_task); - - aws_channel_slot_set_handler(server_connection->slot, &server_connection->handler); - - // STEVE TODO add serializer.c function calls to encode/decode messages - - // aws_mqtt5_encode_init_testing_function_table(&server_connection->encoding_table); - - // struct aws_mqtt5_encoder_options encoder_options = { - // .client = NULL, - // .encoders = &server_connection->encoding_table, - // }; - - // aws_mqtt5_encoder_init(&server_connection->encoder, server_connection->allocator, &encoder_options); - - // aws_mqtt5_decode_init_testing_function_table(&server_connection->decoding_table); - - // struct aws_mqtt5_decoder_options decoder_options = { - // .callback_user_data = server_connection, - // .on_packet_received = s_aws_mqtt5_mock_test_fixture_on_packet_received_fn, - // .decoder_table = &server_connection->decoding_table, - // }; - - // aws_mqtt5_decoder_init(&server_connection->decoder, server_connection->allocator, &decoder_options); - // aws_mqtt5_inbound_topic_alias_resolver_init( - // &server_connection->inbound_alias_resolver, server_connection->allocator); - // aws_mqtt5_inbound_topic_alias_resolver_reset( - // &server_connection->inbound_alias_resolver, test_fixture->maximum_inbound_topic_aliases); - // aws_mqtt5_decoder_set_inbound_topic_alias_resolver( - // &server_connection->decoder, &server_connection->inbound_alias_resolver); - - aws_mutex_lock(&test_fixture->lock); - test_fixture->server_channel = channel; - aws_mutex_unlock(&test_fixture->lock); - - /* - * Just like the tls tests in aws-c-io, it's possible for the server channel setup to execute after the client - * channel setup has already posted data to the socket. In this case, the read notification gets lost because - * the server hasn't subscribed to it yet and then we hang and time out. So do the same thing we do for - * tls server channel setup and force a read of the socket after we're fully initialized. - */ - aws_channel_trigger_read(channel); - } -} - -static void s_on_incoming_channel_shutdown_fn( - struct aws_server_bootstrap *bootstrap, - int error_code, - struct aws_channel *channel, - void *user_data) { - (void)bootstrap; - (void)error_code; - (void)channel; - (void)user_data; -} - -static void s_on_listener_destroy(struct aws_server_bootstrap *bootstrap, void *user_data) { - (void)bootstrap; - struct aws_secure_tunnel_mock_test_fixture *test_fixture = user_data; - aws_mutex_lock(&test_fixture->lock); - test_fixture->listener_destroyed = true; - aws_mutex_unlock(&test_fixture->lock); - aws_condition_variable_notify_one(&test_fixture->signal); -} - -static bool s_is_listener_destroyed(void *arg) { - struct aws_secure_tunnel_mock_test_fixture *test_fixture = arg; - return test_fixture->listener_destroyed; -} - -static void s_wait_on_listener_cleanup(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_is_listener_destroyed, test_fixture); - aws_mutex_unlock(&test_fixture->lock); -} - /***************************************************************************************************************** * WEBSOCKET MOCK FUNCTIONS *****************************************************************************************************************/ +/* Serializes message view and sends as Websocket */ +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) { + + struct aws_byte_buf data_buf; + struct aws_byte_cursor data_cur; + struct aws_byte_buf out_buf; + aws_iot_st_msg_serialize_from_view(&data_buf, test_fixture->allocator, message_view); + data_cur = aws_byte_cursor_from_buf(&data_buf); + aws_byte_buf_init(&out_buf, test_fixture->allocator, data_cur.len + PAYLOAD_BYTE_LENGTH_PREFIX); + aws_byte_buf_write_be16(&out_buf, (int16_t)data_buf.len); + aws_byte_buf_write_to_capacity(&out_buf, &data_cur); + data_cur = aws_byte_cursor_from_buf(&out_buf); + test_fixture->websocket_function_table->on_incoming_frame_payload_fn( + NULL, NULL, data_cur, test_fixture->secure_tunnel); + + aws_byte_buf_clean_up(&out_buf); + aws_byte_buf_clean_up(&data_buf); +} + int aws_websocket_client_connect_mock_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; @@ -386,16 +200,29 @@ int aws_websocket_client_connect_mock_fn(const struct aws_websocket_client_conne return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); } - test_fixture->server_function_table->on_connection_setup_fn = options->on_connection_setup; - test_fixture->server_function_table->on_connection_shutdown_fn = options->on_connection_shutdown; - test_fixture->server_function_table->on_incoming_frame_begin_fn = options->on_incoming_frame_begin; - test_fixture->server_function_table->on_incoming_frame_payload_fn = options->on_incoming_frame_payload; - test_fixture->server_function_table->on_incoming_frame_complete_fn = options->on_incoming_frame_complete; + test_fixture->websocket_function_table->on_connection_setup_fn = options->on_connection_setup; + test_fixture->websocket_function_table->on_connection_shutdown_fn = options->on_connection_shutdown; + test_fixture->websocket_function_table->on_incoming_frame_begin_fn = options->on_incoming_frame_begin; + test_fixture->websocket_function_table->on_incoming_frame_payload_fn = options->on_incoming_frame_payload; + test_fixture->websocket_function_table->on_incoming_frame_complete_fn = options->on_incoming_frame_complete; - // struct aws_websocket_on_connection_setup_data websocket_setup = {.error_code = AWS_ERROR_SUCCESS, - // .websocket = test_fixture}; + struct aws_websocket_on_connection_setup_data websocket_setup = {.error_code = AWS_ERROR_SUCCESS, + .websocket = test_fixture}; - // (test_fixture->server_function_table->on_connection_setup_fn)(&websocket_setup, secure_tunnel); + (test_fixture->websocket_function_table->on_connection_setup_fn)(&websocket_setup, secure_tunnel); + + 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_byte_cursor service_3 = aws_byte_cursor_from_string(s_service_id_3); + + struct aws_secure_tunnel_message_view service_ids_message = { + .type = AWS_SECURE_TUNNEL_MT_SERVICE_IDS, + .service_id = &service_1, + .service_id_2 = &service_2, + .service_id_3 = &service_3, + }; + + aws_secure_tunnel_send_mock_message(test_fixture, &service_ids_message); return AWS_OP_SUCCESS; } @@ -408,9 +235,16 @@ int aws_websocket_send_frame_mock_fn( return AWS_OP_SUCCESS; } -void aws_websocket_release_mock_fn(struct aws_websocket *websocket) {} +void aws_websocket_release_mock_fn(struct aws_websocket *websocket) { + struct aws_secure_tunnel_mock_test_fixture *test_fixture = websocket; +} + +void aws_websocket_close_mock_fn(struct aws_websocket *websocket, bool free_scarce_resources_immediately) { + + struct aws_secure_tunnel_mock_test_fixture *test_fixture = websocket; -void aws_websocket_close_mock_fn(struct aws_websocket *websocket, bool free_scarce_resources_immediately) {} + test_fixture->websocket_function_table->on_connection_shutdown_fn(websocket, 0, test_fixture->secure_tunnel); +} /***************************************************************************************************************** * TEST FIXTURE @@ -427,7 +261,7 @@ int aws_secure_tunnel_mock_test_fixture_init( aws_mutex_init(&test_fixture->lock); aws_condition_variable_init(&test_fixture->signal); - test_fixture->server_function_table = options->server_function_table; + test_fixture->websocket_function_table = options->websocket_function_table; test_fixture->mock_server_user_data = options->mock_server_user_data; struct aws_socket_options socket_options = { @@ -436,8 +270,6 @@ int aws_secure_tunnel_mock_test_fixture_init( }; test_fixture->socket_options = socket_options; - test_fixture->server_elg = aws_event_loop_group_new_default(allocator, 1, NULL); - test_fixture->server_bootstrap = aws_server_bootstrap_new(allocator, test_fixture->server_elg); test_fixture->secure_tunnel_elg = aws_event_loop_group_new_default(allocator, 4, NULL); struct aws_host_resolver_default_options resolver_options = { @@ -463,18 +295,6 @@ int aws_secure_tunnel_mock_test_fixture_init( LOCAL_SOCK_TEST_PATTERN, (long long unsigned)timestamp); - struct aws_server_socket_channel_bootstrap_options server_bootstrap_options = { - .bootstrap = test_fixture->server_bootstrap, - .host_name = test_fixture->endpoint.address, - .port = test_fixture->endpoint.port, - .socket_options = &test_fixture->socket_options, - .incoming_callback = s_on_incoming_channel_setup_fn, - .shutdown_callback = s_on_incoming_channel_shutdown_fn, - .destroy_callback = s_on_listener_destroy, - .user_data = test_fixture, - }; - test_fixture->listener = aws_server_bootstrap_new_socket_listener(&server_bootstrap_options); - options->secure_tunnel_options->endpoint_host = aws_byte_cursor_from_c_str(test_fixture->endpoint.address); options->secure_tunnel_options->bootstrap = test_fixture->secure_tunnel_bootstrap; options->secure_tunnel_options->socket_options = &test_fixture->socket_options; @@ -508,16 +328,10 @@ int aws_secure_tunnel_mock_test_fixture_init( } void aws_secure_tunnel_mock_test_fixture_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); - aws_server_bootstrap_destroy_socket_listener(test_fixture->server_bootstrap, test_fixture->listener); - - s_wait_on_listener_cleanup(test_fixture); - aws_server_bootstrap_release(test_fixture->server_bootstrap); - aws_event_loop_group_release(test_fixture->server_elg); aws_event_loop_group_release(test_fixture->secure_tunnel_elg); // aws_thread_join_all_managed(); @@ -531,7 +345,6 @@ void aws_secure_tunnel_mock_test_fixture_clean_up(struct aws_secure_tunnel_mock_ ********************************************************************************************************************/ static int s_secure_tunneling_serializer_data_message_test_fn(struct aws_allocator *allocator, void *ctx) { - // aws_iotdevice_library_init(allocator); aws_http_library_init(allocator); aws_iotdevice_library_init(allocator); @@ -540,7 +353,7 @@ static int s_secure_tunneling_serializer_data_message_test_fn(struct aws_allocat struct aws_secure_tunnel_mock_test_fixture_options test_fixture_options = { .secure_tunnel_options = &test_options.secure_tunnel_options, - .server_function_table = &test_options.server_function_table, + .websocket_function_table = &test_options.websocket_function_table, }; struct aws_secure_tunnel_mock_test_fixture test_context; @@ -551,8 +364,12 @@ static int s_secure_tunneling_serializer_data_message_test_fn(struct aws_allocat ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); s_wait_for_connected_successfully(&test_context); + ASSERT_SUCCESS(aws_secure_tunnel_stop(secure_tunnel)); + + aws_secure_tunnel_release(secure_tunnel); + aws_secure_tunnel_mock_test_fixture_clean_up(&test_context); - // aws_iotdevice_library_clean_up(); + aws_iotdevice_library_clean_up(); aws_http_library_clean_up(); aws_iotdevice_library_clean_up(); From c7b2adb20ca6935347a3061e927dc0eb3eaf6a64 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Tue, 21 Feb 2023 11:27:20 -0800 Subject: [PATCH 62/69] remove old tests --- tests/aws_iot_secure_tunneling_client_test.c | 199 ----- tests/secure_tunneling_tests.c | 721 ------------------- 2 files changed, 920 deletions(-) delete mode 100644 tests/aws_iot_secure_tunneling_client_test.c delete mode 100644 tests/secure_tunneling_tests.c diff --git a/tests/aws_iot_secure_tunneling_client_test.c b/tests/aws_iot_secure_tunneling_client_test.c deleted file mode 100644 index 797f7eed..00000000 --- a/tests/aws_iot_secure_tunneling_client_test.c +++ /dev/null @@ -1,199 +0,0 @@ -/** - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define UNUSED(x) (void)(x) - -static struct aws_mutex mutex = AWS_MUTEX_INIT; -static struct aws_condition_variable condition_variable = AWS_CONDITION_VARIABLE_INIT; - -static int on_send_data_complete_error_code = 0; - -static void s_on_send_data_complete(int error_code, void *user_data) { - UNUSED(user_data); - on_send_data_complete_error_code = error_code; -} - -// static void s_on_connection_complete(void *user_data) { -// UNUSED(user_data); -// aws_mutex_lock(&mutex); -// aws_condition_variable_notify_one(&condition_variable); -// aws_mutex_unlock(&mutex); -// } - -// static void s_on_connection_shutdown(void *user_data) { -// UNUSED(user_data); -// } - -// static void s_on_data_receive(const struct aws_byte_buf *data, void *user_data) { -// AWS_LOGF_INFO(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Client received data:"); - -// struct aws_allocator *allocator = (struct aws_allocator *)user_data; - -// struct aws_byte_cursor data_cursor = aws_byte_cursor_from_buf(data); -// struct aws_byte_buf data_to_print; -// aws_byte_buf_init(&data_to_print, allocator, data->len + 1); /* +1 for null terminator */ -// aws_byte_buf_append(&data_to_print, &data_cursor); -// aws_byte_buf_append_null_terminator(&data_to_print); -// AWS_LOGF_INFO(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "%s", (char *)data_to_print.buffer); - -// aws_byte_buf_clean_up(&data_to_print); -// } - -// static void s_on_stream_start(void *user_data) { -// UNUSED(user_data); -// AWS_LOGF_INFO(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Client received StreamStart."); -// } - -// static void s_on_stream_reset(void *user_data) { -// UNUSED(user_data); -// AWS_LOGF_INFO(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Client received StreamReset."); -// } - -static void s_on_session_reset(void *user_data) { - UNUSED(user_data); - AWS_LOGF_INFO(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Client received SessionReset."); -} - -enum aws_secure_tunneling_local_proxy_mode s_local_proxy_mode_from_c_str(const char *local_proxy_mode) { - if (strcmp(local_proxy_mode, "src") == 0) { - return AWS_SECURE_TUNNELING_SOURCE_MODE; - } - return AWS_SECURE_TUNNELING_DESTINATION_MODE; -} - -static void s_init_secure_tunneling_connection_config( - struct aws_allocator *allocator, - struct aws_client_bootstrap *bootstrap, - struct aws_socket_options *socket_options, - const char *access_token, - enum aws_secure_tunneling_local_proxy_mode local_proxy_mode, - const char *endpoint, - const char *root_ca, - struct aws_secure_tunnel_options *config) { - - AWS_ZERO_STRUCT(*config); - // config->allocator = allocator; - config->bootstrap = bootstrap; - config->socket_options = socket_options; - - config->access_token = aws_byte_cursor_from_c_str(access_token); - config->local_proxy_mode = local_proxy_mode; - config->endpoint_host = aws_byte_cursor_from_c_str(endpoint); - config->root_ca = root_ca; - - // config->on_connection_complete = s_on_connection_complete; - // config->on_connection_shutdown = s_on_connection_shutdown; - config->on_send_data_complete = s_on_send_data_complete; - // config->on_data_receive = s_on_data_receive; - // config->on_stream_start = s_on_stream_start; - // config->on_stream_reset = s_on_stream_reset; - config->on_session_reset = s_on_session_reset; - - config->user_data = allocator; -} - -int main(int argc, char **argv) { - if (argc < 5) { - printf( - "3 args required, only %d passed. Usage:\n" - "aws-c-iot-secure_tunneling-client [endpoint] [src|dest] [root_ca] [access_token]\n", - argc - 1); - return 1; - } - const char *endpoint = argv[1]; - enum aws_secure_tunneling_local_proxy_mode local_proxy_mode = s_local_proxy_mode_from_c_str(argv[2]); - const char *root_ca = argv[3]; - const char *access_token = argv[4]; - - struct aws_allocator *allocator = aws_mem_tracer_new(aws_default_allocator(), NULL, AWS_MEMTRACE_BYTES, 0); - - aws_iotdevice_library_init(allocator); - - struct aws_logger_standard_options logger_options = { - .level = AWS_LL_TRACE, - .file = stdout, - }; - struct aws_logger logger; - aws_logger_init_standard(&logger, allocator, &logger_options); - aws_logger_set(&logger); - - struct aws_event_loop_group *elg = aws_event_loop_group_new_default(allocator, 1, NULL); - struct aws_host_resolver_default_options host_resolver_default_options; - AWS_ZERO_STRUCT(host_resolver_default_options); - host_resolver_default_options.max_entries = 8; - host_resolver_default_options.el_group = elg; - host_resolver_default_options.shutdown_options = NULL; - host_resolver_default_options.system_clock_override_fn = NULL; - struct aws_host_resolver *resolver = aws_host_resolver_new_default(allocator, &host_resolver_default_options); - struct aws_client_bootstrap_options bootstrap_options = { - .event_loop_group = elg, - .host_resolver = resolver, - }; - struct aws_client_bootstrap *bootstrap = aws_client_bootstrap_new(allocator, &bootstrap_options); - - struct aws_socket_options socket_options; - AWS_ZERO_STRUCT(socket_options); - socket_options.connect_timeout_ms = 3000; - socket_options.type = AWS_SOCKET_STREAM; - socket_options.domain = AWS_SOCKET_IPV4; - - /* setup secure tunneling connection config */ - struct aws_secure_tunnel_options config; - s_init_secure_tunneling_connection_config( - allocator, bootstrap, &socket_options, access_token, local_proxy_mode, endpoint, root_ca, &config); - - /* Create a secure tunnel object and connect */ - struct aws_secure_tunnel *secure_tunnel = aws_secure_tunnel_new(allocator, &config); - aws_secure_tunnel_start(secure_tunnel); - - /* wait here until the connection is done */ - aws_mutex_lock(&mutex); - ASSERT_SUCCESS(aws_condition_variable_wait(&condition_variable, &mutex)); - aws_mutex_unlock(&mutex); - - if (local_proxy_mode == AWS_SECURE_TUNNELING_SOURCE_MODE) { - // AWS_RETURN_ERROR_IF2(aws_secure_tunnel_stream_start(secure_tunnel) == AWS_OP_SUCCESS, AWS_OP_ERR); - - int cLen = 500000; - char *payload = malloc(cLen + 1); - memset(payload, 'a', cLen); - payload[cLen] = 0; - // struct aws_byte_cursor cur = aws_byte_cursor_from_c_str(payload); - // AWS_RETURN_ERROR_IF2(aws_secure_tunnel_send_data(secure_tunnel, &cur) == AWS_OP_SUCCESS, AWS_OP_ERR); - - // AWS_RETURN_ERROR_IF2(aws_secure_tunnel_stream_reset(secure_tunnel) == AWS_OP_SUCCESS, AWS_OP_ERR); - ASSERT_SUCCESS(aws_condition_variable_wait(&condition_variable, &mutex)); - } else if (local_proxy_mode == AWS_SECURE_TUNNELING_DESTINATION_MODE) { - /* Wait a little for data to show up */ - aws_thread_current_sleep((uint64_t)60 * 60 * 1000000000); - } - aws_thread_current_sleep((uint64_t)60 * 60 * 1000000000); - - /* clean up */ - aws_secure_tunnel_stop(secure_tunnel); - aws_secure_tunnel_release(secure_tunnel); - - aws_client_bootstrap_release(bootstrap); - aws_host_resolver_release(resolver); - aws_event_loop_group_release(elg); - aws_logger_clean_up(&logger); - aws_iotdevice_library_clean_up(); - - ASSERT_UINT_EQUALS(0, aws_mem_tracer_count(allocator)); - allocator = aws_mem_tracer_destroy(allocator); - ASSERT_NOT_NULL(allocator); - - return AWS_OP_SUCCESS; -} diff --git a/tests/secure_tunneling_tests.c b/tests/secure_tunneling_tests.c deleted file mode 100644 index 41041bed..00000000 --- a/tests/secure_tunneling_tests.c +++ /dev/null @@ -1,721 +0,0 @@ -/** - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define UNUSED(x) (void)(x) - -#define INVALID_STREAM_ID 0 -#define STREAM_ID 10 -#define ACCESS_TOKEN "my_super_secret_access_token" -#define ENDPOINT "data.tunneling.iot.us-west-2.amazonaws.com" -#define PAYLOAD "secure tunneling data payload" - -/* - * The tests here call these functions directly. - */ - -struct secure_tunneling_test_context { - enum aws_secure_tunneling_local_proxy_mode local_proxy_mode; - uint16_t max_threads; - struct aws_event_loop_group *elg; - struct aws_host_resolver *resolver; - struct aws_client_bootstrap *bootstrap; - struct aws_secure_tunnel *secure_tunnel; -}; -static struct secure_tunneling_test_context s_test_context = {.max_threads = 1}; - -static bool s_on_stream_start_called = false; -// static void s_on_stream_start(void *user_data) { -// UNUSED(user_data); -// s_on_stream_start_called = true; -// } - -static bool s_on_data_receive_correct_payload = false; -// static void s_on_data_receive(const struct aws_byte_buf *data, void *user_data) { -// UNUSED(user_data); -// s_on_data_receive_correct_payload = aws_byte_buf_eq_c_str(data, PAYLOAD); -// } - -static bool s_on_stream_reset_called = false; -// static void s_on_stream_reset(void *user_data) { -// UNUSED(user_data); -// s_on_stream_reset_called = true; -// } - -static bool s_on_session_reset_called = false; -// static void s_on_session_reset(void *user_data) { -// UNUSED(user_data); -// s_on_session_reset_called = true; -// } - -static bool s_on_termination_complete_called = false; -// static void s_on_termination_complete(void *user_data) { -// UNUSED(user_data); -// s_on_termination_complete_called = true; -// } - -static void s_init_secure_tunneling_connection_config( - struct aws_allocator *allocator, - struct aws_client_bootstrap *bootstrap, - struct aws_socket_options *socket_options, - const char *access_token, - enum aws_secure_tunneling_local_proxy_mode local_proxy_mode, - const char *endpoint, - struct aws_secure_tunnel_options *options) { - - AWS_ZERO_STRUCT(*options); - // options->allocator = allocator; - options->bootstrap = bootstrap; - options->socket_options = socket_options; - - options->access_token = aws_byte_cursor_from_c_str(access_token); - options->local_proxy_mode = local_proxy_mode; - options->endpoint_host = aws_byte_cursor_from_c_str(endpoint); - - /* - options->on_stream_start = s_on_stream_start; - options->on_data_receive = s_on_data_receive; - options->on_stream_reset = s_on_stream_reset; - options->on_session_reset = s_on_session_reset; - options->on_termination_complete = s_on_termination_complete; - */ - /* TODO: Initialize the rest of the callbacks */ -} - -/* - * Mock aws websocket api used by the secure tunnel. - */ - -int s_mock_aws_websocket_client_connect(const struct aws_websocket_client_connection_options *options) { - UNUSED(options); - return AWS_OP_SUCCESS; -} - -static size_t s_mock_aws_websocket_send_frame_call_count = 0U; - -static size_t s_mock_aws_websocket_send_frame_payload_len = 0U; - -int s_mock_aws_websocket_send_frame( - struct aws_websocket *websocket, - const struct aws_websocket_send_frame_options *options) { - UNUSED(websocket); - ++s_mock_aws_websocket_send_frame_call_count; - - // struct data_tunnel_pair *pair = (struct data_tunnel_pair *)options->user_data; - // struct aws_byte_buf *buf = &pair->buf; - // struct aws_byte_cursor cursor = aws_byte_cursor_from_buf(buf); - - /* Deserialize the wire format to obtain original payload. */ - // struct aws_iot_st_msg message; - // int rc = aws_iot_st_msg_deserialize_from_cursor(&message, &cursor, s_test_context.secure_tunnel->allocator); - // ASSERT_INT_EQUALS(AWS_OP_SUCCESS, rc); - // s_mock_aws_websocket_send_frame_payload_len += message.payload.len; - // aws_byte_buf_clean_up(&message.payload); - - /* Deallocate memory for the buffer holding the wire protocol data and the tunnel context. */ - // aws_byte_buf_clean_up(buf); - // aws_mem_release(s_test_context.secure_tunnel->allocator, pair); - - return AWS_OP_SUCCESS; -} - -void s_mock_aws_websocket_close(struct aws_websocket *websocket, bool free_scarce_resources_immediately) { - UNUSED(websocket); - UNUSED(free_scarce_resources_immediately); -} - -void s_mock_aws_websocket_release(struct aws_websocket *websocket) { - UNUSED(websocket); - /* Release the handshake_request. In a non-mocked context this would occur after handshake completes. */ - aws_http_message_release(s_test_context.secure_tunnel->handshake_request); -} - -/* s_secure_tunnel_new_mock returns a secure_tunnel that mocks the aws websocket public api. */ -static struct aws_secure_tunnel *s_secure_tunnel_new_mock(const struct aws_secure_tunnel_options *options) { - // struct aws_secure_tunnel *secure_tunnel = aws_secure_tunnel_new(options); - // if (!secure_tunnel) { - // return secure_tunnel; - // } - // secure_tunnel->websocket_vtable.client_connect = s_mock_aws_websocket_client_connect; - // secure_tunnel->websocket_vtable.send_frame = s_mock_aws_websocket_send_frame; - // secure_tunnel->websocket_vtable.close = s_mock_aws_websocket_close; - // secure_tunnel->websocket_vtable.release = s_mock_aws_websocket_release; - - /* - * Initialize a dummy websocket when the tunnel is created. - * - * In the non-mock implementation this websocket would be initialized when - * an http upgrade request is received sometime after the tunnel is created. - * - * Since no http request is exercised by these tests we initialize a websocket - * as soon as the tunnel is created. Since the aws_websocket struct is opaque - * to this module, we use the placeholder value 1 to set the member non-null. - */ - // secure_tunnel->websocket = (void *)1; - - // return secure_tunnel; - return 0; -} - -static int before(struct aws_allocator *allocator, void *ctx) { - struct secure_tunneling_test_context *test_context = ctx; - - /* Initialize aws-c-iot library. */ - aws_iotdevice_library_init(allocator); - - /* Initialize event loop. */ - test_context->elg = aws_event_loop_group_new_default(allocator, test_context->max_threads, NULL); - - /* Initialize dns resolver. */ - struct aws_host_resolver_default_options host_resolver_default_options; - AWS_ZERO_STRUCT(host_resolver_default_options); - host_resolver_default_options.max_entries = 8; - host_resolver_default_options.el_group = test_context->elg; - host_resolver_default_options.shutdown_options = NULL; - host_resolver_default_options.system_clock_override_fn = NULL; - test_context->resolver = aws_host_resolver_new_default(allocator, &host_resolver_default_options); - - /* Initialize client_bootstrap with event loop and dns resolver. */ - struct aws_client_bootstrap_options bootstrap_options = { - .event_loop_group = test_context->elg, - .host_resolver = test_context->resolver, - }; - test_context->bootstrap = aws_client_bootstrap_new(allocator, &bootstrap_options); - - /* Initialize socket_options for secure tunnel. */ - struct aws_socket_options socket_options; - AWS_ZERO_STRUCT(socket_options); - - /* Initialize secure_tunnel_options with client_bootstrap. */ - struct aws_secure_tunnel_options options; - s_init_secure_tunneling_connection_config( - allocator, - test_context->bootstrap, - &socket_options, - ACCESS_TOKEN, - test_context->local_proxy_mode, - ENDPOINT, - &options); - - /* Initialize secure_tunnel. */ - test_context->secure_tunnel = s_secure_tunnel_new_mock(&options); - ASSERT_NOT_NULL(test_context->secure_tunnel); - - return AWS_OP_SUCCESS; -} - -static int after(struct aws_allocator *allocator, int setup_result, void *ctx) { - UNUSED(allocator); - UNUSED(setup_result); - - struct secure_tunneling_test_context *test_context = ctx; - - aws_host_resolver_release(test_context->resolver); - aws_event_loop_group_release(test_context->elg); - aws_client_bootstrap_release(test_context->bootstrap); - - aws_secure_tunnel_release(test_context->secure_tunnel); - - /* - * Under normal circumstances you would need to do a wait on a condition variable signal for the async - * destruction process to unwind. But these tests all use mocks so termination completion happens synchronously - */ - ASSERT_TRUE(s_on_termination_complete_called); - - aws_thread_join_all_managed(); - - aws_iotdevice_library_clean_up(); - - return AWS_OP_SUCCESS; -} - -// static void s_send_secure_tunneling_frame_to_websocket( -// // const struct aws_iot_st_msg *st_msg, -// struct aws_allocator *allocator, -// struct aws_secure_tunnel *secure_tunnel) { - -// struct aws_byte_buf serialized_st_msg; -// // aws_iot_st_msg_serialize_from_struct(&serialized_st_msg, allocator, *st_msg); - -// /* Prepend 2 bytes length */ -// struct aws_byte_buf websocket_frame; -// aws_byte_buf_init(&websocket_frame, allocator, serialized_st_msg.len + 2); -// aws_byte_buf_write_be16(&websocket_frame, (uint16_t)serialized_st_msg.len); -// struct aws_byte_cursor c = aws_byte_cursor_from_buf(&serialized_st_msg); -// aws_byte_buf_append(&websocket_frame, &c); -// c = aws_byte_cursor_from_buf(&websocket_frame); - -// // on_websocket_incoming_frame_payload(NULL, NULL, c, secure_tunnel); - -// aws_byte_buf_clean_up(&serialized_st_msg); -// aws_byte_buf_clean_up(&websocket_frame); -// } - -static int s_test_sent_data( - struct secure_tunneling_test_context *test_context, - const char *expected_payload, - const int32_t expected_stream_id, - const int prefix_bytes, - const enum aws_secure_tunnel_message_type type) { - /* - * The public api used to send data over a secure tunnel is aws_secure_tunnel_send_data. - * - * 1/ The public api accepts an aws_byte_cursor and logically splits this cursor into smaller - * nonoverlapping cursors aka frames using the private secure_tunneling_init_send_frame function. - * - * 2/ Each frame is written to the websocket connection using the public api aws_websocket_send_frame. - * The websocket api pushes the frame on a fifo queue of frames managed by the event loop. - * The event loop thread pops frames from the queue and writes the data to the websocket connection. - * - * The function implemented below differs from the public api in some important ways. - * - * 1/ This function frames a aws_byte_cursor by calling the private api secure_tunneling_init_send_frame. - * As a result, this function does not exercise the logic in the public api aws_secure_tunnel_send_data - * to split the input cursor into multiple frames. - * - * 2/ This function does not exercise any of the websocket api. Instead this function calls a private api - * secure_tunneling_send_data_call with the websocket set to NULL. Instead of queueing frames, this - * api writes frames to a second buffer in the websocket wire protocol format. The test compares the - * second buffer to what is expected from the wire format. In the public api, the functionality to - * write the frame in the websocket wire protocol format is invoked as a callback from the event loop. - * - * To summarize, the test below has value, but such value is limited by carefully avoiding the public - * api to send data over a secure tunnel. A separate group of tests are required to more directly - * exercise the public api. - * - */ - - // struct aws_iot_st_msg message; - // message.type = type; - // message.stream_id = expected_stream_id; - // message.ignorable = 0; - // message.payload = aws_byte_buf_from_c_str(expected_payload); - struct aws_byte_buf serialized_st_msg; - // aws_iot_st_msg_serialize_from_struct(&serialized_st_msg, test_context->secure_tunnel->options->allocator, - // message); - - // struct aws_byte_cursor cur = aws_byte_cursor_from_c_str(expected_payload); - struct aws_websocket_send_frame_options frame_options; - // Steve TODO fix secure tunnel tests - // ASSERT_INT_EQUALS( - // AWS_OP_SUCCESS, - // secure_tunneling_init_send_frame(&frame_options, test_context->secure_tunnel, &cur, message.type)); - - ASSERT_INT_EQUALS(serialized_st_msg.len + prefix_bytes, frame_options.payload_length); - - struct aws_byte_buf out_buf; - // ASSERT_INT_EQUALS( - // AWS_OP_SUCCESS, - // aws_byte_buf_init( - // &out_buf, test_context->secure_tunnel->options->allocator, (size_t)frame_options.payload_length)); - - // ASSERT_TRUE(secure_tunneling_send_data_call(NULL, &out_buf, frame_options.user_data)); - struct aws_byte_cursor out_buf_cur = aws_byte_cursor_from_buf(&out_buf); - - ASSERT_UINT_EQUALS(out_buf_cur.len - prefix_bytes, serialized_st_msg.len); - - uint16_t payload_prefixed_length; - aws_byte_cursor_read_be16(&out_buf_cur, &payload_prefixed_length); - ASSERT_UINT_EQUALS((uint16_t)serialized_st_msg.len, payload_prefixed_length); - ASSERT_BIN_ARRAYS_EQUALS(serialized_st_msg.buffer, serialized_st_msg.len, out_buf_cur.ptr, out_buf_cur.len); - - struct data_tunnel_pair *pair = frame_options.user_data; - aws_byte_buf_clean_up(&pair->buf); - // aws_mem_release(pair->secure_tunnel->options->allocator, (void *)pair); - aws_byte_buf_clean_up(&serialized_st_msg); - aws_byte_buf_clean_up(&out_buf); - - return AWS_OP_SUCCESS; -} - -static int s_byte_buf_init_rand(struct aws_byte_buf *buf, struct aws_allocator *allocator, size_t capacity) { - int rc = aws_byte_buf_init(buf, allocator, capacity); - if (rc != AWS_OP_SUCCESS) { - return rc; - } - return aws_device_random_buffer(buf); -} - -AWS_TEST_CASE_FIXTURE( - secure_tunneling_handle_stream_start_test, - before, - s_secure_tunneling_handle_stream_start_test, - after, - &s_test_context); -static int s_secure_tunneling_handle_stream_start_test(struct aws_allocator *allocator, void *ctx) { - /* - * When secure tunnel running in destination mode receives a StreamStart message, - * verify the stream start callback is invoked and that the stream ID is parsed from the message. - */ - - struct secure_tunneling_test_context *test_context = ctx; - // test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_DESTINATION_MODE; - - // struct aws_iot_st_msg st_msg; - // AWS_ZERO_STRUCT(st_msg); - // st_msg.type = AWS_SECURE_TUNNEL_MT_STREAM_START; - // st_msg.stream_id = STREAM_ID; - s_on_stream_start_called = false; - // s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); - - ASSERT_TRUE(s_on_stream_start_called); - // ASSERT_INT_EQUALS(STREAM_ID, test_context->secure_tunnel->config->stream_id); - ASSERT_UINT_EQUALS(0, test_context->secure_tunnel->received_data.len); - - return AWS_OP_SUCCESS; -} - -AWS_TEST_CASE_FIXTURE( - secure_tunneling_handle_data_receive_test, - before, - s_secure_tunneling_handle_data_receive_test, - after, - &s_test_context); -static int s_secure_tunneling_handle_data_receive_test(struct aws_allocator *allocator, void *ctx) { - /* - * When secure tunnel running in destination mode receives a Data message, - * verify the data callback is invoked with matching message payload. - */ - - struct secure_tunneling_test_context *test_context = ctx; - // test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_DESTINATION_MODE; - - /* Send StreamStart first */ - // struct aws_iot_st_msg st_msg; - // AWS_ZERO_STRUCT(st_msg); - // st_msg.type = AWS_SECURE_TUNNEL_MT_STREAM_START; - // st_msg.stream_id = STREAM_ID; - // s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); - - /* Send data */ - // AWS_ZERO_STRUCT(st_msg); - // st_msg.type = AWS_SECURE_TUNNEL_MT_DATA; - // st_msg.stream_id = STREAM_ID; - // st_msg.payload = aws_byte_buf_from_c_str(PAYLOAD); - s_on_data_receive_correct_payload = false; - // s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); - - ASSERT_TRUE(s_on_data_receive_correct_payload); - // ASSERT_INT_EQUALS(STREAM_ID, test_context->secure_tunnel->config->stream_id); - ASSERT_UINT_EQUALS(0, test_context->secure_tunnel->received_data.len); - - return AWS_OP_SUCCESS; -} - -AWS_TEST_CASE_FIXTURE( - secure_tunneling_handle_stream_reset_test, - before, - s_secure_tunneling_handle_stream_reset_test, - after, - &s_test_context); -static int s_secure_tunneling_handle_stream_reset_test(struct aws_allocator *allocator, void *ctx) { - /* - * When secure tunnel running in destination mode receives a StreamReset message, - * verify the stream reset callback is invoked and the stream ID is unset. - */ - - struct secure_tunneling_test_context *test_context = ctx; - // test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_DESTINATION_MODE; - - /* Send StreamStart first */ - // struct aws_iot_st_msg st_msg; - // AWS_ZERO_STRUCT(st_msg); - // st_msg.type = AWS_SECURE_TUNNEL_MT_STREAM_START; - // st_msg.stream_id = STREAM_ID; - // s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); - - // /* Send StreamReset */ - // AWS_ZERO_STRUCT(st_msg); - // st_msg.type = AWS_SECURE_TUNNEL_MT_STREAM_START; - // st_msg.stream_id = STREAM_ID; - // s_on_stream_reset_called = false; - // s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); - - ASSERT_TRUE(s_on_stream_reset_called); - // ASSERT_INT_EQUALS(INVALID_STREAM_ID, test_context->secure_tunnel->config->stream_id); - ASSERT_UINT_EQUALS(0, test_context->secure_tunnel->received_data.len); - - return AWS_OP_SUCCESS; -} - -AWS_TEST_CASE_FIXTURE( - secure_tunneling_handle_session_reset_test, - before, - s_secure_tunneling_handle_session_reset_test, - after, - &s_test_context); -static int s_secure_tunneling_handle_session_reset_test(struct aws_allocator *allocator, void *ctx) { - /* - * When secure tunnel running in destination mode receives a SessionReset message with a valid stream ID, - * verify the session reset callback is invoked and the stream ID is unset. - */ - - struct secure_tunneling_test_context *test_context = ctx; - // test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_DESTINATION_MODE; - - // /* Send StreamStart first */ - // struct aws_iot_st_msg st_msg; - // AWS_ZERO_STRUCT(st_msg); - // st_msg.type = AWS_SECURE_TUNNEL_MT_STREAM_START; - // st_msg.stream_id = STREAM_ID; - // s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); - - // /* Send StreamReset */ - // AWS_ZERO_STRUCT(st_msg); - // st_msg.type = AWS_SECURE_TUNNEL_MT_SESSION_RESET; - // st_msg.stream_id = STREAM_ID; - // s_on_session_reset_called = false; - // s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); - - ASSERT_TRUE(s_on_session_reset_called); - // ASSERT_INT_EQUALS(INVALID_STREAM_ID, test_context->secure_tunnel->config->stream_id); - ASSERT_UINT_EQUALS(0, test_context->secure_tunnel->received_data.len); - - return AWS_OP_SUCCESS; -} - -AWS_TEST_CASE_FIXTURE( - secure_tunneling_handle_session_reset_no_stream_test, - before, - s_secure_tunneling_handle_session_reset_no_stream_test, - after, - &s_test_context); -static int s_secure_tunneling_handle_session_reset_no_stream_test(struct aws_allocator *allocator, void *ctx) { - /* - * When secure tunnel running in destination mode receives a SessionReset message without valid stream ID, - * verify the session reset callback is not invoked. - */ - - struct secure_tunneling_test_context *test_context = ctx; - // test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_DESTINATION_MODE; - - // /* Send StreamReset without existing stream */ - // struct aws_iot_st_msg st_msg; - // AWS_ZERO_STRUCT(st_msg); - // st_msg.type = AWS_SECURE_TUNNEL_MT_SESSION_RESET; - // s_on_session_reset_called = false; - // s_send_secure_tunneling_frame_to_websocket(&st_msg, allocator, test_context->secure_tunnel); - - ASSERT_FALSE(s_on_session_reset_called); - // ASSERT_INT_EQUALS(INVALID_STREAM_ID, test_context->secure_tunnel->config->stream_id); - ASSERT_UINT_EQUALS(0, test_context->secure_tunnel->received_data.len); - - return AWS_OP_SUCCESS; -} - -AWS_TEST_CASE_FIXTURE( - secure_tunneling_init_websocket_options_test, - before, - s_secure_tunneling_init_websocket_options_test, - after, - &s_test_context); -static int s_secure_tunneling_init_websocket_options_test(struct aws_allocator *allocator, void *ctx) { - /* - * When a client connects to a websocket server, - * verify the client handshake includes the aws secure tunneling protocol string and access token - * provided by the secure tunneling service when the tunnel is provisioned. - */ - - UNUSED(allocator); - - // struct secure_tunneling_test_context *test_context = ctx; - - struct aws_websocket_client_connection_options websocket_options; - // init_websocket_client_connection_options(test_context->secure_tunnel, &websocket_options); - - ASSERT_TRUE(aws_byte_cursor_eq_c_str(&websocket_options.host, ENDPOINT)); - - /* - * Verify handshake request - */ - - ASSERT_TRUE(aws_http_message_is_request(websocket_options.handshake_request)); - - struct aws_byte_cursor method; - aws_http_message_get_request_method(websocket_options.handshake_request, &method); - ASSERT_TRUE(aws_byte_cursor_eq_c_str(&method, "GET")); - - /* Verify path */ - struct aws_byte_cursor path; - aws_http_message_get_request_path(websocket_options.handshake_request, &path); - ASSERT_TRUE(aws_byte_cursor_eq_c_str(&path, "/tunnel?local-proxy-mode=source")); - - /* Verify headers */ - const char *expected_headers[][2] = { - {"Sec-WebSocket-Protocol", "aws.iot.securetunneling-1.0"}, - {"access-token", ACCESS_TOKEN}, - }; - - const struct aws_http_headers *headers = aws_http_message_get_const_headers(websocket_options.handshake_request); - for (size_t i = 0; i < sizeof(expected_headers) / sizeof(expected_headers[0]); i++) { - struct aws_byte_cursor name = aws_byte_cursor_from_c_str(expected_headers[i][0]); - struct aws_byte_cursor value; - ASSERT_INT_EQUALS(AWS_OP_SUCCESS, aws_http_headers_get(headers, name, &value)); - ASSERT_TRUE(aws_byte_cursor_eq_c_str(&value, expected_headers[i][1])); - } - - aws_http_message_release(websocket_options.handshake_request); - - return AWS_OP_SUCCESS; -} - -AWS_TEST_CASE_FIXTURE( - secure_tunneling_handle_send_data, - before, - s_secure_tunneling_handle_send_data, - after, - &s_test_context); -static int s_secure_tunneling_handle_send_data(struct aws_allocator *allocator, void *ctx) { - /* - * When a secure tunnel running in source mode sends data to destination, - * verify the data are written to the tunnel in the expected websocket wire protocol format. - */ - - UNUSED(allocator); - const char *expected_payload = "Hi! I'm Paul / Some random payload\n"; - const int32_t expected_stream_id = 1; - const int prefix_bytes = 2; - const enum aws_secure_tunnel_message_type type = AWS_SECURE_TUNNEL_MT_DATA; - - struct secure_tunneling_test_context *test_context = ctx; - // test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_SOURCE_MODE; - // test_context->secure_tunnel->config->stream_id = expected_stream_id; - - s_test_sent_data(test_context, expected_payload, expected_stream_id, prefix_bytes, type); - - return AWS_OP_SUCCESS; -} - -AWS_TEST_CASE_FIXTURE( - secure_tunneling_handle_send_data_stream_start, - before, - s_secure_tunneling_handle_send_data_stream_start, - after, - &s_test_context); -static int s_secure_tunneling_handle_send_data_stream_start(struct aws_allocator *allocator, void *ctx) { - /* - * When a secure tunnel running in source mode sends StreamStart to destination, - * verify the data are written to the tunnel in the expected websocket wire protocol format. - */ - - UNUSED(allocator); - const char *expected_payload = ""; - const int32_t expected_stream_id = 1; - const int prefix_bytes = 2; - const enum aws_secure_tunnel_message_type type = AWS_SECURE_TUNNEL_MT_STREAM_START; - - struct secure_tunneling_test_context *test_context = ctx; - // test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_SOURCE_MODE; - // test_context->secure_tunnel->config->stream_id = expected_stream_id; - - s_test_sent_data(test_context, expected_payload, expected_stream_id, prefix_bytes, type); - - return AWS_OP_SUCCESS; -} - -AWS_TEST_CASE_FIXTURE( - secure_tunneling_handle_send_data_stream_reset, - before, - s_secure_tunneling_handle_send_data_stream_reset, - after, - &s_test_context); -static int s_secure_tunneling_handle_send_data_stream_reset(struct aws_allocator *allocator, void *ctx) { - /* - * When a secure tunnel running in source mode sends StreamReset to destination, - * verify the data are written to the tunnel in the expected websocket wire protocol format. - */ - - UNUSED(allocator); - const char *expected_payload = ""; - const int32_t expected_stream_id = 1; - const int prefix_bytes = 2; - const enum aws_secure_tunnel_message_type type = AWS_SECURE_TUNNEL_MT_STREAM_RESET; - - struct secure_tunneling_test_context *test_context = ctx; - // test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_SOURCE_MODE; - // test_context->secure_tunnel->config->stream_id = expected_stream_id; - - s_test_sent_data(test_context, expected_payload, expected_stream_id, prefix_bytes, type); - - return AWS_OP_SUCCESS; -} - -AWS_TEST_CASE_FIXTURE( - secure_tunneling_handle_send_data_public, - before, - s_secure_tunneling_handle_send_data_public, - after, - &s_test_context); -static int s_secure_tunneling_handle_send_data_public(struct aws_allocator *allocator, void *ctx) { - /* - * When a secure tunnel running in source mode sends data to destination using the public api, - * verify that the payload length matches what the client sends and the number of frames sent - * is equal to size of the payload divided by the maximum frame length. - */ - - struct secure_tunneling_test_context *test_context = ctx; - // test_context->secure_tunnel->options->local_proxy_mode = AWS_SECURE_TUNNELING_SOURCE_MODE; - - /* Open the tunnel. */ - int rc = aws_secure_tunnel_start(test_context->secure_tunnel); - ASSERT_INT_EQUALS(AWS_OP_SUCCESS, rc); - - size_t buf_sizes[] = {10, 100, 1000, AWS_IOT_ST_SPLIT_MESSAGE_SIZE + 1, 2 * AWS_IOT_ST_SPLIT_MESSAGE_SIZE + 1}; - size_t buf_sizes_len = sizeof(buf_sizes) / sizeof(buf_sizes[0]); - - for (size_t i = 0; i < buf_sizes_len; ++i) { - /* Start a stream. */ - s_mock_aws_websocket_send_frame_call_count = 0U; - s_mock_aws_websocket_send_frame_payload_len = 0U; - // rc = aws_secure_tunnel_stream_start(test_context->secure_tunnel); - ASSERT_INT_EQUALS(AWS_OP_SUCCESS, rc); - ASSERT_UINT_EQUALS(1U, s_mock_aws_websocket_send_frame_call_count); - ASSERT_UINT_EQUALS(0U, s_mock_aws_websocket_send_frame_payload_len); - - /* Initialize buffer of random values to send. */ - struct aws_byte_buf buf; - rc = s_byte_buf_init_rand(&buf, allocator, buf_sizes[i]); - ASSERT_INT_EQUALS(AWS_OP_SUCCESS, rc); - - // struct aws_byte_cursor cur = aws_byte_cursor_from_buf(&buf); - - /* Call public api to send data over secure tunnel. */ - s_mock_aws_websocket_send_frame_call_count = 0U; - s_mock_aws_websocket_send_frame_payload_len = 0U; - // rc = aws_secure_tunnel_send_data(test_context->secure_tunnel, &cur); - ASSERT_INT_EQUALS(AWS_OP_SUCCESS, rc); - int expected_call_count = (int)buf_sizes[i] / AWS_IOT_ST_SPLIT_MESSAGE_SIZE + 1; - ASSERT_UINT_EQUALS(expected_call_count, s_mock_aws_websocket_send_frame_call_count); - ASSERT_UINT_EQUALS(buf_sizes[i], s_mock_aws_websocket_send_frame_payload_len); - - /* Free buffer. */ - aws_byte_buf_clean_up(&buf); - } - - /* Close the tunnel. */ - aws_secure_tunnel_stop(test_context->secure_tunnel); - - return AWS_OP_SUCCESS; -} From ec73eee11ad1c707a185f6cae24de172d09977dd Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Tue, 21 Feb 2023 12:59:26 -0800 Subject: [PATCH 63/69] fix race condition --- source/secure_tunneling.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 8430e278..3d7a28fe 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -840,14 +840,13 @@ static void s_change_current_state_to_websocket_shutdown(struct aws_secure_tunne AWS_FATAL_ASSERT( current_state == AWS_STS_CONNECTING || current_state == AWS_STS_CONNECTED || current_state == AWS_STS_CLEAN_DISCONNECT); + secure_tunnel->current_state = AWS_STS_WEBSOCKET_SHUTDOWN; if (secure_tunnel->websocket) { secure_tunnel->vtable->aws_websocket_close_fn(secure_tunnel->websocket, false); } else { s_on_websocket_shutdown(secure_tunnel->websocket, AWS_ERROR_UNKNOWN, secure_tunnel); } - - secure_tunnel->current_state = AWS_STS_WEBSOCKET_SHUTDOWN; } static void s_update_reconnect_delay_for_pending_reconnect(struct aws_secure_tunnel *secure_tunnel) { From bde1c017098c977e53e47cfa64cf35638ce30dc9 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Tue, 21 Feb 2023 16:22:58 -0800 Subject: [PATCH 64/69] secure tunnel tests --- source/secure_tunneling.c | 2 +- tests/CMakeLists.txt | 30 +- tests/secure_tunnel_tests.c | 915 +++++++++++++++++++++++++++++++++--- 3 files changed, 852 insertions(+), 95 deletions(-) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 3d7a28fe..94af4e09 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -185,7 +185,7 @@ static int s_aws_secure_tunnel_set_stream_id( "id=%p: Secure tunnel request for unsupported service_id: " PRInSTR, (void *)secure_tunnel, AWS_BYTE_CURSOR_PRI(*service_id)); - return false; + return AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_BAD_SERVICE_ID; } struct aws_service_id_element *replacement_elem = diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e0649267..19bf90aa 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -19,18 +19,15 @@ if (UNIX AND NOT APPLE) add_test_case(devicedefender_publish_failure_callback_invoked) endif() -# add_test_case(secure_tunneling_serializer_data_message_test) - -# add_net_test_case(secure_tunneling_handle_stream_start_test) -# add_net_test_case(secure_tunneling_handle_data_receive_test) -# add_net_test_case(secure_tunneling_handle_stream_reset_test) -# add_net_test_case(secure_tunneling_handle_session_reset_test) -# add_net_test_case(secure_tunneling_handle_session_reset_no_stream_test) -# add_net_test_case(secure_tunneling_init_websocket_options_test) -# add_net_test_case(secure_tunneling_handle_send_data) -# add_net_test_case(secure_tunneling_handle_send_data_stream_start) -# add_net_test_case(secure_tunneling_handle_send_data_stream_reset) -# add_net_test_case(secure_tunneling_handle_send_data_public) +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) generate_test_driver(${PROJECT_NAME}-tests) @@ -43,15 +40,6 @@ aws_add_sanitizers(${TEST_DD_CLIENT_BINARY_NAME} ${${PROJECT_NAME}_SANITIZERS}) target_compile_definitions(${TEST_DD_CLIENT_BINARY_NAME} PRIVATE AWS_UNSTABLE_TESTING_API=1) target_include_directories(${TEST_DD_CLIENT_BINARY_NAME} PRIVATE ${CMAKE_CURRENT_LIST_DIR}) -# Secure Tunneling test client -# set(TEST_ST_CLIENT_BINARY_NAME ${PROJECT_NAME}-secure_tunneling-client) -# add_executable(${TEST_ST_CLIENT_BINARY_NAME} "aws_iot_secure_tunneling_client_test.c") -# target_link_libraries(${TEST_ST_CLIENT_BINARY_NAME} PRIVATE ${PROJECT_NAME}) -# aws_set_common_properties(${TEST_ST_CLIENT_BINARY_NAME} NO_WEXTRA NO_PEDANTIC) -# aws_add_sanitizers(${TEST_ST_CLIENT_BINARY_NAME} ${${PROJECT_NAME}_SANITIZERS}) -# target_compile_definitions(${TEST_ST_CLIENT_BINARY_NAME} PRIVATE AWS_UNSTABLE_TESTING_API=1) -# target_include_directories(${TEST_ST_CLIENT_BINARY_NAME} PRIVATE ${CMAKE_CURRENT_LIST_DIR}) - if ($ENV{PROTOBUF_TEST}) add_subdirectory(tests_protobuf) endif () diff --git a/tests/secure_tunnel_tests.c b/tests/secure_tunnel_tests.c index 2ec3afb6..3ee8d6c8 100644 --- a/tests/secure_tunnel_tests.c +++ b/tests/secure_tunnel_tests.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -22,10 +23,13 @@ #define PAYLOAD_BYTE_LENGTH_PREFIX 2 AWS_STATIC_STRING_FROM_LITERAL(s_access_token, "IAmAnAccessToken"); +AWS_STATIC_STRING_FROM_LITERAL(s_client_token, "IAmAClientToken"); AWS_STATIC_STRING_FROM_LITERAL(s_endpoint_host, "IAmAnEndpointHost"); AWS_STATIC_STRING_FROM_LITERAL(s_service_id_1, "ServiceId1"); AWS_STATIC_STRING_FROM_LITERAL(s_service_id_2, "ServiceId2"); AWS_STATIC_STRING_FROM_LITERAL(s_service_id_3, "ServiceId3"); +AWS_STATIC_STRING_FROM_LITERAL(s_service_id_wrong, "ServiceIdWrong"); +AWS_STATIC_STRING_FROM_LITERAL(s_payload_text, "IAmABunchOfPayloadText"); #ifdef _WIN32 # define LOCAL_SOCK_TEST_PATTERN "\\\\.\\pipe\\testsock%llu" @@ -33,27 +37,6 @@ AWS_STATIC_STRING_FROM_LITERAL(s_service_id_3, "ServiceId3"); # define LOCAL_SOCK_TEST_PATTERN "testsock%llu.sock" #endif -struct aws_secure_tunnel_server_mock_connection_context { - struct aws_allocator *allocator; - - struct aws_channel *channel; - struct aws_channel_handler handler; - struct aws_channel_slot *slot; - - struct aws_secure_tunnel_mock_test_fixture *test_fixture; - - struct aws_task service_task; -}; - -typedef int(aws_secure_tunnel_on_mock_server_message_received_fn)( - void *message_view, - struct aws_secure_tunnel_server_mock_connection_context *connection, - void *message_received_user_data); - -typedef void(aws_secure_tunnel_mock_server_service_fn)( - struct aws_secure_tunnel_server_mock_connection_context *mock_server, - void *user_data); - 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; @@ -69,6 +52,24 @@ struct aws_secure_tunnel_mock_test_fixture_options { void *mock_server_user_data; }; +struct secure_tunnel_test_options { + struct aws_secure_tunnel_options secure_tunnel_options; + struct aws_secure_tunnel_mock_websocket_vtable websocket_function_table; +}; + +static void s_secure_tunnel_test_init_default_options(struct secure_tunnel_test_options *test_options) { + struct aws_secure_tunnel_options local_secure_tunnel_options = { + .endpoint_host = aws_byte_cursor_from_string(s_endpoint_host), + .access_token = aws_byte_cursor_from_string(s_access_token), + .local_proxy_mode = AWS_SECURE_TUNNELING_DESTINATION_MODE, + }; + test_options->secure_tunnel_options = local_secure_tunnel_options; +} + +typedef int(aws_secure_tunnel_mock_test_fixture_header_check_fn)( + const struct aws_http_headers *request_headers, + void *user_data); + struct aws_secure_tunnel_mock_test_fixture { struct aws_allocator *allocator; @@ -84,22 +85,31 @@ struct aws_secure_tunnel_mock_test_fixture { struct aws_secure_tunnel *secure_tunnel; struct aws_secure_tunnel_vtable secure_tunnel_vtable; + aws_secure_tunnel_mock_test_fixture_header_check_fn *header_check; + struct aws_mutex lock; struct aws_condition_variable signal; bool listener_destroyed; bool secure_tunnel_terminated; bool secure_tunnel_connected_succesfully; + bool secure_tunnel_connection_shutdown; bool secure_tunnel_connection_failed; -}; + bool secure_tunnel_stream_started; + bool secure_tunnel_bad_stream_request; + bool secure_tunnel_stream_reset_received; + bool secure_tunnel_session_reset_received; -static void s_on_test_secure_tunnel_termination(void *user_data) { - struct aws_secure_tunnel_mock_test_fixture *test_fixture = user_data; + struct aws_byte_buf last_message_payload_buf; - 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); -} + int secure_tunnel_message_received_count; + int secure_tunnel_stream_started_count; + int secure_tunnel_stream_started_count_target; + int secure_tunnel_message_count_target; +}; + +/***************************************************************************************************************** + * SECURE TUNNEL CALLBACKS + *****************************************************************************************************************/ static void s_on_test_secure_tunnel_connection_complete( const struct aws_secure_tunnel_connection_view *connection_view, @@ -117,30 +127,107 @@ static void s_on_test_secure_tunnel_connection_complete( aws_condition_variable_notify_all(&test_fixture->signal); } -struct secure_tunnel_test_options { - struct aws_secure_tunnel_options secure_tunnel_options; - struct aws_secure_tunnel_mock_websocket_vtable websocket_function_table; -}; +static void s_on_test_secure_tunnel_connection_shutdown(int error_code, void *user_data) { + struct aws_secure_tunnel_mock_test_fixture *test_fixture = user_data; -static void s_secure_tunnel_test_init_default_options(struct secure_tunnel_test_options *test_options) { - struct aws_secure_tunnel_options local_secure_tunnel_options = { - .endpoint_host = aws_byte_cursor_from_string(s_endpoint_host), - .access_token = aws_byte_cursor_from_string(s_access_token), - .local_proxy_mode = AWS_SECURE_TUNNELING_DESTINATION_MODE, + aws_mutex_lock(&test_fixture->lock); + test_fixture->secure_tunnel_connection_shutdown = true; + aws_mutex_unlock(&test_fixture->lock); + aws_condition_variable_notify_all(&test_fixture->signal); +} + +static void s_on_test_secure_tunnel_message_received( + const struct aws_secure_tunnel_message_view *message, + void *user_data) { + struct aws_secure_tunnel_mock_test_fixture *test_fixture = user_data; + aws_mutex_lock(&test_fixture->lock); + test_fixture->secure_tunnel_message_received_count++; + aws_byte_buf_clean_up(&test_fixture->last_message_payload_buf); + aws_byte_buf_init(&test_fixture->last_message_payload_buf, test_fixture->allocator, message->payload->len); + struct aws_byte_cursor payload_cur = { + .ptr = message->payload->ptr, + .len = message->payload->len, }; - test_options->secure_tunnel_options = local_secure_tunnel_options; + 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); +} + +static void s_on_test_secure_tunnel_send_data_complete(int error_code, void *user_data) { + (void)error_code; + (void)user_data; } +static void s_on_test_secure_tunnel_on_session_reset(void *user_data) { + struct aws_secure_tunnel_mock_test_fixture *test_fixture = 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); +} + +static void s_on_test_secure_tunnel_on_stopped(void *user_data) { + (void)user_data; +} + +static void s_on_test_secure_tunnel_termination(void *user_data) { + struct aws_secure_tunnel_mock_test_fixture *test_fixture = 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); +} + +static void s_on_test_secure_tunnel_on_stream_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_stream_reset_received = true; + aws_mutex_unlock(&test_fixture->lock); + aws_condition_variable_notify_all(&test_fixture->signal); +} + +static void s_on_test_secure_tunnel_on_stream_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_stream_started = true; + test_fixture->secure_tunnel_stream_started_count++; + } else { + test_fixture->secure_tunnel_bad_stream_request = true; + } + aws_mutex_unlock(&test_fixture->lock); + aws_condition_variable_notify_all(&test_fixture->signal); +} + +/***************************************************************************************************************** + * SECURE TUNNEL STATUS CHECKS + *****************************************************************************************************************/ + static bool s_has_secure_tunnel_terminated(void *arg) { struct aws_secure_tunnel_mock_test_fixture *test_fixture = arg; return test_fixture->secure_tunnel_terminated; } -static void s_wait_for_secure_tunnel_terminated(struct aws_secure_tunnel_mock_test_fixture *test_context) { - aws_mutex_lock(&test_context->lock); +static void s_wait_for_secure_tunnel_terminated(struct aws_secure_tunnel_mock_test_fixture *test_fixture) { + aws_mutex_lock(&test_fixture->lock); aws_condition_variable_wait_pred( - &test_context->signal, &test_context->lock, s_has_secure_tunnel_terminated, test_context); - aws_mutex_unlock(&test_context->lock); + &test_fixture->signal, &test_fixture->lock, s_has_secure_tunnel_terminated, test_fixture); + aws_mutex_unlock(&test_fixture->lock); } static bool s_has_secure_tunnel_connected_succesfully(void *arg) { @@ -148,11 +235,95 @@ static bool s_has_secure_tunnel_connected_succesfully(void *arg) { return test_fixture->secure_tunnel_connected_succesfully; } -static void s_wait_for_connected_successfully(struct aws_secure_tunnel_mock_test_fixture *test_context) { - aws_mutex_lock(&test_context->lock); +static void s_wait_for_connected_successfully(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, 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; +} + +static void s_wait_for_connection_shutdown(struct aws_secure_tunnel_mock_test_fixture *test_fixture) { + aws_mutex_lock(&test_fixture->lock); aws_condition_variable_wait_pred( - &test_context->signal, &test_context->lock, s_has_secure_tunnel_connected_succesfully, test_context); - aws_mutex_unlock(&test_context->lock); + &test_fixture->signal, &test_fixture->lock, s_has_secure_tunnel_connection_shutdown, test_fixture); + aws_mutex_unlock(&test_fixture->lock); +} + +static bool s_has_secure_tunnel_stream_started(void *arg) { + struct aws_secure_tunnel_mock_test_fixture *test_fixture = arg; + return test_fixture->secure_tunnel_stream_started; +} + +static void s_wait_for_stream_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_stream_started, 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; +} + +static void s_wait_for_bad_stream_request(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_stream_request, test_fixture); + aws_mutex_unlock(&test_fixture->lock); +} + +static bool s_has_secure_tunnel_stream_reset_received(void *arg) { + struct aws_secure_tunnel_mock_test_fixture *test_fixture = arg; + return test_fixture->secure_tunnel_stream_reset_received; +} + +static void s_wait_for_stream_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_stream_reset_received, test_fixture); + aws_mutex_unlock(&test_fixture->lock); +} + +static bool s_has_secure_tunnel_n_stream_started(void *arg) { + struct aws_secure_tunnel_mock_test_fixture *test_fixture = arg; + return test_fixture->secure_tunnel_stream_started_count == test_fixture->secure_tunnel_stream_started_count_target; +} + +static void s_wait_for_n_stream_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_n_stream_started, test_fixture); + aws_mutex_unlock(&test_fixture->lock); +} + +static bool s_has_secure_tunnel_session_reset_received(void *arg) { + struct aws_secure_tunnel_mock_test_fixture *test_fixture = arg; + return test_fixture->secure_tunnel_session_reset_received; +} + +static void s_wait_for_session_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_session_reset_received, test_fixture); + aws_mutex_unlock(&test_fixture->lock); +} + +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; +} + +static void s_wait_for_n_messages_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_n_messages_received, test_fixture); + aws_mutex_unlock(&test_fixture->lock); } /***************************************************************************************************************** @@ -192,12 +363,8 @@ int aws_websocket_client_connect_mock_fn(const struct aws_websocket_client_conne } const struct aws_http_headers *request_headers = aws_http_message_get_headers(options->handshake_request); - struct aws_byte_cursor access_token_cur; - if (aws_http_headers_get(request_headers, aws_byte_cursor_from_c_str("access-token"), &access_token_cur)) { - AWS_LOGF_ERROR( - AWS_LS_HTTP_WEBSOCKET_SETUP, - "id=static: Websocket handshake request is missing required 'access-token' header"); - return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + if (test_fixture->header_check) { + ASSERT_SUCCESS(test_fixture->header_check(request_headers, test_fixture)); } test_fixture->websocket_function_table->on_connection_setup_fn = options->on_connection_setup; @@ -206,8 +373,9 @@ int aws_websocket_client_connect_mock_fn(const struct aws_websocket_client_conne test_fixture->websocket_function_table->on_incoming_frame_payload_fn = options->on_incoming_frame_payload; test_fixture->websocket_function_table->on_incoming_frame_complete_fn = options->on_incoming_frame_complete; + void *pointer = test_fixture; struct aws_websocket_on_connection_setup_data websocket_setup = {.error_code = AWS_ERROR_SUCCESS, - .websocket = test_fixture}; + .websocket = pointer}; (test_fixture->websocket_function_table->on_connection_setup_fn)(&websocket_setup, secure_tunnel); @@ -236,13 +404,12 @@ int aws_websocket_send_frame_mock_fn( } void aws_websocket_release_mock_fn(struct aws_websocket *websocket) { - struct aws_secure_tunnel_mock_test_fixture *test_fixture = websocket; + (void)websocket; } void aws_websocket_close_mock_fn(struct aws_websocket *websocket, bool free_scarce_resources_immediately) { - - struct aws_secure_tunnel_mock_test_fixture *test_fixture = websocket; - + void *pointer = websocket; + struct aws_secure_tunnel_mock_test_fixture *test_fixture = pointer; test_fixture->websocket_function_table->on_connection_shutdown_fn(websocket, 0, test_fixture->secure_tunnel); } @@ -301,20 +468,21 @@ int aws_secure_tunnel_mock_test_fixture_init( options->secure_tunnel_options->access_token = aws_byte_cursor_from_string(s_access_token); options->secure_tunnel_options->user_data = test_fixture; - // Steve TODO set secure tunnel callbacks + /* Secure Tunnel Callbacks */ options->secure_tunnel_options->on_connection_complete = s_on_test_secure_tunnel_connection_complete; - // options->secure_tunnel_options->on_connection_shutdown - // options->secure_tunnel_options->on_message_received - // options->secure_tunnel_options->on_send_data_complete - // options->secure_tunnel_options->on_session_reset - // options->secure_tunnel_options->on_stopped - // options->secure_tunnel_options->on_stream_reset - // options->secure_tunnel_options->on_stream_start + 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_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_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); + /* Replace Secure Tunnel's vtable functions */ 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_mock_fn; test_fixture->secure_tunnel_vtable.aws_websocket_send_frame_fn = aws_websocket_send_frame_mock_fn; @@ -334,8 +502,7 @@ void aws_secure_tunnel_mock_test_fixture_clean_up(struct aws_secure_tunnel_mock_ aws_event_loop_group_release(test_fixture->secure_tunnel_elg); - // aws_thread_join_all_managed(); - + 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); } @@ -344,6 +511,575 @@ void aws_secure_tunnel_mock_test_fixture_clean_up(struct aws_secure_tunnel_mock_ * TESTS ********************************************************************************************************************/ +/* [Func-UC1] */ +int secure_tunneling_access_token_check(const struct aws_http_headers *request_headers, void *user_data) { + struct aws_byte_cursor access_token_cur; + if (aws_http_headers_get(request_headers, aws_byte_cursor_from_c_str("access-token"), &access_token_cur)) { + AWS_LOGF_ERROR( + AWS_LS_HTTP_WEBSOCKET_SETUP, + "id=static: Websocket handshake request is missing required 'access-token' header"); + return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + } + ASSERT_CURSOR_VALUE_STRING_EQUALS(access_token_cur, s_access_token); + return AWS_ERROR_SUCCESS; +} + +static int s_secure_tunneling_functionality_connect_test_fn(struct aws_allocator *allocator, 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)); + + 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(); + + 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) { + struct aws_byte_cursor client_token_cur; + if (aws_http_headers_get(request_headers, aws_byte_cursor_from_c_str("client-token"), &client_token_cur)) { + AWS_LOGF_ERROR( + AWS_LS_HTTP_WEBSOCKET_SETUP, + "id=static: Websocket handshake request is missing required 'client-token' header"); + return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + } + ASSERT_CURSOR_VALUE_STRING_EQUALS(client_token_cur, s_client_token); + return AWS_ERROR_SUCCESS; +} + +static int s_secure_tunneling_functionality_client_token_test_fn(struct aws_allocator *allocator, 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)); + + 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(); + + 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; + bool is_connection_failed_once = false; + + aws_mutex_lock(&test_fixture->lock); + is_connection_failed_once = test_fixture->secure_tunnel_connection_failed; + aws_mutex_unlock(&test_fixture->lock); + + if (is_connection_failed_once) { + if (!options->handshake_request) { + AWS_LOGF_ERROR( + AWS_LS_HTTP_WEBSOCKET_SETUP, + "id=static: Invalid connection options, missing required request for websocket client handshake."); + return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + } + + const struct aws_http_headers *request_headers = aws_http_message_get_headers(options->handshake_request); + if (test_fixture->header_check) { + ASSERT_SUCCESS(test_fixture->header_check(request_headers, test_fixture)); + } + + test_fixture->websocket_function_table->on_connection_setup_fn = options->on_connection_setup; + test_fixture->websocket_function_table->on_connection_shutdown_fn = options->on_connection_shutdown; + test_fixture->websocket_function_table->on_incoming_frame_begin_fn = options->on_incoming_frame_begin; + test_fixture->websocket_function_table->on_incoming_frame_payload_fn = options->on_incoming_frame_payload; + test_fixture->websocket_function_table->on_incoming_frame_complete_fn = options->on_incoming_frame_complete; + + void *pointer = test_fixture; + struct aws_websocket_on_connection_setup_data websocket_setup = {.error_code = AWS_ERROR_SUCCESS, + .websocket = pointer}; + + (test_fixture->websocket_function_table->on_connection_setup_fn)(&websocket_setup, secure_tunnel); + + 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_byte_cursor service_3 = aws_byte_cursor_from_string(s_service_id_3); + + struct aws_secure_tunnel_message_view service_ids_message = { + .type = AWS_SECURE_TUNNEL_MT_SERVICE_IDS, + .service_id = &service_1, + .service_id_2 = &service_2, + .service_id_3 = &service_3, + }; + + aws_secure_tunnel_send_mock_message(test_fixture, &service_ids_message); + + return AWS_OP_SUCCESS; + } else { + return AWS_OP_ERR; + } +} + +static int s_secure_tunneling_fail_and_retry_connection_test_fn(struct aws_allocator *allocator, 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)); + + 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; + test_fixture.secure_tunnel_vtable.aws_websocket_send_frame_fn = aws_websocket_send_frame_mock_fn; + test_fixture.secure_tunnel_vtable.aws_websocket_release_fn = aws_websocket_release_mock_fn; + 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(); + + 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) { + 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)); + + 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); + + /* 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); + 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); + 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); + ASSERT_NOT_NULL(elem); + + 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(); + + 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) { + 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)); + + 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); + + 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(); + + 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) { + 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)); + + 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 bad stream start message from the server to the destination client */ + struct aws_byte_cursor service_id = aws_byte_cursor_from_string(s_service_id_wrong); + struct aws_secure_tunnel_message_view stream_start_message_view = { + .type = AWS_SECURE_TUNNEL_MT_STREAM_START, + .service_id = &service_id, + .stream_id = 1, + }; + aws_secure_tunnel_send_mock_message(&test_fixture, &stream_start_message_view); + + /* Wait and confirm that a bad stream request was received */ + s_wait_for_bad_stream_request(&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(); + + return AWS_OP_SUCCESS; +} + +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) { + 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)); + + 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); + + /* Send a stream reset message from the server to the destination client */ + stream_start_message_view.type = AWS_SECURE_TUNNEL_MT_STREAM_RESET; + + aws_secure_tunnel_send_mock_message(&test_fixture, &stream_start_message_view); + + /* 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); + + 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(); + + return AWS_OP_SUCCESS; +} + +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) { + 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)); + + 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_byte_cursor service_2 = aws_byte_cursor_from_string(s_service_id_2); + struct aws_byte_cursor service_3 = aws_byte_cursor_from_string(s_service_id_3); + 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); + stream_start_message_view.service_id = &service_2; + aws_secure_tunnel_send_mock_message(&test_fixture, &stream_start_message_view); + stream_start_message_view.service_id = &service_3; + aws_secure_tunnel_send_mock_message(&test_fixture, &stream_start_message_view); + + test_fixture.secure_tunnel_stream_started_count_target = 3; + 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); + + /* Create and send a session reset message from the server to the destination client */ + struct aws_secure_tunnel_message_view reset_message_view = { + .type = AWS_SECURE_TUNNEL_MT_SESSION_RESET, + }; + aws_secure_tunnel_send_mock_message(&test_fixture, &reset_message_view); + + 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_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(); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(secure_tunneling_session_reset_test, s_secure_tunneling_session_reset_test_fn) + +/* +static int s_secure_tunneling_template_test_fn(struct aws_allocator *allocator, 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)); + + 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(); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(secure_tunneling_template_test, s_secure_tunneling_template_test_fn) +*/ + +/* [Func-UC9] */ + static int s_secure_tunneling_serializer_data_message_test_fn(struct aws_allocator *allocator, void *ctx) { aws_http_library_init(allocator); aws_iotdevice_library_init(allocator); @@ -356,19 +1092,52 @@ static int s_secure_tunneling_serializer_data_message_test_fn(struct aws_allocat .websocket_function_table = &test_options.websocket_function_table, }; - struct aws_secure_tunnel_mock_test_fixture test_context; - ASSERT_SUCCESS(aws_secure_tunnel_mock_test_fixture_init(&test_context, allocator, &test_fixture_options)); + struct aws_secure_tunnel_mock_test_fixture test_fixture; + ASSERT_SUCCESS(aws_secure_tunnel_mock_test_fixture_init(&test_fixture, allocator, &test_fixture_options)); - struct aws_secure_tunnel *secure_tunnel = test_context.secure_tunnel; + struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); - s_wait_for_connected_successfully(&test_context); + 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); + + /* 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_count_target = 1; + s_wait_for_n_messages_received(&test_fixture); + + struct aws_byte_cursor payload_comp_cur = { + .ptr = test_fixture.last_message_payload_buf.buffer, + .len = test_fixture.last_message_payload_buf.len, + }; + 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_fixture_clean_up(&test_context); + 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(); From 3736917bafcfffdbddff3dc8286d362323bfe388 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Tue, 21 Feb 2023 16:28:00 -0800 Subject: [PATCH 65/69] Clean Up --- CMakeLists.txt | 1 - bin/securetunnel/CMakeLists.txt | 29 -- bin/securetunnel/main.c | 513 -------------------------------- 3 files changed, 543 deletions(-) delete mode 100644 bin/securetunnel/CMakeLists.txt delete mode 100644 bin/securetunnel/main.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 0cd6bdaf..811d3d2c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -123,5 +123,4 @@ install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake" include(CTest) if (BUILD_TESTING) add_subdirectory(tests) - add_subdirectory(bin/securetunnel) endif () diff --git a/bin/securetunnel/CMakeLists.txt b/bin/securetunnel/CMakeLists.txt deleted file mode 100644 index 63b9845b..00000000 --- a/bin/securetunnel/CMakeLists.txt +++ /dev/null @@ -1,29 +0,0 @@ -project(securetunnel C) - -list(APPEND CMAKE_MODULE_PATH "${CMAKE_INSTALL_PREFIX}/lib/cmake") - -file(GLOB SECURETUNNEL_SRC - "*.c" - ) - -set(SECURETUNNEL_PROJECT_NAME securetunnel) -add_executable(${SECURETUNNEL_PROJECT_NAME} ${SECURETUNNEL_SRC}) -aws_set_common_properties(${SECURETUNNEL_PROJECT_NAME}) - - -target_include_directories(${SECURETUNNEL_PROJECT_NAME} PUBLIC - $ - $) - -target_link_libraries(${SECURETUNNEL_PROJECT_NAME} aws-c-iot) - -if (BUILD_SHARED_LIBS AND NOT WIN32) - message(INFO " securetunnel will be built with shared libs, but you may need to set LD_LIBRARY_PATH=${CMAKE_INSTALL_PREFIX}/lib to run the application") -endif() - -install(TARGETS ${SECURETUNNEL_PROJECT_NAME} - EXPORT ${SECURETUNNEL_PROJECT_NAME}-targets - COMPONENT Runtime - RUNTIME - DESTINATION bin - COMPONENT Runtime) diff --git a/bin/securetunnel/main.c b/bin/securetunnel/main.c deleted file mode 100644 index 41ee88f3..00000000 --- a/bin/securetunnel/main.c +++ /dev/null @@ -1,513 +0,0 @@ -/** - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -#define SLEEP_TIME_NS 1000000000 -#define MAX_WEBSOCKET_PAYLOAD 131076 - -struct app_ctx { - struct aws_allocator *allocator; - struct aws_secure_tunnel *secure_tunnel; - struct aws_mutex lock; - struct aws_condition_variable signal; - struct aws_uri uri; - uint16_t port; - const char *cacert; - const char *access_token; - const char *access_token_file; - const char *client_token; - const char *client_token_file; - int connect_timeout; - enum aws_secure_tunneling_local_proxy_mode local_proxy_mode; - - struct aws_tls_connection_options tls_connection_options; - - const char *log_filename; - enum aws_log_level log_level; -}; - -static void s_usage(int exit_code) { - - fprintf(stderr, "usage: securetunnel [options] endpoint\n"); - fprintf(stderr, " endpoint: url to connect to\n"); - fprintf(stderr, " access-token: token for secure tunnel\n"); - fprintf(stderr, " access-token-file: File containing token for secure tunnel\n"); - fprintf(stderr, "\n Options:\n\n"); - fprintf(stderr, " client-token: token for secure tunnel\n"); - fprintf(stderr, " client-token-file: File containing token for secure tunnel\n"); - fprintf(stderr, " --cacert FILE: path to a CA certficate file.\n"); - fprintf(stderr, " --connect-timeout INT: time in milliseconds to wait for a connection.\n"); - fprintf(stderr, " -s, --source: use secure tunnel client in source mode.\n"); - fprintf(stderr, " -d, --destination: use secure tunnel client in destination mode.\n"); - fprintf(stderr, " -l, --log FILE: dumps logs to FILE instead of stderr.\n"); - fprintf(stderr, " -v, --verbose: ERROR|INFO|DEBUG|TRACE: log level to configure. Default is none.\n"); - fprintf(stderr, " -w, --websockets: use mqtt-over-websockets rather than direct mqtt\n"); - fprintf(stderr, " -h, --help\n"); - fprintf(stderr, " Display this message and quit.\n"); - exit(exit_code); -} - -static struct aws_cli_option s_long_options[] = { - {"cacert", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 'a'}, - {"access-token", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 't'}, - {"access-token-file", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 'T'}, - {"client-token", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 'c'}, - {"client-token-file", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 'C'}, - {"source", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 's'}, - {"destination", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 'd'}, - {"connect-timeout", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 'f'}, - {"log", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 'l'}, - {"verbose", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 'v'}, - {"help", AWS_CLI_OPTIONS_NO_ARGUMENT, NULL, 'h'}, - {"endpoint", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 'E'}, - /* Per getopt(3) the last element of the array has to be filled with all zeros */ - {NULL, AWS_CLI_OPTIONS_NO_ARGUMENT, NULL, 0}, -}; - -static void s_parse_options(int argc, char **argv, struct app_ctx *ctx) { - bool uri_found = false; - - while (true) { - int option_index = 0; - int c = aws_cli_getopt_long(argc, argv, "a:t:T:c:C:s:d:f:l:v:h:", s_long_options, &option_index); - if (c == -1) { - break; - } - - switch (c) { - case 0: - /* getopt_long() returns 0 if an option.flag is non-null */ - break; - case 'a': - ctx->cacert = aws_cli_optarg; - break; - case 't': - ctx->access_token = aws_cli_optarg; - break; - case 'T': - ctx->access_token_file = aws_cli_optarg; - break; - case 'c': - ctx->client_token = aws_cli_optarg; - break; - case 'C': - ctx->client_token_file = aws_cli_optarg; - break; - case 's': - ctx->local_proxy_mode = AWS_SECURE_TUNNELING_SOURCE_MODE; - break; - case 'd': - ctx->local_proxy_mode = AWS_SECURE_TUNNELING_DESTINATION_MODE; - break; - case 'f': - ctx->connect_timeout = atoi(aws_cli_optarg); - break; - case 'l': - ctx->log_filename = aws_cli_optarg; - break; - case 'v': - if (!strcmp(aws_cli_optarg, "TRACE")) { - ctx->log_level = AWS_LL_TRACE; - } else if (!strcmp(aws_cli_optarg, "INFO")) { - ctx->log_level = AWS_LL_INFO; - } else if (!strcmp(aws_cli_optarg, "DEBUG")) { - ctx->log_level = AWS_LL_DEBUG; - } else if (!strcmp(aws_cli_optarg, "ERROR")) { - ctx->log_level = AWS_LL_ERROR; - } else if (!strcmp(aws_cli_optarg, "WARN")) { - ctx->log_level = AWS_LL_WARN; - } else { - fprintf(stderr, "unsupported log level %s.\n", aws_cli_optarg); - s_usage(1); - } - break; - case 'h': - s_usage(0); - break; - case 0x02: { - struct aws_byte_cursor uri_cursor = aws_byte_cursor_from_c_str(aws_cli_positional_arg); - if (aws_uri_init_parse(&ctx->uri, ctx->allocator, &uri_cursor)) { - fprintf( - stderr, - "Failed to parse uri %s with error %s\n", - (char *)uri_cursor.ptr, - aws_error_debug_str(aws_last_error())); - s_usage(1); - } - uri_found = true; - break; - } - - default: - fprintf(stderr, "Unknown option\n"); - s_usage(1); - } - } - - if (!uri_found) { - fprintf(stderr, "A URI for the request must be supplied.\n"); - s_usage(1); - } -} - -static void s_on_message_received(const struct aws_secure_tunnel_message_view *message, void *user_data) { - (void)user_data; - if (message->service_id != NULL) { - if (message->payload != NULL) { - printf( - "\nMessage received on service id: '" PRInSTR "' with payload: '" PRInSTR "'\n", - AWS_BYTE_CURSOR_PRI(*message->service_id), - AWS_BYTE_CURSOR_PRI(*message->payload)); - } else { - printf( - "\nMessage received on service id: '" PRInSTR "' with no payload\n", - AWS_BYTE_CURSOR_PRI(*message->service_id)); - } - } else if (message->payload != NULL) { - printf("\nMessage received with payload: '" PRInSTR "'\n", AWS_BYTE_CURSOR_PRI(*message->payload)); - } -} - -static void s_on_connection_complete( - const struct aws_secure_tunnel_connection_view *connection_view, - int error_code, - void *user_data) { - (void)connection_view; - printf( - "\nSecure Tunnel Client received s_on_connection_complete callback with error_code:%d (%s)\n", - error_code, - aws_error_name(error_code)); - struct app_ctx *ctx = user_data; - struct aws_secure_tunnel *secure_tunnel = ctx->secure_tunnel; - - if (secure_tunnel->config->local_proxy_mode == AWS_SECURE_TUNNELING_DESTINATION_MODE) { - printf("\nConnected in Destination Mode\n"); - } else { - printf("\nConnected in Source Mode\nSending Stream Start\n"); - struct aws_byte_cursor service_id_cur = aws_byte_cursor_from_c_str("ssh"); - struct aws_secure_tunnel_message_view message_data = {.service_id = &service_id_cur}; - // struct aws_secure_tunnel_message_view message_data = {}; - - printf("\nSending Stream Start Message\n"); - aws_secure_tunnel_stream_start(secure_tunnel, &message_data); - } -} - -static void s_on_connection_shutdown(int error_code, void *user_data) { - (void)user_data; - printf( - "\nSecure Tunnel Client received s_on_connection_shutdown callback with error_code:%d (%s)\n", - error_code, - aws_error_name(error_code)); -} - -static void s_on_stream_start( - const struct aws_secure_tunnel_message_view *message_view, - int error_code, - void *user_data) { - (void)user_data; - (void)error_code; - if (message_view->service_id != NULL) { - printf( - "\nSecure Tunnel Client received s_on_stream_start callback with service id:" PRInSTR " stream id:%d", - AWS_BYTE_CURSOR_PRI(*message_view->service_id), - message_view->stream_id); - } - struct app_ctx *app_ctx_user = user_data; - struct aws_secure_tunnel *secure_tunnel = app_ctx_user->secure_tunnel; - if (secure_tunnel->config->local_proxy_mode == AWS_SECURE_TUNNELING_DESTINATION_MODE) { - printf("\nStream Start recieved in Destination Mode\n"); - - struct aws_byte_cursor payload_cur = aws_byte_cursor_from_c_str("TEST PAYLOAD"); - struct aws_secure_tunnel_message_view message_data = { - .payload = &payload_cur, - .service_id = message_view->service_id, - }; - - printf("\nSending Data Message\n"); - aws_secure_tunnel_send_message(secure_tunnel, &message_data); - } -} - -static void s_on_stream_reset( - const struct aws_secure_tunnel_message_view *message_view, - int error_code, - void *user_data) { - (void)user_data; - (void)error_code; - if (message_view->service_id != NULL) { - printf( - "\nSecure Tunnel Client received s_on_stream_reset callback with service id:" PRInSTR " stream id:%d", - AWS_BYTE_CURSOR_PRI(*message_view->service_id), - message_view->stream_id); - } -} - -static void s_on_send_data_complete(int error_code, void *user_data) { - (void)user_data; - printf( - "\nSecure Tunnel Client received s_on_send_data_complete callback with error_code:%d (%s)\n", - error_code, - aws_error_name(error_code)); -} - -int main(int argc, char **argv) { - - /***************************************************************************************************************** - * Initialize - *****************************************************************************************************************/ - struct aws_allocator *allocator = aws_mem_tracer_new(aws_default_allocator(), NULL, AWS_MEMTRACE_STACKS, 15); - - aws_io_library_init(allocator); - aws_http_library_init(allocator); - aws_iotdevice_library_init(allocator); - - struct app_ctx app_ctx; - AWS_ZERO_STRUCT(app_ctx); - app_ctx.allocator = allocator; - app_ctx.signal = (struct aws_condition_variable)AWS_CONDITION_VARIABLE_INIT; - app_ctx.connect_timeout = 3000; - aws_mutex_init(&app_ctx.lock); - app_ctx.port = 1883; /* STEVE TODO NOT NECESSARY */ - - s_parse_options(argc, argv, &app_ctx); - if (app_ctx.uri.port) { - app_ctx.port = app_ctx.uri.port; - } - - struct aws_logger logger; - AWS_ZERO_STRUCT(logger); - - struct aws_logger_standard_options options = { - .level = app_ctx.log_level, - }; - - if (app_ctx.log_level) { - if (app_ctx.log_filename) { - if (remove(app_ctx.log_filename)) { - fprintf(stderr, "\nDeleted existing log\n"); - } else { - fprintf(stderr, "\nFailed to delete existing log\n"); - } - options.filename = app_ctx.log_filename; - } else { - options.file = stderr; - } - - if (aws_logger_init_standard(&logger, allocator, &options)) { - fprintf(stderr, "Failed to initialize logger with error %s\n", aws_error_debug_str(aws_last_error())); - exit(1); - } - - aws_logger_set(&logger); - } - - struct aws_event_loop_group *el_group = aws_event_loop_group_new_default(allocator, 2, NULL); - - struct aws_host_resolver_default_options resolver_options = { - .el_group = el_group, - .max_entries = 8, - }; - - struct aws_host_resolver *resolver = aws_host_resolver_new_default(allocator, &resolver_options); - - struct aws_client_bootstrap_options bootstrap_options = { - .event_loop_group = el_group, - .host_resolver = resolver, - }; - - struct aws_client_bootstrap *bootstrap = aws_client_bootstrap_new(allocator, &bootstrap_options); - - struct aws_socket_options socket_options = { - .type = AWS_SOCKET_STREAM, - .connect_timeout_ms = (uint32_t)app_ctx.connect_timeout, - .keep_alive_timeout_sec = 0, - .keepalive = false, - .keep_alive_interval_sec = 0, - }; - - /***************************************************************************************************************** - * Create Secure Tunnel - *****************************************************************************************************************/ - - /* ACCESS TOKEN */ - struct aws_byte_cursor access_token; - AWS_ZERO_STRUCT(access_token); - - struct aws_byte_buf access_token_tmp; - AWS_ZERO_STRUCT(access_token_tmp); - if (app_ctx.access_token_file) { - if (aws_byte_buf_init_from_file(&access_token_tmp, allocator, app_ctx.access_token_file)) { - goto error; - } - access_token = aws_byte_cursor_from_buf(&access_token_tmp); - } - if (access_token.ptr == NULL) { - access_token = aws_byte_cursor_from_array(app_ctx.access_token, strlen(app_ctx.access_token)); - } - - /* CLIENT TOKEN */ - struct aws_byte_cursor client_token; - AWS_ZERO_STRUCT(client_token); - struct aws_byte_buf client_token_tmp; - AWS_ZERO_STRUCT(client_token_tmp); - if (app_ctx.client_token_file) { - if (aws_byte_buf_init_from_file(&client_token_tmp, allocator, app_ctx.client_token_file)) { - goto error; - } - client_token = aws_byte_cursor_from_buf(&client_token_tmp); - } else if (app_ctx.client_token != NULL) { - client_token = aws_byte_cursor_from_array(app_ctx.client_token, strlen(app_ctx.client_token)); - } - - /* SECURE TUNNEL OPTIONS */ - struct aws_secure_tunnel_options secure_tunnel_options = { - .endpoint_host = app_ctx.uri.host_name, - .bootstrap = bootstrap, - .socket_options = &socket_options, - .access_token = access_token, - .client_token = client_token, - .on_message_received = &s_on_message_received, - .on_connection_complete = &s_on_connection_complete, - .on_connection_shutdown = &s_on_connection_shutdown, - .on_stream_start = &s_on_stream_start, - .on_stream_reset = &s_on_stream_reset, - .on_send_data_complete = &s_on_send_data_complete, - .local_proxy_mode = app_ctx.local_proxy_mode, - .user_data = &app_ctx, - }; - - printf("\nCreating Secure Tunnel\n"); - struct aws_secure_tunnel *secure_tunnel = aws_secure_tunnel_new(allocator, &secure_tunnel_options); - app_ctx.secure_tunnel = secure_tunnel; - - printf("\nStarting Secure Tunnel\n"); - aws_secure_tunnel_start(secure_tunnel); - - uint64_t start_1_sleep_time_sec = 30; - bool is_keep_running = true; - - do { - - printf("\nRunning secure tunnel for %llu seconds\n", start_1_sleep_time_sec); - aws_thread_current_sleep(SLEEP_TIME_NS * start_1_sleep_time_sec); - - } while (is_keep_running); - - uint16_t payload_size = (rand() % MAX_WEBSOCKET_PAYLOAD) + 1; - uint8_t payload_data[MAX_WEBSOCKET_PAYLOAD]; - - struct aws_byte_cursor payload_cur = { - .ptr = payload_data, - .len = payload_size, - }; - - struct aws_byte_cursor service_id_cur = aws_byte_cursor_from_c_str("ssh"); - - struct aws_secure_tunnel_message_view message_data = { - .stream_id = 0, - .payload = &payload_cur, - .service_id = &service_id_cur, - }; - - printf("\nSending Data Message\n"); - aws_secure_tunnel_send_message(secure_tunnel, &message_data); - - printf("\nRunning secure tunnel for %llu seconds\n", start_1_sleep_time_sec); - aws_thread_current_sleep(SLEEP_TIME_NS * start_1_sleep_time_sec); - - printf("\nStopping Secure Tunnel\n"); - aws_secure_tunnel_stop(secure_tunnel); - - uint64_t stop_1_sleep_time_sec = 30; - printf("\nSleeping after STOP for %llu seconds\n", stop_1_sleep_time_sec); - aws_thread_current_sleep(SLEEP_TIME_NS * stop_1_sleep_time_sec); - - printf("\nStarting Secure Tunnel Again\n"); - aws_secure_tunnel_start(secure_tunnel); - - uint64_t start_2_sleep_time_sec = 120; - printf("\nRunning secure tunnel for %llu seconds\n", start_2_sleep_time_sec); - aws_thread_current_sleep(SLEEP_TIME_NS * start_2_sleep_time_sec); - - printf("\nStopping Secure Tunnel again\n"); - aws_secure_tunnel_stop(secure_tunnel); - - /* CLEAN UP */ - aws_secure_tunnel_release(secure_tunnel); - aws_client_bootstrap_release(bootstrap); - aws_host_resolver_release(resolver); - aws_event_loop_group_release(el_group); - aws_byte_buf_clean_up(&client_token_tmp); - aws_byte_buf_clean_up(&access_token_tmp); - - aws_thread_join_all_managed(); - - const size_t outstanding_bytes = aws_mem_tracer_bytes(allocator); - printf("\n\nSummary:\n\n"); - printf(" Outstanding bytes: %zu\n\n", outstanding_bytes); - - if (app_ctx.log_level) { - aws_logger_set(NULL); - aws_logger_clean_up(&logger); - } - - aws_uri_clean_up(&app_ctx.uri); - - aws_http_library_clean_up(); - aws_io_library_clean_up(); - aws_iotdevice_library_clean_up(); - - const size_t leaked_bytes = aws_mem_tracer_bytes(allocator); - if (leaked_bytes) { - struct aws_logger memory_logger; - AWS_ZERO_STRUCT(memory_logger); - - aws_logger_init_noalloc(&memory_logger, aws_default_allocator(), &options); - aws_logger_set(&memory_logger); - - aws_mqtt_library_init(aws_default_allocator()); - - printf("Writing memory leaks to log.\n"); - aws_mem_tracer_dump(allocator); - - aws_logger_set(NULL); - aws_logger_clean_up(&memory_logger); - - aws_mqtt_library_clean_up(); - } else { - printf("Finished, with no memory leaks\n"); - } - - aws_mem_tracer_destroy(allocator); - - return 0; - -error: - return 1; -} From 99f35b0919ad0044a988409672449fc9a99f3f19 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 22 Feb 2023 09:08:55 -0800 Subject: [PATCH 66/69] unused test parameters (void) --- tests/secure_tunnel_tests.c | 65 ++++++++++++------------------------- 1 file changed, 20 insertions(+), 45 deletions(-) diff --git a/tests/secure_tunnel_tests.c b/tests/secure_tunnel_tests.c index 3ee8d6c8..c5678ae3 100644 --- a/tests/secure_tunnel_tests.c +++ b/tests/secure_tunnel_tests.c @@ -3,6 +3,12 @@ * SPDX-License-Identifier: Apache-2.0. */ +#include +#include +#include +#include +#include + #include #include #include @@ -13,11 +19,6 @@ #include #include #include -#include -#include -#include -#include -#include #include #include @@ -115,6 +116,7 @@ static void s_on_test_secure_tunnel_connection_complete( const struct aws_secure_tunnel_connection_view *connection_view, int error_code, void *user_data) { + (void)connection_view; struct aws_secure_tunnel_mock_test_fixture *test_fixture = user_data; aws_mutex_lock(&test_fixture->lock); @@ -128,6 +130,7 @@ static void s_on_test_secure_tunnel_connection_complete( } static void s_on_test_secure_tunnel_connection_shutdown(int error_code, void *user_data) { + (void)error_code; struct aws_secure_tunnel_mock_test_fixture *test_fixture = user_data; aws_mutex_lock(&test_fixture->lock); @@ -408,6 +411,7 @@ void aws_websocket_release_mock_fn(struct aws_websocket *websocket) { } void aws_websocket_close_mock_fn(struct aws_websocket *websocket, bool free_scarce_resources_immediately) { + (void)free_scarce_resources_immediately; void *pointer = websocket; struct aws_secure_tunnel_mock_test_fixture *test_fixture = pointer; test_fixture->websocket_function_table->on_connection_shutdown_fn(websocket, 0, test_fixture->secure_tunnel); @@ -513,6 +517,7 @@ void aws_secure_tunnel_mock_test_fixture_clean_up(struct aws_secure_tunnel_mock_ /* [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; if (aws_http_headers_get(request_headers, aws_byte_cursor_from_c_str("access-token"), &access_token_cur)) { AWS_LOGF_ERROR( @@ -525,6 +530,7 @@ 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); @@ -564,6 +570,7 @@ AWS_TEST_CASE(secure_tunneling_functionality_connect_test, s_secure_tunneling_fu /* [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; if (aws_http_headers_get(request_headers, aws_byte_cursor_from_c_str("client-token"), &client_token_cur)) { AWS_LOGF_ERROR( @@ -576,6 +583,7 @@ 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); @@ -670,6 +678,7 @@ 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); @@ -715,6 +724,7 @@ AWS_TEST_CASE(secure_tunneling_fail_and_retry_connection_test, s_secure_tunnelin /* [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); @@ -767,6 +777,7 @@ AWS_TEST_CASE(secure_tunneling_store_service_ids_test, s_secure_tunneling_store_ /* [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); @@ -824,6 +835,7 @@ AWS_TEST_CASE(secure_tunneling_receive_stream_start_test, s_secure_tunneling_rec /* [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); @@ -876,6 +888,7 @@ AWS_TEST_CASE( /* [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); @@ -942,6 +955,7 @@ AWS_TEST_CASE( /* [Func-UC8] */ static int s_secure_tunneling_session_reset_test_fn(struct aws_allocator *allocator, void *ctx) { + (void)ctx; aws_http_library_init(allocator); aws_iotdevice_library_init(allocator); @@ -1040,47 +1054,8 @@ static int s_secure_tunneling_session_reset_test_fn(struct aws_allocator *alloca AWS_TEST_CASE(secure_tunneling_session_reset_test, s_secure_tunneling_session_reset_test_fn) -/* -static int s_secure_tunneling_template_test_fn(struct aws_allocator *allocator, 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)); - - 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(); - - return AWS_OP_SUCCESS; -} - -AWS_TEST_CASE(secure_tunneling_template_test, s_secure_tunneling_template_test_fn) -*/ - -/* [Func-UC9] */ - 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); From f0bbb07091b43fd8f4f5bdd214b058c404d826eb Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 22 Feb 2023 09:24:38 -0800 Subject: [PATCH 67/69] ignore specific warning --- source/secure_tunneling.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 94af4e09..cee94f59 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -18,6 +18,11 @@ #include #include +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable : 4232) /* function pointer to dll symbol */ +#endif + #define MAX_WEBSOCKET_PAYLOAD 131076 #define INVALID_STREAM_ID 0 #define PAYLOAD_BYTE_LENGTH_PREFIX 2 From e7abc0d9ac923c7f03ca9b835f05b1781d2f2d91 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 22 Feb 2023 10:29:00 -0800 Subject: [PATCH 68/69] add logging --- source/secure_tunneling.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index cee94f59..450b328e 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -98,8 +98,11 @@ static int s_reset_service_id(void *context, struct aws_hash_element *p_element) static void s_secure_tunnel_final_destroy(struct aws_secure_tunnel *secure_tunnel) { if (secure_tunnel == NULL) { + AWS_LOGF_TRACE( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: secure_tunnel is NULL on final destroy", (void *)secure_tunnel); return; } + AWS_LOGF_TRACE(AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: secure_tunnel final destroy", (void *)secure_tunnel); aws_secure_tunneling_on_termination_complete_fn *on_termination_complete = NULL; void *termination_complete_user_data = NULL; From 0c3cd36278b4927373d87eecce8aa68106a8bf12 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Wed, 22 Feb 2023 15:17:23 -0800 Subject: [PATCH 69/69] code review changes --- source/secure_tunneling.c | 13 +++++++++---- source/serializer.c | 10 +++++++++- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index 450b328e..e0a2eb11 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -318,7 +318,7 @@ static void s_aws_secure_tunnel_connected_on_message_received( case AWS_SECURE_TUNNEL_MT_UNKNOWN: default: if (!message_view->ignorable) { - AWS_LOGF_WARN( + AWS_LOGF_ERROR( AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Encountered an unknown but un-ignorable message. type=%s", aws_secure_tunnel_message_type_to_c_string(message_view->type)); @@ -327,7 +327,7 @@ static void s_aws_secure_tunnel_connected_on_message_received( } } -static void s_process_received_data(struct aws_secure_tunnel *secure_tunnel) { +static int s_process_received_data(struct aws_secure_tunnel *secure_tunnel) { 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; @@ -352,6 +352,7 @@ static void s_process_received_data(struct aws_secure_tunnel *secure_tunnel) { (void *)secure_tunnel, error_code, aws_error_debug_str(error_code)); + return error_code; } } @@ -360,6 +361,8 @@ static void s_process_received_data(struct aws_secure_tunnel *secure_tunnel) { received_data->len = 0; aws_byte_buf_append(received_data, &cursor); } + + return AWS_OP_SUCCESS; } /***************************************************************************************************************** @@ -473,7 +476,9 @@ static bool s_on_websocket_incoming_frame_payload( if (data.len > 0) { struct aws_secure_tunnel *secure_tunnel = user_data; aws_byte_buf_append(&secure_tunnel->received_data, &data); - s_process_received_data(secure_tunnel); + if (s_process_received_data(secure_tunnel)) { + return false; + } } return true; @@ -1207,7 +1212,7 @@ int aws_secure_tunnel_service_operational_state(struct aws_secure_tunnel *secure switch (current_operation->operation_type) { case AWS_STOT_PING:; /* - * Currently, pings are sent to keep the websocket alive but we do not receive responses from the + * 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. diff --git a/source/serializer.c b/source/serializer.c index 2c3a99a2..565178a3 100644 --- a/source/serializer.c +++ b/source/serializer.c @@ -350,6 +350,10 @@ int aws_secure_tunnel_deserialize_varint_from_cursor_to_message( message->ignorable = result; break; default: + AWS_LOGF_WARN( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Unexpected field number in message encountered.", + (void *)message); /* Unexpected field_number */ break; } @@ -448,6 +452,10 @@ int aws_secure_tunnel_deserialize_message_from_cursor( case AWS_SECURE_TUNNEL_PBWT_START_GROUP: case AWS_SECURE_TUNNEL_PBWT_END_GROUP: case AWS_SECURE_TUNNEL_PBWT_32_BIT: + AWS_LOGF_ERROR( + AWS_LS_IOTDEVICE_SECURE_TUNNELING, + "id=%p: Unexpected wire type in message encountered.", + (void *)secure_tunnel); goto error; break; } @@ -464,7 +472,7 @@ int aws_secure_tunnel_deserialize_message_from_cursor( 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 "UNKNOWN"; + return "ST_MT_UNKNOWN"; case AWS_SECURE_TUNNEL_MT_DATA: return "DATA";