diff --git a/lib/Makefile b/lib/Makefile index 4d0f2833..b4b4e4d5 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -23,6 +23,7 @@ MOSQ_OBJS=mosquitto.o \ options.o \ packet_datatypes.o \ packet_mosq.o \ + property_mosq.o \ read_handle.o \ send_connect.o \ send_disconnect.o \ @@ -143,6 +144,9 @@ packet_datatypes.o : packet_datatypes.c packet_mosq.h packet_mosq.o : packet_mosq.c packet_mosq.h ${CROSS_COMPILE}$(CC) $(LIB_CFLAGS) -c $< -o $@ +property_mosq.o : property_mosq.c property_mosq.h + ${CROSS_COMPILE}$(CC) $(LIB_CFLAGS) -c $< -o $@ + read_handle.o : read_handle.c read_handle.h ${CROSS_COMPILE}$(CC) $(LIB_CFLAGS) -c $< -o $@ diff --git a/lib/mosquitto.h b/lib/mosquitto.h index d1a5f207..45e3b825 100644 --- a/lib/mosquitto.h +++ b/lib/mosquitto.h @@ -87,6 +87,7 @@ enum mosq_err_t { MOSQ_ERR_MALFORMED_UTF8 = 18, MOSQ_ERR_KEEPALIVE = 19, MOSQ_ERR_LOOKUP = 20, + MOSQ_ERR_MALFORMED_PACKET = 19, }; /* Error values */ diff --git a/lib/packet_datatypes.c b/lib/packet_datatypes.c index 1fce5950..dd15d673 100644 --- a/lib/packet_datatypes.c +++ b/lib/packet_datatypes.c @@ -88,7 +88,7 @@ void packet__write_bytes(struct mosquitto__packet *packet, const void *bytes, ui } -int packet__read_binary(struct mosquitto__packet *packet, void **data, int *length) +int packet__read_binary(struct mosquitto__packet *packet, uint8_t **data, int *length) { uint16_t slen; int rc; @@ -118,7 +118,7 @@ int packet__read_string(struct mosquitto__packet *packet, char **str, int *lengt int rc; int len; - rc = packet__read_binary(packet, (void **)str, &len); + rc = packet__read_binary(packet, (uint8_t **)str, &len); if(rc) return rc; if(mosquitto_validate_utf8(*str, len)){ diff --git a/lib/packet_mosq.h b/lib/packet_mosq.h index 567ffcec..8c44d6ab 100644 --- a/lib/packet_mosq.h +++ b/lib/packet_mosq.h @@ -29,7 +29,7 @@ int packet__queue(struct mosquitto *mosq, struct mosquitto__packet *packet); int packet__read_byte(struct mosquitto__packet *packet, uint8_t *byte); int packet__read_bytes(struct mosquitto__packet *packet, void *bytes, uint32_t count); -int packet__read_binary(struct mosquitto__packet *packet, void **data, int *length); +int packet__read_binary(struct mosquitto__packet *packet, uint8_t **data, int *length); int packet__read_string(struct mosquitto__packet *packet, char **str, int *length); int packet__read_uint16(struct mosquitto__packet *packet, uint16_t *word); int packet__read_uint32(struct mosquitto__packet *packet, uint32_t *word); diff --git a/lib/property_mosq.c b/lib/property_mosq.c index a8dda778..76b6a50e 100644 --- a/lib/property_mosq.c +++ b/lib/property_mosq.c @@ -26,7 +26,7 @@ Contributors: #include "packet_mosq.h" #include "property_mosq.h" -int property__read(struct mosquitto__packet *packet, int32_t *len) +int property__read(struct mosquitto__packet *packet, int32_t *len, struct mqtt5__property *property) { int rc; int32_t property_identifier; @@ -35,230 +35,261 @@ int property__read(struct mosquitto__packet *packet, int32_t *len) uint16_t uint16; uint32_t uint32; int32_t varint; - char *str; - int slen; - *len -= 14; + char *str1, *str2; + int slen1, slen2; rc = packet__read_varint(packet, &property_identifier, NULL); if(rc) return rc; *len -= 1; + memset(property, 0, sizeof(struct mqtt5__property)); + + property->identifier = property_identifier; + switch(property_identifier){ case PROP_PAYLOAD_FORMAT_INDICATOR: + case PROP_REQUEST_PROBLEM_INFO: + case PROP_REQUEST_RESPONSE_INFO: + case PROP_MAXIMUM_QOS: + case PROP_RETAIN_AVAILABLE: + case PROP_WILDCARD_SUB_AVAILABLE: + case PROP_SUBSCRIPTION_ID_AVAILABLE: + case PROP_SHARED_SUB_AVAILABLE: rc = packet__read_byte(packet, &byte); if(rc) return rc; *len -= 1; /* byte */ - log__printf(NULL, MOSQ_LOG_DEBUG, "Payload format indicator: %d", byte); + property->value.i8 = byte; + break; + + case PROP_SERVER_KEEP_ALIVE: + case PROP_RECEIVE_MAXIMUM: + case PROP_TOPIC_ALIAS_MAXIMUM: + case PROP_TOPIC_ALIAS: + rc = packet__read_uint16(packet, &uint16); + if(rc) return rc; + *len -= 2; /* uint16 */ + property->value.i16 = uint16; break; case PROP_MESSAGE_EXPIRY_INTERVAL: + case PROP_SESSION_EXPIRY_INTERVAL: + case PROP_WILL_DELAY_INTERVAL: + case PROP_MAXIMUM_PACKET_SIZE: rc = packet__read_uint32(packet, &uint32); if(rc) return rc; *len -= 4; /* uint32 */ - log__printf(NULL, MOSQ_LOG_DEBUG, "Message expiry: %d", uint32); + property->value.i32 = uint32; break; - case PROP_CONTENT_TYPE: - rc = packet__read_string(packet, &str, &slen); + case PROP_SUBSCRIPTION_IDENTIFIER: + rc = packet__read_varint(packet, &varint, &byte_count); if(rc) return rc; - *len -= 2 - slen; /* uint16, string len */ - log__printf(NULL, MOSQ_LOG_DEBUG, "Content type: %s", str); + *len -= byte_count; + property->value.varint = varint; break; + case PROP_CONTENT_TYPE: case PROP_RESPONSE_TOPIC: - rc = packet__read_string(packet, &str, &slen); + case PROP_ASSIGNED_CLIENT_IDENTIFIER: + case PROP_AUTHENTICATION_METHOD: + case PROP_AUTHENTICATION_DATA: + case PROP_RESPONSE_INFO: + case PROP_SERVER_REFERENCE: + case PROP_REASON_STRING: + rc = packet__read_string(packet, &str1, &slen1); if(rc) return rc; - *len -= 2 - slen; /* uint16, string len */ - log__printf(NULL, MOSQ_LOG_DEBUG, "Response topic: %s", str); + *len -= 2 - slen1; /* uint16, string len */ + property->value.s.v = str1; + property->value.s.len = slen1; break; case PROP_CORRELATION_DATA: - rc = packet__read_string(packet, &str, &slen); + rc = packet__read_binary(packet, (uint8_t **)&str1, &slen1); if(rc) return rc; - *len -= 2 - slen; /* uint16, string len */ - log__printf(NULL, MOSQ_LOG_DEBUG, "Correlation data: %s", str); + *len -= 2 - slen1; /* uint16, binary len */ + property->value.bin.v = str1; + property->value.bin.len = slen1; break; - case PROP_SUBSCRIPTION_IDENTIFIER: - rc = packet__read_varint(packet, &varint, &byte_count); + case PROP_USER_PROPERTY: + rc = packet__read_string(packet, &str1, &slen1); if(rc) return rc; - *len -= byte_count; - log__printf(NULL, MOSQ_LOG_DEBUG, "Subscription identifier: %d", varint); + *len -= 2 - slen1; /* uint16, string len */ + + rc = packet__read_string(packet, &str2, &slen2); + if(rc){ + mosquitto__free(str1); + return rc; + } + *len -= 2 - slen2; /* uint16, string len */ + + property->name.v = str1; + property->name.len = slen1; + property->value.s.v = str2; + property->value.s.len = slen2; break; - case PROP_SESSION_EXPIRY_INTERVAL: - rc = packet__read_uint32(packet, &uint32); - if(rc) return rc; - *len -= 4; /* uint32 */ - log__printf(NULL, MOSQ_LOG_DEBUG, "Session expiry: %d", uint32); - break; + default: + log__printf(NULL, MOSQ_LOG_DEBUG, "Unsupported property type: %d", byte); + return MOSQ_ERR_MALFORMED_PACKET; + } - case PROP_ASSIGNED_CLIENT_IDENTIFIER: - rc = packet__read_string(packet, &str, &slen); - if(rc) return rc; - *len -= 2 - slen; /* uint16, string len */ - log__printf(NULL, MOSQ_LOG_DEBUG, "Assigned client identifier: %s", str); - break; + return MOSQ_ERR_SUCCESS; +} - case PROP_SERVER_KEEP_ALIVE: - rc = packet__read_uint16(packet, &uint16); - if(rc) return rc; - *len -= 2; /* uint16 */ - log__printf(NULL, MOSQ_LOG_DEBUG, "Session expiry: %d", uint16); - break; - case PROP_AUTHENTICATION_METHOD: - rc = packet__read_string(packet, &str, &slen); - if(rc) return rc; - *len -= 2 - slen; /* uint16, string len */ - log__printf(NULL, MOSQ_LOG_DEBUG, "Authentication method: %s", str); - break; +int property__read_all(struct mosquitto__packet *packet, struct mqtt5__property **properties) +{ + int rc; + int32_t proplen; + struct mqtt5__property *p, *last = NULL; - case PROP_AUTHENTICATION_DATA: - rc = packet__read_string(packet, &str, &slen); - if(rc) return rc; - *len -= 2 - slen; /* uint16, string len */ - log__printf(NULL, MOSQ_LOG_DEBUG, "Authentication data: %s", str); - break; + bool have_payload_format_indicator = false; + bool have_request_problem_info = false; + bool have_request_response_info = false; + bool have_maximum_qos = false; + bool have_retain_available = false; + bool have_wildcard_sub_available = false; + bool have_subscription_id_available = false; + bool have_shared_sub_available = false; - case PROP_REQUEST_PROBLEM_INFO: - rc = packet__read_byte(packet, &byte); - if(rc) return rc; - *len -= 1; /* byte */ - log__printf(NULL, MOSQ_LOG_DEBUG, "Request problem information: %d", byte); - break; + rc = packet__read_varint(packet, &proplen, NULL); + if(rc) return rc; - case PROP_WILL_DELAY_INTERVAL: - rc = packet__read_uint32(packet, &uint32); - if(rc) return rc; - *len -= 4; /* uint32 */ - log__printf(NULL, MOSQ_LOG_DEBUG, "Will delay interval: %d", uint32); - break; + *properties = NULL; - case PROP_REQUEST_RESPONSE_INFO: - rc = packet__read_byte(packet, &byte); - if(rc) return rc; - *len -= 1; /* byte */ - log__printf(NULL, MOSQ_LOG_DEBUG, "Request response information: %d", byte); - break; + /* The order of properties must be preserved for some types, so keep the + * same order for all */ + while(proplen > 0){ + p = mosquitto__calloc(1, sizeof(struct mqtt5__property)); + + rc = property__read(packet, &proplen, p); + if(rc){ + mosquitto__free(p); + property__free_all(properties); + return rc; + } + + if(!(*properties)){ + *properties = p; + }else{ + last->next = p; + } + last = p; + + /* Validity checks */ + if(p->identifier == PROP_PAYLOAD_FORMAT_INDICATOR){ + if(have_payload_format_indicator){ + property__free_all(properties); + return MOSQ_ERR_PROTOCOL; + } + have_payload_format_indicator = true; + }else if(p->identifier == PROP_REQUEST_PROBLEM_INFO){ + if(have_request_problem_info || p->value.i8 > 1){ + property__free_all(properties); + return MOSQ_ERR_PROTOCOL; + } + have_request_problem_info = true; + }else if(p->identifier == PROP_REQUEST_RESPONSE_INFO){ + if(have_request_response_info || p->value.i8 > 1){ + property__free_all(properties); + return MOSQ_ERR_PROTOCOL; + } + have_request_response_info = true; + }else if(p->identifier == PROP_MAXIMUM_QOS){ + if(have_maximum_qos || p->value.i8 > 1){ + property__free_all(properties); + return MOSQ_ERR_PROTOCOL; + } + have_maximum_qos = true; + }else if(p->identifier == PROP_RETAIN_AVAILABLE){ + if(have_retain_available || p->value.i8 > 1){ + property__free_all(properties); + return MOSQ_ERR_PROTOCOL; + } + have_retain_available = true; + }else if(p->identifier == PROP_WILDCARD_SUB_AVAILABLE){ + if(have_wildcard_sub_available || p->value.i8 > 1){ + property__free_all(properties); + return MOSQ_ERR_PROTOCOL; + } + have_wildcard_sub_available = true; + }else if(p->identifier == PROP_SUBSCRIPTION_ID_AVAILABLE){ + if(have_subscription_id_available || p->value.i8 > 1){ + property__free_all(properties); + return MOSQ_ERR_PROTOCOL; + } + have_subscription_id_available = true; + }else if(p->identifier == PROP_SHARED_SUB_AVAILABLE){ + if(have_shared_sub_available || p->value.i8 > 1){ + property__free_all(properties); + return MOSQ_ERR_PROTOCOL; + } + have_shared_sub_available = true; + } + } - case PROP_RESPONSE_INFO: - rc = packet__read_string(packet, &str, &slen); - if(rc) return rc; - *len -= 2 - slen; /* uint16, string len */ - log__printf(NULL, MOSQ_LOG_DEBUG, "Response information: %s", str); - break; + return MOSQ_ERR_SUCCESS; +} - case PROP_SERVER_REFERENCE: - rc = packet__read_string(packet, &str, &slen); - if(rc) return rc; - *len -= 2 - slen; /* uint16, string len */ - log__printf(NULL, MOSQ_LOG_DEBUG, "Server reference: %s", str); - break; +void property__free(struct mqtt5__property **property) +{ + if(!property || !(*property)) return; + + switch((*property)->identifier){ + case PROP_CONTENT_TYPE: + case PROP_RESPONSE_TOPIC: + case PROP_CORRELATION_DATA: + case PROP_ASSIGNED_CLIENT_IDENTIFIER: + case PROP_AUTHENTICATION_METHOD: + case PROP_AUTHENTICATION_DATA: + case PROP_RESPONSE_INFO: + case PROP_SERVER_REFERENCE: case PROP_REASON_STRING: - rc = packet__read_string(packet, &str, &slen); - if(rc) return rc; - *len -= 2 - slen; /* uint16, string len */ - log__printf(NULL, MOSQ_LOG_DEBUG, "Reason string: %s", str); + mosquitto__free((*property)->value.s.v); break; - case PROP_RECEIVE_MAXIMUM: - rc = packet__read_uint16(packet, &uint16); - if(rc) return rc; - *len -= 2; /* uint16 */ - log__printf(NULL, MOSQ_LOG_DEBUG, "Receive maximum: %d", uint16); + case PROP_USER_PROPERTY: + mosquitto__free((*property)->name.v); + mosquitto__free((*property)->value.s.v); break; + case PROP_PAYLOAD_FORMAT_INDICATOR: + case PROP_MESSAGE_EXPIRY_INTERVAL: + case PROP_SUBSCRIPTION_IDENTIFIER: + case PROP_SESSION_EXPIRY_INTERVAL: + case PROP_SERVER_KEEP_ALIVE: + case PROP_REQUEST_PROBLEM_INFO: + case PROP_WILL_DELAY_INTERVAL: + case PROP_REQUEST_RESPONSE_INFO: + case PROP_RECEIVE_MAXIMUM: case PROP_TOPIC_ALIAS_MAXIMUM: - rc = packet__read_uint16(packet, &uint16); - if(rc) return rc; - *len -= 2; /* uint16 */ - log__printf(NULL, MOSQ_LOG_DEBUG, "Topic alias maximum: %d", uint16); - break; - case PROP_TOPIC_ALIAS: - rc = packet__read_uint16(packet, &uint16); - if(rc) return rc; - *len -= 2; /* uint16 */ - log__printf(NULL, MOSQ_LOG_DEBUG, "Topic alias: %d", uint16); - break; - case PROP_MAXIMUM_QOS: - rc = packet__read_byte(packet, &byte); - if(rc) return rc; - *len -= 1; /* byte */ - log__printf(NULL, MOSQ_LOG_DEBUG, "Maximum QoS: %d", byte); - break; - case PROP_RETAIN_AVAILABLE: - rc = packet__read_byte(packet, &byte); - if(rc) return rc; - *len -= 1; /* byte */ - log__printf(NULL, MOSQ_LOG_DEBUG, "Retain available: %d", byte); - break; - - case PROP_USER_PROPERTY: - rc = packet__read_string(packet, &str, &slen); - if(rc) return rc; - *len -= 2 - slen; /* uint16, string len */ - log__printf(NULL, MOSQ_LOG_DEBUG, "User property name: %s", str); - - rc = packet__read_string(packet, &str, &slen); - if(rc) return rc; - *len -= 2 - slen; /* uint16, string len */ - log__printf(NULL, MOSQ_LOG_DEBUG, "User property value: %s", str); - break; - case PROP_MAXIMUM_PACKET_SIZE: - rc = packet__read_uint32(packet, &uint32); - if(rc) return rc; - *len -= 4; /* uint32 */ - log__printf(NULL, MOSQ_LOG_DEBUG, "Maximum packet size: %d", uint32); - break; - case PROP_WILDCARD_SUB_AVAILABLE: - rc = packet__read_byte(packet, &byte); - if(rc) return rc; - *len -= 1; /* byte */ - log__printf(NULL, MOSQ_LOG_DEBUG, "Wildcard subscription available: %d", byte); - break; - case PROP_SUBSCRIPTION_ID_AVAILABLE: - rc = packet__read_byte(packet, &byte); - if(rc) return rc; - *len -= 1; /* byte */ - log__printf(NULL, MOSQ_LOG_DEBUG, "Subscription identifier available: %d", byte); - break; - case PROP_SHARED_SUB_AVAILABLE: - rc = packet__read_byte(packet, &byte); - if(rc) return rc; - *len -= 1; /* byte */ - log__printf(NULL, MOSQ_LOG_DEBUG, "Shared subscription available: %d", byte); + /* Nothing to free */ break; - - default: - log__printf(NULL, MOSQ_LOG_DEBUG, "Unsupported property type: %d", byte); - return 1; } - return MOSQ_ERR_SUCCESS; + free(*property); + *property = NULL; } -int property__read_all(struct mosquitto__packet *packet) +void property__free_all(struct mqtt5__property **property) { - int rc; - int32_t proplen; - - rc = packet__read_varint(packet, &proplen, NULL); - if(rc) return rc; + struct mqtt5__property *p, *next; - while(proplen > 0){ - rc = property__read(packet, &proplen); - if(rc) return rc; + p = *property; + while(p){ + next = p->next; + property__free(&p); + p = next; } - - return MOSQ_ERR_SUCCESS; + *property = NULL; } diff --git a/lib/property_mosq.h b/lib/property_mosq.h index 7fea9a76..6ce67d83 100644 --- a/lib/property_mosq.h +++ b/lib/property_mosq.h @@ -19,6 +19,28 @@ Contributors: #include "mosquitto_internal.h" #include "mosquitto.h" -int property__read_all(struct mosquitto__packet *packet); +struct mqtt__string { + char *v; + int len; +}; + +struct mqtt5__property { + struct mqtt5__property *next; + union { + uint8_t i8; + uint16_t i16; + uint32_t i32; + uint32_t varint; + struct mqtt__string bin; + struct mqtt__string s; + } value; + struct mqtt__string name; + int32_t identifier; +}; + + +int property__read_all(struct mosquitto__packet *packet, struct mqtt5__property **property); +void property__free(struct mqtt5__property **property); +void property__free_all(struct mqtt5__property **property); #endif diff --git a/src/handle_connect.c b/src/handle_connect.c index e2780daf..5b3e0b47 100644 --- a/src/handle_connect.c +++ b/src/handle_connect.c @@ -131,6 +131,7 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) struct mosquitto__subleaf *leaf; int i; struct mosquitto__security_options *security_opts; + struct mqtt5__property *properties; #ifdef WITH_TLS X509 *client_cert = NULL; X509_NAME *name; @@ -241,8 +242,9 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) } if(protocol_version == PROTOCOL_VERSION_v5){ - rc = property__read_all(&context->in_packet); + rc = property__read_all(&context->in_packet, &properties); if(rc) return rc; + property__free_all(&properties); } if(packet__read_string(&context->in_packet, &client_id, &slen)){ diff --git a/src/handle_publish.c b/src/handle_publish.c index e9aa7016..2b03ef68 100644 --- a/src/handle_publish.c +++ b/src/handle_publish.c @@ -45,6 +45,8 @@ int handle__publish(struct mosquitto_db *db, struct mosquitto *context) int len; int slen; char *topic_mount; + struct mqtt5__property *properties; + #ifdef WITH_BRIDGE char *topic_temp; int i; @@ -132,8 +134,9 @@ int handle__publish(struct mosquitto_db *db, struct mosquitto *context) } if(context->protocol == mosq_p_mqtt5){ - rc = property__read_all(&context->in_packet); + rc = property__read_all(&context->in_packet, &properties); if(rc) return rc; + property__free_all(&properties); } payloadlen = context->in_packet.remaining_length - context->in_packet.pos; diff --git a/test/unit/Makefile b/test/unit/Makefile index 038c5a23..95249076 100644 --- a/test/unit/Makefile +++ b/test/unit/Makefile @@ -5,9 +5,21 @@ include ../../config.mk CFLAGS=-I../.. -I../../lib -coverage -Wall -ggdb TEST_LDFLAGS=-lcunit -coverage +TEST_OBJS = test.o \ + datatype_read.o \ + datatype_write.o \ + property_read.o \ + stubs.o \ + utf8.o + +LIB_OBJS = memory_mosq.o \ + packet_datatypes.o \ + property_mosq.o \ + utf8_mosq.o + all : test -mosq_test : test.o datatype_read.o datatype_write.o utf8.o memory_mosq.o packet_datatypes.o utf8_mosq.o +mosq_test : ${TEST_OBJS} ${LIB_OBJS} $(CROSS_COMPILE)$(CC) -o $@ $^ ${TEST_LDFLAGS} memory_mosq.o : ../../lib/memory_mosq.c @@ -16,6 +28,9 @@ memory_mosq.o : ../../lib/memory_mosq.c packet_datatypes.o : ../../lib/packet_datatypes.c $(CROSS_COMPILE)$(CC) $(CFLAGS) -c -o $@ $^ +property_mosq.o : ../../lib/property_mosq.c + $(CROSS_COMPILE)$(CC) $(CFLAGS) -c -o $@ $^ + utf8_mosq.o : ../../lib/utf8_mosq.c $(CROSS_COMPILE)$(CC) $(CFLAGS) -c -o $@ $^ diff --git a/test/unit/datatype_read.c b/test/unit/datatype_read.c index 76978050..f0234bf4 100644 --- a/test/unit/datatype_read.c +++ b/test/unit/datatype_read.c @@ -97,7 +97,7 @@ static void binary_read_helper( memset(&packet, 0, sizeof(struct mosquitto__packet)); packet.payload = payload; packet.remaining_length = remaining_length; - rc = packet__read_binary(&packet, (void **)&value, &length); + rc = packet__read_binary(&packet, (uint8_t **)&value, &length); CU_ASSERT_EQUAL(rc, rc_expected); if(value_expected){ /* FIXME - this should be a memcmp */ diff --git a/test/unit/datatype_write.c b/test/unit/datatype_write.c index 7287168e..9fb9a5b0 100644 --- a/test/unit/datatype_write.c +++ b/test/unit/datatype_write.c @@ -104,7 +104,6 @@ static void TEST_string_write(void) { uint8_t payload[100]; struct mosquitto__packet packet; - int i; memset(&packet, 0, sizeof(struct mosquitto__packet)); memset(payload, 0, 100); diff --git a/test/unit/property_read.c b/test/unit/property_read.c new file mode 100644 index 00000000..bad77857 --- /dev/null +++ b/test/unit/property_read.c @@ -0,0 +1,390 @@ +#include +#include + +#include "mqtt_protocol.h" +#include "property_mosq.h" +#include "packet_mosq.h" + +static void byte_prop_read_helper( + uint8_t *payload, + int remaining_length, + int rc_expected, + int identifier, + uint8_t value_expected) +{ + struct mosquitto__packet packet; + struct mqtt5__property *properties; + int rc; + + memset(&packet, 0, sizeof(struct mosquitto__packet)); + packet.payload = payload; + packet.remaining_length = remaining_length; + rc = property__read_all(&packet, &properties); + + CU_ASSERT_EQUAL(rc, rc_expected); + CU_ASSERT_EQUAL(packet.pos, remaining_length); + if(properties){ + CU_ASSERT_EQUAL(properties->identifier, identifier); + CU_ASSERT_EQUAL(properties->value.i8, value_expected); + CU_ASSERT_PTR_EQUAL(properties->next, NULL); + property__free_all(&properties); + } + CU_ASSERT_PTR_EQUAL(properties, NULL); +} + +static void duplicate_byte_helper(int identifier) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 4; /* Proplen = (Identifier + byte)*2 */ + payload[1] = identifier; + payload[2] = 1; + payload[3] = identifier; + payload[4] = 0; + + byte_prop_read_helper(payload, 5, MOSQ_ERR_PROTOCOL, identifier, 1); +} + +static void bad_byte_helper(int identifier) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 2; /* Proplen = Identifier + byte */ + payload[1] = identifier; + payload[2] = 2; /* 0, 1 are only valid values */ + + byte_prop_read_helper(payload, 3, MOSQ_ERR_PROTOCOL, identifier, 0); +} + + +/* ======================================================================== + * NO PROPERTIES + * ======================================================================== */ + +static void TEST_no_properties(void) +{ + struct mosquitto__packet packet; + struct mqtt5__property *properties = NULL; + uint8_t payload[5]; + int rc; + + memset(&packet, 0, sizeof(struct mosquitto__packet)); + memset(payload, 0, sizeof(payload)); + packet.payload = payload; + packet.remaining_length = 1; + rc = property__read_all(&packet, &properties); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_EQUAL(properties, NULL); + CU_ASSERT_EQUAL(packet.pos, 1); +} + +static void TEST_truncated(void) +{ + struct mosquitto__packet packet; + struct mqtt5__property *properties = NULL; + uint8_t payload[5]; + int rc; + + /* Zero length packet */ + memset(&packet, 0, sizeof(struct mosquitto__packet)); + memset(payload, 0, sizeof(payload)); + packet.payload = payload; + packet.remaining_length = 0; + rc = property__read_all(&packet, &properties); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_PROTOCOL); + CU_ASSERT_PTR_EQUAL(properties, NULL); + CU_ASSERT_EQUAL(packet.pos, 0); + + /* Proplen > 0 but not enough data */ + memset(&packet, 0, sizeof(struct mosquitto__packet)); + memset(payload, 0, sizeof(payload)); + payload[0] = 2; + packet.payload = payload; + packet.remaining_length = 1; + rc = property__read_all(&packet, &properties); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_PROTOCOL); + CU_ASSERT_PTR_EQUAL(properties, NULL); + CU_ASSERT_EQUAL(packet.pos, 1); + + /* Proplen > 0 but not enough data */ + memset(&packet, 0, sizeof(struct mosquitto__packet)); + memset(payload, 0, sizeof(payload)); + payload[0] = 4; + payload[1] = PROP_PAYLOAD_FORMAT_INDICATOR; + packet.payload = payload; + packet.remaining_length = 2; + rc = property__read_all(&packet, &properties); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_PROTOCOL); + CU_ASSERT_PTR_EQUAL(properties, NULL); + CU_ASSERT_EQUAL(packet.pos, 2); +} + +/* ======================================================================== + * INVALID PROPERTY ID + * ======================================================================== */ + +static void TEST_invalid_property_id(void) +{ + struct mosquitto__packet packet; + struct mqtt5__property *properties = NULL; + uint8_t payload[5]; + int rc; + + /* ID = 0 */ + memset(&packet, 0, sizeof(struct mosquitto__packet)); + memset(payload, 0, sizeof(payload)); + payload[0] = 4; + packet.payload = payload; + packet.remaining_length = 2; + rc = property__read_all(&packet, &properties); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_MALFORMED_PACKET); + CU_ASSERT_PTR_EQUAL(properties, NULL); + CU_ASSERT_EQUAL(packet.pos, 2); + + /* ID = 4 */ + memset(&packet, 0, sizeof(struct mosquitto__packet)); + memset(payload, 0, sizeof(payload)); + payload[0] = 4; + payload[1] = 4; + packet.payload = payload; + packet.remaining_length = 2; + rc = property__read_all(&packet, &properties); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_MALFORMED_PACKET); + CU_ASSERT_PTR_EQUAL(properties, NULL); + CU_ASSERT_EQUAL(packet.pos, 2); +} + +/* ======================================================================== + * SINGLE PROPERTIES + * ======================================================================== */ + +static void TEST_single_payload_format_indicator(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 2; /* Proplen = Identifier + byte */ + payload[1] = PROP_PAYLOAD_FORMAT_INDICATOR; + payload[2] = 1; + + byte_prop_read_helper(payload, 3, MOSQ_ERR_SUCCESS, PROP_PAYLOAD_FORMAT_INDICATOR, 1); +} + +static void TEST_single_request_problem_information(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 2; /* Proplen = Identifier + byte */ + payload[1] = PROP_REQUEST_PROBLEM_INFO; + payload[2] = 1; + + byte_prop_read_helper(payload, 3, MOSQ_ERR_SUCCESS, PROP_REQUEST_PROBLEM_INFO, 1); +} + +static void TEST_single_request_response_information(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 2; /* Proplen = Identifier + byte */ + payload[1] = PROP_REQUEST_RESPONSE_INFO; + payload[2] = 1; + + byte_prop_read_helper(payload, 3, MOSQ_ERR_SUCCESS, PROP_REQUEST_RESPONSE_INFO, 1); +} + +static void TEST_single_maximum_qos(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 2; /* Proplen = Identifier + byte */ + payload[1] = PROP_MAXIMUM_QOS; + payload[2] = 1; + + byte_prop_read_helper(payload, 3, MOSQ_ERR_SUCCESS, PROP_MAXIMUM_QOS, 1); +} + +static void TEST_single_retain_available(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 2; /* Proplen = Identifier + byte */ + payload[1] = PROP_RETAIN_AVAILABLE; + payload[2] = 1; + + byte_prop_read_helper(payload, 3, MOSQ_ERR_SUCCESS, PROP_RETAIN_AVAILABLE, 1); +} + +static void TEST_single_wildcard_subscription_available(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 2; /* Proplen = Identifier + byte */ + payload[1] = PROP_WILDCARD_SUB_AVAILABLE; + payload[2] = 0; + + byte_prop_read_helper(payload, 3, MOSQ_ERR_SUCCESS, PROP_WILDCARD_SUB_AVAILABLE, 0); +} + +static void TEST_single_subscription_identifier_available(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 2; /* Proplen = Identifier + byte */ + payload[1] = PROP_SUBSCRIPTION_ID_AVAILABLE; + payload[2] = 0; + + byte_prop_read_helper(payload, 3, MOSQ_ERR_SUCCESS, PROP_SUBSCRIPTION_ID_AVAILABLE, 0); +} + +static void TEST_single_shared_subscription_available(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 2; /* Proplen = Identifier + byte */ + payload[1] = PROP_SHARED_SUB_AVAILABLE; + payload[2] = 1; + + byte_prop_read_helper(payload, 3, MOSQ_ERR_SUCCESS, PROP_SHARED_SUB_AVAILABLE, 1); +} + +/* ======================================================================== + * DUPLICATE PROPERTIES + * ======================================================================== */ + +static void TEST_duplicate_payload_format_indicator(void) +{ + duplicate_byte_helper(PROP_PAYLOAD_FORMAT_INDICATOR); +} + +static void TEST_duplicate_request_problem_information(void) +{ + duplicate_byte_helper(PROP_REQUEST_PROBLEM_INFO); +} + +static void TEST_duplicate_request_response_information(void) +{ + duplicate_byte_helper(PROP_REQUEST_RESPONSE_INFO); +} + +static void TEST_duplicate_maximum_qos(void) +{ + duplicate_byte_helper(PROP_MAXIMUM_QOS); +} + +static void TEST_duplicate_retain_available(void) +{ + duplicate_byte_helper(PROP_RETAIN_AVAILABLE); +} + +static void TEST_duplicate_wildcard_subscription_available(void) +{ + duplicate_byte_helper(PROP_WILDCARD_SUB_AVAILABLE); +} + +static void TEST_duplicate_subscription_identifier_available(void) +{ + duplicate_byte_helper(PROP_SUBSCRIPTION_ID_AVAILABLE); +} + +static void TEST_duplicate_shared_subscription_available(void) +{ + duplicate_byte_helper(PROP_SHARED_SUB_AVAILABLE); +} + +/* ======================================================================== + * BAD PROPERTY VALUES + * ======================================================================== */ + +static void TEST_bad_request_problem_information(void) +{ + bad_byte_helper(PROP_REQUEST_PROBLEM_INFO); +} + +static void TEST_bad_request_response_information(void) +{ + bad_byte_helper(PROP_REQUEST_RESPONSE_INFO); +} + +static void TEST_bad_maximum_qos(void) +{ + bad_byte_helper(PROP_MAXIMUM_QOS); +} + +static void TEST_bad_retain_available(void) +{ + bad_byte_helper(PROP_RETAIN_AVAILABLE); +} + +static void TEST_bad_wildcard_sub_available(void) +{ + bad_byte_helper(PROP_WILDCARD_SUB_AVAILABLE); +} + +static void TEST_bad_subscription_id_available(void) +{ + bad_byte_helper(PROP_SUBSCRIPTION_ID_AVAILABLE); +} + +static void TEST_bad_shared_sub_available(void) +{ + bad_byte_helper(PROP_SHARED_SUB_AVAILABLE); +} + +/* ======================================================================== + * TEST SUITE SETUP + * ======================================================================== */ + +int init_property_read_tests(void) +{ + CU_pSuite test_suite = NULL; + + test_suite = CU_add_suite("Property read", NULL, NULL); + if(!test_suite){ + printf("Error adding CUnit Property read test suite.\n"); + return 1; + } + + if(0 + || !CU_add_test(test_suite, "Truncated packet", TEST_truncated) + || !CU_add_test(test_suite, "Invalid property ID", TEST_invalid_property_id) + || !CU_add_test(test_suite, "No properties", TEST_no_properties) + || !CU_add_test(test_suite, "Single Payload Format Indicator", TEST_single_payload_format_indicator) + || !CU_add_test(test_suite, "Single Request Problem Information", TEST_single_request_problem_information) + || !CU_add_test(test_suite, "Single Request Response Information", TEST_single_request_response_information) + || !CU_add_test(test_suite, "Single Maximum QoS", TEST_single_maximum_qos) + || !CU_add_test(test_suite, "Single Retain Available", TEST_single_retain_available) + || !CU_add_test(test_suite, "Single Wildcard Subscription Available", TEST_single_wildcard_subscription_available) + || !CU_add_test(test_suite, "Single Subscription Identifier Available", TEST_single_subscription_identifier_available) + || !CU_add_test(test_suite, "Single Shared Subscription Available", TEST_single_shared_subscription_available) + || !CU_add_test(test_suite, "Duplicate Payload Format Indicator", TEST_duplicate_payload_format_indicator) + || !CU_add_test(test_suite, "Duplicate Request Problem Information", TEST_duplicate_request_problem_information) + || !CU_add_test(test_suite, "Duplicate Request Response Information", TEST_duplicate_request_response_information) + || !CU_add_test(test_suite, "Duplicate Maximum QoS", TEST_duplicate_maximum_qos) + || !CU_add_test(test_suite, "Duplicate Retain Available", TEST_duplicate_retain_available) + || !CU_add_test(test_suite, "Duplicate Wildcard Subscription Available", TEST_duplicate_wildcard_subscription_available) + || !CU_add_test(test_suite, "Duplicate Subscription Identifier Available", TEST_duplicate_subscription_identifier_available) + || !CU_add_test(test_suite, "Duplicate Shared Subscription Available", TEST_duplicate_shared_subscription_available) + || !CU_add_test(test_suite, "Bad Request Problem Information", TEST_bad_request_problem_information) + || !CU_add_test(test_suite, "Bad Request Response Information", TEST_bad_request_response_information) + || !CU_add_test(test_suite, "Bad Maximum QoS", TEST_bad_maximum_qos) + || !CU_add_test(test_suite, "Bad Retain Available", TEST_bad_retain_available) + || !CU_add_test(test_suite, "Bad Wildcard Subscription Available", TEST_bad_wildcard_sub_available) + || !CU_add_test(test_suite, "Bad Subscription Identifier Available", TEST_bad_subscription_id_available) + || !CU_add_test(test_suite, "Bad Shared Subscription Available", TEST_bad_shared_sub_available) + ){ + + printf("Error adding Property read CUnit tests.\n"); + return 1; + } + + return 0; +} diff --git a/test/unit/stubs.c b/test/unit/stubs.c new file mode 100644 index 00000000..f4b7c97a --- /dev/null +++ b/test/unit/stubs.c @@ -0,0 +1,6 @@ +#include + +int log__printf(struct mosquitto *mosq, int priority, const char *fmt, ...) +{ + return 0; +} diff --git a/test/unit/test.c b/test/unit/test.c index e89fa4d2..dea045a4 100644 --- a/test/unit/test.c +++ b/test/unit/test.c @@ -5,6 +5,7 @@ int init_datatype_read_tests(void); int init_datatype_write_tests(void); +int init_property_read_tests(void); int init_utf8_tests(void); int main(int argc, char *argv[]) @@ -19,6 +20,7 @@ int main(int argc, char *argv[]) || init_utf8_tests() || init_datatype_read_tests() || init_datatype_write_tests() + || init_property_read_tests() ){ CU_cleanup_registry();