Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove Iot Core specific topic and client id validation checks in MQTT5 #315

Merged
merged 2 commits into from
Aug 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 0 additions & 9 deletions include/aws/mqtt/private/v5/mqtt5_options_storage.h
Original file line number Diff line number Diff line change
Expand Up @@ -257,9 +257,6 @@ AWS_MQTT_API struct aws_mqtt5_operation_publish *aws_mqtt5_operation_publish_new

AWS_MQTT_API int aws_mqtt5_packet_publish_view_validate(const struct aws_mqtt5_packet_publish_view *publish_view);

AWS_MQTT_API int aws_mqtt5_packet_publish_view_validate_vs_iot_core(
const struct aws_mqtt5_packet_publish_view *publish_view);

AWS_MQTT_API void aws_mqtt5_packet_publish_view_log(
const struct aws_mqtt5_packet_publish_view *publish_view,
enum aws_log_level level);
Expand All @@ -284,9 +281,6 @@ AWS_MQTT_API struct aws_mqtt5_operation_subscribe *aws_mqtt5_operation_subscribe

AWS_MQTT_API int aws_mqtt5_packet_subscribe_view_validate(const struct aws_mqtt5_packet_subscribe_view *subscribe_view);

AWS_MQTT_API int aws_mqtt5_packet_subscribe_view_validate_vs_iot_core(
const struct aws_mqtt5_packet_subscribe_view *subscribe_view);

AWS_MQTT_API void aws_mqtt5_packet_subscribe_view_log(
const struct aws_mqtt5_packet_subscribe_view *subscribe_view,
enum aws_log_level level);
Expand All @@ -308,9 +302,6 @@ AWS_MQTT_API struct aws_mqtt5_operation_unsubscribe *aws_mqtt5_operation_unsubsc
AWS_MQTT_API int aws_mqtt5_packet_unsubscribe_view_validate(
const struct aws_mqtt5_packet_unsubscribe_view *unsubscribe_view);

AWS_MQTT_API int aws_mqtt5_packet_unsubscribe_view_validate_vs_iot_core(
const struct aws_mqtt5_packet_unsubscribe_view *unsubscribe_view);

AWS_MQTT_API void aws_mqtt5_packet_unsubscribe_view_log(
const struct aws_mqtt5_packet_unsubscribe_view *unsubscribe_view,
enum aws_log_level level);
Expand Down
5 changes: 2 additions & 3 deletions include/aws/mqtt/private/v5/mqtt5_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,8 @@ struct aws_mqtt5_negotiated_settings;
#define AWS_MQTT5_SUBSCRIBE_FLAGS_QOS_BIT_MASK 0x03

/* Static AWS IoT Core Limit/Quota Values */
#define AWS_IOT_CORE_MAXIMUM_CLIENT_ID_LENGTH 128
#define AWS_IOT_CORE_MAXIMUM_TOPIC_LENGTH 256
#define AWS_IOT_CORE_MAXIMUM_TOPIC_SEGMENTS 8
#define AWS_IOT_CORE_MAXIMUM_SUSBCRIPTIONS_PER_SUBSCRIBE 8

/* Dynamic IoT Core Limits */
#define AWS_IOT_CORE_PUBLISH_PER_SECOND_LIMIT 100
Expand Down Expand Up @@ -328,7 +326,8 @@ AWS_MQTT_API uint64_t aws_mqtt5_client_random_in_range(uint64_t from, uint64_t t
* @param topic_cursor topic to get the non-rules suffix for
* @return remaining part of the topic after the leading AWS IoT Rules prefix has been skipped, if present
*/
AWS_MQTT_API struct aws_byte_cursor aws_mqtt5_topic_skip_aws_iot_rules_prefix(struct aws_byte_cursor topic_cursor);
AWS_MQTT_API struct aws_byte_cursor aws_mqtt5_topic_skip_aws_iot_core_uncounted_prefix(
struct aws_byte_cursor topic_cursor);

/**
* Computes the number of topic segments in a topic or topic filter
Expand Down
10 changes: 2 additions & 8 deletions include/aws/mqtt/v5/mqtt5_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -181,16 +181,10 @@ enum aws_mqtt5_extended_validation_and_flow_control_options {
AWS_MQTT5_EVAFCO_NONE,

/**
* Apply additional client-side validation and operational flow control that respects the
* Apply additional client-side operational flow control that respects the
* default AWS IoT Core limits.
*
* Currently applies the following additional validation:
* (1) No more than 8 subscriptions per SUBSCRIBE packet
* (2) Topics and topic filters have a maximum of 7 slashes (8 segments), not counting any AWS rules prefix
* (3) Topics must be <= 256 bytes in length
* (4) Client id must be <= 128 bytes in length
*
* Also applies the following flow control:
* Applies the following flow control:
* (1) Outbound throughput throttled to 512KB/s
* (2) Outbound publish TPS throttled to 100
*/
Expand Down
107 changes: 3 additions & 104 deletions source/v5/mqtt5_options_storage.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,19 +80,6 @@ size_t aws_mqtt5_user_property_set_size(const struct aws_mqtt5_user_property_set
return aws_array_list_length(&property_set->properties);
}

int aws_mqtt5_user_property_set_get_property(
const struct aws_mqtt5_user_property_set *property_set,
size_t index,
struct aws_mqtt5_user_property *property_out) {
return aws_array_list_get_at(&property_set->properties, property_out, index);
}

int aws_mqtt5_user_property_set_add_stored_property(
struct aws_mqtt5_user_property_set *property_set,
struct aws_mqtt5_user_property *property) {
return aws_array_list_push_back(&property_set->properties, property);
}

static void s_aws_mqtt5_user_property_set_log(
struct aws_logger *log_handle,
const struct aws_mqtt5_user_property *properties,
Expand Down Expand Up @@ -1725,19 +1712,6 @@ int aws_mqtt5_packet_publish_view_validate(const struct aws_mqtt5_packet_publish
return AWS_OP_SUCCESS;
}

int aws_mqtt5_packet_publish_view_validate_vs_iot_core(const struct aws_mqtt5_packet_publish_view *publish_view) {
if (!aws_mqtt_is_valid_topic_for_iot_core(publish_view->topic)) {
AWS_LOGF_ERROR(
AWS_LS_MQTT5_GENERAL,
"id=%p: aws_mqtt5_packet_publish_view - topic not valid for AWS Iot Core limits: \"" PRInSTR "\"",
(void *)publish_view,
AWS_BYTE_CURSOR_PRI(publish_view->topic));
return AWS_OP_ERR;
}

return AWS_OP_SUCCESS;
}

static int s_aws_mqtt5_packet_publish_view_validate_vs_connection_settings(
const void *packet_view,
const struct aws_mqtt5_client *client) {
Expand Down Expand Up @@ -2114,6 +2088,7 @@ struct aws_mqtt5_operation_publish *aws_mqtt5_operation_publish_new(
const struct aws_mqtt5_client *client,
const struct aws_mqtt5_packet_publish_view *publish_options,
const struct aws_mqtt5_publish_completion_options *completion_options) {
(void)client;
AWS_PRECONDITION(allocator != NULL);
AWS_PRECONDITION(publish_options != NULL);

Expand All @@ -2130,12 +2105,6 @@ struct aws_mqtt5_operation_publish *aws_mqtt5_operation_publish_new(
return NULL;
}

if (client != NULL && client->config->extended_validation_and_flow_control_options != AWS_MQTT5_EVAFCO_NONE) {
if (aws_mqtt5_packet_publish_view_validate_vs_iot_core(publish_options)) {
return NULL;
}
}

struct aws_mqtt5_operation_publish *publish_op =
aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt5_operation_publish));
if (publish_op == NULL) {
Expand Down Expand Up @@ -2392,25 +2361,6 @@ int aws_mqtt5_packet_unsubscribe_view_validate(const struct aws_mqtt5_packet_uns
return AWS_OP_SUCCESS;
}

AWS_MQTT_API int aws_mqtt5_packet_unsubscribe_view_validate_vs_iot_core(
const struct aws_mqtt5_packet_unsubscribe_view *unsubscribe_view) {

for (size_t i = 0; i < unsubscribe_view->topic_filter_count; ++i) {
const struct aws_byte_cursor *topic_filter = &unsubscribe_view->topic_filters[i];
if (!aws_mqtt_is_valid_topic_filter_for_iot_core(*topic_filter)) {
AWS_LOGF_ERROR(
AWS_LS_MQTT5_GENERAL,
"id=%p: aws_mqtt5_packet_unsubscribe_view - topic filter not valid for AWS Iot Core limits: \"" PRInSTR
"\"",
(void *)unsubscribe_view,
AWS_BYTE_CURSOR_PRI(*topic_filter));
return aws_raise_error(AWS_ERROR_MQTT5_UNSUBSCRIBE_OPTIONS_VALIDATION);
}
}

return AWS_OP_SUCCESS;
}

void aws_mqtt5_packet_unsubscribe_view_log(
const struct aws_mqtt5_packet_unsubscribe_view *unsubscribe_view,
enum aws_log_level level) {
Expand Down Expand Up @@ -2597,6 +2547,7 @@ struct aws_mqtt5_operation_unsubscribe *aws_mqtt5_operation_unsubscribe_new(
const struct aws_mqtt5_client *client,
const struct aws_mqtt5_packet_unsubscribe_view *unsubscribe_options,
const struct aws_mqtt5_unsubscribe_completion_options *completion_options) {
(void)client;
AWS_PRECONDITION(allocator != NULL);
AWS_PRECONDITION(unsubscribe_options != NULL);

Expand All @@ -2613,12 +2564,6 @@ struct aws_mqtt5_operation_unsubscribe *aws_mqtt5_operation_unsubscribe_new(
return NULL;
}

if (client != NULL && client->config->extended_validation_and_flow_control_options != AWS_MQTT5_EVAFCO_NONE) {
if (aws_mqtt5_packet_unsubscribe_view_validate_vs_iot_core(unsubscribe_options)) {
return NULL;
}
}

struct aws_mqtt5_operation_unsubscribe *unsubscribe_op =
aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt5_operation_unsubscribe));
if (unsubscribe_op == NULL) {
Expand Down Expand Up @@ -2774,37 +2719,6 @@ int aws_mqtt5_packet_subscribe_view_validate(const struct aws_mqtt5_packet_subsc
return AWS_OP_SUCCESS;
}

AWS_MQTT_API int aws_mqtt5_packet_subscribe_view_validate_vs_iot_core(
const struct aws_mqtt5_packet_subscribe_view *subscribe_view) {

if (subscribe_view->subscription_count > AWS_IOT_CORE_MAXIMUM_SUSBCRIPTIONS_PER_SUBSCRIBE) {
AWS_LOGF_ERROR(
AWS_LS_MQTT5_GENERAL,
"id=%p: aws_mqtt5_packet_subscribe_view - number of subscriptions (%zu) exceeds default AWS IoT Core limit "
"(%d)",
(void *)subscribe_view,
subscribe_view->subscription_count,
(int)AWS_IOT_CORE_MAXIMUM_SUSBCRIPTIONS_PER_SUBSCRIBE);
return AWS_OP_ERR;
}

for (size_t i = 0; i < subscribe_view->subscription_count; ++i) {
const struct aws_mqtt5_subscription_view *subscription = &subscribe_view->subscriptions[i];
const struct aws_byte_cursor *topic_filter = &subscription->topic_filter;
if (!aws_mqtt_is_valid_topic_filter_for_iot_core(*topic_filter)) {
AWS_LOGF_ERROR(
AWS_LS_MQTT5_GENERAL,
"id=%p: aws_mqtt5_packet_subscribe_view - topic filter not valid for AWS Iot Core limits: \"" PRInSTR
"\"",
(void *)subscribe_view,
AWS_BYTE_CURSOR_PRI(*topic_filter));
return aws_raise_error(AWS_ERROR_MQTT5_UNSUBSCRIBE_OPTIONS_VALIDATION);
}
}

return AWS_OP_SUCCESS;
}

void aws_mqtt5_packet_subscribe_view_log(
const struct aws_mqtt5_packet_subscribe_view *subscribe_view,
enum aws_log_level level) {
Expand Down Expand Up @@ -3016,6 +2930,7 @@ struct aws_mqtt5_operation_subscribe *aws_mqtt5_operation_subscribe_new(
const struct aws_mqtt5_client *client,
const struct aws_mqtt5_packet_subscribe_view *subscribe_options,
const struct aws_mqtt5_subscribe_completion_options *completion_options) {
(void)client;
AWS_PRECONDITION(allocator != NULL);
AWS_PRECONDITION(subscribe_options != NULL);

Expand All @@ -3032,12 +2947,6 @@ struct aws_mqtt5_operation_subscribe *aws_mqtt5_operation_subscribe_new(
return NULL;
}

if (client != NULL && client->config->extended_validation_and_flow_control_options != AWS_MQTT5_EVAFCO_NONE) {
if (aws_mqtt5_packet_subscribe_view_validate_vs_iot_core(subscribe_options)) {
return NULL;
}
}

struct aws_mqtt5_operation_subscribe *subscribe_op =
aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt5_operation_subscribe));
if (subscribe_op == NULL) {
Expand Down Expand Up @@ -3472,16 +3381,6 @@ int aws_mqtt5_client_options_validate(const struct aws_mqtt5_client_options *opt
return aws_raise_error(AWS_ERROR_MQTT5_CLIENT_OPTIONS_VALIDATION);
}

if (options->extended_validation_and_flow_control_options != AWS_MQTT5_EVAFCO_NONE) {
if (options->connect_options->client_id.len > AWS_IOT_CORE_MAXIMUM_CLIENT_ID_LENGTH) {
AWS_LOGF_ERROR(
AWS_LS_MQTT5_GENERAL,
"AWS IoT Core limits client_id to be less than or equal to %d bytes in length",
(int)AWS_IOT_CORE_MAXIMUM_CLIENT_ID_LENGTH);
return aws_raise_error(AWS_ERROR_MQTT5_CLIENT_OPTIONS_VALIDATION);
}
}

return AWS_OP_SUCCESS;
}

Expand Down
87 changes: 51 additions & 36 deletions source/v5/mqtt5_utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ uint64_t aws_mqtt5_client_random_in_range(uint64_t from, uint64_t to) {

static uint8_t s_aws_iot_core_rules_prefix[] = "$aws/rules/";

struct aws_byte_cursor aws_mqtt5_topic_skip_aws_iot_rules_prefix(struct aws_byte_cursor topic_cursor) {
static struct aws_byte_cursor s_aws_mqtt5_topic_skip_aws_iot_rules_prefix(struct aws_byte_cursor topic_cursor) {
size_t prefix_length = AWS_ARRAY_SIZE(s_aws_iot_core_rules_prefix) - 1; /* skip 0-terminator */

struct aws_byte_cursor rules_prefix = {
Expand Down Expand Up @@ -454,47 +454,18 @@ struct aws_byte_cursor aws_mqtt5_topic_skip_aws_iot_rules_prefix(struct aws_byte
return topic_cursor_copy;
}

size_t aws_mqtt5_topic_get_segment_count(struct aws_byte_cursor topic_cursor) {
size_t segment_count = 0;

struct aws_byte_cursor segment_cursor;
AWS_ZERO_STRUCT(segment_cursor);

while (aws_byte_cursor_next_split(&topic_cursor, '/', &segment_cursor)) {
++segment_count;
}

return segment_count;
}

bool aws_mqtt_is_valid_topic_filter_for_iot_core(struct aws_byte_cursor topic_filter_cursor) {
struct aws_byte_cursor post_rule_suffix = aws_mqtt5_topic_skip_aws_iot_rules_prefix(topic_filter_cursor);
return aws_mqtt5_topic_get_segment_count(post_rule_suffix) <= AWS_IOT_CORE_MAXIMUM_TOPIC_SEGMENTS;
}

bool aws_mqtt_is_valid_topic_for_iot_core(struct aws_byte_cursor topic_cursor) {
struct aws_byte_cursor post_rule_suffix = aws_mqtt5_topic_skip_aws_iot_rules_prefix(topic_cursor);
if (aws_mqtt5_topic_get_segment_count(post_rule_suffix) > AWS_IOT_CORE_MAXIMUM_TOPIC_SEGMENTS) {
return false;
}

return post_rule_suffix.len <= AWS_IOT_CORE_MAXIMUM_TOPIC_LENGTH;
}

static uint8_t s_shared_subscription_prefix[] = "$share";

static bool s_is_not_hash_or_plus(uint8_t byte) {
return byte != '+' && byte != '#';
}

/* $share/{ShareName}/{filter} */
bool aws_mqtt_is_topic_filter_shared_subscription(struct aws_byte_cursor topic_cursor) {

static struct aws_byte_cursor s_aws_mqtt5_topic_skip_shared_prefix(struct aws_byte_cursor topic_cursor) {
/* shared subscription filters must have an initial segment of "$share" */
struct aws_byte_cursor first_segment_cursor;
AWS_ZERO_STRUCT(first_segment_cursor);
if (!aws_byte_cursor_next_split(&topic_cursor, '/', &first_segment_cursor)) {
return false;
return topic_cursor;
}

struct aws_byte_cursor share_prefix_cursor = {
Expand All @@ -503,7 +474,7 @@ bool aws_mqtt_is_topic_filter_shared_subscription(struct aws_byte_cursor topic_c
};

if (!aws_byte_cursor_eq_ignore_case(&share_prefix_cursor, &first_segment_cursor)) {
return false;
return topic_cursor;
}

/*
Expand All @@ -512,12 +483,12 @@ bool aws_mqtt_is_topic_filter_shared_subscription(struct aws_byte_cursor topic_c
*/
struct aws_byte_cursor second_segment_cursor = first_segment_cursor;
if (!aws_byte_cursor_next_split(&topic_cursor, '/', &second_segment_cursor)) {
return false;
return topic_cursor;
}

if (second_segment_cursor.len == 0 ||
!aws_byte_cursor_satisfies_pred(&second_segment_cursor, s_is_not_hash_or_plus)) {
return false;
return topic_cursor;
}

/*
Expand All @@ -527,11 +498,55 @@ bool aws_mqtt_is_topic_filter_shared_subscription(struct aws_byte_cursor topic_c
size_t remaining_length =
topic_cursor.ptr + topic_cursor.len - (second_segment_cursor.len + second_segment_cursor.ptr);
if (remaining_length == 0) {
return false;
return topic_cursor;
}

aws_byte_cursor_advance(&remaining_cursor, topic_cursor.len - remaining_length + 1);

return remaining_cursor;
}

struct aws_byte_cursor aws_mqtt5_topic_skip_aws_iot_core_uncounted_prefix(struct aws_byte_cursor topic_cursor) {
struct aws_byte_cursor skip_shared = s_aws_mqtt5_topic_skip_shared_prefix(topic_cursor);
struct aws_byte_cursor skip_rules = s_aws_mqtt5_topic_skip_aws_iot_rules_prefix(skip_shared);

return skip_rules;
}

size_t aws_mqtt5_topic_get_segment_count(struct aws_byte_cursor topic_cursor) {
size_t segment_count = 0;

struct aws_byte_cursor segment_cursor;
AWS_ZERO_STRUCT(segment_cursor);

while (aws_byte_cursor_next_split(&topic_cursor, '/', &segment_cursor)) {
++segment_count;
}

return segment_count;
}

bool aws_mqtt_is_valid_topic_filter_for_iot_core(struct aws_byte_cursor topic_filter_cursor) {
struct aws_byte_cursor post_rule_suffix = aws_mqtt5_topic_skip_aws_iot_core_uncounted_prefix(topic_filter_cursor);
return aws_mqtt5_topic_get_segment_count(post_rule_suffix) <= AWS_IOT_CORE_MAXIMUM_TOPIC_SEGMENTS;
}

bool aws_mqtt_is_valid_topic_for_iot_core(struct aws_byte_cursor topic_cursor) {
struct aws_byte_cursor post_rule_suffix = aws_mqtt5_topic_skip_aws_iot_core_uncounted_prefix(topic_cursor);
if (aws_mqtt5_topic_get_segment_count(post_rule_suffix) > AWS_IOT_CORE_MAXIMUM_TOPIC_SEGMENTS) {
return false;
}

return post_rule_suffix.len <= AWS_IOT_CORE_MAXIMUM_TOPIC_LENGTH;
}

/* $share/{ShareName}/{filter} */
bool aws_mqtt_is_topic_filter_shared_subscription(struct aws_byte_cursor topic_cursor) {
struct aws_byte_cursor remaining_cursor = s_aws_mqtt5_topic_skip_shared_prefix(topic_cursor);
if (remaining_cursor.len == topic_cursor.len) {
return false;
}

if (!aws_mqtt_is_valid_topic_filter(&remaining_cursor)) {
return false;
}
Expand Down
Loading